【问题标题】:iText fails to propery merge signatures in PDF filesiText 无法正确合并 PDF 文件中的签名
【发布时间】:2014-09-26 09:34:33
【问题描述】:

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

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

合并后的 PDF 文件包含:

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

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

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

谢谢 科斯塔斯

PS:我可以发布示例 PDF 文件来重现错误,但最简单的可以完成这项工作(我用 MS Word 创建了 2 个 PDF 文件并分别在每一页上盖章)

【问题讨论】:

  • 您不能合并两个带有签名的文档并希望保持它们有效。
  • 当然,我不能那样做。这不是我问的,我不想有有效的签名。我描述的问题与混淆的实际字段有关。当然,签名将无效。
  • 如果缺少签名值是您的焦点问题,请分享示例文件,包括源文件和结果文件。这可能是由于字段名称重合,但也可能是由于其他原因。
  • 谢谢。我在下面的帖子中分享了一个 zip。

标签: itext


【解决方案1】:

关于观察

第 3 页和第 4 页的签名不是原始签名,但它们与第 1 页和第 2 页的签名相同。为了验证签名不同,我在签名中添加了唯一的“原因”代码。

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

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

...

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

  • Doc1-signed.pdf 有
    • 第 1 页上的签名签名字段 Signature1(字段和小部件合并),其值为 Reason Doc1-Page1
    • 第 2 页上的签名签名字段 Signature2(字段和小部件合并),其值为 Reason Doc1-Page2
  • Doc2-signed.pdf 有
    • 第 1 页上的签名签名字段 Signature1(字段和小部件合并),其值为 Reason Doc2-Page1
    • 第 2 页上的签名签名字段 Signature2(字段和小部件合并),其值为 Reason Doc2-Page2

MERGED-PDF.pdf 中的合并结果

  • 一个签名的签名字段Signature1,其值为Reason Doc1-Page1,第1页和第3页上有明确的小部件和
  • 一个签名的签名字段Signature2,其值是Reason Doc1-Page2,第2页和第4页上有明确的小部件。

因为整个 PDF 被视为单个表单,所以表单字段名称只能有一个关联值。

因此,将两个源文档中具有相同名称的多个字段合并为具有多个小部件的单个字段(就像PdfCopyFields 似乎所做的那样)是一个明智的做法。

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

正确合并我假设您的意思是它们仍然具有原始的不同值。这反过来又表明该服务没有像上述那样合并字段。

但这并不比PdfCopyFields正确,它更笨,因为现在字段 Signature1 的值不清楚,就像 Signature2 的值一样。

如果您想保留具有重复名称的源字段的不同值,正确的做法是在合并过程中重命名此类重复字段 . (如果在线 PDF 合并服务也这样做了,那就不傻了。但是你没有表明字段名称有任何变化......)

您可以在iText in Action chapter 6 中找到合并文档的示例代码,并在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();
}

显然,您可以通过仅重命名签名字段来进行调整。这会将其他字段与“正常”表单内容合并,但保留签名原样。

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

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

关于合并已签名 PDF 的一般说明

你的意图

我有 2 个要合并的文档,每个文档有 2 页,每个文档中的每一页都有不同的签名

如果不完全使签名无效,就无法实施,至少来自除一个文档之外的所有签名。查看here 以了解集成 PDF 签名的介绍。特别注意同一文档中的多个集成签名如何工作:

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

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

合并到更当前的 iText 版本

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

@Bruno 关于合并签名字段

上述合并的结果在 PDF-2 中将完全无效,这不仅是因为签名本身无效,还因为签名具有多个外观。您可能需要重新考虑 Pdf*Copy* 类族在签名字段方面的行为。

【讨论】:

  • 这里是一个 zip 链接,其中包含 2 个 zip 文件和合并的 PDF:link。我尝试使用在线 pdf 合并服务,并且签名字段已正确合并(请记住,我不指签名本身的有效性,仅指签名字段)。
  • 您更新的答案让事情变得清晰。这正是我想要的。不幸的是,我不知道 pdf 内部结构的详细信息,但我了解这里的问题所在。我也了解在线 pdf 合并实用程序可能如何处理这种情况(通过重命名签名字段)。我将通过重命名签名字段来尝试您的建议。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-09
相关资源
最近更新 更多