【问题标题】:How to embed XMP metadata into multi-pages PDF/A3 file?如何将 XMP 元数据嵌入到多页 PDF/A3 文件中?
【发布时间】:2017-01-17 21:44:07
【问题描述】:

我目前正在做一个项目,这是一个 TIFF 到 PDF 格式的转换器。它需要一系列扫描的集合 TIFF 文件,并将它们转换为单个多页 PDF/A3 文件。我完成了这部分项目,现在专注于元数据处理问题。

我的老板希望我将每个 TIFF 的元数据嵌入到 PDF 文件的每个相应页面中。我不知道该怎么做。根据我对 PDF/A 元数据结构的研究,似乎 PDF 中应该只有一个 xmp 文件,如果我想嵌入特定页面的元数据,我必须给出一个指针,指向我想要的位置是。在我的项目中,到目前为止我认为的基本想法是,我应该从每个 TIFF 文件中提取元数据(我知道如何执行此步骤),将所有这些组合并转换为 PDF 文件。我尝试使用 iText,但似乎不支持这样做。

有人知道怎么做吗?有没有一个开放的工具可以做到这一点?我的主要语言是 Java。

谢谢大家!!!

【问题讨论】:

  • “根据我对 PDF/A 元数据结构的研究,PDF 中似乎应该只有一个 xmp 文件” - 通常只有一个元数据流与整个文档相关联的那个。不过,还有更多,请参阅我对塞缪尔回答的评论。

标签: java itext metadata xmp pdfa


【解决方案1】:

你的研究是正确的。

嗯,主要是因为区分属于整个 pdf 文档的元数据和属于 TIFF 图像的元数据很重要。 第一个确实仅限于每个 pdf 的单个实例。 第二个独立于 pdf 元数据,但可以作为文件附件添加,这是 PDF/A-3 标准允许的。 这两种类型都独立于任何页面,因此从这个意义上说,您的老板的请求表明缺乏对 pdf 格式的了解。

但是,您可以在指向其元数据的每个 Tiff 上放置一个链接注释,可选择存储在第二个 pdf 文件中,从而产生数据以某种方式存在于页面上的错觉。

现在,我必须恭敬地不同意您关于 iText 没有为您提供处理此问题的工具的说法。 Chapter 7 of the iText7 Jumpstart tutorial 处理 PDF/A-X 的创建,包括嵌入文件。 PDF/A-3 是第三个例子。

对于链接注释,只要了解一些 Pdf-spec(嵌入式 Go-To Actions)和 iText 的低级操作方法,就可以实现。我现在没有现成的示例,但我会看看我是否可以酿造一些东西并稍后将其添加到这个答案中。

编辑: 好吧,这令人失望,Foxit 和 Adob​​e 的读者都不支持嵌入式 go-to 操作。不过,如果您有兴趣,下面是我使用 iText7 创建 PDF/A-3 兼容文档的代码,将元数据添加为单独的 Pdf。

public static String INTENT = "src/test/resources/StackOverflow/EmbeddedLinking/sRGB_CS_profile.icm";
public static String IMG = "src/test/resources/StackOverflow/EmbeddedLinking/itis.jpg";
public static String META = "target/output/StackOverflow/EmbeddedLinking/metadata.pdf";
public static String DEST = "target/output/StackOverFlow/EmbeddedLinking/embeddedMetaData.pdf";

public static void main(String[] args) throws IOException, java.io.IOException {
    File file = new File(DEST);
    file.getParentFile().mkdirs();
    new EmbeddedLinking().createPdf(META);
    new EmbeddedLinking().createPdfWithEmbeddedFile(DEST,META,IMG,INTENT);
}

public void createPdf(String dest) throws IOException, FileNotFoundException{
    PdfWriter writer = new PdfWriter(dest);
    PdfDocument pdfDoc = new PdfDocument(writer);
    Document doc = new Document(pdfDoc);
    //Put some data here
    doc.add(new Paragraph("This is the metadata"));
    doc.add(new Paragraph("The Cake is Lie"));
    doc.add(new Paragraph("42"));
    doc.add(new Paragraph("The Spice must flow"));
    doc.close();
}

public void createPdfWithEmbeddedFile(String dest, String embeddedPath, String imgPath, String intent) throws java.io.IOException {
    PdfWriter writer = new PdfWriter(dest);
    PdfOutputIntent outputIntent = new PdfOutputIntent("Custom", "","http://www.color.org", "sRGB IEC61966-2.1", new FileInputStream(intent));
    PdfADocument pdfADoc = new PdfADocument(writer, PdfAConformanceLevel.PDF_A_3A,outputIntent);

    //Setting some required parameters
    pdfADoc.setTagged();
    pdfADoc.getCatalog().setLang(new PdfString("en-US"));
    pdfADoc.getCatalog().setViewerPreferences(
            new PdfViewerPreferences().setDisplayDocTitle(true));
    PdfDocumentInfo info = pdfADoc.getDocumentInfo();
    info.setTitle("iText7 PDF/A-3 Embedded Go-To example");

    //Add attachment
    PdfDictionary parameters = new PdfDictionary();
    parameters.put(PdfName.ModDate, new PdfDate().getPdfObject());
    PdfFileSpec fileSpec = PdfFileSpec.createEmbeddedFileSpec(
            pdfADoc, Files.readAllBytes(Paths.get(embeddedPath)), "metadata.pdf",
            "metadata.pdf", new PdfName("application/pdf"), parameters,
            PdfName.Data, false);
    fileSpec.put(new PdfName("AFRelationship"), new PdfName("Data"));
    pdfADoc.addFileAttachment("metadata.pdf", fileSpec);
    PdfArray array = new PdfArray();
    array.add(fileSpec.getPdfObject().getIndirectReference());
    pdfADoc.getCatalog().put(new PdfName("AF"), array);

    //Add Image
    int imagePage = 1; //We know the image will end up on the first page since it's the only thing we add to the document
    Document doc = new Document(pdfADoc, PageSize.A4);
    Image img = new Image(ImageDataFactory.create(imgPath));
    doc.add(img);


    //Add link annotation to embedded file
    float pageHeight = PageSize.A4.getHeight();
    float imageWidth = img.getImageWidth();
    float imageHeight = img.getImageHeight();
    float x = doc.getLeftMargin();
    float y = pageHeight - doc.getTopMargin() - imageHeight;
    Rectangle linkAnnotationPosition = new Rectangle(x,y,imageWidth,imageHeight);

    PdfLinkAnnotation linkAnnotation = new PdfLinkAnnotation(linkAnnotationPosition);
    //Setup the Embedded GoTO action
    PdfExplicitDestination explicitDestination = PdfExplicitDestination.createFit(imagePage);//Destination in the target file
    PdfTargetDictionary targetDictionary = PdfTargetDictionary.createChildTarget("metadata.pdf"); //Target embedded file
    PdfAction action = PdfAction.createGoToE(fileSpec,explicitDestination,true,targetDictionary);
    linkAnnotation.setAction(action);
    //PDF/A requires the presence of the F -bit flag array in every dictionary. The print flag needs to be 1, and some other flags 0.
    //See the spec for details and options, but the bit pattern represented by the integer 4 suffices for conformance to PDF/A-3
    int fBitArray = 4;
    linkAnnotation.put(PdfName.F,new PdfNumber(fBitArray));
    //Add annotation to page
    pdfADoc.getPage(imagePage).addAnnotation(linkAnnotation);

    //Close document
    doc.close();
}

【讨论】:

  • “属于 pdf 文件 [...] 的元数据确实仅限于每个 pdf 的单个实例。” - 是吗?根据 ISO 32000-1 任何 PDF 流或字典都可能附加元数据(第 14.3.2 节),相反,我在 ISO 19005-3 中没有发现对此的限制它要求PDF 中存在的所有元数据流都应符合 XMP 规范(第 6.6.2.1 节),这意味着可能存在多个元数据流。还是您的意思是可能只有一个元数据实例引用整个文档?那么你是对的。
  • @mkl 后者,只有一个元数据实例属于整个文档。我会编辑答案,因为措辞确实有点模棱两可。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-02
相关资源
最近更新 更多