当前位置: 首页 > 知识库问答 >
问题:

在自定义签名中使用字体使用iText7的外观签名会破坏PDF/A一致性?

吴高畅
2023-03-14

我试图从PDF/A-1A输入文件创建签名的PDF,输出必须保持一致性级别。

签名必须添加定制外观。

如果我沿着下面的代码行进行操作,所有的东西都在签名端工作,签名被正确显示并验证为OK。

但PDF/A一致性被不包含所需toUnicode CMAP的嵌入式字体打破。

PdfADocument pdf = ... the doc to be signed
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
PdfReader reader = pdf.getReader();
PrivateKey privateKey = ...
Provider signatureProvider = new BouncyCastleProvider();
Certificate[] signChain = ...
PdfSigner pdfSigner = new PdfSigner(reader, buffer, true);
PdfSignatureAppearance signatureAppearance = pdfSigner.getSignatureAppearance();
signatureAppearance.setReuseAppearance(false);
 signatureAppearance.setPageNumber(pdf.getNumberOfPages());
 pdfSigner.setFieldName("Custom Signature");
 float margin = 35;        
 Rectangle pageSize = pdf.getLastPage().getMediaBox();
 Rectangle signaturePosition = new Rectangle(pageSize.getLeft()+margin,
                                                pageSize.getBottom()+margin,
                                                pageSize.getWidth()-2*margin, 
                                                (pageSize.getHeight()-2*margin)/3);


    // need to do this before creating any *Canvas object, else the pageRect will be null and the signature invisible
    signatureAppearance.setPageRect(signaturePosition);

PdfFont regularFont = PdfFontFactory.createFont("/path/to/truetypefile-regular.ttf", "ISO-8859-1", true);
PdfFont boldFont = PdfFontFactory.createFont("/path/to/truetypefile-bold.ttf", "ISO-8859-1", true);

int fontSize = 10;

PdfFormXObject n0 = signatureAppearance.getLayer0();
PdfCanvas n0Canvas = new PdfCanvas(n0, pdfSigner.getDocument());
PdfFormXObject n2 = signatureAppearance.getLayer2();
Canvas n2Canvas = new Canvas(n2, pdfSigner.getDocument());
if(regularFont != null) {
    n2Canvas.setFont(regularFont);
    n0Canvas.setFontAndSize(regularFont, fontSize);
}
ImageData imageData = ImageDataFactory.create("/path/to/image.png");
Image image = new Image(imageData);
n2Canvas.add(image);

String layer2Text = ... some lines of text containing newlines and some simple markdown
String[] paragraphs = layer2text.split("\n\n");
for (String text : paragraphs) {
    boolean bold = false;
    if(text.startsWith("[bold]")) {
        bold = true;
        text = text.replaceFirst("^\\s*\\[bold\\]\\s*", "");
    }

    Paragraph p = new Paragraph(text);
    p.setFontSize(fontSize);
    if(bold) {
        p.setFont(boldFont);
    }
    n2Canvas.add(p);
}
...   pdfSigner.setCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS);

PrivateKeySignature externalSignature = new PrivateKeySignature(privateKey, DigestAlgorithms.SHA512, signatureProvider.getName());
BouncyCastleDigest externalDigest = new BouncyCastleDigest();

pdfSigner.signDetached(externalDigest, externalSignature, signChain, null, null, null, 0, PdfSigner.CryptoStandard.CMS);

所以我想这里少了些什么。嵌入的字体不符合PDF/A,因为它们缺少ToUnicode CMAP键。pdf tools validator的另一个错误是:“密钥编码的值是Difference,但必须是WinAnSienceODing或MacRomanEncoding。”这似乎是同样的问题。

签名本身是可以的,看起来,样式和图像都应该是正确的。只是字体似乎不太好。

共有1个答案

元阳荣
2023-03-14

违反PDF/A合规性的触发因素是此处创建字体的方式

PdfFont regularFont = PdfFontFactory.createFont("/path/to/truetypefile-regular.ttf", "ISO-8859-1", true);
PdfFont boldFont = PdfFontFactory.createFont("/path/to/truetypefile-bold.ttf", "ISO-8859-1", true);

或者甚至更具体地将编码参数"ISO-8859-1"用于其中。

PDF/A-1规范要求:

所有非符号TrueType字体都应指定MacRomanEncoding或WinAnSiceCodeing作为字体字典中编码项的值。所有符号TrueType字体不得在字体字典中指定编码条目,其字体程序的“cmap”表格应仅包含一种编码。

使用编码参数"ISO-8859-1"导致在字体字典中的编码项的值既不指定MacRomanEncode也不指定WinAnsiEncode。相反,该值是只包含包含显式映射的差异项的字典。

根据PDF/A验证程序的不同,这可能会导致不同的错误消息。

由于(我假设)历史原因,在字体创建过程中有几个不同的编码参数值,导致iText使用WinAnsiEncode:

  • "
  • PdfEncoding. WINANSI="Cp1252"
  • "winansi"(不区分大小写)
  • "winansi编码"(不区分大小写)

OP使用了PdfName。温安斯廷。getValue()返回与最新选项匹配的字符串。

虽然这表明iText可以用于正确签名PDF/A文档,但可能应该引入一个特定的PDFASigner类来强制一致性。

 类似资料:
  • 我正在尝试使用iText(sharp,5.5.13版)创建自定义数字签名,用户可以从四个位置(顶部、底部、左侧和右侧)设置图像位置,如下所示: 刚度: 左: 顶部: 底部: 到目前为止,我试着处理签名的第0层,但我认为我做得不对,因为签名细节是在第2层设置的。 然而,这只是设置图像位置的初始草图。在下面的代码中,我加载图像并将其放入一个块中(想法取自此示例) 结果或多或少是预期的,但有两个问题:签

  • 我有一个私人钥匙在档案里。“privatekey.pem”并且不知道如何创建谁能帮助我吗?我正在尝试使用IText7签名pdf。

  • 我有一个场景,我需要用iText7库从pdf中获取签名信息。签名可能存在,也可能不存在。当我为没有任何数字签名的PDF实例化一个新的对象时,会出现异常 “没有相关的PdfWriter用于进行间接操作。” .如果有签名,就很好用。我不确定如何纠正这个异常。 更新为包含代码示例

  • 使用itext v5对文档进行数字签名时。5.11 PDF/A-2b文档被破坏——这意味着它们不再作为PDF/A文档有效。违反以下规则:https://github.com/veraPDF/veraPDF-validation-profiles/wiki/PDFA-Parts-2-and-3-rules#rule-643-1 在上面的链接中,它指定摘要无效,因此我也给你一个代码段,在使用iText

  • 我需要将一个使用iText5进行PDF签名验证/创建的Java程序移植到iText7。 旧代码显然不能按原样工作,因为iText的大部分内容都经过了重组。 我找到的所有关于如何做到这一点的例子和教程都是针对iText5的。(非常好的)白皮书也是如此。它们依赖于通过方法返回的列表,在该方法上执行所有与签名相关的操作。 在iText7中,不再具有该方法。 有人知道iText7的例子/文档吗?

  • 对于IText5,添加数字签名相当容易。其留档的链接是:http://developers.itextpdf.com/examples/security/digital-signatures-white-paper/digital-signatures-chapter-2 有人可以在ITEXT 7中共享文档链接吗?我试过各种方法,但都没有用。在网上找不到任何链接。我可以取消签名并检查签名,但不能添