6

我(以编程方式)在 PDF 文档中填写表格(AcroPdf),然后在文档上签名。我从 doc.pdf 开始,使用 PDFBox 的 setFields.java 示例创建 doc_filled.pdf。然后我签署 doc_filled.pdf,创建 doc?filled_signed.pdf,使用一些代码,基于签名示例并在 Acrobat Reader 中打开 pdf。输入的字段数据可见,签名面板告诉我

“此签名中包含的格式或信息存在错误(签名字节数组无效)”

到目前为止,我知道:

  • 单独应用的签名代码(即直接创建一些 doc_signed.pdf)创建一个有效的签名
  • 添加到现有签名字段的“不可见签名”、可见签名和可见签名存在问题。
  • 如果我不填写表格,而只是打开并保存它,甚至会出现问题,即:

    PDDocument doc = PDDocument.load(new File("doc.pdf"));
    doc.save(new File("doc_filled.pdf"));
    doc.close();
    

足以破坏后来应用的签名代码。

另一方面,如果我采用相同的 doc.pdf,在 Adob​​e 中手动输入字段值,签名代码会生成有效签名。

我究竟做错了什么?

更新:

@mkl 要求我提供文件,我正在谈论(我目前没有足够的声誉,无法将所有文件作为链接发布,抱歉给您带来不便):

最后一个是通过一次性签署和填写文档来创建的,使用

    doc.saveIncremental(); 

正如我已经在评论中写的,一些

    setNeedToBeUpdate(true);

不过,似乎不见了。参考 @mkl 的第二条评论,我发现了这个 SO 问题:Saved Text Field value is not displayed proper in PDF generated using PDFBOX,这也涵盖了一些未显示的输入文本。我第一次尝试,申请

    setBoolean(COSName.getPDFName("NeedAppearances"), true); 

到字段和表单的字典,然后显示字段上下文,但最后没有添加签名。我仍然需要进一步研究。

更新: 故事在这里继续:PDFBox 1.8.10:填写并签署文档,再次填写失败

4

1 回答 1

3

OP原始问题的原因,即用PDFBox加载他的PDF(用于填写表格)然后保存后,这个新的PDF无法使用PDFBox签名代码成功签名,已经在这个答案中详细解释了,简而言之:

  • 定期保存文档时,PDFBox 使用交叉引用表进行保存。

    • 如果要定期保存的文档是从带有交叉引用流的 PDF 加载的,则交叉引用流字典的所有条目都保存在尾部字典中。
  • 在应用签名过程中保存文档时,PDFBox 会创建增量更新;由于此类增量更新要求更新使用与原始修订相同类型的交叉引用,因此 PDFBox 在这种情况下尝试使用相同的技术。

    • 为了识别最初使用的技术,PDFBox 在其文档表示中查看字典的类型条目,其中拖车或交叉引用流字典已加载到该条目中:如果存在具有值XRef的类型条目(这是为交叉引用流指定的) ,则假定为流,否则为表。

doc.pdf因此,对于具有交叉引用流的 OP 原始 PDF :

  • 加载和表单填写后,文档会定期保存,即使用交叉引用表,但所有以前的交叉引用流条目,其中包括Type,都被复制到预告片。( doc_filled.pdf)

  • 在使用交叉引用表加载此保存的 PDF 以进行签名后,使用增量更新再次保存它。PDFBox 假定(由于类型预告片条目)现有文件具有交叉引用流,因此在增量更新结束时也使用交叉引用流。( doc_filled_signed.pdf)

  • 因此,最终填写后签名的 PDF 有两个修订版,内部一个带有交叉引用表,外部一个带有交叉引用流。

  • 由于这是无效的,Adobe Reader 在加载 PDF 时会在其内部文档表示中修复它。修复会更改文档字节。这样,Adobe Reader 眼中的签名就被破坏了。

  • 大多数其他签名验证器不会尝试进行此类修复,而是按原样检查文档的签名。他们成功验证了签名。

上面引用的答案也提供了一些解决方法:

  • A: 加载 PDF 以填写表格后,请在定期保存之前从预告片中删除Type条目。如果对该文件应用签名,PDFBox 将假定一个交叉引用表(因为误导性的类型条目不存在。因此,签名增量更新将是有效的。

  • B:使用增量更新来保存表单填写更改,无论是在单独的运行中还是在与签名相同的运行中。这也会导致有效的增量更新。

一般来说,我会建议后一个选项,因为如果 PDFBox 保存例程彼此兼容,前一个选项可能会中断。

但不幸的是,后一种选择需要将添加和更改的对象标记为已更新,包括文档目录中的路径。如果这不可能或至少太麻烦,第一个选项可能更可取。


在手头的情况下,OP尝试了后一个选项(doc_filled_and_signed.pdf):

此刻,文本框的内容仅在选择文本框时可见(使用 Acrobat 阅读器和预览相同的行为)。我标记了 PDField、它的所有父级、AcroForm、目录以及显示它的页面。

他将更改的字段标记为已更新,但没有在设置表单字段值时由 PDFBox 自动生成的关联外观流。

因此,在结果 PDF 文件中,该字段具有新值,但具有旧的空外观流。仅当单击该字段时,Adobe Reader 才会根据编辑值创建新外观。

因此,OP 还必须标记新的正常外观流(表单字段字典包含一个条目AP,引用一个字典,其中N引用正常外观流)。或者(如果查找更改或添加的条目变得太麻烦)他可能会尝试其他选项。

于 2015-10-02T11:03:52.257 回答