【问题标题】:Convert external links in PDF to link to embedded attachment in iTextSharp将 PDF 中的外部链接转换为 iTextSharp 中嵌入附件的链接
【发布时间】:2019-03-21 09:20:51
【问题描述】:

我有一个奇怪的任务。我们即将关闭一个相当大的内部解决方案,并希望一次性导出它所拥有的数据。

该解决方案生成 PDF 格式的报告。这些 PDF 主要包含文本,但也包含图像。图片是链接,激活后会打开浏览器并指向图片的全尺寸版本。

由于我们即将关闭底层系统,因此指向全尺寸图像的外部链接也将停止工作。

由于各种奇怪的原因,我们对报告生成本身的控制有限,因此我们主要限于对报告进行后处理。

到目前为止,我设计的计划是从系统生成所有必要的报告并通过 iTextSharp 处理它们。我想要实现的“全部”是处理每个 PDF 并且:

  1. 搜索外部链接
  2. 下载链接指向的全尺寸图像并将其作为嵌入文件附加到 PDF
  3. 删除原来的外部链接,替换为相关嵌入资源的链接

我不熟悉 PDF 的底层结构,因此在尝试使用 iTextSharp 时遇到了很多困难。但是,到目前为止,我已经设法将 (1) 和 (2) 整理出来。但我正在努力解决(3):

我主要使用this作为我的支持文件,但我还没有完全达到我的目标。

这是我处理每个注释的代码。作为说明,我正在使用 iTextSharp 5.5.13 版:

if (AnnotationDictionary.Get(PdfName.A) != null)
{
    var annotActionObject = AnnotationDictionary.Get(PdfName.A);
    var AnnotationAction = (PdfDictionary)(annotActionObject.IsIndirect() ? PdfReader.GetPdfObject(annotActionObject) : annotActionObject);

    var type = AnnotationAction.Get(PdfName.S);
    //Test if it is a URI action
    if (type.Equals(PdfName.URI))
    {
        //Attach the downloaded file
        PdfFileSpecification pfs = PdfFileSpecification.FileEmbedded(writer, embFile.Path, embFile.Description, null);
        pfs.AddDescription(embFile.Description, false);
        writer.AddFileAttachment(pfs);

        //Removing old annotation
        AnnotationAction.Remove(PdfName.A);
        AnnotationDictionary.Remove(PdfName.A);

        PdfDestination destination = new PdfDestination(PdfDestination.FIT);
        destination.AddFirst(new PdfNumber(1));

        var target = new PdfTargetDictionary(true);
        target.EmbeddedFileName = embFile.Name;

        PdfAction action = PdfAction.GotoEmbedded(null, target, destination, true);

        AnnotationDictionary.Put(PdfName.D, action.Get(PdfName.D));
        AnnotationAction.Put(PdfName.D, action.Get(PdfName.D));
    }
}

这对某些人来说很可能很明显,为什么它不起作用:)

现在,一切运行良好,并且在另一端输出了一个 PDF。如前所述,PDF 中的图像不再具有活动链接,并且所有附件都按预期嵌入。但是,嵌入式资源的链接不起作用,并且在任何地方都没有任何指示。

非常感谢所有反馈。谢谢。

【问题讨论】:

  • 这是一个很遗憾无法满足的要求。请参阅this page 和 PDF 规范的相关部分。通过goToEmbedded() 方法创建的GoTo 操作只能有效地打开嵌入的PDF 文件。 PDF 规范的最新版本 ISO 32000-2 甚至更加明确:“注意:嵌入式 to-to 操作仅适用于 PDF 类型的文件”。其他文件格式,很可能是出于安全考虑,不会通过该操作打开。
  • @JonReilly,谢谢你。我最近在其他地方遇到了这些细节。我刚刚尝试添加 PDF 而不是图像图像。不幸的是,我的仍然无法正常工作。 Acrobat Reader 中没有任何错误或警告被抛出,所以我真的没有任何线索可循。你精通 iTextSharp 可以评论我的代码吗?
  • 理论上因为我为 iText =P 工作。我会添加一个快速的答案。

标签: c# itext pdf-generation


【解决方案1】:

如 cmets 中所述,您只能链接到嵌入的 PDF 文件。

您只是更改了D 条目。您需要覆盖整个 A 条目,但请确保保留目标的位置。

这是我创建的一个快速 POC:

PdfReader reader = new PdfReader(INPUT_FILE);
    PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(OUTPUT_FILE));

    PdfFileSpecification fs = PdfFileSpecification.fileEmbedded(stamper.getWriter(), null, "EmbeddedFile.pdf", FileUtils.readFileToByteArray(new File(INPUT_FOLDER + "embeddedfile.pdf")));
    fs.addDescription("specificname", false);
    stamper.getWriter().addFileAttachment(fs);
    PdfTargetDictionary targetDictionary = new PdfTargetDictionary(true);
    targetDictionary.setEmbeddedFileName("specificname");


    PdfDestination dest = new PdfDestination(PdfDestination.FIT);
    dest.addFirst(new PdfNumber(1));
    PdfAction action = PdfAction.gotoEmbedded(null, targetDictionary, dest, true);

    PdfDictionary page = reader.getPageN(1);
    PdfArray annotations = page.getAsArray(PdfName.ANNOTS);


    for(int x=0;x<annotations.size();x++) {
        PdfDictionary annotation = annotations.getAsDict(x);
        PdfArray location = annotation.getAsArray(PdfName.RECT);
        action.put(PdfName.RECT,location);
        annotation.put(PdfName.A, action);
    }


    stamper.close();

INPUT_FILE 指向原始文件,OUTPUT_FILE 指向您希望保存它的位置,INPUT_FOLDER + "embeddedFile.pdf" 指向您希望注释链接到的 PDF 文件。

所以action 是您的新操作,它指向嵌入的 PDF 文件(并且一个 PDF 文件)。我们只需将旧注释的A 条目替换为action。然后我们确保将action的位置设置为旧注释的位置。

【讨论】:

  • 非常感谢!通过一些调整,我设法让它在我的应用程序中工作。现在只是内部讨论走什么路线。再次,非常感谢您提供我们详尽的答案和示例。
猜你喜欢
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-03
  • 2014-07-03
  • 1970-01-01
相关资源
最近更新 更多