spring boot:用itextpdf处理pdf表格文件(spring boot 2.3.2)

一,什么是itextpdf?

1,itextpdf的用途

itextpdf是用来生成PDF文档的一个java类库,

通过iText可以生成PDF文档,

还可以把XML/Html文件转化为PDF文件

2,官方网站:

https://itextpdf.com/en

3,itextpdf使用中的几个问题:

使用中文字体

插入表格

插入图片时设置图片宽度

浏览器直接显示pdf

说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest

         对应的源码可以访问这里获取: https://github.com/liuhongdi/

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,演示项目的相关信息

1,代码地址:

https://github.com/liuhongdi/exportpdf

2,功能说明:

     直接显示pdf

     把数据保存成pdf文件

     pdf文件下载

3,项目结构:如图:

三,配置文件说明

1,pom.xml

        <!--pdf begin-->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.13.1</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <!--pdf   end-->

        <!--mybatis begin-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--mybatis end-->

        <!--mysql begin-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--mysql end-->

说明:要引入itextpdf

2,把自己要使用的字体文件,复制到

    resources/font目录下供访问

3,数据表建表sql

CREATE TABLE `goods` (
 `goodsId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
 `goodsName` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'name',
 `subject` varchar(200) NOT NULL DEFAULT '' COMMENT '标题',
 `price` decimal(15,2) NOT NULL DEFAULT '0.00' COMMENT '价格',
 `stock` int(11) NOT NULL DEFAULT '0' COMMENT 'stock',
 PRIMARY KEY (`goodsId`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表'

四,java代码说明

1,AbstractITextPdfView.java

/**
新建一个pdfview,主要是为了避免AbstractPdfView中使用的pdf库太旧的问题
 AbstractPdfView只支持到 com.lowagie.itext的2.1.7版本,
 版本太旧,文档也缺少
 修改后可以支持itextpdf库的类,
 新增AbstractITextPdfView后此问题完美解决
 by liuhongdi
*/
public abstract class AbstractITextPdfView extends AbstractView {
    public AbstractITextPdfView() {
        setContentType("application/pdf");
    }

    @Override
    protected boolean generatesDownloadContent() {
        return true;
    }

    @Override
    protected final void renderMergedOutputModel(Map<String, Object> model,
                                                 HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // 获得流
        ByteArrayOutputStream baos = createTemporaryOutputStream();
        Document document = newDocument();
        PdfWriter writer = newWriter(document, baos);
        prepareWriter(model, writer, request);
        buildPdfMetadata(model, document, request);
        buildPdfDocument(model, document, writer, request, response);
        writeToResponse(response, baos);
    }

    protected Document newDocument() {
        return new Document(PageSize.A4);
    }

    protected PdfWriter newWriter(Document document, OutputStream os)
            throws DocumentException {
        return PdfWriter.getInstance(document, os);
    }

    protected void prepareWriter(Map<String, Object> model, PdfWriter writer,
                                 HttpServletRequest request) throws DocumentException {

        writer.setViewerPreferences(getViewerPreferences());
    }

    protected int getViewerPreferences() {
        return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage;
    }

    protected void buildPdfMetadata(Map<String, Object> model,
                                    Document document, HttpServletRequest request) {
    }

    protected abstract void buildPdfDocument(Map<String, Object> model,
                                             Document document, PdfWriter writer, HttpServletRequest request,
                                             HttpServletResponse response) throws Exception;
}

说明:如果在浏览器的页面上直接显示pdf,而不是下载文件后再打开,

         则需要使用AbstractPdfView,但spring boot默认支持的itext库代码太旧,

        注释中已做了说明,所以我们另外自己定义一个

2, ViewPdfUtil.java

public class ViewPdfUtil extends AbstractITextPdfView {

    //文件名
    private String fileName;
    public String getFileName() {
        return this.fileName;
    }
    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    //指定一个类型,方便知道调用哪个类处理
    private String pdfType;
    public String getPdfType() {
        return this.pdfType;
    }
    public void setPdfType(String pdfType) {
        this.pdfType = pdfType;
    }

    //生成pdf的document并显示出来
    @Override
    protected void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition","filename=" + URLEncoder.encode(this.fileName, "UTF-8"));
        List<Goods> products = (List<Goods>) model.get("sheet");
        if (this.pdfType.equals("goods")) {
            PdfTableService pdfTableService = new PdfTableServiceImpl();
            //不保存成文件,直接显示,所以不指定保存路径
            pdfTableService.createPDF(document, products,"");
        }
    }
}

说明:主要是实现buildPdfDocument方法,供ModelAndView调用时直接显示到浏览器页面

3,PdfTableServiceImpl.java

@Service
public class PdfTableServiceImpl implements PdfTableService {

    //创建pdf文件,
    // savePath是保存路径,如果是空字串,则直接输出到document
    //document:pdf内容
    //goods:写入到pdf表格中的数据
    @Override
    public void createPDF(Document document, List<Goods> goods,String savePath)  {
        try {
            if (!savePath.equals("")) {
                PdfWriter.getInstance(document, new FileOutputStream(savePath));
            }
            document.addTitle("商品库存统计表");
            document.addAuthor("老刘");
            document.addSubject("2020年商品库存统计");
            document.addKeywords("商品库存");
            document.open();
            Paragraph para = getParagraphText("整个白酒行业从2012年开始,都迅速下滑,销量和利润都是大跌。2014年和2015年,茅台的股价涨得不错,但也没有超过同期的白马股太多,加上利润增速一直没有恢复塑化剂之前的状态,我就一直没有再买入");
            document.add(para);
            String imagePath = "/data/springboot2/logo.jpg";      // 图片的绝对路径
            Image image = Image.getInstance(imagePath);       // 取得图片对象
            //计算得到目标宽高
            File gifFile = new File(imagePath);
            int origWidth = 0;
            int origHeight = 0;
            try {
                BufferedImage imageBuffer = ImageIO.read(gifFile);
                if (imageBuffer != null) {//如果image=null 表示上传的不是图片格式
                    origWidth = imageBuffer.getWidth();
                    origHeight = imageBuffer.getHeight();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            System.out.println(""+document.getPageSize().getWidth());
            System.out.println("margin:"+document.leftMargin());
            //得到新的高度和新的宽度
            float newwidth = document.getPageSize().getWidth()-document.leftMargin()-document.rightMargin();
            float newHeight = (newwidth*origHeight) / origWidth;

            image.scaleAbsolute(newwidth, newHeight);

            document.add(image);

            PdfPTable table = createTable(goods);
            document.add(table);

        } catch ( IOException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        } finally {
            if (document.isOpen()) {
                document.close();
            }
        }
    }

    //从text得到可以添加到document的Paragraph
    public static Paragraph getParagraphText(String text)  {

        try {
            Font font = new Font(BaseFont.createFont(new ClassPathResource("/font/FZLTHK.TTF").getPath(),BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED));
            font.setColor(BaseColor.GRAY);

            Paragraph para = new Paragraph(text,font);
            return para;
        } catch ( IOException | DocumentException e) {
            e.printStackTrace();
            return null;
        }
    }

    //创建PdfTable
    public static PdfPTable createTable(List<Goods> products) throws IOException, DocumentException {
        PdfPTable table = new PdfPTable(4);//生成一个4列的表格

        int widths[] = { 10,40,40,10 };//指定各列的宽度百分比
        table.setWidthPercentage(100);
        table.setSpacingBefore(10);
        table.setWidths(widths);

        PdfPCell cell;
        int size = 20;

        Font font = new Font(BaseFont.createFont(new ClassPathResource("/font/FZLTHK.TTF").getPath(),BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED));
        font.setColor(BaseColor.BLACK);

        Font font_head = new Font(BaseFont.createFont(new ClassPathResource("/font/FZLTHK.TTF").getPath(),BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED));
        font_head.setColor(BaseColor.BLUE);

        Font font_title = new Font(BaseFont.createFont(new ClassPathResource("/font/FZLTHK.TTF").getPath(),BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED));
        font_title.setColor(BaseColor.GREEN);
        font_title.setSize(36);

        cell = new PdfPCell(new Phrase("商品库存信息表",font_title));
        cell.setColspan(4);//设置所占列数
        cell.setFixedHeight(50);//设置高度
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
        table.addCell(cell);

        cell = new PdfPCell(new Phrase("ID",font_head));//商品编号
        cell.setFixedHeight(size);
        table.addCell(cell);
        cell = new PdfPCell(new Phrase("商品名称",font_head));//商品名称
        cell.setFixedHeight(size);
        table.addCell(cell);
        cell = new PdfPCell(new Phrase("描述",font_head));//描述
        cell.setFixedHeight(size);
        table.addCell(cell);
        cell = new PdfPCell(new Phrase("价格",font_head));//商品价格
        cell.setFixedHeight(size);
        table.addCell(cell);

        for(int i = 0;i<products.size();i++) {
            cell = new PdfPCell(new Phrase(String.valueOf(products.get(i).getGoodsId()),font));//商品编号
            cell.setFixedHeight(size);
            table.addCell(cell);
            cell = new PdfPCell(new Phrase(products.get(i).getGoodsName(),font));//商品名称
            cell.setFixedHeight(size);
            table.addCell(cell);
            cell = new PdfPCell(new Phrase(products.get(i).getSubject(),font));//描述
            cell.setFixedHeight(size);
            table.addCell(cell);
            cell = new PdfPCell(new Phrase(products.get(i).getPrice()+"",font));//商品价格
            cell.setFixedHeight(size);
            table.addCell(cell);
        }
        return table;
    }

}

用途:把数据添加到pdf的 document,注意对中文字体的引用

         另外注意插入图片时得到默认宽度的计算,需要减掉两侧的margin

4,HomeController.java

@RestController
@RequestMapping("/home")
public class HomeController {

    @Resource
    private GoodsMapper goodsMapper;

    @Resource
    PdfTableService pdfTableService;

    //把数据保存到pdf文件
    @GetMapping("/savepdf")
    public String savepdf() {
        List<Goods> goodsList = goodsMapper.selectAllGoods();
        String savePath = "/data/springboot2/goodslist.pdf";
        pdfTableService.createPDF(new Document(PageSize.A4), goodsList,savePath);
        return "pdf saveed";
    }

    //从浏览器直接显示pdf
    @GetMapping("/viewpdf")
    public ModelAndView viewpdf() {
        List<Goods> goodsList = goodsMapper.selectAllGoods();
        Map<String, Object> model = new HashMap<>();
        model.put("sheet", goodsList);
        ViewPdfUtil viewPdf = new ViewPdfUtil();
        viewPdf.setFileName("测试.pdf");
        viewPdf.setPdfType("goods");
        return new ModelAndView(viewPdf, model);
    }

    //下载pdf文件
    @GetMapping("/downpdf")
    public void downpdf() {
          String filepath = "/data/springboot2/goodslist.pdf";
          PdfUtil.downPdfFile(filepath);
     }
}

三个功能:直接显示,保存成文件,下载

五,效果测试

1,直接显示:

访问:

http://127.0.0.1:8080/home/viewpdf

如图:

2,直接保存成pdf文件:

访问:

http://127.0.0.1:8080/home/savepdf

效果如图:

3,下载pdf文件:

访问:

http://127.0.0.1:8080/home/downpdf

六,查看spring boot的版本:

  .   ____          _            __ _ _
 /\ / ___'_ __ _ _(_)_ __  __ _    
( ( )\___ | '_ | '_| | '_ / _` |    
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.2.RELEASE)
原文地址:https://www.cnblogs.com/architectforest/p/13398442.html