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

时间:2016-07-26 23:59:40

标签: java pdf itext itext7

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

必须使用自定义外观添加签名。

如果我沿着下面的代码行进行操作,那么一切都在签名方面起作用,签名正确显示并验证确定。

但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验证器的另一个错误是: "密钥编码的值是差异,但必须是WinAnsiEncoding或MacRomanEncoding。"这似乎是同样的问题。

签名本身是可以正常的,造型和图像应该是适当的。它只是看起来不行的字体。

1 个答案:

答案 0 :(得分:1)

违反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规范要求:

  

6.3.7字符编码

     

所有非符号TrueType字体都应指定 MacRomanEncoding WinAnsiEncoding 作为   字体词典中的编码条目。所有符号TrueType字体都不应在其中指定编码条目   字体字典及其字体程序' “cmap”表应包含一个编码。

使用编码参数"ISO-8859-1"导致 MacRomanEncoding WinAnsiEncoding 都没有被指定为 字体词典中的编码条目。相反,该值是仅包含差异条目的字典,其中包含显式映射。

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

对于(我假设)历史原因,在字体创建过程中有一些不同的编码参数值会导致iText使用 WinAnsiEncoding

  • ""
  • PdfEncodings.WINANSI== "Cp1252"
  • "winansi"(不区分大小写)
  • "winansiencoding"(不区分大小写)

OP使用PdfName.WinAnsiEncoding.getValue()返回与最新选项匹配的字符串。

虽然这表明iText可用于正确签署PDF / A文档,但可能应引入特定PDFASigner类强制执行一致性。