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的主要内容,如果未能解决你的问题,请参考以下文章
Rails 为重复的 JSON 请求自动更新 CSRF 令牌