PDFBox:通过重复添加包含表单的单页模板来填写 PDF

Posted

技术标签:

【中文标题】PDFBox:通过重复添加包含表单的单页模板来填写 PDF【英文标题】:PDFBox: Fill out a PDF with adding repeatively a one-page template containing a form 【发布时间】:2018-05-11 08:42:23 【问题描述】:

遵循 SO 问题 Java pdfBox: Fill out pdf form, append it to pddocument, and repeat 我在将克隆页面附加到新 PDF 时遇到问题。

这个页面的代码看起来很有趣,但对我不起作用。

实际上,答案不起作用,因为这是您经常修改并添加到列表中的同一个 PDField。所以下次你用初始名称调用“getField”时,它不会找到它,你会得到一个 NPE。我尝试在 nice github 项目中使用相同的 pdfbox 版本(1.8.12),但不明白他是如何工作的。

我今天遇到了同样的问题,试图在其中包含不同值的页面上附加一个表单。我想知道解决方案是否不是复制字段,但不能成功地做到这一点。我总是以包含每个表单相同值的 PDF 结尾。

(我提供了 Mkl 模板文档的链接,但现在我删除了它,因为它不属于我)

编辑:按照 Mkl 的建议,我弄清楚了我缺少什么,但是复制每一页的性能真的很差。文件大小不令人满意。也许有一种方法可以优化这一点,重用 PDF 中的类似部分。

【问题讨论】:

"答案不起作用,因为这是您总是修改并添加到列表中的同一个 PDField。所以下次您使用初始名称调用 'getField' 时,它不会找到它,你就会得到一个 NPE。” - 请再看一遍代码,它 相同的 PDField,它是具有相同原始名称的字段,来自 新加载的原始源文档的副本,因此如果第一次找到该字段,它将一次又一次地找到。因此,考虑到您的错误分析,您可能没有使用该答案中的原始代码,而是使用了它的某种损坏版本。请出示。 也请分享您的模板文件。可能该字段根本不存在,您对 NPE 情况的分析是从第一次访问该字段而不是第二次开始,因此您误解了该情况。 感谢您的快速回复!你对新加载的副本是正确的,我错过了。因为在我的代码中,我尝试重用加载一次的同一个 PDDocument。这似乎是合理的,因为它永远不会改变,但我猜这个过程会改变它。我再次尝试使用信息并很快回来。 是的,它可以工作,但正如我所料,性能真的很差,而且最重要的是,PDF 文件的大小会随着页数的增加而增加。我认为我可以重用相同的模板,以使用基于小部件复制的代码节省一些 Mb(例如:***.com/questions/39260500/…)。如果这实际上是可能的,我可以发布一些代码来了解我的期望是否现实? 嗯。你能分享你的模板文件吗?根据实际使合并文件变大的原因,防止重复数据或多或少容易。例如。如果应该很容易让所有导入的页面使用相同的页面资源对象。不同的优化将有助于防止重复的注释资源。 【参考方案1】:

最后我不用每次都重新加载模板就可以正常工作了。所以生成的文件是我想要的:不太大(164 页为 4Mb)。 我想我之前犯过 2 个错误:一个是关于页面创建的,一个可能是关于字段复制的。 所以这里是工作代码,如果有人碰巧遇到同样的问题。

表单创建:

    PDAcroForm finalForm = new PDAcroForm(finalDoc, new COSDictionary());
    finalForm.setDefaultResources(originForm.getDefaultResources())

页面创建:

    PDPage clonedPage = templateDocument.getPage(0);

    COSDictionary clonedDict = new COSDictionary(clonedPage.getCOSObject());

    clonedDict.removeItem(COSName.ANNOTS);
    clonedPage = new PDPage(clonedDict);
    finalDoc.addPage(clonedPage);

字段重复:(将字段重命名为唯一并设置值)

    PDTextField field = (PDTextField) originForm.getField(fieldName);
    PDPage page = finalDoc.getPages().get(nPage);
    PDTextField clonedField = new PDTextField(finalForm);
    List<PDAnnotationWidget> widgetList = new ArrayList<>();
    for (PDAnnotationWidget paw : field.getWidgets()) 
        PDAnnotationWidget newWidget = new PDAnnotationWidget();
        newWidget.getCOSObject().setString(COSName.DA,  paw.getCOSObject().getString(COSName.DA));
        newWidget.setRectangle(paw.getRectangle());
        widgetList.add(newWidget);
    
    clonedField.setQ(field.getQ()); // To get text centered
    clonedField.setWidgets(widgetList);
    clonedField.setValue(value);
    clonedField.setPartialName(fieldName + cnt++);
    fields.add(clonedField);

    page.getAnnotations().addAll(clonedField.getWidgets());

在流程结束时:

    finalDoc.getDocumentCatalog().setAcroForm(finalForm);
    finalForm.setFields(fields);
    finalForm.flatten();

【讨论】:

我认为该代码可能存在问题:您完成了生成 clonedField 的步骤,但随后重命名了原始 field 并将其添加到 AcroForm 和页面。 其实你是对的 ;) 我只是错过了我的复制/粘贴操作。这是因为我的真实代码与我在这里编写的方法相比略有不同。但我只是对其进行了编辑,所以它现在是对原作的准确反映。再次感谢您的帮助! 为了获得更好的结果,您还应该将模板文档的默认资源导入结果文档的AcroForm 实际上,我像这样创建我的最终 AcroForm: this.finalForm = new PDAcroForm(finalDoc, new COSDictionary()); finalForm.setDefaultResources(originForm.getDefaultResources());你的意思是“导入默认资源”吗?也许我还可以改进......但那将是另一天。 “你的意思是“导入默认资源”吗?” - 是的。您应该将其添加到答案中,因为它很重要。

以上是关于PDFBox:通过重复添加包含表单的单页模板来填写 PDF的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 pdfbox 用特定字体填写 PDF 表单?

弹出确认框[重复]

Rails 为重复的 JSON 请求自动更新 CSRF 令牌

Google Analytics:统一包含 ID 的单页应用程序 URL

如何创建和填写 PDF 表单

在django中将注册表单填写到数据库后如何存储用户选择的单选按钮值?