我在PDF文档中填写(以编程方式)表单(AcroPdf),然后在文档中签名。我从doc.pdf开始,使用PDFBox的setFields.java示例创建doc_filled.pdf。然后我签署doc_filled.pdf,使用一些代码创建doc?filled_signed.pdf,基于签名示例并在Acrobat Reader中打开pdf。输入的Field数据可见,签名面板告诉我
"此签名中包含的格式或信息存在错误(签名字节数组无效)"
到目前为止,我知道:
问题甚至发生,如果我不填写表格,但只打开并保存,即:
PDDocument doc = PDDocument.load(new File("doc.pdf"));
doc.save(new File("doc_filled.pdf"));
doc.close();
足以打破后来应用的签名代码。
另一方面,如果我使用相同的doc.pdf,在Adobe中手动输入字段值,则签名代码会生成有效的签名。
我做错了什么?
更新
@mkl让我提供文件,我说的是(我目前没有足够的声誉,将所有文件发布为链接,抱歉给您带来不便):
通过使用
一次签署并填写文档来创建最后一个 doc.saveIncremental();
正如我在评论中写的那样,有些
setNeedToBeUpdate(true);
但是,似乎缺少了。
参考@mkl的第二条评论,我发现了这一点
问题:Saved Text Field value is not displayed properly in PDF generated using PDFBOX,其中也包含一些未显示的输入文字。我试了一下第一次尝试
setBoolean(COSName.getPDFName("NeedAppearances"), true);
到字段和表单字典,然后显示字段上下文,但最终不会添加签名。我仍然需要进一步研究。
更新 故事在这里继续:PDFBox 1.8.10: Fill and Sign Document, Filling again fails
答案 0 :(得分:2)
OP原始问题的原因,即在用PDFBox加载PDF(用于表单填写)然后保存之后,这个新PDF无法使用PDFBox签名代码成功签名,已经存在在this answer中详细解释,简而言之:
定期保存文档时,PDFBox使用交叉引用表进行保存。
在应用签名的过程中保存文档时,PDFBox会创建增量更新;因为这样的增量更新要求更新使用与原始版本相同类型的交叉引用,在这种情况下,PDFBox会尝试使用相同的技术。
因此,对于OP的原始PDF doc.pdf
,它有一个交叉引用流:
加载并填写表格后,文档会定期保存,即使用交叉引用表,但所有以前的交叉引用流条目(其中类型)都会被复制到预告片。 (doc_filled.pdf
)
将此保存的PDF加载到交叉引用表进行签名后,将使用增量更新再次保存。 PDFBox假定(由于类型预告片条目)现有文件具有交叉引用流,因此在增量更新结束时也使用交叉引用流。 (doc_filled_signed.pdf
)
因此,最后填写的,然后签名的PDF有两个修订版,内部版本带有交叉引用表,外部版本带有交叉引用流。
由于这是无效的,Adobe Reader在加载PDF时会在其内部文档表示中对其进行修复。修复会更改文档字节。因此,Adobe Reader眼中的签名被打破了。
大多数其他签名验证人都不会尝试进行此类修复,但会按原样检查文档的签名。他们成功验证了签名。
The answer referenced above也提供了一些解决方法:
答:在加载表单填写的PDF后,请在预定保存之前从预告片中删除类型条目。如果对此文件应用了签名,则PDFBox将采用交叉引用表(因为误导性类型条目不存在。因此,签名增量更新将有效。
B:使用增量更新来保存表单填写更改,无论是在单独运行中还是在与签名相同的运行中。这也会导致有效的增量更新。
通常我会提出后一种选择,因为如果PDFBox保存例程彼此兼容,前一选项可能会中断。
不幸的是,后一个选项要求将添加和更改的对象标记为已更新,包括文档目录中的路径。如果这不可能或至少太麻烦,第一个选项可能更好。
在手头的情况下,OP尝试了后一种选择(doc_filled_and_signed.pdf
):
此刻,文本框的内容仅在选中文本框时可见(使用Acrobat Reader和预览相同的行为)。我标记了PDField,它的所有父项,AcroForm,目录以及显示它的页面。
他将更改的字段标记为已更新,但未将相关的外观流标记为PDFBox在设置表单字段值时自动生成。
因此,在结果PDF文件中,该字段具有新值,但旧的空外观流。只有在单击该字段时,Adobe Reader才会根据要编辑的值创建新外观。
因此,OP还必须标记新的正常外观流(表单字段字典包含一个条目 AP 引用字典,其中 N 引用正常外观流)。或者(如果发现更改或添加的条目变得太麻烦),他可能会尝试其他选项。