3分钟,给我实现一个导出 PDF 功能

124 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情


哈喽,大家好,我是一条。

之前写过如何导入导出 excel ,在实际开发中,还会出现导出为 pdf 的需求,实现起来也很简单,今天就和大家聊一下实现步骤。

基于 spring boot 工程,过于基础的部分不做讲解,只说和导出 pdf 相关的部分。

pom

				<!--   核心导出类     -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.13</version>
        </dependency>
        <!--   去掉会无法加载中文字体     -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

代码

接口

@GetMapping("/export/pdf")
    public void exportPdf(HttpServletResponse response) {
        // 持久层查询全量数据
        List<Article> articleList = articleService.getAllArticle();

        OutputStream outputStream = null;
        try {
            outputStream = new BufferedOutputStream(response.getOutputStream());
            // 生成pdf文件核心方法
            articleService.generatePdf(articleList, outputStream);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

核心方法

其原理大概是和制作表格一样,一行一行的去渲染,并不是限制做好模板在替换的方式。

从表头到表格,每行每个部分所占的列数,都规定好,程序按照我们设定好的步骤将表格画出来。

public void generatePdf(List<Article> articles, OutputStream outputStream) {
        Document document = new Document();
        try {
            PdfWriter instance = PdfWriter.getInstance(document, outputStream);
            document.open();

            // 文字水印
            // document.newPage();
            // instance.setPageEvent(new TextWaterMark("一条coding"));

            // 图片水印
//            Resource resource = new ClassPathResource(pdfConfigProperties.getImage().getFile());
//            instance.setPageEvent(new ImageWaterMark(resource.getURL().getPath()));

            // 解决中文不显示问题
            BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

            // 标题加粗
            Font fontChina18 = new Font(bfChinese, 18, Font.BOLD);
            Font fontChina15 = new Font(bfChinese, 15);
            Font fontChina10 = new Font(bfChinese, 10);

            // 获取pdf头文件信息
            Map<String, String> headInfo = new HashMap<>();
            headInfo.put("firstHeadInfo", "文章列表");
            // 空格
            Paragraph blank1 = new Paragraph(" ", new Font(bfChinese, 5));

            Paragraph firstTitle = new Paragraph(headInfo.get("firstHeadInfo"), fontChina18);
            firstTitle.setAlignment(Element.ALIGN_CENTER);// 居中
            document.add(firstTitle);

            // 添加空格
            document.add(blank1);

            // 3 创建表格
            PdfPTable table = new PdfPTable(7);// 表格总共几列
            table.setWidthPercentage(100);// 表格宽度为100%

            PdfUtil.addTableCell(table, "名称", fontChina15, false, false, 0, 0);
            PdfUtil.addTableCell(table, "目录", fontChina15, false, false, 0, 0);
            PdfUtil.addTableCell(table, "观看", fontChina15, false, false, 0, 0);
            PdfUtil.addTableCell(table, "喜欢", fontChina15, false, false, 0, 0);
            PdfUtil.addTableCell(table, "标签", fontChina15, false, false, 0, 0);
            // 这里跨两列,需要先把第一个 false 改成 true ,再设置具体跨几列
            PdfUtil.addTableCell(table, "摘要", fontChina15, true, false, 2, 0);


            articles.forEach(article -> {
                PdfUtil.addTableCell(table, article.getName(), fontChina10, true, false, 0, 0);
                PdfUtil.addTableCell(table, article.getCategoryId().toString(), fontChina10, true, false, 0, 0);
                PdfUtil.addTableCell(table, article.getViewCount().toString(), fontChina10, true, false, 0, 0);
                PdfUtil.addTableCell(table, article.getLikeCount().toString(), fontChina10, true, false, 0, 0);
                PdfUtil.addTableCell(table, article.getLabel(), fontChina10, true, false, 0, 0);
                PdfUtil.addTableCell(table, article.getDescription(), fontChina10, true, false, 2, 0);
            });

            document.add(table);
        } catch (DocumentException | IOException e) {
            e.printStackTrace();
        } finally {
            document.close();
        }
    }

效果图

文字水印

相当于一个工具类,传入水印的具体的文字即可。

也可以在配置文件设定好是否开启水印和水印文字,达到动态配置。

package com.yitiao.util;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import lombok.RequiredArgsConstructor;

import java.io.IOException;

@RequiredArgsConstructor
public class TextWaterMark extends PdfPageEventHelper {
    private final String waterMarkText;

    public void onEndPage(PdfWriter writer, Document document) {
        try {
            float pageWidth = document.right() + document.left();//获取pdf内容正文页面宽度
            float pageHeight = document.top() + document.bottom();//获取pdf内容正文页面高度
            //设置水印字体格式
            BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            Font waterMarkFont = new Font(base, 20, Font.BOLD, BaseColor.LIGHT_GRAY);
            PdfContentByte waterMarkPdfContent = writer.getDirectContentUnder();
            Phrase phrase = new Phrase(waterMarkText, waterMarkFont);
            //两行三列
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.25f, pageHeight * 0.2f, 45);
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.25f, pageHeight * 0.5f, 45);
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.25f, pageHeight * 0.8f, 45);
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.65f, pageHeight * 0.2f, 45);
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.65f, pageHeight * 0.5f, 45);
            ColumnText.showTextAligned(waterMarkPdfContent, Element.ALIGN_CENTER, phrase,
                    pageWidth * 0.65f, pageHeight * 0.8f, 45);
        } catch (DocumentException | IOException de) {
            de.printStackTrace();
        }
    }

}

水印效果

最后

参考文章