首页 文章

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

提问于
浏览
0

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

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

合并的 PDF 文件包含:

  • 4 PDF 页面(这是正确的)

  • 所有 4 页都有签名

  • 第 3 页和第 4 页的签名不是原始签名,但它们与第 1 页和第 2 页的签名相同。为了验证签名是否不同,我在签名中放置了一个唯一的“Reason”代码。

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

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

谢谢科斯塔斯

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

1 回答

  • 1

    关于观察

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

    如果文档中签名字段的名称重合,则可能发生这种情况。具有相同名称的多个 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,其值为原因 Doc1-Page1,在第 1 页和第 3 页上显示小部件

    • 签名签名字段Signature2,其值为原因 Doc1-Page2,第 2 页和第 4 页为显式小部件。

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

    因此,将具有相同名称的多个字段从两个源文档合并到具有多个小部件的单个字段中(如PdfCopyFields似乎那样)是一个明智的行动。

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

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

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

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

    您可以找到合并文档的示例代码,其中的字段在iText 在行动中 第 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 个页面,每个文档中的每个页面都签有不同的签名

    如果没有完全使签名无效,至少那些来自除一个文档之外的所有签名都无法实现。看一下这里,了解集成 PDF 签名的介绍。特别注意同一文档中的多个集成签名如何工作:

    多个集成 PDF 签名的示意图

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

    因此,在不破坏至少一些签名的情况下合并是不可能的。

    合并更多当前的 iText 版本

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

    _1_on 合并签名字段

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

相关问题