我有一个 pdf 文件,其中包含一些我想从 java 中填写的表单字段。现在我正试图填写一张我通过它的名字找到的表格。我的代码如下所示:
File file = new File("c:/Testy/luxmed/Skierowanie3.pdf");
PDDocument document = PDDocument.load(file);
PDDocumentCatalog doc = document.getDocumentCatalog();
PDAcroForm Form = doc.getAcroForm();
String formName = "topmostSubform[0].Page1[0].pana_pania[0]";
PDField f = Form.getField(formName);
setField(document, formName, "Artur");
System.out.println("New value 2nd: " + f.getValueAsString());
document.saveIncremental(new FileOutputStream("c:/Testy/luxmed/nowy_pd3.pdf"));
document.close();
还有这个:
public static void setField(PDDocument pdfDocument, String name, String Value) throws IOException
{
PDDocumentCatalog docCatalog = pdfDocument.getDocumentCatalog();
PDAcroForm acroForm = docCatalog.getAcroForm();
PDField field = acroForm.getField(name);
if (field instanceof PDCheckBox){
field.setValue("Yes");
}
else if (field instanceof PDTextField){
System.out.println("Original value: " + field.getValueAsString());
field.setValue(Value);
System.out.println("New value: " + field.getValueAsString());
}
else{
System.out.println("Nie znaleziono pola");
}
}
如 system.out 所述,值设置正确,但在新生成的 pdf 文件中,新值未显示(显示原始字符串)所以我猜增量保存无法正常工作。我错过了什么?
我使用 2.0.2 版的 pdfbox,这是我使用的 pdf 文件:pdf
最佳答案
一般情况
当使用 PDFBox 2.0.x 将更改保存为增量更新时,您必须为每个更改的 PDF 对象将属性 NeedToBeUpdated
设置为 true
。此外,该对象必须可以通过引用链从 PDF 目录访问,并且该链中的每个 PDF 对象还必须将属性 NeedToBeUpdated
设置为 true
。
这是由于 PDFBox 增量保存的方式,从目录开始它检查 NeedToBeUpdated
属性,如果它设置为 true
,PDFBox 存储对象,只有在这种情况下,它才会更深入地递归到从此对象引用的对象中,以搜索要存储的更多对象。
特别是这意味着一些对象不必要地被标记为NeedToBeUpdated
,例如PDF 目录本身,在某些情况下,这甚至完全违背了增量更新的目的,请参见下文。
如果是 OP 的文档
设置NeedToBeUpdated
属性
一方面,必须扩展 setField
方法来标记字段字典链,包括更改的字段和外观:
public static void setField(PDDocument pdfDocument, String name, String Value) throws IOException
{
PDDocumentCatalog docCatalog = pdfDocument.getDocumentCatalog();
PDAcroForm acroForm = docCatalog.getAcroForm();
PDField field = acroForm.getField(name);
if (field instanceof PDCheckBox) {
field.setValue("Yes");
}
else if (field instanceof PDTextField) {
System.out.println("Original value: " + field.getValueAsString());
field.setValue(Value);
System.out.println("New value: " + field.getValueAsString());
}
else {
System.out.println("Nie znaleziono pola");
}
// vvv--- new
COSDictionary fieldDictionary = field.getCOSObject();
COSDictionary dictionary = (COSDictionary) fieldDictionary.getDictionaryObject(COSName.AP);
dictionary.setNeedToBeUpdated(true);
COSStream stream = (COSStream) dictionary.getDictionaryObject(COSName.N);
stream.setNeedToBeUpdated(true);
while (fieldDictionary != null)
{
fieldDictionary.setNeedToBeUpdated(true);
fieldDictionary = (COSDictionary) fieldDictionary.getDictionaryObject(COSName.PARENT);
}
// ^^^--- new
}
( FillInFormSaveIncremental 方法 setField
)
另一方面,必须扩展主要代码以标记从目录到字段数组的链:
PDDocument document = PDDocument.load(...);
PDDocumentCatalog doc = document.getDocumentCatalog();
PDAcroForm Form = doc.getAcroForm();
String formName = "topmostSubform[0].Page1[0].pana_pania[0]";
PDField f = Form.getField(formName);
setField(document, formName, "Artur");
System.out.println("New value 2nd: " + f.getValueAsString());
// vvv--- new
COSDictionary dictionary = document.getDocumentCatalog().getCOSObject();
dictionary.setNeedToBeUpdated(true);
dictionary = (COSDictionary) dictionary.getDictionaryObject(COSName.ACRO_FORM);
dictionary.setNeedToBeUpdated(true);
COSArray array = (COSArray) dictionary.getDictionaryObject(COSName.FIELDS);
array.setNeedToBeUpdated(true);
// ^^^--- new
document.saveIncremental(new FileOutputStream(...));
document.close();
( FillInFormSaveIncremental 测试 testFillInSkierowanie3
)
注意:为了与通用 PDF 一起使用,显然应该引入一些 null
测试...
不幸的是,在 Adobe Reader 中打开结果文件会看到程序提示禁用文件中扩展功能的更改。
这是由于 PDFBox 的增量保存中的一个怪癖,它在更新部分需要一些不必要的对象。特别是目录保存在那里,其中包含使用权签名(授予扩展功能的技术)。重新保存的签名显然已经不在原来修改的位置了。因此,无效。
OP OP 很可能希望以增量方式保存 PDF 以不破坏此签名,但 PDFBox 不允许这样做。哦好吧...
因此,唯一能做的就是通过完全删除签名来防止警告。
删除使用权限签名
我们已经在上面的添加中检索了目录对象,因此删除签名很容易:
COSDictionary dictionary = document.getDocumentCatalog().getCOSObject();
// vvv--- new
dictionary.removeItem(COSName.PERMS);
// ^^^--- new
dictionary.setNeedToBeUpdated(true);
( FillInFormSaveIncremental 测试 testFillInSkierowanie3
)
不幸的是,在 Adobe Reader 中打开结果文件会看到该程序提示文件中缺少扩展功能以保存它。
这是因为 Adobe Reader 需要扩展功能来保存对 XFA 表单的更改,我们必须在此步骤中删除扩展功能。
但手头的文档是混合的 AcroForm & XFA 表单文档,Adobe Reader 不需要扩展功能来保存 AcroForm 文档。因此,我们所要做的就是删除 XFA 表单。由于我们的代码仅设置 AcroForm 值,因此无论如何这是一个好主意...
删除 XFA 表单
我们已经在上面的添加中检索到了 acroform 对象,因此从那里删除引用的 XFA 表单很容易:
dictionary = (COSDictionary) dictionary.getDictionaryObject(COSName.ACRO_FORM);
// vvv--- new
dictionary.removeItem(COSName.XFA);
// ^^^--- new
dictionary.setNeedToBeUpdated(true);
( FillInFormSaveIncremental 测试 testFillInSkierowanie3
)
在 Adobe Reader 中打开结果文件,您会发现现在可以毫不费力地编辑表格并保存文件。
注意,为此需要足够新的 Adobe Reader 版本,早期版本(至少版本 9)确实需要扩展功能,即使是将更改保存到 AcroForm 表单也是如此
关于java - PDFBox 表单填写 - saveIncremental 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42802996/