SpringBoot使用itext填充pdf表单及导出pdf的流程

 更新时间:2024年09月03日 09:20:18   作者:长不大搬不完砖  
由于最近开发的项目需要用到打印单据,就在网上找了一下方案,反反复复,都没有找到合适的,借鉴了网上资源,使用itext5、itext7的工具包,所以本文介绍了SpringBoot使用itext填充pdf表单及导出pdf的流程,需要的朋友可以参考下

前言

由于最近开发的项目需要用到打印单据,就在网上找了一下方案,反反复复,都没有找到合适的,借鉴了网上资源,使用itext5、itext7的工具包,自己写了一个通用的pdf表单填充工具,简单易用,支持单页、分页单据打印,当然还有更多的定制化,需要大家自行扩展。

一、设计表单

1.使用excel设计了一个pdf单据,因为比较整齐设计方便,如下图:

在这里插入图片描述

2.使用免费工具PDFgear将excel文件转为pdf文件,如下图:

在这里插入图片描述

3.使用免费Adobe Acrobat DC工具编辑pdf文件

打开转换好的pdf文件,选择工具栏右边的编辑表单,然后拖动顶部栏的表单域设计属性,属性值和和代码填充的要一致,字体下拉最后面的,不然你打印出来的单据字体会很难看,当然也可以在程序代码里去设计字体,一般设计好就够用了,如果没有特定要求的话。

ps:有些可能编辑表单,使用默认的,有时候可能显示不了数据,这个把这个表单域删除了,重新添加一个即可。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

OK,表单设计好,下面就是编码啦

二、项目搭建

我这里是使用阿里云官方提供快速创建脚手架的在线网站生成了一个springboot项目,项目结构图如下:

在这里插入图片描述

三、引入依赖

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

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13</version>
</dependency>

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.2.5</version>
    <type>pom</type>
</dependency>

四、编写工具类PdfItext7Util代码

package com.example.pdfdemo.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.example.pdfdemo.form.PdfFormTemplate;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.utils.PdfMerger;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * <p>pdfItext7工具类</p>
 *
 * @author aliqingge
 * @date 2024/8/30 14:57
 */
@Slf4j
public class PdfItext7Util {

    /**
     * 每页显示数,可自行修改
     */
    public static final int PAGE_SIZE = 12;

    /**
     * 临时文件路径
     */
    public final static String FOLDER = "/tempPdfFile";

    /**
     * 文件路径
     */
    public final static String FOLDER_PATH = FOLDER + "/";


    /**
     * 填充表单
     *
     * @param template 模版对象
     * @return 填充表单文件路径
     */
    public static String fillTemplate(PdfFormTemplate template) {
        Assert.notNull(template, "模版对象不能为空");

        // 创建临时文件夹
        File file = new File(FOLDER);
        if (!file.exists() && !file.isDirectory()) {
            file.setWritable(true, false);
            file.mkdirs();
        }

        // 合并后生成文件的目标路径
        String destPdfPath = StringUtils.join(
                template.isGenerateToLocalFileFlag() ? template.getLocalFileStoragePath() : FOLDER_PATH,
                System.currentTimeMillis(),
                template.getTemplateFileName(),
                ".pdf"
        );

        // 获取pdf字节数组列表
        List<byte[]> pdfByteList = getPdfByteList(template);
        // 合并文件
        mergePdf(pdfByteList, destPdfPath);

        return destPdfPath;
    }


    /**
     * 填充表单,并返回字节数组
     *
     * @param template 模版对象
     * @return 填充表单文件路径
     */
    public static byte[] fillAndGetPdfByte(PdfFormTemplate template) {
        Assert.notNull(template, "模版对象不能为空");
        return mergePdf(getPdfByteList(template));
    }

    /**
     * 获取pdf字节数组列表
     *
     * @param template
     * @return
     */
    private static List<byte[]> getPdfByteList(PdfFormTemplate template) {
        List<byte[]> pdfByteList = Lists.newArrayList();
        // 数据分页
        List<List<Map<String, String>>> pageList = Lists.partition(template.getPageDataList(), PAGE_SIZE);
        int pageTotal = pageList.size();
        for (int i = 0; i < pageList.size(); i++) {
            // 生成每页pdf的字节数组
            byte[] currentPagePdf = createCurrentPagePdf(template, pageList.get(i), (i + 1), pageTotal);
            pdfByteList.add(currentPagePdf);
        }
        return pdfByteList;
    }

    /**
     * 生成当前页pdf字节数组
     *
     * @param template
     * @param currentPageDataList
     * @param currentPageNo
     * @param pageTotal
     * @return
     */
    private static byte[] createCurrentPagePdf(PdfFormTemplate template, List<Map<String, String>> currentPageDataList, int currentPageNo, int pageTotal) {
        PdfDocument pdfDoc = null;
        try {
            // 输出流用于存储填充好数据的PDF文件
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // 打开PDF模板文件
            PdfReader reader = new PdfReader(template.getTemplatePath());
            // 创建PDF写入器
            PdfWriter writer = new PdfWriter(bos);
            // 创建PDF文档对象,连接读取器和写入器
            pdfDoc = new PdfDocument(reader, writer);
            // 设置默认页面大小为A4
            pdfDoc.setDefaultPageSize(PageSize.A4);
            // 获取PDF表单
            PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
            // 设置页码
            PdfFormField formField = form.getField(template.getCustomPageNoFiledName());
            if (formField != null) {
                formField.setValue(StringUtils.join(currentPageNo, "/", pageTotal));
            }

            Map<String, String> baseDataMap = template.getBaseDataMap();
            if (CollUtil.isNotEmpty(baseDataMap)) {
                // 填充基本信息
                for (String dataKey : baseDataMap.keySet()) {
                    PdfFormField field = form.getField(dataKey);
                    if (field != null) {
                        field.setValue(baseDataMap.get(dataKey));
                    }
                }
            }

            if (CollUtil.isNotEmpty(currentPageDataList)) {
                int i = 1;
                // 填充分页数据
                for (Map<String, String> currentPageDataMap : currentPageDataList) {
                    // 填充序号, 可以自定义属性,从外部传入
                    PdfFormField noFormField = form.getField(StringUtils.join("no", i));
                    if (noFormField != null) {
                        noFormField.setValue(String.valueOf((currentPageNo - 1) * PAGE_SIZE + i));
                    }

                    // 填充其他数据
                    Set<Map.Entry<String, String>> entries = currentPageDataMap.entrySet();
                    int finalNo = i;
                    entries.forEach(entry -> {
                        PdfFormField field = form.getField(StringUtils.join(entry.getKey(), finalNo));
                        if (field != null) {
                            field.setValue(entry.getValue());
                        }
                    });
                    i++;
                }
            }

            // 设置表单不可编辑
            form.flattenFields();

            // 可以自定义字体
                /*PdfFont pdfFont = loadPdfFont();
                if (pdfFont != null) {
                    // 给表单域字段设置字体和大小
                    Map<String, PdfFormField> allFormFields = form.getFormFields();
                    for (String formFiled : allFormFields.keySet()) {
                        if (template.getFontSize() > 0) {
                            allFormFields.get(formFiled).setFontAndSize(pdfFont, template.getFontSize());
                        } else {
                            allFormFields.get(formFiled).setFont(pdfFont);
                        }
                    }
                }*/

            pdfDoc.close();

            return bos.toByteArray();
        } catch (IOException e) {
            log.error("createCurrentPagePdf 填充PDF表单失败", e);
            throw new RuntimeException(e);
        } finally {
            if (pdfDoc != null) {
                pdfDoc.close();
            }
        }
    }

    /**
     * 合并pdf文件
     *
     * @param pdfByteList 待合并PDF文档字节集合
     * @param destPath 生成合并PDF目标文档路径
     */
    public static void mergePdf(List<byte[]> pdfByteList, String destPath) {
        try {
            int size = pdfByteList.size();
            byte[] pdfData = pdfByteList.get(0);
            for (int i = 1; i < size; i++) {
                pdfData = mergePdfBytes(pdfData, pdfByteList.get(i));
            }

            if (pdfData != null) {
                FileOutputStream fis = new FileOutputStream(destPath);
                fis.write(pdfData);
                fis.close();
            }

        } catch (Exception e) {
            log.error("mergePdf 合并PDF异常", e);
            throw new RuntimeException("合并PDF异常");
        }
    }


    /**
     * 合并pdf文件
     *
     * @param pdfByteList 待合并PDF文档字节集合
     */
    public static byte[] mergePdf(List<byte[]> pdfByteList) {
        try {
            int size = pdfByteList.size();
            byte[] pdfData = pdfByteList.get(0);
            for (int i = 1; i < size; i++) {
                pdfData = mergePdfBytes(pdfData, pdfByteList.get(i));
            }
            return pdfData;
        } catch (Exception e) {
            log.error("mergePdf 合并PDF异常", e);
            throw new RuntimeException("合并PDF异常");
        }
    }

    /**
     * 基于内存中的字节数组进行PDF文档的合并
     *
     * @param firstPdf 第一个PDF文档字节数组
     * @param secondPdf 第二个PDF文档字节数组
     */
    @SneakyThrows
    private static byte[] mergePdfBytes(byte[] firstPdf, byte[] secondPdf) {
        if (firstPdf == null || secondPdf == null) {
            return null;
        }

        // 1.创建字节数组,基于内存进行合并
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PdfDocument destDoc = new PdfDocument(new PdfWriter(os));

        // 2.合并的pdf文件对象
        PdfDocument firstDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(firstPdf)));
        PdfDocument secondDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(secondPdf)));

        // 3.合并对象
        PdfMerger merger = new PdfMerger(destDoc);
        log.info("mergePdfBytes 合并PDF文档,第一个PDF文档页数:{}, 第二个PDF文档页数:{}", firstDoc.getNumberOfPages(), secondDoc.getNumberOfPages());
        merger.merge(firstDoc, 1, firstDoc.getNumberOfPages());
        merger.merge(secondDoc, 1, secondDoc.getNumberOfPages());
        log.info("mergePdfBytes 合并PDF文档成功");

        // 4.关闭文档流
        merger.close();
        firstDoc.close();
        secondDoc.close();
        destDoc.close();

        return os.toByteArray();
    }

    /**
     * 判断当前系统是否是Windows系统
     *
     * @return true:Windows系统,false:Linux系统
     */
    public static boolean isWindowsSystem() {
        String property = System.getProperty("os.name").toLowerCase();
        return property.contains("windows");
    }

    /**
     * 加载字体
     * @return pdf字体
     */
    public static PdfFont loadPdfFont(String fontName) {
        PdfFont pdfFont = null;
        try {
            if (StrUtil.isBlank(fontName)) {
                return pdfFont;
            }

            // 自行扩展

            String fontStr = isWindowsSystem() ? "/fonts/STSONG.TTF" : "/fonts/extend/simsun.ttc,0";
            pdfFont = PdfFontFactory.createFont(fontStr, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED);
        } catch (IOException e) {
            log.error("loadPdfFont 无法加载字体", e);
        }
        return pdfFont;
    }


    public static void main(String[] args) {
        // 构建基本信息
        Map<String, String> baseDataMap = Maps.newHashMap();
        baseDataMap.put("orderNo", "X202408301625");
        baseDataMap.put("memberName", "张三");
        baseDataMap.put("memberPhone", "18888888888");
        baseDataMap.put("orderDate", LocalDateTimeUtil.format(LocalDate.now(), DatePattern.NORM_DATE_PATTERN));

        baseDataMap.put("remark", "我是备注呀");
        baseDataMap.put("auditUserName", "李四");
        baseDataMap.put("totalAmount", "¥182.25");

        List<Map<String, String>> pageDataList = Lists.newArrayList();
        for (int i = 1; i < 15; i++) {
            Map<String, String> pageDataMap = Maps.newHashMap();
            BigDecimal price = BigDecimal.valueOf(i * Math.random()).setScale(2, RoundingMode.HALF_UP);
            pageDataMap.put("goodsName", StringUtils.join("商品名称", i));
            pageDataMap.put("price", StringUtils.join("¥", price));
            pageDataMap.put("totalAmount", StringUtils.join("¥", price));
            pageDataMap.put("address", StringUtils.join("广东省广州市越秀区北京路", i, "号"));
            pageDataList.add(pageDataMap);
        }

        // 构建业务数据
            /*PdfFormTemplate formTemplate = PdfFormTemplate.builder()
                    .baseDataMap(baseDataMap)
                    .pageDataList(pageDataList)
                    .customPageNoFiledName("pageNo")
                    .templatePath("E:\\soft\\IdeaProjects\\pdf-form-fill-demo\\src\\main\\resources\\telephone\\出库单据模版form.pdf")
                    .templateFileName("pdf表单模版")
                    .build();*/

        PdfFormTemplate formTemplate = new PdfFormTemplate()
                .setBaseDataMap(baseDataMap)
                .setPageDataList(pageDataList)
                // 本地测试是绝对路径、发布web环境就相对路径
                .setTemplatePath("E:\\soft\\IdeaProjects\\pdf-form-fill-demo\\src\\main\\resources\\template\\出库单据模版form.pdf")
                .setTemplateFileName("pdf表单模版");

        String pdfPath = fillTemplate(formTemplate);
        // byte[] andGetPdfByte = fillAndGetPdfByte(formTemplate);
        log.info("PDF路径:{}", pdfPath);
    }


}

五、编辑工具类PdfItext5Util代码

package com.example.pdfdemo.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.example.pdfdemo.form.PdfFormTemplate;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.*;

/**
 * <p>pdfItext5工具类</p>
 *
 * @author aliqingge
 * @date 2024/8/30 18:38
 */
@Slf4j
public class PdfItext5Util {

    /**
     * 每页显示数,可自行修改
     */
    public static final int PAGE_SIZE = 12;

    /**
     * 临时文件路径
     */
    public final static String FOLDER = "/tempPdfFile";

    /**
     * 文件路径
     */
    public final static String FOLDER_PATH = FOLDER + "/";

    /**
     * 获取pdf字节数组列表
     *
     * @param template
     * @return
     */
    private static String createPdf(PdfFormTemplate template) throws Exception {
        List<PdfReader> pdfReaderList = new ArrayList<>(Collections.emptyList());

        // 数据分页
        List<List<Map<String, String>>> pageList = Lists.partition(template.getPageDataList(), PAGE_SIZE);
        int pageTotal = pageList.size();
        for (int i = 0; i < pageList.size(); i++) {
            // 生成每页pdf的字节数组
            PdfReader currentPagePdf = createCurrentPagePdf(template, pageList.get(i), (i + 1), pageTotal);
            pdfReaderList.add(currentPagePdf);
        }

        // 合并后生成文件的目标路径
        String destPdfPath = StringUtils.join(
                template.isGenerateToLocalFileFlag() ? template.getLocalFileStoragePath() : FOLDER_PATH,
                System.currentTimeMillis(),
                template.getTemplateFileName(),
                ".pdf"
        );

        //输出流
        FileOutputStream outputStream = new FileOutputStream(destPdfPath);
        Document document = new Document(new PdfReader(pdfReaderList.get(0)).getPageSize(1));
        PdfSmartCopy copy = new PdfSmartCopy(document, outputStream);
        document.open();
        //合并所有分页pdf
        for (int k = 1; k <= pdfReaderList.size(); k++) {
            PdfReader reader = pdfReaderList.get(k - 1);
            document.newPage();
            PdfImportedPage page = copy.getImportedPage(reader, 1);
            copy.addPage(page);
        }
        copy.close();
        outputStream.close();

        return destPdfPath;
    }

    /**
     * 获取分页pdf
     *
     * @param template pdf表单模版
     * @param currentTableData 当前页表格数据
     * @param currentPage 当前页
     * @param pageCount 总页数
     * @return
     * @throws Exception
     */
    /**
     * 生成当前页pdf字节数组
     *
     * @param template
     * @param currentPageDataList
     * @param currentPageNo
     * @param pageTotal
     * @return
     */
    private static PdfReader createCurrentPagePdf(PdfFormTemplate template, List<Map<String, String>> currentPageDataList, int currentPageNo, int pageTotal) throws Exception {
        PdfReader templateReader;
        ByteArrayOutputStream bos;
        //读取pdf模板
        templateReader = new PdfReader(template.getTemplatePath());
        bos = new ByteArrayOutputStream();
        PdfStamper stamper = new PdfStamper(templateReader, bos);
        AcroFields form = stamper.getAcroFields();

        // 自定义字体设置
        /*BaseFont baseFont;
        if (isWindowsSystem()) {
            baseFont = BaseFont.createFont("/fonts/STSONG.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        } else {
            baseFont = BaseFont.createFont("/fonts/extend/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        }
        form.setSubstitutionFonts(Lists.newArrayList(baseFont));*/

        // 设置页码
        form.setField(template.getCustomPageNoFiledName(), StringUtils.join(currentPageNo, "/", pageTotal));
        // 填充基本信息
        Map<String, String> baseDataMap = template.getBaseDataMap();
        if (CollUtil.isNotEmpty(baseDataMap)) {
            for (String dataKey : baseDataMap.keySet()) {
                form.setField(dataKey, baseDataMap.get(dataKey));
            }
        }

        if (CollUtil.isNotEmpty(currentPageDataList)) {
            int i = 1;
            // 填充分页数据
            for (Map<String, String> currentPageDataMap : currentPageDataList) {
                // 填充序号, 可以自定义属性,从外部传入
                form.setField("no", String.valueOf((currentPageNo - 1) * PAGE_SIZE + i));
                // 填充其他数据
                Set<Map.Entry<String, String>> entries = currentPageDataMap.entrySet();
                int finalNo = i;
                entries.forEach(entry -> {
                    try {
                        form.setField(StringUtils.join(entry.getKey(), finalNo), entry.getValue());
                    } catch (Exception e) {
                        log.error("填充表单数据异常", e);
                    }
                });
                i++;
            }
        }

        /*Map<String, AcroFields.Item> formFields = form.getFields();
        for (String key : formFields.keySet()) {
            form.setFieldProperty(key, "textfont", baseFont, null);
            form.setFieldProperty(key, "textsize", 8f, null);
        }*/

        String pagePdfPath = FOLDER_PATH + System.currentTimeMillis() + ".pdf";
        FileOutputStream outputStream = new FileOutputStream(pagePdfPath);
        //如果为false那么生成的PDF文件还能编辑,一定要设为true
        stamper.setFormFlattening(true);
        stamper.close();
        Document doc = new Document();
        PdfSmartCopy copy = new PdfSmartCopy(doc, outputStream);
        doc.open();
        PdfReader reader = new PdfReader(bos.toByteArray());
        PdfImportedPage importPage = copy.getImportedPage(reader, 1);
        copy.addPage(importPage);
        doc.close();
        outputStream.close();
        return reader;
    }

    /**
     * 判断当前系统是否是Windows系统
     *
     * @return
     */
    public static boolean isWindowsSystem() {
        String property = System.getProperty("os.name").toLowerCase();
        return property.contains("windows");
    }


    public static void main(String[] args) throws Exception {

        // 构建基本信息
        Map<String, String> baseDataMap = Maps.newHashMap();
        baseDataMap.put("orderNo", "X202408301625");
        baseDataMap.put("memberName", "张三");
        baseDataMap.put("memberPhone", "18888888888");
        baseDataMap.put("orderDate", LocalDateTimeUtil.format(LocalDate.now(), DatePattern.NORM_DATE_PATTERN));

        baseDataMap.put("remark", "我是备注呀");
        baseDataMap.put("auditUserName", "李四");
        baseDataMap.put("totalAmount", "¥182.25");

        List<Map<String, String>> pageDataList = Lists.newArrayList();
        for (int i = 1; i < 15; i++) {
            Map<String, String> pageDataMap = Maps.newHashMap();
            BigDecimal price = BigDecimal.valueOf(i * Math.random()).setScale(2, RoundingMode.HALF_UP);
            pageDataMap.put("goodsName", StringUtils.join("商品名称", i));
            pageDataMap.put("price", StringUtils.join("¥", price));
            pageDataMap.put("totalAmount", StringUtils.join("¥", price));
            pageDataMap.put("address", StringUtils.join("广东省广州市越秀区北京路", i, "号"));
            pageDataList.add(pageDataMap);
        }

        // 构建业务数据
        /*PdfFormTemplate formTemplate = PdfFormTemplate.builder()
                .baseDataMap(baseDataMap)
                .pageDataList(pageDataList)
                .customPageNoFiledName("pageNo")
                .templatePath("E:\\soft\\IdeaProjects\\pdf-form-fill-demo\\src\\main\\resources\\telephone\\出库单据模版form.pdf")
                .templateFileName("pdf表单模版")
                .build();*/

        PdfFormTemplate formTemplate = new PdfFormTemplate()
                .setBaseDataMap(baseDataMap)
                .setPageDataList(pageDataList)
                .setTemplatePath("E:\\soft\\IdeaProjects\\pdf-form-fill-demo\\src\\main\\resources\\template\\出库单据模版form.pdf")
                .setTemplateFileName("测试pdf表单模版");

        String pdf = createPdf(formTemplate);
        log.info("pdf文件路径: {}", pdf);


    }

}

六、统一模版代码类说明

统一模板类,对业务数据、模板、字体等属性进行封装,要对哪个pdf模版进行填充数据,入口处只需按照模版要求组装业务数据,调用填充接口即可,无需过多操作,当然很多操作还可以自行扩展。

package com.example.pdfdemo.form;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * <p>pdf表单模版对象</p>
 *
 * @author aliqingge
 * @date 2024/8/30 14:57
 */
@Data
@Builder
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class PdfFormTemplate implements Serializable {

    private static final long serialVersionUID = 3935687509620254261L;

    /**
     * 基本数据
     */
    private Map<String, String> baseDataMap;

    /**
     * 分页数据集合
     */
    private List<Map<String, String>> pageDataList;

    /**
     * 自定义页码字段名,默认pageNumber
     */
    private String customPageNoFiledName = "pageNo";

    /**
     * 填充模版路径
     */
    private String templatePath;

    /**
     * 模版文件名, 默认pdf表单模版
     */
    private String templateFileName = "pdf表单模版";

    /**
     * 字体大小,默认8f
     */
    private float fontSize = 8f;

    /**
     * 字体名称,默认宋体
     */
    private String fontName;

    /**
     * 是否生成到本地文件,默认生成
     */
    private boolean generateToLocalFileFlag = true;

    /**
     * 本地文件存储路径
     */
    private String localFileStoragePath = "C:\\tempPdfFile\\";

}

七、填充pdf表单核心代码

1.填充入口

 /**
     * 填充表单
     *
     * @param template 模版对象
     * @return 填充表单文件路径
     */
    public static String fillTemplate(PdfFormTemplate template) {
        Assert.notNull(template, "模版对象不能为空");

        // 创建临时文件夹
        File file = new File(FOLDER);
        if (!file.exists() && !file.isDirectory()) {
            file.setWritable(true, false);
            file.mkdirs();
        }

        // 合并后生成文件的目标路径
        String destPdfPath = StringUtils.join(
                template.isGenerateToLocalFileFlag() ? template.getLocalFileStoragePath() : FOLDER_PATH,
                System.currentTimeMillis(),
                template.getTemplateFileName(),
                ".pdf"
        );

        // 获取pdf字节数组列表
        List<byte[]> pdfByteList = getPdfByteList(template);
        // 合并文件
        mergePdf(pdfByteList, destPdfPath);

        return destPdfPath;
    }

2.填充模版

	/**
	 * 获取pdf字节数组列表
	 *
	 * @param template
	 * @return
	 */
	private static List<byte[]> getPdfByteList(PdfFormTemplate template) {
	    List<byte[]> pdfByteList = Lists.newArrayList();
	    // 数据分页
	    List<List<Map<String, String>>> pageList = Lists.partition(template.getPageDataList(), PAGE_SIZE);
	    int pageTotal = pageList.size();
	    for (int i = 0; i < pageList.size(); i++) {
	        // 生成每页pdf的字节数组
	        byte[] currentPagePdf = createCurrentPagePdf(template, pageList.get(i), (i + 1), pageTotal);
	        pdfByteList.add(currentPagePdf);
	    }
	    return pdfByteList;
	}
	
	/**
     * 生成当前页pdf字节数组
     *
     * @param template
     * @param currentPageDataList
     * @param currentPageNo
     * @param pageTotal
     * @return
     */
    private static byte[] createCurrentPagePdf(PdfFormTemplate template, List<Map<String, String>> currentPageDataList, int currentPageNo, int pageTotal) {
        PdfDocument pdfDoc = null;
        try {
            // 输出流用于存储填充好数据的PDF文件
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // 打开PDF模板文件
            PdfReader reader = new PdfReader(template.getTemplatePath());
            // 创建PDF写入器
            PdfWriter writer = new PdfWriter(bos);
            // 创建PDF文档对象,连接读取器和写入器
            pdfDoc = new PdfDocument(reader, writer);
            // 设置默认页面大小为A4
            pdfDoc.setDefaultPageSize(PageSize.A4);
            // 获取PDF表单
            PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
            // 设置页码
            PdfFormField formField = form.getField(template.getCustomPageNoFiledName());
            if (formField != null) {
                formField.setValue(StringUtils.join(currentPageNo, "/", pageTotal));
            }

            Map<String, String> baseDataMap = template.getBaseDataMap();
            if (CollUtil.isNotEmpty(baseDataMap)) {
                // 填充基本信息
                for (String dataKey : baseDataMap.keySet()) {
                    PdfFormField field = form.getField(dataKey);
                    if (field != null) {
                        field.setValue(baseDataMap.get(dataKey));
                    }
                }
            }

            if (CollUtil.isNotEmpty(currentPageDataList)) {
                int i = 1;
                // 填充分页数据
                for (Map<String, String> currentPageDataMap : currentPageDataList) {
                    // 填充序号, 可以自定义属性,从外部传入
                    PdfFormField noFormField = form.getField(StringUtils.join("no", i));
                    if (noFormField != null) {
                        noFormField.setValue(String.valueOf((currentPageNo - 1) * PAGE_SIZE + i));
                    }

                    // 填充其他数据
                    Set<Map.Entry<String, String>> entries = currentPageDataMap.entrySet();
                    int finalNo = i;
                    entries.forEach(entry -> {
                        PdfFormField field = form.getField(StringUtils.join(entry.getKey(), finalNo));
                        if (field != null) {
                            field.setValue(entry.getValue());
                        }
                    });
                    i++;
                }
            }

            // 设置表单不可编辑
            form.flattenFields();
            pdfDoc.close();

            return bos.toByteArray();
        } catch (IOException e) {
            log.error("createCurrentPagePdf 填充PDF表单失败", e);
            throw new RuntimeException(e);
        } finally {
            if (pdfDoc != null) {
                pdfDoc.close();
            }
        }
    }

八、效果图

在这里插入图片描述

在这里插入图片描述

以上就是SpringBoot使用itext填充pdf表单及导出pdf的流程的详细内容,更多关于SpringBoot itext填充pdf及导出的资料请关注脚本之家其它相关文章!

相关文章

  • Maven引入本地Jar包并打包进War包中的方法

    Maven引入本地Jar包并打包进War包中的方法

    本篇文章主要介绍了Maven引入本地Jar包并打包进War包中的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • Java使用WatchService监控文件内容变化的示例

    Java使用WatchService监控文件内容变化的示例

    本篇文章主要介绍了Java使用WatchService监控文件变化的示例,非常具有实用价值,需要的朋友可以参考下
    2017-10-10
  • SpringBoot中@ConfigurationProperties 配置绑定

    SpringBoot中@ConfigurationProperties 配置绑定

    本文主要介绍了SpringBoot中@ConfigurationProperties 配置绑定,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • Java经典面试题汇总--多线程

    Java经典面试题汇总--多线程

    本篇总结的是Java多线程相关的面试题,后续会持续更新,希望我的分享可以帮助到正在备战面试的实习生或者已经工作的同行,如果发现错误还望大家多多包涵,不吝赐教,谢谢
    2021-06-06
  • java制作带界面的聊天工具

    java制作带界面的聊天工具

    这篇文章主要教大家如何利用java制作带界面的聊天工具,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • Java中Cglib代理和JDK代理的区别详解

    Java中Cglib代理和JDK代理的区别详解

    这篇文章主要介绍了Java中Cglib代理和JDK代理的区别详解,Cglib代理功能更强,无论目标类是否实现了接口都可以代理,他是基于继承的方式来代理目标类,如果目标类也实现了接口,代理类也会实现一次,需要的朋友可以参考下
    2023-09-09
  • 为什么阿里要慎重使用ArrayList中的subList方法

    为什么阿里要慎重使用ArrayList中的subList方法

    这篇文章主要介绍了为什么要慎重使用ArrayList中的subList方法,subList是List接口中定义的一个方法,该方法主要用于返回一个集合中的一段、可以理解为截取一个集合中的部分元素,他的返回值也是一个List。,需要的朋友可以参考下
    2019-06-06
  • SpringBoot实现WebSocket服务并让客户端实时接收

    SpringBoot实现WebSocket服务并让客户端实时接收

    使用SpringBoot和WebSocket可创建实时消息推送服务,首先添加WebSocket依赖至pom.xml,配置WebSocket端点和逻辑处理器,通过WebSocketHandler处理消息,使用AnnouncementController模拟消息推送,支持HTML和微信小程序客户端接收消息,感兴趣的可以了解一下
    2024-10-10
  • Java 数据库连接池 Tomcat介绍

    Java 数据库连接池 Tomcat介绍

    这篇文章主要给大家分享了 Java 数据库连接池 Tomcat介绍,omcat 是一个小型的轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。下面来看看文章内容的详细介绍吧
    2021-11-11
  • 轻松掌握Java策略模式

    轻松掌握Java策略模式

    这篇文章主要帮助大家轻松掌握Java策略模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09

最新评论