iText 7.0.5:如何组合PDF并在每个文档的新书签下缩进现有书签?

时间:2018-01-18 01:09:12

标签: java pdf pdf-generation itext7

问题:   com.itextpdf.kernel.PdfException:Pdf间接对象属于其他PDF文档。将对象复制到当前的pdf文档。

我希望将PDF文档与一组经过编辑的书签结合起来,这些书签可以将书签与每个原始文档保持清晰的配对。我还想要一个新的顶级书签,描述整个集合,以便在用户选择后改进与其他文档的结合。合并的文件数量和每个书签的数量都是未知的,有些文件可能没有任何书签。

为简单起见,假设我有两个文档,其中有两个页面,每个文档都有第二页的书签。我希望组合文档具有这样的书签结构,其中" NEW"我正在根据每个源文档的元数据创建的那些" EXISTING"是我从个别文件中复制的内容:

-- NEW Combined Document meta(page 1)
---- NEW Document one meta   (page 1)
------ EXISTING Doc one link (page 2)
---- NEW Document two meta   (page 3)
------ EXISTING Doc two link (page 4)

代码:

private static String combinePdf(List<String> allFile, LinkedHashMap<String, String> bookmarkMetaMap, Connection conn) throws IOException {
System.out.println("=== combinePdf()  ENTER");  // TODO REMOVE
File outFile = File.createTempFile("combinePdf", "pdf", new File(DocumentObj.TEMP_DIR_ON_SERVER));
if (!outFile.exists() || !outFile.canWrite()) {
  throw new IOException("Unable to create writeable file in " + DocumentObj.TEMP_DIR_ON_SERVER);
}
if (bookmarkMetaMap == null || bookmarkMetaMap.isEmpty()) {
  bookmarkMetaMap = new LinkedHashMap<>(); // prevent NullPointer below
  bookmarkMetaMap.put("Documents", "Documents");
}
try ( PdfDocument allPdfDoc = new PdfDocument(new PdfWriter(outFile)) ) {
  allPdfDoc.initializeOutlines();
  allPdfDoc.getCatalog().setPageMode(PdfName.UseOutlines);
  PdfMerger allPdfMerger = new PdfMerger(allPdfDoc, true, false); // build own outline
  Iterator<Map.Entry<String, String>> itr = bookmarkMetaMap.entrySet().iterator();
  PdfOutline rootOutline = allPdfDoc.getOutlines(false);
  PdfOutline mainOutline;
  mainOutline = rootOutline.addOutline(itr.next().getValue());
  mainOutline.addDestination(PdfExplicitDestination.createFit(allPdfDoc.getNumberOfPages() + 1));
  int fileNum = 0;
  for (String oneFile : allFile) {
    PdfDocument onePdfDoc = new PdfDocument(new PdfReader(oneFile));
    PdfAcroForm oneForm = PdfAcroForm.getAcroForm(onePdfDoc, false);
    if (oneForm != null) {
      oneForm.flattenFields();
    }
    allPdfMerger.merge(onePdfDoc, 1, onePdfDoc.getNumberOfPages());
    fileNum++;
    String bookmarkLabel = itr.hasNext() ? itr.next().getKey() : "Document " + fileNum;
    PdfOutline linkToDoc = mainOutline.addOutline(bookmarkLabel);
    linkToDoc.addDestination(PdfExplicitDestination.createFit(allPdfDoc.getNumberOfPages() + 1));
    PdfOutline srcDocOutline = onePdfDoc.getOutlines(false);
    if (srcDocOutline != null) {
      List<PdfOutline> outlineList = srcDocOutline.getAllChildren();
      if (!outlineList.isEmpty()) {
        for (PdfOutline p : outlineList) {
          linkToDoc.addOutline(p);    // if I comment this out, no error, but links wrong order
        }
      }
    }
    onePdfDoc.close();
  }
  System.out.println("=== combinePdf()  DONE ADDING PAGES ===");    //TODO REMOVE
}
return outFile.getAbsolutePath();
}

问题:   com.itextpdf.kernel.PdfException:Pdf间接对象属于其他PDF文档。将对象复制到当前的pdf文档。

调试行后发生错误&#34; === combinePdf()DONE ADDING PAGES ===&#34;所以for循环按预期完成。 这意味着当allPdfDoc自动关闭时会发生错误。 如果我删除了行linkToDoc.addOutline(p);,我会获得所有链接并转到正确的页面,但它们没有按照我的要求嵌套/排序:

-- NEW Combined Document meta(page 1)
---- NEW Document one meta   (page 1)
---- NEW Document two meta   (page 3)
-- EXISTING Doc one link   (page 2)
-- EXISTING Doc two link   (page 4)

上述行被注释掉后,我甚至不确定如何包含EXISTING链接。我在PdfMerger构造函数中将mergeOutlines标志设置为false,因为我认为我必须构建自己的大纲。无论我是将getOutlines()设置为true还是false,以及我是否取出任意顶级新书签,我都会得到类似的结果。

我知道如何按所需顺序创建新旧书签的拼合列表。所以我的问题是如何根据需要获得缩进和排序。

谢谢你看看!

1 个答案:

答案 0 :(得分:1)

我没有在组合PDF中移动书签,而是在合并之前在组件PDF中完成了它。 欢迎反馈,特别是如果随着PDF尺寸的增加,某些东西非常低效:

private static void shiftPdfBookmarksUnderNewBookmark(PdfDocument pdfDocument, String bookmarkLabel) {
if (pdfDocument == null || pdfDocument.getWriter() == null) {
  log.warn("shiftPdfBookmarksUnderNewBookmark(): no writer linked to PDFDocument, cannot modify bookmarks");
  return;
}
pdfDocument.initializeOutlines();
try {
  PdfOutline rootOutline = pdfDocument.getOutlines(false);
  PdfOutline subOutline = rootOutline.addOutline(bookmarkLabel);
  subOutline.addDestination(PdfExplicitDestination.createFit(pdfDocument.getFirstPage())); // Not sure why this is needed, but problems if omitted.
  List<PdfOutline> pdfOutlineChildren = rootOutline.getAllChildren();
  if (pdfOutlineChildren.size() == 1) {
    return;
  }
  int i = 0;
  for (PdfOutline p : rootOutline.getAllChildren()) {
    if (p != subOutline) {
      if (p.getDestination() == null) {
        continue;
      }
      subOutline.addOutline(p);
    }
  }
  rootOutline.getAllChildren().clear();
  rootOutline.addOutline(subOutline);
  subOutline.addDestination(PdfExplicitDestination.createFit(pdfDocument.getFirstPage())); // not sure why duplicate line above seems to be needed
}
catch (Exception logAndIgnore) {
  log.warn("shiftPdfBookmarksUnderNewBookmark ignoring error and not shifting bookmarks: " +logAndIgnore, logAndIgnore);
}
}