PDF 表单常用于信息登记、申请表、调查问卷、合同确认和业务审批等场景。与普通 PDF 不同,表单型 PDF 中包含可交互的文本框、复选框、单选按钮、下拉列表等控件,可以通过程序自动填写。
在需要批量生成申请表、自动回填客户资料或从业务系统导出 PDF 表单时,手动逐个填写显然效率较低。通过 Java 读取 PDF 表单域并写入数据,可以将这类操作集成到后台服务、桌面程序或批处理流程中。
本文将介绍如何:
本文方法适用于包含可交互表单域的 PDF 文件。如果 PDF 只是扫描图片、普通文本页面,或者表单已经被扁平化,则无法直接通过表单字段 API 进行填写。
Java 标准库本身不提供 PDF 表单读取和填写功能,因此需要引入第三方 PDF 处理库。
本文示例使用 Spire.PDF for Java 读取 PDF 表单域,并填写不同类型的表单控件。
可以从以下页面下载安装包:
解压后,将 Spire.Pdf.jar 添加到 Java 项目的构建路径中。
在 IntelliJ IDEA 中,可以依次打开:
File > Project Structure > Modules > Dependencies
然后添加对应的 JAR 文件。
在 Eclipse 中,可以右键项目并依次选择:
Build Path > Configure Build Path > Libraries
然后添加 JAR 文件。
如果项目使用 Maven,可以在 pom.xml 中添加以下仓库和依赖配置:
<repositories>
<repository>
<id>com.e-iceblue</id>
<name>e-iceblue</name>
<url>https://repo.e-iceblue.cn/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.pdf</artifactId>
<version>12.6.4</version>
</dependency>
</dependencies>PDF 表单中可能包含多种控件。程序需要先判断字段的具体类型,再调用对应的方法写入内容。
表单类型 | 对应类 | 常用设置方法 |
|---|---|---|
文本框 | PdfTextBoxFieldWidget | setText() |
单选按钮组 | PdfRadioButtonListFieldWidget | setSelectedIndex() |
列表框 | PdfListBoxWidgetFieldWidget | setSelectedIndex() |
复选框 | PdfCheckBoxWidgetFieldWidget | setChecked() |
下拉框 | PdfComboBoxWidgetFieldWidget | setSelectedIndex() |
其中,列表框、下拉框和单选按钮通常通过选项索引进行设置。
需要注意,选项索引从 0 开始:
0 表示第一个选项1 表示第二个选项2 表示第三个选项下面的示例会读取 PDF 中的所有表单字段,并根据字段类型填写内容。
PdfDocument 对象。loadFromFile() 加载包含表单的 PDF。getForm() 获取 PDF 表单。import com.spire.pdf.FileFormat;
import com.spire.pdf.PdfDocument;
import com.spire.pdf.fields.PdfField;
import com.spire.pdf.widget.PdfCheckBoxWidgetFieldWidget;
import com.spire.pdf.widget.PdfComboBoxWidgetFieldWidget;
import com.spire.pdf.widget.PdfFormFieldWidgetCollection;
import com.spire.pdf.widget.PdfFormWidget;
import com.spire.pdf.widget.PdfListBoxWidgetFieldWidget;
import com.spire.pdf.widget.PdfRadioButtonListFieldWidget;
import com.spire.pdf.widget.PdfTextBoxFieldWidget;
public class FillFormFields {
public static void main(String[] args) {
String inputPath = "Forms.pdf";
String outputPath = "FillFormFields.pdf";
// 创建 PdfDocument 对象
PdfDocument document = new PdfDocument();
try {
// 加载包含表单域的 PDF 文件
document.loadFromFile(inputPath);
// 获取 PDF 表单
PdfFormWidget form =
(PdfFormWidget) document.getForm();
// 获取表单字段集合
PdfFormFieldWidgetCollection fields =
form.getFieldsWidget();
// 遍历并填写所有表单字段
for (int i = 0; i < fields.getCount(); i++) {
PdfField field = fields.get(i);
// 填写文本框
if (field instanceof PdfTextBoxFieldWidget) {
PdfTextBoxFieldWidget textBox =
(PdfTextBoxFieldWidget) field;
textBox.setText("Kaila Smith");
}
// 选择单选按钮组中的第二项
else if (
field instanceof PdfRadioButtonListFieldWidget
) {
PdfRadioButtonListFieldWidget radioButton =
(PdfRadioButtonListFieldWidget) field;
radioButton.setSelectedIndex(1);
}
// 选择列表框中的第一项
else if (
field instanceof PdfListBoxWidgetFieldWidget
) {
PdfListBoxWidgetFieldWidget listBox =
(PdfListBoxWidgetFieldWidget) field;
listBox.setSelectedIndex(0);
}
// 根据字段名称勾选复选框
else if (
field instanceof PdfCheckBoxWidgetFieldWidget
) {
PdfCheckBoxWidgetFieldWidget checkBox =
(PdfCheckBoxWidgetFieldWidget) field;
switch (checkBox.getName()) {
case "checkbox1":
case "checkbox2":
checkBox.setChecked(true);
break;
default:
break;
}
}
// 选择下拉框中的第二项
else if (
field instanceof PdfComboBoxWidgetFieldWidget
) {
PdfComboBoxWidgetFieldWidget comboBox =
(PdfComboBoxWidgetFieldWidget) field;
comboBox.setSelectedIndex(1);
}
}
// 保存填写后的 PDF
document.saveToFile(
outputPath,
FileFormat.PDF
);
System.out.println(
"PDF 表单填写完成,输出文件:"
+ outputPath
);
} finally {
// 关闭文档并释放资源
document.close();
}
}
}PdfDocument document = new PdfDocument();
document.loadFromFile("Forms.pdf");PdfDocument 用于表示和处理 PDF 文档。loadFromFile() 方法从指定路径加载文件。
PdfFormWidget form =
(PdfFormWidget) document.getForm();getForm() 用于获取 PDF 中的表单对象。
由于需要访问表单控件集合,因此将返回结果转换为 PdfFormWidget。
PdfFormFieldWidgetCollection fields =
form.getFieldsWidget();getFieldsWidget() 返回 PDF 中所有可交互表单字段的集合。
可以通过以下方法获取字段数量:
int fieldCount = fields.getCount();并根据索引获取指定字段:
PdfField field = fields.get(i);每个字段都继承或关联于 PdfField,但实际类型可能不同,因此需要使用 instanceof 判断。
例如:
if (field instanceof PdfTextBoxFieldWidget) {
PdfTextBoxFieldWidget textBox =
(PdfTextBoxFieldWidget) field;
textBox.setText("Kaila Smith");
}对于不同类型的字段,应转换为对应的 Widget 类,再调用专用设置方法。
document.saveToFile(
"FillFormFields.pdf",
FileFormat.PDF
);填写完成后,将文档保存为新的 PDF 文件,避免直接覆盖源文件。
前面的代码会为所有文本框填写相同的值:
textBox.setText("Kaila Smith");但在实际业务中,不同文本框通常对应姓名、地址、电话、邮箱等不同信息。此时应根据字段名称写入不同内容。
if (field instanceof PdfTextBoxFieldWidget) {
PdfTextBoxFieldWidget textBox =
(PdfTextBoxFieldWidget) field;
String fieldName = textBox.getName();
switch (fieldName) {
case "name":
textBox.setText("Kaila Smith");
break;
case "email":
textBox.setText("kaila@example.com");
break;
case "phone":
textBox.setText("13800000000");
break;
case "address":
textBox.setText(
"No. 100, Example Road"
);
break;
default:
break;
}
}这种方式比按照字段索引填写更可靠。
原因是表单设计发生变化后,字段顺序可能改变,但只要字段名称不变,程序仍然可以正确识别目标字段。
在编写填写逻辑之前,通常需要先了解 PDF 中有哪些字段,以及每个字段的名称和类型。
可以使用以下代码打印表单字段信息:
PdfFormWidget form =
(PdfFormWidget) document.getForm();
PdfFormFieldWidgetCollection fields =
form.getFieldsWidget();
for (int i = 0; i < fields.getCount(); i++) {
PdfField field = fields.get(i);
System.out.println(
"字段索引:" + i
);
System.out.println(
"字段名称:" + field.getName()
);
System.out.println(
"字段类型:"
+ field.getClass().getSimpleName()
);
System.out.println("--------------------");
}输出结果可能类似:
字段索引:0
字段名称:name
字段类型:PdfTextBoxFieldWidget
--------------------
字段索引:1
字段名称:gender
字段类型:PdfRadioButtonListFieldWidget
--------------------
字段索引:2
字段名称:checkbox1
字段类型:PdfCheckBoxWidgetFieldWidget
--------------------根据这些信息,可以更准确地编写字段填写规则。
PdfTextBoxFieldWidget textBox =
(PdfTextBoxFieldWidget) field;
textBox.setText("Kaila Smith");文本框通常用于填写:
PdfRadioButtonListFieldWidget radioButton =
(PdfRadioButtonListFieldWidget) field;
radioButton.setSelectedIndex(1);单选按钮组只能选择其中一个选项。
如果索引为 1,表示选择第二个选项。
PdfCheckBoxWidgetFieldWidget checkBox =
(PdfCheckBoxWidgetFieldWidget) field;
checkBox.setChecked(true);取消勾选时,将参数设置为 false:
checkBox.setChecked(false);PdfListBoxWidgetFieldWidget listBox =
(PdfListBoxWidgetFieldWidget) field;
listBox.setSelectedIndex(0);索引 0 表示选择列表中的第一项。
PdfComboBoxWidgetFieldWidget comboBox =
(PdfComboBoxWidgetFieldWidget) field;
comboBox.setSelectedIndex(1);索引 1 表示选择下拉列表中的第二项。
并非所有看起来像“表格”或“申请表”的 PDF 都包含可交互表单。
以下文件通常不能直接通过表单字段 API 填写:
可以先检查表单字段数量:
PdfFormWidget form =
(PdfFormWidget) document.getForm();
PdfFormFieldWidgetCollection fields =
form.getFieldsWidget();
if (fields.getCount() == 0) {
System.out.println(
"当前 PDF 中未检测到可填写的表单域。"
);
}以下写法依赖字段顺序:
PdfField field = fields.get(0);如果表单经过修改,字段顺序可能发生变化。
对于业务系统,建议优先通过字段名称匹配:
if ("email".equals(field.getName())) {
// 填写邮箱
}单选按钮、列表框和下拉框均通过索引选择项目。如果索引超出实际选项数量,可能导致异常或无法正确选择。
使用前应确认:
0 开始建议不要覆盖原始模板:
document.saveToFile(
"Forms_Filled.pdf",
FileFormat.PDF
);这样可以保留未填写的原始表单,便于重复使用。
无论填写是否成功,都应关闭 PdfDocument:
PdfDocument document =
new PdfDocument();
try {
document.loadFromFile("Forms.pdf");
// 填写和保存操作
} finally {
document.close();
}对于批量处理任务,这一点尤其重要。
可能原因包括:
可以先打印 fields.getCount(),确认表单字段数量。
因为示例代码对每个 PdfTextBoxFieldWidget 都执行了相同的:
textBox.setText("Kaila Smith");实际项目中应根据字段名称分别设置内容。
通过字段名称判断:
if (
field instanceof PdfTextBoxFieldWidget
&& "name".equals(field.getName())
) {
PdfTextBoxFieldWidget textBox =
(PdfTextBoxFieldWidget) field;
textBox.setText("Kaila Smith");
}setSelectedIndex() 使用从 0 开始的索引。
例如:
comboBox.setSelectedIndex(0);表示选择第一项,而不是第二项。
将 setChecked() 参数设置为 false:
checkBox.setChecked(false);可以。可以遍历文件夹中的 PDF 文件,为每个文件创建独立的 PdfDocument 对象,填写后分别保存。
批量处理时应注意:
通过 Java 可以自动读取和填写 PDF 中的交互式表单字段。
在实际项目中,建议先输出字段名称和类型,再根据字段名称分别填写内容。相比依赖字段索引,这种方式在表单模板发生调整后更加稳定。
同时需要确认源 PDF 确实包含可交互表单域。对于扫描件、普通页面或已扁平化表单,需要使用其他方式识别和写入内容。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。