PDF 表单填写完成后,有时需要保留表单域但禁止继续修改,有时则需要直接移除字段的交互属性,生成用于提交或归档的最终文件。
在 Java 中,可以将整个 PDF 表单或指定表单域设置为只读,也可以将全部或指定表单域扁平化。本文分别介绍这几种处理方法,并说明只读和扁平化在实际使用中的区别。
添加 PDF 处理库
下面的示例通过 Spire.PDF for Java 处理 PDF 表单。使用 Maven 时,可在 pom.xml 中添加其 Maven 仓库和依赖:
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.8.6</version>
</dependency>
</dependencies>
使用 Java 将 PDF 表单设置为只读
如果需要保留表单域及其中的数据,但不允许用户继续填写,可以通过 PdfFormWidget.setReadOnly() 将整个表单设置为只读。
java
import com.spire.pdf.PdfDocument;
import com.spire.pdf.widget.PdfFormWidget;
public class SetFormReadOnly {
public static void main(String[] args) {
// 加载 PDF 表单
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile("申请表.pdf");
// 获取表单并设置为只读
PdfFormWidget form = (PdfFormWidget) pdf.getForm();
form.setReadOnly(true);
// 保存结果
pdf.saveToFile("申请表_只读.pdf");
pdf.close();
}
}
设置为只读以后,表单域仍然保留在 PDF 中,因此后续程序仍然可以获取字段名称、读取字段值或进行其他表单处理。
如果只需要限制某一个字段,可以先取得对应的 PdfField,再调用 setReadOnly():
java
import com.spire.pdf.PdfDocument;
import com.spire.pdf.fields.PdfField;
import com.spire.pdf.widget.PdfFormWidget;
public class SetFieldReadOnly {
public static void main(String[] args) {
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile("申请表.pdf");
PdfFormWidget form = (PdfFormWidget) pdf.getForm();
// 根据字段名称获取表单域
PdfField field = form.getFieldsWidget().get("RequestAmount");
if (field != null) {
field.setReadOnly(true);
}
pdf.saveToFile("申请表_部分只读.pdf");
pdf.close();
}
}
按字段处理时,代码中使用的是 PDF 内部的字段名称,而不是页面上显示的标题。如果模板由其他人员制作,可以先读取字段名称,再确认程序需要操作的目标字段。
例如:
ini
PdfFormWidget form = (PdfFormWidget) pdf.getForm();
for (int i = 0; i < form.getFieldsWidget().getCount(); i++) {
PdfField field = form.getFieldsWidget().get(i);
System.out.println(field.getName());
}
对于经常更新的 PDF 模板,最好不要依赖字段索引,因为字段顺序变化后,原来的索引可能对应到其他字段。
使用 Java 扁平化 PDF 中的所有表单域
如果不需要继续保留表单结构,可以通过 isFlatten(true) 将整个表单扁平化。
typescript
import com.spire.pdf.PdfDocument;
public class FlattenForm {
public static void main(String[] args) {
// 加载填写完成的 PDF 表单
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile("审批表.pdf");
// 扁平化所有表单域
pdf.getForm().isFlatten(true);
// 保存结果
pdf.saveToFile("审批表_扁平化.pdf");
pdf.close();
}
}
扁平化后,字段当前显示的内容会保留在 PDF 页面中,但原来的文本框、复选框、下拉列表等交互式表单域将不再作为可填写字段使用。
因此,如果程序还需要读取或修改表单数据,应在扁平化之前完成。
使用 Java 扁平化指定的表单域
也可以通过 PdfField.setFlatten() 只处理指定字段,而保留其他表单域。
java
import com.spire.pdf.PdfDocument;
import com.spire.pdf.fields.PdfField;
import com.spire.pdf.widget.PdfFormWidget;
public class FlattenField {
public static void main(String[] args) {
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile("申请表.pdf");
PdfFormWidget form = (PdfFormWidget) pdf.getForm();
// 获取指定表单域
PdfField field = form.getFieldsWidget().get("RequestAmount");
if (field != null) {
field.setFlatten(true);
}
pdf.saveToFile("申请表_部分扁平化.pdf");
pdf.close();
}
}
这种方式适合只需要固定部分字段内容,而其他字段仍需继续填写的情况。
在实际代码中,建议检查字段是否存在。否则模板字段名称发生变化后,后续操作容易直接出现空指针异常。
只读和扁平化有什么区别?
只读和扁平化都可以让用户无法正常修改字段,但两者对 PDF 表单结构的处理不同。
| 对比项 | 设置只读 | 扁平化 |
|---|---|---|
| 保留表单域 | 是 | 否 |
| 用户可继续填写 | 否 | 否 |
| 可继续按字段名称读取数据 | 是 | 否 |
| 可重新修改字段属性 | 是 | 否 |
如果 PDF 后面仍然需要参与数据读取、表单校验或其他自动化处理,保留字段并设置为只读通常更方便。
如果 PDF 已经不再作为表单使用,只需要保留最终显示结果,则可以进行扁平化。
另外,这两个功能都只针对表单域本身。将字段设置为只读或进行扁平化,并不等同于限制整个 PDF 文件的编辑、复制或打印。如果需要控制这些权限,应另外使用 PDF 权限设置;如果需要验证文件内容是否被修改,则应使用数字签名。
使用时需要注意的几点
处理固定模板时,建议优先通过字段名称定位表单域,而不是依赖字段索引。对于由外部提供或经常调整的模板,可以在开发阶段先输出所有字段名称,确认内部字段定义。
扁平化还应该放在字段读取、赋值和校验之后。一旦生成最终的扁平化文件,后续逻辑就不应该继续依赖原来的表单域结构。
如果程序部署在服务器上,还需要检查扁平化后的实际显示结果,尤其是使用中文字体、自定义字体或特殊符号的字段。开发环境和服务器字体环境不同,有时会导致最终页面的文字显示发生变化。
结语
对于需要长期维护的业务系统,最好保留原始的可编辑 PDF,同时另存只读版或扁平化后的最终文件。这样即使后续模板、数据或业务流程发生变化,也不必尝试从已经处理过的文件中恢复原来的表单结构。