【问题标题】:Adding hyperlink to inner PDF files添加超链接到内部 PDF 文件
【发布时间】:2021-09-12 09:13:54
【问题描述】:

我必须通过使用 Java 中的 iText 在生成的 PDF 文件中添加两个 PDF 文件作为树结构来创建 PDF 文件。

我必须用 PDF 文件名创建书签并添加一个超链接到书签。单击书签时,应在该 PDF 文件本身中打开相应的 PDF,而不是作为单独的 PDF。

PDF树

 pdf1 
 pdf2 

【问题讨论】:

    标签: java pdf itext pdf-generation bookmarks


    【解决方案1】:

    此类书签在 PDF 规范中称为 大纲元素PDF 32000-1:2008,p.367):

    大纲由大纲项(有时称为书签)的树形结构层次结构组成,用作可视目录,将文档的结构显示给用户。

    如果您将文档与PdfMerger 合并,则大纲将默认复制到生成的 PDF 中。但是,您希望每个文档都有一个主节点,而不是书签的平面列表。由于克隆和复制轮廓并非易事,因此最好让 iText 处理。不幸的是,我们几乎无法直接控制轮廓的合并方式。

    我们可以构建一个SpecialMerger 作为PdfMerger 的包装器,以提取克隆的轮廓(第一步),然后将它们放入层次结构中(第二步)。每个合并 PDF 的大纲与所需的主节点名称及其参考(合并 PDF 中的页码)一起临时存储在 outlineList 中。合并所有 PDF 后,我们可以将临时存储的轮廓附加回根节点。

    public static class SpecialMerger {
    
        private final PdfDocument outputPdf;
        private final PdfMerger merger;
        private final PdfOutline rootOutline;
        private final List<DocumentOutline> outlineList = new ArrayList<>();
        private int nextPageNr = 1;
    
        public SpecialMerger(final PdfDocument outputPdf) {
            if (outputPdf.getNumberOfPages() != 0) {
                throw new IllegalArgumentException("PDF must be empty");
            }
            this.outputPdf = outputPdf;
            this.merger = new PdfMerger(outputPdf, true, true);
            this.rootOutline = outputPdf.getOutlines(false);
        }
    
        public void merge(PdfDocument from, int fromPage, int toPage, String filename) {
            merger.merge(from, fromPage, toPage); // merge with normal PdfMerger
    
            // extract and clone outline of merged document
            final List<PdfOutline> children = new ArrayList<>(rootOutline.getAllChildren());
            rootOutline.getAllChildren().clear(); // clear root outline
            outlineList.add(new DocumentOutline(filename, nextPageNr, children));
            nextPageNr = outputPdf.getNumberOfPages() + 1; // update next page number
        }
    
        public void writeOutline() {
            outlineList.forEach(o -> {
                final PdfOutline outline = rootOutline.addOutline(o.getName()); // bookmark with PDF name
                outline.addDestination(PdfExplicitDestination.createFit(outputPdf.getPage(o.getPageNr())));
                outline.setStyle(PdfOutline.FLAG_BOLD);
                o.getChildern().forEach(outline::addOutline); // add all extracted child bookmarks
            });
        }
    
        private static class DocumentOutline {
    
            private final String name;
            private final int pageNr;
            private final List<PdfOutline> childern;
    
            public DocumentOutline(final String pdfName, final int pageNr, final List<PdfOutline> childern) {
                this.name = pdfName;
                this.pageNr = pageNr;
                this.childern = childern;
            }
    
            public String getName() {
                return name;
            }
    
            public int getPageNr() {
                return pageNr;
            }
    
            public List<PdfOutline> getChildern() {
                return childern;
            }
        }
    }
    

    现在,我们可以使用这个自定义合并来合并 PDF,然后添加带有writeOutline 的大纲:

    public static void main(String[] args) throws IOException {
        String filename1 = "pdf1.pdf";
        String filename2 = "pdf2.pdf";
        try (
                PdfDocument generatedPdf = new PdfDocument(new PdfWriter("output.pdf"));
                PdfDocument pdfDocument1 = new PdfDocument(new PdfReader(filename1));
                PdfDocument pdfDocument2 = new PdfDocument(new PdfReader(filename2))
        ) {
            final SpecialMerger merger = new SpecialMerger(generatedPdf);
            merger.merge(pdfDocument1, 1, pdfDocument1.getNumberOfPages(), filename1);
            merger.merge(pdfDocument2, 1, pdfDocument2.getNumberOfPages(), filename2);
            merger.writeOutline();
        }
    }
    

    结果如下所示(PreviewAdobe Acrobat Reader 在 macOS 上):


    另一种选择是通过嵌入 PDF 来制作作品集。但是,并非所有 PDF 查看器都支持此功能,并且大多数用户不习惯这些作品集。

    public static void main(String[] args) throws IOException {
        String filename1 = "pdf1.pdf";
        String filename2 = "pdf2.pdf";
    
        try (PdfDocument generatedPdf = new PdfDocument(new PdfWriter("portfolio.pdf"))) {
            Document doc = new Document(generatedPdf);
            doc.add(new Paragraph("This PDF contains embedded documents."));
            doc.add(new Paragraph("Use a compatible PDF viewer if you cannot see them."));
    
            PdfCollection collection = new PdfCollection();
            collection.setView(PdfCollection.TILE);
            generatedPdf.getCatalog().setCollection(collection);
    
            addAttachment(generatedPdf, filename1, filename1);
            addAttachment(generatedPdf, filename2, filename2);
        }
    }
    
    private static void addAttachment(PdfDocument doc, String attachmentPath, String name) throws IOException {
        PdfFileSpec fileSpec = PdfFileSpec.createEmbeddedFileSpec(doc, attachmentPath, name, name, null, null);
        doc.addFileAttachment(name, fileSpec);
    }
    

    macOS 上的 Adob​​e Acrobat Reader 中的结果:

    【讨论】:

      猜你喜欢
      • 2012-10-04
      • 1970-01-01
      • 2014-07-13
      • 2018-12-01
      • 1970-01-01
      • 2013-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多