PDFBox表单填写 – saveIncremental不起作用

我有一个pdf文件,其中包含一些我希望从java填写的表单字段。 现在我正试图填写一个我正在寻找的forms。 我的代码如下所示:

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文件中,新值未显示(显示原始字符串),因此我猜增量保存不能正常工作。 我错过了什么?

我使用的是pdfbox的2.0.2版本,这里是我工作的pdf文件: 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

另一方面,必须扩展主代码以标记从目录到fields数组的链:

 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 test testFillInSkierowanie3

注意:与通用PDF一起使用时,显然应该引入一些null测试…


不幸的是,在Adobe Reader中打开结果文件会看到程序抱怨有关禁用文件中扩展function的更改。

这是由于PDFBox的增量保存中的怪癖,它需要更新部分中的一些不必要的对象。 特别是目录保存在那里,其中包含使用权签名(授予扩展function的技术)。 重新保存的签名显然不再处于原始版本的原始位置。 因此,无效。

很可能OP OP想要逐步保存PDF以破坏此签名,但PDFBox不允许这样做。 那好吧…

因此,唯一能做的就是通过完全删除签名来防止警告。

删除使用权签名

我们已经在上面的附加内容中检索了目录对象,因此删除签名很容易:

 COSDictionary dictionary = document.getDocumentCatalog().getCOSObject(); // vvv--- new dictionary.removeItem(COSName.PERMS); // ^^^--- new dictionary.setNeedToBeUpdated(true); 

( FillInFormSaveIncremental test testFillInSkierowanie3


不幸的是,在Adobe Reader中打开结果文件会发现该程序抱怨文件中缺少扩展function以保存它。

这是因为Adobe Reader需要扩展function来保存对XFA表单的更改,我们必须在此步骤中删除扩展function。

但是,手头的文档是混合的AcroForm和XFA表单文档,Adobe Reader不需要扩展function来保存AcroForm文档。 因此,我们所要做的就是删除XFA表单。 因为我们的代码只设置了AcroForm值,所以无论如何这都是一个好主意……

删除XFA表单

我们已经在上面的添加中检索了acroform对象,因此从那里删除引用的XFA表单很容易:

 dictionary = (COSDictionary) dictionary.getDictionaryObject(COSName.ACRO_FORM); // vvv--- new dictionary.removeItem(COSName.XFA); // ^^^--- new dictionary.setNeedToBeUpdated(true); 

( FillInFormSaveIncremental test testFillInSkierowanie3


在Adobe Reader中打开结果文件,可以看到现在可以不用再编辑表单并保存文件了。

请注意,这需要一个足够新的Adobe Reader版本,早期版本(至少版本9)确实需要扩展function,即使是为了保存对AcroForm表单的更改