SpringBoot + iTextPDF + Acrobat 构建动态PDF表单的完整实践

需求

有个单据需要打印,单据需要自定义内容展示,单据尺寸、各个模块的布局都是固定的,内容包含文本内容以及Barcode128条形码等,该如何实现?单据部分截图如下:

分析

整个单据包含多个模块,如果用html网页的形式去开发,那么布局开发比较复杂,所以采用iTextPDF + Acrobat 构建动态PDF表单的方式开发。先用Acrobat制作好pdf模板,然后使用iTextPDF框架将数据写入pdf模板后直接打印。

分析下其中的难点以及关键点问题

1、springboot如何整合itextpdf ,需要引入itextpdf 的哪些包?

2、表单中的文本域有横向和纵向排列,如何设置文本域纵向展示?

3、itextpdf 如何生成Barcode128条形码?

4、itextpdf 如何将Barcode128条形码插入pdf表单中?

5、表单中的Barcode128条形码有横向和纵向排列,如何设置Barcode128条形码纵向展示?

实现过程

下面就一步步解决上述问题。

引入itextpdf 相关依赖

1、对于iText 5.x版,需要引入核心库itextpdf和中文字体支持的依赖包itext-asian,如下:

xml 复制代码
<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、对于iText 7.x版本,引入核心类库itext7-core即可,当前最新版是9.3.0如下。

xml 复制代码
<dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itext7-core</artifactId>
  <version>9.3.0</version>
  <type>pom</type>
</dependency>

iText 7采用了全新的模块化架构,将功能拆分为多个独立模块,如itext7-core(核心功能)、itext7-layout(高级布局)、itext7-forms(表单处理)等。相比之下,iText 5采用单一架构,功能较为集中,许多模块相互混杂,引入的一些类可能不会被用到。新项目推荐使用iText 7‌,因为它具有更好的模块化设计和性能优化。

写入表单

1、第二个问题如何设置文本域纵向展示?这个主要是在Acrobat中编辑pdf表单来完成,双击文本域弹出文本域属性输入框->一般->通用属性->方向设置为90度。

2、第3到第5个问题,直接上代码进行解析,代码基于iText 7。

java 复制代码
/**
 * 填充PDF表单并生成新文件
 *
 * @param templatePath 模板文件路径
 * @param outputPath   输出文件路径
 * @param formData     表单数据
 * @param barcodeData  二维码数据
 * @param flattenForm  是否扁平化表单(使字段不可编辑)
 * @throws IOException 文件操作异常
 */
public static void fillPdfForm(String templatePath, String outputPath, Map<String, String> formData,Map<String, String> barcodeData,boolean flattenForm) throws IOException {
    // 创建PDF读取器和写入器
    PdfReader reader = new PdfReader(templatePath);
    PdfWriter writer = new PdfWriter(new FileOutputStream(outputPath));
    PdfDocument pdfDoc = new PdfDocument(reader, writer);
    Document document = new Document(pdfDoc);
    // 获取表单对象
    PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
    // 填充表单数据
    for (Map.Entry<String, String> entry : formData.entrySet()) {
        String fieldName = entry.getKey();
        String fieldValue = entry.getValue();
        PdfFormField field = form.getField(fieldName);
        if (field != null) {
            field.setValue(fieldValue);
        } else {
            log.info("警告:未找到字段 '" + fieldName + "'");
        }
    }
    //横向条形码设置
    // X坐标
    float x = 208;
    // Y坐标
    float y = 546;
    // 宽度
    float width = 120;
    // 高度
    float height = 25;
    addBarcode(pdfDoc, document, x, y, width, height, barcodeData.get("barcodeData1"), false);

    //纵向条形码设置
    // X坐标
    float x2 = 390;
    // Y坐标
    float y2 = 520;
    // 宽度
    float width2 = 120;
    // 高度
    float height2 = 25;
    addBarcode(pdfDoc, document, x2, y2, width2, height2, barcodeData.get("barcodeData2"), true);

    // 可选:扁平化表单(使字段成为文档内容的一部分,不可编辑)
    if (flattenForm) {
        form.flattenFields();
    }
    // 关闭文档
    pdfDoc.close();
    document.close();
}

上面这个函数主要对表单进行写入数据,生成一个新的pdf文件主要方便测试查看。生成条形码的的方法如下:

java 复制代码
/**
 * 添加Barcode128条形码
 *
 * @param pdfDoc pdf文档对象
 * @param x 条形码横坐标
 * @param y 条形码纵坐标
 * @param width 条形码宽度
 * @param height 条形码高度
 * @param barcodeData 条形码数据
 * @param vertical 是否垂直展示
 */
private static void addBarcode(PdfDocument pdfDoc, Document document, float x, float y, float width, float height, String barcodeData, boolean vertical) {
    // 创建Barcode128条形码
    Barcode128 barcode128 = new Barcode128(pdfDoc);
    barcode128.setCode(barcodeData);
    // 不显示文本
    barcode128.setFont(null);
    barcode128.fitWidth(width);
    barcode128.setBarHeight(height);
    // 将条形码转换为表单XObject
    PdfFormXObject barcodeXObject = barcode128.createFormXObject(ColorConstants.BLACK, null, pdfDoc);
    // 在指定位置绘制条形码
    PdfCanvas canvas = new PdfCanvas(pdfDoc.getFirstPage());
    // 添加条形码标签
    document.add(new Paragraph(""));
    // 旋转90度实现纵向展示
    if (vertical) {
        // 3. 平移坐标系原点至目标位置
        canvas.concatMatrix(1, 0, 0, 1, x, y);
        // 4. 应用90度顺时针旋转变换,旋转后,原本水平绘制的条码将变为垂直
        canvas.concatMatrix(0, 1, -1, 0, 0, 0);
        barcode128.placeBarcode(canvas, null, null);
    } else {
        // 绘制条形码
        canvas.addXObjectAt(barcodeXObject, x, y);
    }
}

其中通过new Barcode128(pdfDoc)创建Barcode128条形码对象,横向展示条形码的时候通过

canvas.addXObjectAt(barcodeXObject, x, y) 将条形码插入到pdf表单的指定坐标(x,y)位置。

纵向展示的时候需要将二维码平移坐标系原点至目标位置,然后90度顺时针旋转变换,关键函数:

java 复制代码
public PdfCanvas concatMatrix(double a, double b, double c, double d, double e, double f) {
   ...
}

方法中的六个参数共同定义了一个用于图形变换的 3×3 矩阵,各参数具体作用如下:

参数 a‌:控制图形在 X 轴方向的缩放比例。

参数 b‌:控制图形在水平方向的倾斜程度。

参数 c‌:控制图形在垂直方向的倾斜程度。

参数 d‌:控制图形在 Y 轴方向的缩放比例。

参数 e‌:指定图形在 X 轴方向的平移距离。

参数 f‌:指定图形在 Y 轴方向的平移距离。

测试

编写main函数进行测试。

java 复制代码
public static void main(String[] args) {
        try {
            // 准备表单数据
            Map<String, String> formData = new HashMap<>();
            formData.put("orderNo", "*77582587758258");
            formData.put("position", "400B ");
            formData.put("position_no", "C262-11 C3");
         	//其他省略

            Map<String, String> barcodeData = new HashMap<>();
            barcodeData.put("barcodeData1","PROD-2025-001");
            barcodeData.put("barcodeData2","PROD-2025-002");
            // 填充表单并生成新文件
            fillPdfForm("C:\\Users\\user\\Desktop\\order.pdf", "new_order.pdf", formData,barcodeData,true);
            log.info("PDF表单填充完成,已生成新文件:new_order.pdf");
        } catch (IOException e) {
            log.error("处理PDF时发生错误:",e);
        }
    }
相关推荐
武子康7 小时前
大数据-142 ClickHouse分片×副本×Distributed 实战 ReplicatedMergeTree、Keeper、insert_quorum
大数据·后端·nosql
程序员小赵同学7 小时前
Spring AI 极简入门:15分钟集成AI到SpringBoot应用
人工智能·spring boot·spring·springai
陈随易8 小时前
编程语言MoonBit:在前端开发中的妙用
前端·后端·程序员
Giant1008 小时前
小白也能懂!跨域问题到底是啥,咋解决?
后端
用户90555842148058 小时前
Milvus源码分析:写流程
后端
易元8 小时前
Spring 应用记录(Bean的注册与注入机制)
spring boot·后端
Hooomeey8 小时前
深度解析线程与线程池:从 OS 调度内核到 Java 并发架构的演进逻辑
java·后端·架构
狂奔小菜鸡8 小时前
Day8 | Java 方法全解析
java·后端·java ee
红鼻子时代9 小时前
从零开始构建PDF文档生成器(二)- 添加页眉页脚
pdf·页眉页脚·canvas绘制