当前位置: 首页 > 知识库问答 >
问题:

PDFBox表单填充-保存增量不起作用

常睿范
2023-03-14

我有一个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");
    }
}

作为系统。out状态下,该值设置正确,但在生成的pdf文件中,新值未显示(显示原始字符串),因此我猜增量保存无法正常工作。我错过了什么?

我使用2.0.2版本的pdfbox,下面是我使用的pdf文件:pdf

共有1个答案

柳胜
2023-03-14

使用PDFBox 2.0将更改保存为PDF增量更新时。x、 对于每个更改的PDF对象,必须将属性NeedToBeUpdated设置为true。此外,该对象必须可以通过引用链从PDF目录中访问,并且该链中的每个PDF对象还必须将属性needtoUpdated设置为true

这是由于PDFBox以增量方式保存的方式,从它检查NeedToBeUpated属性的曲库开始,如果它设置为true,PDFBox将存储该对象,并且只有在这种情况下,它才会更深入地递归到从此对象引用的对象中,以搜索要存储的更多对象。

特别是,这意味着某些对象不必要地必须标记NeedToBeUpdate,例如PDF曲库本身,在某些情况下,这甚至破坏了增量更新的目的,见下文。

一方面,必须扩展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方法设置字段

另一方面,必须扩展主html" target="_blank">代码,以标记从目录到字段数组的链:

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 testtestFillInSkierowanie3

注意:对于通用PDF,显然应该引入一些null测试。。。

在Adobe Reader中打开结果文件会不幸地看到程序抱怨禁用文件中扩展功能的更改。

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

OP很可能希望以增量方式保存PDF,以避免破坏此签名,但PDFBox不允许这样做。哦,好吧。。。

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

我们已经在上面的添加中检索到了catalog对象,因此删除签名很容易:

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

(FillInFormSaveIncremental testtestFillInSkierowanie3

不幸的是,在Adobe Reader one中打开结果文件时,程序会抱怨文件中缺少用于保存的扩展功能。

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

但手头的文档是混合AcroForm

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

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

(FillInFormSaveIncremental testtestFillInSkierowanie3

在AdobeReader中打开结果文件,您将看到现在可以无需进一步的ado即可编辑表单并保存文件。

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

 类似资料:
  • 当我使用这个长字段名时,我没有得到任何错误,但是得到的PDF不包含我放在字段中的值。我想可能字段名有问题,所以我使用了Pdftk工具,它只给出了作为字段名。但是当我仅仅使用它时,我会得到error。救命?

  • 我的PDFBox有问题。我有一个PDF格式的空白页,我想在其中插入图像。因为我也使用签名PDF,所以所有更改都必须保存为“saveIncremental”。 当我只插入一个图像时,一切都很好(图像已被插入)。当我试图在这个PDF中插入另一个图像时,它没有被插入,当在Adobe Acrobat Reader中打开时,它说“此页面存在错误。Adobe可能无法正确显示页面...”。 奇怪的事情-当PDF

  • 在Thymeleaf中,我可以使用 但是我不知道如何输出到表单动作属性中。 有人对Thymeleaf超文本标记语言有什么想法吗? 样例JavaSpring-MVC控制器代码

  • 问题内容: 我无法手动或自动在新保存的对象上填充创建者字段……我能找到的唯一方法是重新查询我已经想要做的对象。 这是设置: 这是我拉头发的地方 编辑:最新的猫鼬解决了此问题并添加了填充功能,请参见新的接受的答案。 问题答案: 您应该能够使用模型的填充函数来执行此操作:http : //mongoosejs.com/docs/api.html#model_Model.populate 在书籍的保存处

  • 您是否设法在横向模式下填充PDF文档? 谢谢你的帮助。

  • 我在用ApachePDFBox填写PDF表格。我使用的是TrueType字体(不是默认字体),叫做“Impact”,非常标准。在模板中,我有一个名为“Title”的字段,指定了影响字体。我使用下面的代码获取该模板,并用一个包含多个单词的值填充该字段。 问题是,当你查看创建的PDF时,单词之间有很大的空格。如果在Acrobat中打开PDF并单击该字段,文本将发生变化,大间距将消失。以任何方式编辑该字