Dan*_*ldt 6 java pdf sign pdf-form pdfbox
我在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();
Run Code Online (Sandbox Code Playgroud)足以打破后来应用的签名代码.
另一方面,如果我使用相同的doc.pdf,在Adobe中手动输入字段的值,则签名代码会生成有效的签名.
我究竟做错了什么?
更新:
@mkl要求我提供文件,我正在谈论(我目前没有足够的声誉,将所有文件发布为链接,抱歉给您带来不便):
最后一个是通过一次性使用签署和填写文档来创建的
doc.saveIncremental();
Run Code Online (Sandbox Code Playgroud)
正如我已经在评论中写的那样
setNeedToBeUpdate(true);
Run Code Online (Sandbox Code Playgroud)
但似乎缺少了.参考@mkl的第二条评论,我发现了这个问题:使用PDFBOX生成的PDF中无法正确显示保存的文本字段值,这也包括某些输入的文本未显示.申请时,我先试了一下
setBoolean(COSName.getPDFName("NeedAppearances"), true);
Run Code Online (Sandbox Code Playgroud)
到字段和表单的字典,然后显示字段上下文,但签名最终没有添加.我仍然需要进一步研究.
更新: 故事在这里继续:PDFBox 1.8.10:填写并签署文档,再次填充失败
OP最初问题的原因,即用PDFBox加载他的PDF(用于表单填写)然后保存后,这个新的PDF无法使用PDFBox签名代码成功签名,已经在这个答案中详细解释了,简而言之:
定期保存文档时,PDFBox 使用交叉引用表来执行此操作。
在应用签名的过程中保存文档时,PDFBox 会创建增量更新;由于此类增量更新要求更新使用与原始修订版相同类型的交叉引用,因此 PDFBox 在这种情况下尝试使用相同的技术。
doc.pdf因此,在 OP 的原始 PDF具有交叉引用流的情况下:
加载并填写表格后,定期保存文档,即使用交叉引用表,但所有以前的交叉引用流条目(其中 Type )都会复制到预告片。( doc_filled.pdf)
使用交叉引用表加载此保存的 PDF 进行签名后,将使用增量更新再次保存它。PDFBox 假定(由于类型预告片条目)现有文件具有交叉引用流,因此也在增量更新结束时使用交叉引用流。( doc_filled_signed.pdf)
因此,最终填写并签名的 PDF 有两个修订版,内部修订版带有交叉引用表,外部修订版带有交叉引用流。
由于这是无效的,Adobe Reader 在加载 PDF 时会在其内部文档表示中修复此问题。修复更改文档字节。这样,Adobe Reader眼中的签名就被破坏了。
大多数其他签名验证器不会尝试进行此类修复,而是按原样检查文档的签名。他们成功验证了签名。
上面引用的答案也提供了一些解决此问题的方法:
答:加载用于填写表格的 PDF 后,请从预告片中删除“类型”条目,然后再定期保存。如果签名应用于此文件,PDFBox 将采用交叉引用表(因为不存在误导性的类型条目。因此,签名增量更新将有效。
B:也使用增量更新来保存表单填写更改,无论是在单独运行中还是在与签名相同的运行中。这也会导致有效的增量更新。
一般来说,我会建议后一个选项,因为如果 PDFBox 保存例程彼此兼容,前一个选项可能会损坏。
但不幸的是,后一个选项需要将添加和更改的对象标记为已更新,包括文档目录中的路径。如果这是不可能的或者至少太麻烦,那么第一个选项可能是更好的选择。
在当前的情况下,OP尝试了后一种选择(doc_filled_and_signed.pdf):
目前,只有选择文本框时,文本框的内容才可见(Acrobat reader 和 Preview 具有相同的行为)。我标记了 PDField、其所有父项、AcroForm、目录以及显示它的页面。
他将更改的字段标记为已更新,但未将 PDFBox 在设置表单字段值时自动生成的关联外观流标记为已更新。
因此,在结果 PDF 文件中,该字段具有新值,但具有旧的空外观流。仅当单击该字段时,Adobe Reader 才会根据编辑值创建新外观。
因此,OP还必须标记新的正常外观流(表单字段字典包含引用字典的条目AP,其中N引用正常外观流)。或者(如果查找更改或添加的条目变得太麻烦)他可能会尝试其他选项。