iText无法在PDF文件中合并签名

时间:2014-09-26 09:34:33

标签: itext

我在使用iText合并PDF文件时遇到问题。我正在使用PdfCopyFields类进行合并,现有PDF文件的签名未正确合并。似乎合并的PDF文件的字典中的某些数据不正确。

具体来说: 我有2个文件要合并,每个文档有2个页面,每个文档中的每个页面都用不同的签名签名(在每个签名中我放了一个不同的原因代码,以便能够直观地告诉每个签名)。

合并的PDF文件包含:

  • 4个PDF页面(这是正确的)
  • 所有4页都有签名
  • 第3页和第4页的签名不是原始签名,但它们与第1页和第2页的签名相同。为了验证签名是否不同,我在签名中添加了一个唯一的“Reason”代码。

很明显,虽然生成的PDF有4个签名(我知道是PDF字段),但似乎这些签名字段引用了错误文档的签名数据。

此外,当我用文本编辑器打开合并的pdf文件时,我会查看PDF文件的“标题”,只找到2个签名条目(而不是4个)。这意味着页面中的实际签名仅引用了这两个签名,因此混淆了。

谢谢 科斯塔斯

PS:我可以发布样本PDF文件来重现错误,但最简单的将会完成这项工作(我用MS Word创建了2个PDF文件并分别盖章)

1 个答案:

答案 0 :(得分:1)

关于观察

  

第3页和第4页的签名不是原始签名,但它们与第1页和第2页的签名相同。为了验证签名是否不同,我提出了一个独特的原因'签名中的代码。

如果文档中签名字段的名称一致,则可能发生这种情况。具有相同名称的多个PDF字段被视为同一字段的多个可视化。在这种情况下,合并过程可能会丢弃重复值。

但是,我不确定你的文件是否属于这种情况。如果您想知道,请分享。

...

检查了示例文件后,很明显问题确实是由合并文档中相同的签名字段名称引起的:

  • Doc1-signed.pdf有
    • 第1页上签名的签名字段Signature1(字段和窗口小部件合并),其值为原因 Doc1-Page1
    • 第2页上已签名的签名字段Signature2(字段和窗口小部件已合并),其值中包含原因 Doc1-Page2
  • Doc2-signed.pdf有
    • 第1页上签名的签名字段Signature1(字段和窗口小部件合并),其值为原因 Doc2-Page1
    • 第2页上已签名的签名字段Signature2(字段和窗口小部件已合并),其值中包含原因 Doc2-Page2

将结果合并到具有

的MERGED-PDF.pdf中
  • 签名的签名字段Signature1,其值为原因 Doc1-Page1 ,并在第1页和第3页显示小部件
  • 签名的签名字段Signature2,其值为原因 Doc1-Page2 ,并在第2页和第4页显示小部件。

由于整个PDF被视为单个表单,因此表单字段名称只能包含一个关联值。

因此,将两个源文档中具有相同名称的多个字段合并为具有多个小部件的单个字段(如PdfCopyFields所示),这是一个明智的行动。

  

我尝试使用在线pdf合并服务,并正确合并了签名字段

通过正确合并我认为你的意思是他们仍然拥有原始的,不同的值。这反过来表明该服务没有合并上述字段。

但这不是正确的而不是PdfCopyFields所做的事情,因为Signature1字段的值现在不清楚,就像Signature2的值一样。

如果要保持源字段的不同值具有重复名称,正确的要执行的操作是在合并过程中重命名此类重复字段 。 (如果在线PDF合并服务也这样做了,那也不是愚蠢的。但是你没有指出字段名称的任何变化......)

您可以找到合并文档的示例代码,其中的字段已在iText in Action chapter 6中重命名。使用现有PDF示例ConcatenateForms2.java

    PdfCopyFields copy
        = new PdfCopyFields(new FileOutputStream(RESULT));
    // add a document
    PdfReader reader1 = new PdfReader(renameFieldsIn(DATASHEET, 1));
    copy.addDocument(reader1);
    // add a document
    PdfReader reader2 = new PdfReader(renameFieldsIn(DATASHEET, 2));
    copy.addDocument(reader2);
    // Close the PdfCopyFields object
    copy.close();
    reader1.close();
    reader2.close();

使用辅助方法

private static byte[] renameFieldsIn(String datasheet, int i)
    throws IOException, DocumentException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    // Create the stamper
    PdfStamper stamper = new PdfStamper(new PdfReader(datasheet), baos);
    // Get the fields
    AcroFields form = stamper.getAcroFields();
    // Loop over the fields
    Set<String> keys = new HashSet<String>(form.getFields().keySet());
    for (String key : keys) {
        // rename the fields
        form.renameField(key, String.format("%s_%d", key, i));
    }
    // close the stamper
    stamper.close();
    return baos.toByteArray();
}

显然,你可以通过重命名签名字段来调整它。这会将其他字段合并为&#34; normal&#34;形式内容,但保留签名。

  

(请记住,我不是指签名本身的有效性,只是签名字段。)

作为事后的想法,在合并之前展平签名字段可能是另一种方法。视觉表示仍然存在,但验证失败消息已消失,因为不再验证任何内容。

关于合并签名PDF的一般性评论

你的意图

  

我想要合并2个文件,每个文件有2个页面,每个文件中的每个页面都用不同的签名签名

如果没有完全使签名无效,

就无法实现,至少是除了一个文档之外的所有签名。有关集成PDF签名的介绍,请查看here。特别注意同一文档中的多个集成签名如何工作:

Schematic of multiple integrated PDF signatures

合并两个文档后,您可以保持一个文档的签名有效,但另一个文档的添加签名仅涵盖其文档中的数据,而在合并之后,它们必须覆盖两个文档中的数据。

因此,如果不破坏至少一些签名,就不可能进行合并。

合并更多当前的iText版本

OP使用iText版本4.2.0。在当前的iText版本(5.5.x)中,大部分表单感知逻辑已从PdfCopyFields移至PdfCopy。如果您使用此类版本或更新版本,请尝试使用PdfCopy

@Bruno合并签名字段

上面合并的结果在PDF-2中完全无效,不仅仅是因为签名本身无效,而且因为签名有多个外观。您可能想重新考虑Pdf*Copy*类家族关于签名字段的行为。