Spring Boot中集成IText模版导出PDF

一、准备工作

1. POM依赖

Spring Boot这里是用的2.3.12这个版本,比较老,但是别的版本应该也差不多

<properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.12.RELEASE</spring-boot.version>
    </properties>

紧接着放入IText依赖,我是随便放了一个稳定版本,别的版本应该也可以,这个真没有过多的研究,感觉应该差不多,需要使用中文字体的话,导入itext-asian,一般应该是都需要的。

<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>

2. 准备字体

字体的话是比较方便的,如果是windows电脑的话,自己电脑里面就有,直接去C:\Windows\Fonts里面就有自己电脑上的字体,一般采用的宋体,这个就看自己的需要了,不过操作的时候可要小心了,别直接裁剪出去了
在这里插入图片描述
找到自己需要使用的字体,随后复制到自己项目的resource目录下,我这里用的是仿宋,不过这个字体有一个坑,下面和大家说
在这里插入图片描述

3. 绘制模版

绘制采用Adobe全家桶中的Adobe Acrobat DC,这个工具的主要的功能就是处理PDF,下面给大家提供个老版本的工具,因为只是简单的绘制模版,其次电脑配置不高,就整个简单的画下算了,百度网盘链接:

【Win DC】Acrobat DC CC2019(右键管理员打开setup.exe安装前退出杀毒).rar
链接: https://pan.baidu.com/s/1a_B-E_dhgdFDaJzioEakUw 提取码: vbe3

打开自己word绘制一个表格模版;
在这里插入图片描述
这里就简单画画算了
然后直接保存为PDF格式
在这里插入图片描述
随后打开PDF处理工具 Adobe Acrobat DC,点击文件,点击创建,创建一个表单
在这里插入图片描述
创建表单之后选择文档
在这里插入图片描述
选择PDF之后开始绘制模版
在这里插入图片描述
双击表单域,之后设置名字,设置字体、
在这里插入图片描述
设置好之后,点击保存,保存PDF模版
在这里插入图片描述

准备工作结束,接下来进入正题!!!

二、单个模版导出PDF

上代码:首先是到处单个模版的,这个比较简单,直接渲染就可以
测试类中的代码,如果要是导出放在某个位置,可以直接借鉴这个代码

/**
     * 导出PDF单个
     */
    @Test
    void exportPdfByTemplate() {
        // 模板文件路径
        String inputFileName = "D:\\Code\\pdf-handle\\src\\main\\resources\\pdf\\template.pdf";
        // 生成的文件路径
        String outputFileName = "D:\\Code\\pdf-handle\\src\\main\\resources\\pdf\\result.pdf";

        OutputStream os = null;
        PdfStamper ps = null;
        PdfReader reader = null;
        PdfStamper stamper = null;

        try {
            os = Files.newOutputStream(new File(outputFileName).toPath());
            // 1. 读入PDF表单
            reader = new PdfReader(inputFileName);
            // 2. 根据表单生成一个新的PDF
            ps = new PdfStamper(reader, os);
            // 3. 获取PDF表单
            AcroFields form = ps.getAcroFields();
            // 4. 给表单添加中文字体
            BaseFont bf = BaseFont.createFont("font/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            form.addSubstitutionFont(bf);
            // 5. 查询数据
            Map<String, Object> data = new HashMap<String, Object>();
            data.put("content", "内容");
            data.put("name", "姓名");
            data.put("leader", "王则");
            data.put("sportleader", "王策");
            data.put("phone", "188888888");
            // 6. 遍历data 给PDF表单表格赋值
            for (String key : data.keySet()) {
                form.setField(key, data.get(key).toString());
            }
            ps.setFormFlattening(true);
            System.out.println("===============PDF导出成功=============");
        } catch (Exception e) {
            System.out.println("===============PDF导出失败=============");
            e.printStackTrace();
        } finally {
            try {
                ps.close();
                reader.close();
                os.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


    }

这里大家注意,如果你也使用的仿宋,那么记得选择,你是使用的仿宋中的那种字体,如果不写索引的话会报错,我当初因为这个问题,研究了好久。

下面是controller接口的实现方法:

/**
     * 下载pdf(单个)
     */
    @RequestMapping("/exportPdfByTemplate")
    public void exportPdfByTemplate(HttpServletResponse response) throws FileNotFoundException {
        // 获取模版
        String template = "/pdf/template.pdf";
        InputStream inputStream = this.getClass().getResourceAsStream(template);
        if (inputStream == null) {
            throw new FileNotFoundException(template);
        }
        PdfStamper ps = null;
        PdfReader reader = null;

        try {
            // 1. 读入PDF表单
            reader = new PdfReader(inputStream);
            // 2. 根据表单生成一个新的PDF
            ps = new PdfStamper(reader, response.getOutputStream());
            // 3. 获取PDF表单
            AcroFields form = ps.getAcroFields();
            // 4. 给表单添加中文字体
            BaseFont bf = BaseFont.createFont("font/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            form.addSubstitutionFont(bf);
            // 5. 查询数据
            Map<String, Object> data = new HashMap<String, Object>();
            data.put("content", "内容");
            data.put("name", "名称");
            data.put("leader", "代表人");
            data.put("sportleader", "校长");
            data.put("phone", "11635788709");
            // 6. 遍历data 给PDF表单表格赋值
            for (String key : data.keySet()) {
                form.setField(key, data.get(key).toString());
            }
            ps.setFormFlattening(true);
            System.out.println("===============PDF导出成功=============");
        } catch (Exception e) {
            System.out.println("===============PDF导出失败=============");
            e.printStackTrace();
        } finally {
            try {
                if (ps != null) {
                    ps.close();
                }
                if (reader != null) {
                    reader.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

三、遍历模版导出PDF

这种肯定是比较简单,但是很多时候会有一些复杂的情况出现,比如我们pdf很复杂,并且需要生成多个,并且每个表格一样但是里面的数据不一样,我在网上找了很多方案,最后想到一种方法,直接遍历模版,填充数据,最后将模版拼装起来,这样就实现了我们想要的效果
类似于这种:
在这里插入图片描述
话不多说,我就直接上代码了,核心的原理就是,先拿到数据,然后遍历数组,获取模版,填充数据,之后合并生成的PDF,最后导出。
首先是测试代码:

/**
     * 导出PDF遍历
     */
    @Test
    void exportPdfByForTemplate() {
        try {
            // 模板文件路径
            String inputFileName = "D:\\Code\\pdf-handle\\src\\main\\resources\\pdf\\forTemplate.pdf";
            // 生成的文件路径
            String outputFileName = "D:\\Code\\pdf-handle\\src\\main\\resources\\pdf\\forResult.pdf";
            List<Map<String, Object>> data = initData();
            // 创建输出文件流
            OutputStream outputStream = Files.newOutputStream(Paths.get(outputFileName));
            Document document = new Document();
            PdfCopy copy = new PdfCopy(document, outputStream);
            document.open();

            for (Map<String, Object> datum : data) {
                ByteArrayOutputStream tempOutputStream = new ByteArrayOutputStream();
                PdfStamper ps = null;
                PdfReader reader = null;
                try {
                    // 1. 读入PDF表单
                    reader = new PdfReader(inputFileName);
                    // 2. 根据表单生成一个新的PDF
                    ps = new PdfStamper(reader, tempOutputStream);
                    // 3. 获取PDF表单
                    AcroFields form = ps.getAcroFields();
                    // 4. 给表单添加中文字体
                    BaseFont bf = BaseFont.createFont("font/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
                    form.addSubstitutionFont(bf);
                    // 5. 遍历datum 给pdf表单表格赋值
                    for (String key : datum.keySet()) {
                        form.setField(key, datum.get(key).toString());
                    }
                    ps.setFormFlattening(true);
                    ps.close();

                    // 读取临时生成的PDF内容
                    ByteArrayInputStream tempInputStream = new ByteArrayInputStream(tempOutputStream.toByteArray());
                    PdfReader tempReader = new PdfReader(tempInputStream);

                    // 合并PDF
                    int numberOfPages = tempReader.getNumberOfPages();
                    for (int i = 1; i <= numberOfPages; ) {
                        copy.addPage(copy.getImportedPage(tempReader, i++));
                    }
                    tempReader.close();

                    System.out.println("===============PDF部分导出成功=============");
                } catch (Exception e) {
                    System.out.println("===============PDF部分导出失败=============");
                    e.printStackTrace();
                } finally {
                    try {
                        if (ps != null) {
                            ps.close();
                        }
                        if (reader != null) {
                            reader.close();
                        }
                        tempOutputStream.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            document.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 初始化数据
    public List<Map<String, Object>> initData() {
        List<Map<String, Object>> res = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            Map<String, Object> item = new HashMap<>();
            item.put("name", "条目名称_" + i);
            item.put("selfScore", "10_" + i);
            item.put("againScore", "11_" + i);
            item.put("finallyScore", "12_" + i);
            item.put("content", "检查过程、资料来源及计算的文字说明_" + i);
            item.put("Instructions", "简要说明_" + i);
            item.put("leader", "王晨林_" + i);
            item.put("leads", "王策_" + i);
            item.put("time", "2024年12月0" + (i + 1) + "日");
            res.add(item);
        }
        return res;
    }

下面是controller接口的实现方法:

/**
     * 下载pdf(多个)
     */
    @RequestMapping("/exportPdfByForTemplate")
    public void exportPdfByForTemplate(HttpServletResponse response) throws IOException {
        try {
            // 模版url
            String template = "/pdf/forTemplate.pdf";
            // 总PDF
            ServletOutputStream outputStream = response.getOutputStream();
            Document document = new Document();
            PdfCopy copy = new PdfCopy(document, outputStream);
            document.open();

            // 获取数据
            List<Map<String, Object>> data = initData();
            for (Map<String, Object> datum : data) {
                try {
                    // 获取拆分的需要遍历模版
                    InputStream inputStream = this.getClass().getResourceAsStream(template);
                    if (inputStream == null) {
                        throw new FileNotFoundException(template);
                    }
                    ByteArrayOutputStream tempOutputStream = new ByteArrayOutputStream();
                    PdfStamper ps = null;
                    PdfReader reader = null;
                    // 读入pdf表单
                    reader = new PdfReader(inputStream);
                    // 根据表单生成一个新的pdf
                    ps = new PdfStamper(reader, tempOutputStream);
                    // 获取pdf表单
                    AcroFields form = ps.getAcroFields();
                    // 给表单添加中文字体
                    BaseFont bf = BaseFont.createFont("font/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
                    form.addSubstitutionFont(bf);
                    // 查询数据
                    // 遍历data 给pdf表单表格赋值
                    for (String key : datum.keySet()) {
                        form.setField(key, datum.get(key).toString());
                    }
                    ps.setFormFlattening(true);
                    ps.close();
                    reader.close();
                    // 读取临时生成的PDF内容
                    ByteArrayInputStream tempInputStream = new ByteArrayInputStream(tempOutputStream.toByteArray());
                    PdfReader tempReader = new PdfReader(tempInputStream);
                    // 合并PDF
                    int numberOfPages = tempReader.getNumberOfPages();
                    for (int i = 1; i <= numberOfPages; ) {
                        copy.addPage(copy.getImportedPage(tempReader, i++));
                    }
                    tempReader.close();
                    System.out.println("===============PDF部分导出成功=============");
                } catch (Exception e) {
                    System.out.println("===============PDF部分导出失败=============");
                    e.printStackTrace();
                }
            }
            document.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 初始化数据
    public List<Map<String, Object>> initData() {
        List<Map<String, Object>> res = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            Map<String, Object> item = new HashMap<>();
            item.put("name", "条目名称_" + i);
            item.put("selfScore", "10_" + i);
            item.put("againScore", "11_" + i);
            item.put("finallyScore", "12_" + i);
            item.put("content", "检查过程、资料来源及计算的文字说明_" + i);
            item.put("Instructions", "简要说明_" + i);
            item.put("leader", "王晨林_" + i);
            item.put("leads", "王策_" + i);
            item.put("time", "2024年12月0" + (i + 1) + "日");
            res.add(item);
        }
        return res;
    }

到此我的业务需求就实现了,如果有业务更复杂的小伙伴,可以自己再研究研究!!

下面是测试项目的地址,其实内容在上面已经贴出来了,供大家参考:

https://gitee.com/wwttz/pdf-handle

如果有什么问题,可以给我留言,看到必答。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值