我有一个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文件中,新值未显示(显示原始字符串),因此我猜增量保存无法正常工作。我错过了什么?
我使用的是pdfbox的2.0.2版本,这里是我工作的pdf文件:pdf
答案 0 :(得分:5)
将PDF的更改保存为PDFBox 2.0.x的增量更新时,必须为每个更改的PDF对象将属性NeedToBeUpdated
设置为true
。此外,必须通过一系列引用从PDF目录中访问该对象,并且此链中的每个PDF对象还必须将属性NeedToBeUpdated
设置为true
。
这是由于PDFBox以递增方式保存的方式,从它检查NeedToBeUpdated
属性的目录开始,如果它设置为true
,PDFBox存储该对象,并且仅在这种情况下它更深入地处理从此对象引用的对象,以搜索更多要存储的对象。
特别是这意味着某些对象不必要被标记为NeedToBeUpdated
,例如PDF目录本身,在某些情况下甚至会破坏增量更新的目的,见下文。
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中打开结果文件遗憾的是,程序会抱怨有关禁用文件中扩展功能的更改。
这是由于PDFBox的增量保存中的怪癖,它需要更新部分中的一些不必要的对象。特别是目录保存在那里,其中包含使用权签名(授予扩展功能的技术)。重新保存的签名显然不再处于原始版本的原始位置。因此,无效。
OP OP很可能希望将PDF逐步保存到而不是打破此签名,但PDFBox不允许这样做。哦,好吧......
因此,唯一能做的就是通过完全删除签名来防止警告。
我们已经在上面的附加内容中检索了目录对象,因此删除签名很简单:
COSDictionary dictionary = document.getDocumentCatalog().getCOSObject();
// vvv--- new
dictionary.removeItem(COSName.PERMS);
// ^^^--- new
dictionary.setNeedToBeUpdated(true);
(FillInFormSaveIncremental test testFillInSkierowanie3
)
不幸的是,在Adobe Reader中打开结果文件会发现该程序抱怨文件中缺少扩展功能以保存它。
这是因为Adobe Reader需要扩展功能来保存对XFA表单的更改,我们必须在此步骤中删除扩展功能。
但手边的文件是混合AcroForm& XFA表单文档,Adobe Reader不需要扩展功能来保存AcroForm文档。因此,我们所要做的就是删除XFA表单。由于我们的代码只设置了AcroForm值,所以无论如何这都是个好主意......
我们已经在上面的添加中检索了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)确实需要扩展功能,即使保存对AcroForm表单的更改