创建PDF/A-3:嵌入文件应包含有效的参数密钥

创建PDF/A-3:嵌入文件应包含有效的参数密钥,pdf,itext,Pdf,Itext,我正在尝试用itextpdf-5.4.5和itext-pdfa-5.4.5创建一个PDF/a-3。 设置PdfileSpecification时,会出现以下异常: com.itextpdf.text.pdf.PdfAConformanceException: Embedded file shall contain valid Params key. 这是我创建PdfDictionary的部分: PdfDictionary params = new PdfDictionary(); params

我正在尝试用itextpdf-5.4.5和itext-pdfa-5.4.5创建一个PDF/a-3。 设置PdfileSpecification时,会出现以下异常:

com.itextpdf.text.pdf.PdfAConformanceException: Embedded file shall contain valid Params key.
这是我创建PdfDictionary的部分:

PdfDictionary params = new PdfDictionary();
params.put(PdfName.MODDATE, new PdfDate());
PdfFileSpecification fileSpec = PdfFileSpecification.fileEmbedded(
writer, "./src/main/resources/com/itextpdf/invoice.xml", "invoice.xml", null, false, "text/xml", params);
我找到了进行检查的方法,但没有找到任何解决方案:

com.itextpdf.text.pdf.internal.PdfA3Checker.checkEmbeddedFile

protected void checkEmbeddedFile(PdfDictionary embeddedFile) {
    PdfDictionary params = getDirectDictionary(embeddedFile.get(PdfName.PARAMS));
    if (params == null) {
        throw new PdfAConformanceException(embeddedFile, MessageLocalization.getComposedMessage("embedded.file.shall.contain.valid.params.key"));
    }
有什么想法吗? 提前谢谢你

大体上 PDF/A-3对嵌入式文件有一些特殊要求,其中

嵌入文件的流字典应包含Params键,其值应为至少包含ModDate键的字典,其值应为源文件的最新修改日期

(ISO 19000-3附录E.1)

iText support解释了如何从头开始创建符合PDF/A-3的PDF,并演示了如何以符合PDF/A-3的方式嵌入文件,示例代码:

PdfDictionary parameters = new PdfDictionary();
parameters.put(PdfName.MODDATE, new PdfDate());
PdfFileSpecification fileSpec = PdfFileSpecification.fileEmbedded(
    writer, "./src/main/resources/com/itextpdf/invoice.xml",
    "invoice.xml", null, "application/xml", parameters, 0);
fileSpec.put(new PdfName("AFRelationship"), new PdfName("Data"));
writer.addFileAttachment("invoice.xml", fileSpec);
PdfArray array = new PdfArray();
array.add(fileSpec.getReference());
writer.getExtraCatalog().put(new PdfName("AF"), array);
此示例还将关联的文件条目(AF)添加到目录中,这是另一个要求

顺便说一句,严格地说,参数字典只在应的基础上需要,而不是应。因此,这实际上是一个建议,在野外可能存在没有此条目的带有附件的有效PDF/a-3文档

尽管没有明显的理由说明为什么iText在创建PDF/A-3文件时不遵循这一建议会更好,但通过检查严格解释是可以的

iText 5.4.5中的问题 最近引入了OP发现的检查。将文件存储在PDF文件中时已触发此检查。不幸的是,此时尚未分配params字典,只保留了一个引用。因此,即使在编写词典不久之后,检查也会失败

PdfFileSpecification.java

        stream.put(PdfName.TYPE, PdfName.EMBEDDEDFILE);
        stream.flateCompress(compressionLevel);
        PdfDictionary param = new PdfDictionary();
        if (fileParameter != null) {
            param.merge(fileParameter);
        }
        if (!param.contains(PdfName.MODDATE)) {
            param.put(PdfName.MODDATE, new PdfDate());
        }
        if (fileStore != null) {
            param.put(PdfName.SIZE, new PdfNumber(stream.getRawLength()));
            stream.put(PdfName.PARAMS, param);
        }
        else
            stream.put(PdfName.PARAMS, refFileLength);

        if (mimeType != null)
            stream.put(PdfName.SUBTYPE, new PdfName(mimeType));

        ref = writer.addToBody(stream).getIndirectReference();
在此操作过程中,检查发生(并失败)

在这里,紧接着,参数会被写入

变通办法
PdfFileSpecification.fileEmbedded
允许您将数据显示为
字节[]
而不是文件系统中的文件。正如您在上面的源代码中看到的,在这种情况下处理不同:
fileStore
包含
byte[]
参数。如果您在那里继续使用
文件存储
,您将看到,对于它的非
null
值,params dictionary作为直接对象写入,因此出现在测试中


因此,如果您将文件作为
byte[]
实例提供,则可以将iText 5.4.5用于PDF/A-3文件附件。

如果您使用iText-5.5.9,则还需要添加以下内容:

final PdfDictionary extraCatalog = writer.getExtraCatalog();
final PdfDictionary markInfoDict = new PdfDictionary();
markInfoDict.put(PdfName.MARKED, new PdfBoolean(true));
extraCatalog.put(PdfName.MARKINFO, markInfoDict);

否则,您将得到一个
PdfAConformanceException

说明如何在PDF/a-3的情况下正确附加文件。它是如何工作的?我用过它(当然是在iText 5.4.4中),它确实有效。谢谢,我试过了,但它不起作用。在我看来,本例中fileEmbedded方法的顺序不正确。我的itext版本需要以下顺序:-writer-filePath-fileDisplay-fileStore-compress-mimeType-fileparameter该方法有5个重载。因此,有多个正确的参数顺序。我假设在5.4.4和5.4.5之间引入了一个bug。谢谢您的解释。我将使用5.4.4,直到错误被解决:-)。@user3177193此问题已修复。该修复程序将在下一版本中提供。“因此,如果您将文件作为byte[]实例提供,则可以将iText 5.4.5用于PDF/A-3文件附件。”有人举过这样的例子吗,我没有看到一种方法在5.5中使用byte[]而不是字符串作为文件路径和文件名。8@user1567291 “在5.5.8中,我没有看到一种方法使用byte[]而不是字符串作为文件路径和文件名”-所有
PdfFileSpecification.fileEmbedded
都有一个
byte[]fileStore
参数,该参数记录为文件的字节数组。如果它不是
null
则优先于
filePath
final PdfDictionary extraCatalog = writer.getExtraCatalog();
final PdfDictionary markInfoDict = new PdfDictionary();
markInfoDict.put(PdfName.MARKED, new PdfBoolean(true));
extraCatalog.put(PdfName.MARKINFO, markInfoDict);