apache pdfbox 设置PDF表单域,Java生成PDF模板简单案例。

1. 编辑模板PDF

1.1 编辑内容

描述:打开用WPS打开需要编辑的PDF模板,编辑内容。

1.2 编辑表单

1.3 添加表单域

描述:添加表单域,并给各个表单域命名,本案例表单域命名为x1,x2,x3,读者可以自行修改,退出编辑保存即可。

1.4 存在水印

描述:由于没有WPS会员,所以存在试用水印,通过java工具去掉即可。

2. 项目结构

2.1 pom.xml

xml 复制代码
 <!-- https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox -->
      <dependency>
        <groupId>org.apache.pdfbox</groupId>
        <artifactId>pdfbox</artifactId>
        <version>3.0.2</version>
      </dependency>

2.2 ClearWaterMarksUtils.java

java 复制代码
package org.liberx;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.io.RandomAccessReadBuffer;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * 清除 PDF 文件中的水印的工具类。
 * 此类使用 Apache PDFBox 库加载 PDF 文档并移除其水印。
 * 水印通过用空白图像替换原有图像来清除。
 */
public class ClearWaterMarksUtils {

    /**
     * 程序的入口点。
     * 加载指定路径的 PDF 文件,清除水印并保存为新的 PDF 文件。
     *
     * @param args 命令行参数,当前未使用
     */
    public static void main(String[] args) {
        try {
            // 加载 PDF 文档
            RandomAccessReadBuffer readPdf = new RandomAccessReadBuffer(ClearWaterMarksUtils.class.getResourceAsStream("/edit.pdf"));
            PDDocument document = Loader.loadPDF(readPdf);

            // 清除水印
            checkTextWatermark(document);

            // 保存修改后的 PDF路径
            document.save("无水印.pdf");
            document.close();
        } catch (IOException e) {
            // 处理 IO 异常
            throw new RuntimeException(e);
        }
    }

    /**
     * 检查并移除 PDF 文档中的文本水印。
     * 此方法会遍历 PDF 文档的每一页,并用空白图像替换所有图像对象,
     * 从而有效地去除水印。
     *
     * @param document 需要处理的 PDF 文档
     * @throws IOException 如果在处理文档时发生 IO 异常
     */
    private static void checkTextWatermark(PDDocument document) throws IOException {
        // 在内存中创建一个空白图像并验证其创建
        BufferedImage emptyImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
        if (emptyImage.getWidth() != 1 || emptyImage.getHeight() != 1) {
            throw new IOException("未能创建内存图像。");
        }

        // 将空白图像写入字节数组输出流
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ImageIO.write(emptyImage, "png", byteArrayOutputStream);
        byte[] imageData = byteArrayOutputStream.toByteArray();

        // 验证图像数据长度
        if (imageData.length == 0) {
            throw new IOException("图像数据为空,未能创建内存图像。");
        }

        // 从内存图像数据创建 PDImageXObject
        PDImageXObject blankImageXObject = PDImageXObject.createFromByteArray(document, imageData, "empty");
        if (blankImageXObject.getWidth() == 0 || blankImageXObject.getHeight() == 0) {
            throw new IOException("未能从内存图像数据创建 PDImageXObject。");
        }

        // 用空白图像替换 PDF 页面中的每个图像
        for (PDPage page : document.getPages()) {
            PDResources resources = page.getResources();
            for (COSName name : resources.getXObjectNames()) {
                resources.put(name, blankImageXObject); // 替换图像
                System.out.println("在页面上用空白图像替换了图像。");
            }
        }
    }
}

2.3 去除水印

操作: 运行ClearWaterMarksUtils的main方法,会生成如下无水印文件。

2.4 验证水印是否去除成功

描述:通过WPS打开无水印.pdf发现水印已经消失。

2.5 移动无水印.pdf位置

描述:移动到resources根目录下,因为我都是通过类加载pdf和font文件。

2.6 ModifyPDFForm.java

java 复制代码
package org.liberx;

import org.apache.fontbox.ttf.OTFParser;
import org.apache.fontbox.ttf.OpenTypeFont;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.io.RandomAccessRead;
import org.apache.pdfbox.io.RandomAccessReadBuffer;
import org.apache.pdfbox.pdfwriter.compress.CompressParameters;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 修改 PDF 表单中的字段内容的工具类。
 * 此类使用 Apache PDFBox 库加载 PDF 文档,修改表单字段的内容,并嵌入自定义字体。
 */
public class ModifyPDFForm {
    public static void dealPdf(String readPath, String outputPath,String fontPath,Map<String,String>fieldContentMap) {

        // 尝试加载 PDF 文档并修改表单字段
        try (
                // 使用随机访问读取 PDF 文件
                RandomAccessRead readPdf = new RandomAccessReadBuffer(ModifyPDFForm.class.getResourceAsStream(readPath));
                PDDocument document = Loader.loadPDF(readPdf) // 加载 PDF 文档
        ) {
            // 获取 PDF 表单
            PDAcroForm pdfForm = document.getDocumentCatalog().getAcroForm();
            if (pdfForm != null) {
                // 加载并嵌入字体
                try (RandomAccessRead readFont = new RandomAccessReadBuffer(ModifyPDFForm.class.getResourceAsStream(fontPath))) {

                  /*  OpenTypeFont otfFont = new OTFParser().parse(readFont); // 解析 OTF 字体
                    PDFont font = PDType0Font.load(document, otfFont, false); // 加载 OTF 字体*/

                    PDFont font = PDType0Font.load(document,readFont, false,false); //加载ttf

                    // 设置默认字体资源
                    PDResources resources = new PDResources();
                    resources.put(COSName.getPDFName("F1"), font); // 将字体添加到资源中
                    pdfForm.setDefaultResources(resources); // 设置 PDF 表单的默认字体资源

                    // 遍历所有表单字段并设置新内容
                    for (PDField field : pdfForm.getFields()) {
                        // 检查字段是否在定义的内容映射中
                        if (fieldContentMap.containsKey(field.getFullyQualifiedName())) {
                            if (field instanceof PDTextField) { // 确保字段是文本字段
                                PDTextField textField = (PDTextField) field;
                                // 设置文本字段的外观和内容
                                textField.setDefaultAppearance("/F1 12 Tf 0 g");  // 设置字体和字号
                                textField.setValue(fieldContentMap.get(field.getFullyQualifiedName())); // 设置字段值
                                textField.setReadOnly(true); // 将字段设置为只读
                            }
                        }
                    }
                } catch (IOException e) {
                    System.err.println("加载字体时出错: " + e.getMessage()); // 输出字体加载错误信息
                }
            } else {
                System.out.println("PDF 表单未找到!"); // 如果没有找到表单
            }
            // 保存修改后的 PDF
            document.save(outputPath, CompressParameters.NO_COMPRESSION); // 不压缩保存 PDF
            System.out.println("PDF 保存成功!"); // 输出保存成功信息
        } catch (IOException e) {
            System.out.println(e.getMessage()); // 输出 IO 异常信息
        }
    }

    public static void main(String[] args) {
        String filePath = "/无水印.pdf"; // PDF 文件路径
        String outputPath = "填充.pdf"; // 输出文件路径
        String fontPath = "/fonts/SourceHanSans-Regular.ttf";
        // 定义表单字段和对应的新内容
        Map<String, String> fieldContentMap = new HashMap<>();
        fieldContentMap.put("x1", "测试一下"); // 中文内容
        fieldContentMap.put("x2", "テスト");   // 日文内容
        fieldContentMap.put("x3", "대한민국English123456789"); // 韩文、英文、数字内容
        dealPdf(filePath,outputPath,fontPath,fieldContentMap);
    }
}

2.7 查看填充表单域结果

描述:填充表单域文件。

描述:通过WPS查看填充结果。

3 字体

source-han-sans-ttf
NotoSansCJKkr-Regular.otf

描述:字体只是测试字体,还是需要自行查找合适的字体,比较容易出现下面的错误。

Exception in thread "main" java.lang.IllegalArgumentException: No glyph for U+B300 (대) in font SourceHanSansCN-Regular

at org.apache.pdfbox.pdmodel.font.PDCIDFontType2.encode(PDCIDFontType2.java:404)...

本案例仅供学习交流使用。

相关推荐
hello_syz2 分钟前
lock4j 不生效的问题(个人原因导致的)
java·spring boot·spring·log4j
白总Server6 分钟前
OpenHarmony
后端·spring cloud·华为·微服务·ribbon·架构·系统架构
祁思妙想26 分钟前
6.《双指针篇》---⑥和为S的两个数字(中等但简单)(牛客)
java·数据结构·算法
hummhumm29 分钟前
Oracle 第19章:高级查询技术
java·数据库·python·sql·mysql·oracle·database
FserSuN44 分钟前
Apache Calcite - 查询优化之自定义优化规则
apache·calcite
LKID体1 小时前
docker加载目录中所有的镜像
java·docker·容器
小周不摆烂1 小时前
Java基础-组件及事件处理(上)
java·开发语言
hummhumm1 小时前
Oracle 第26章:Oracle Data Guard
java·大数据·前端·数据库·后端·python·oracle
哎呦没1 小时前
导师双选系统开发新解:Spring Boot技术
java·spring boot·后端
乌南竹1 小时前
Spring Boot接收参数的19种方式
数据库·spring boot·后端