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);
        }
    }
相关推荐
SomeB1oody19 分钟前
【RustyML入门】7.4. 按需裁剪与模块化集成
开发语言·后端·机器学习·rust·教程
罗超驿22 分钟前
SpringBoot 快速入门
java·spring boot·后端
Source.Liu30 分钟前
【A11】基于 PDF 渲染引擎的统一文档界面架构设计
rust·pdf
卷无止境1 小时前
除了开发api,FastAPI其实也可以配合jinja2模板写页面
后端·python·fastapi
newerp1 小时前
Redis 操作与缓存策略
后端·程序员·go
newerp1 小时前
GORM ORM 基础
后端·程序员·go
小岛前端1 小时前
AI Skills 已经封神,但新的问题却越来越严重!
前端·后端·github
拖孩1 小时前
一个人 + AI 做的小程序,上线 15 天赚了 10 块 5
前端·后端·微信小程序
newerp1 小时前
CRUD 操作与预处理语句
后端·程序员·go
wei_shuo2 小时前
KES 数据同步与ETL实战:数据集成、转换与实时同步方案
后端