当前位置: 首页 > 知识库问答 >
问题:

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

闾丘晨
2023-03-14

问题:com。itextpdf。内核PDFEException:Pdf间接对象属于其他Pdf文档。将对象复制到当前pdf文档。

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

为了简单起见,假设我有两个文档,每个文档有两页,第二页有一个书签。我希望合并后的文档有这样一个书签结构,其中“新”是我根据每个源文档的元数据创建的,而“现有”是我从单个文档复制的内容:

-- 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。内核PDFEException:Pdf间接对象属于其他Pdf文档。将对象复制到当前pdf文档。

在调试行“==combinePdf()完成添加页面===”之后发生错误,因此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)

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

我知道如何按所需顺序创建新书签和现有书签的扁平列表。因此,我的问题是如何获得所需的缩进和排序。

谢谢你看!


共有1个答案

秦安怡
2023-03-14

我没有在合并后的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);
}
}
 类似资料:
  • 问题内容: 如何使用iText将书签添加到现有PDF? 我将多个PDF合并为一个PDF,并且需要为最终PDF构建书签。例如,我有三个PDF:doc1.pdf,doc2.pdf和doc3.pdf,doc1和doc2属于Group1,doc3属于Group2。我需要合并它们,并且必须为生成的PDF构建嵌套书签,如下所示: 等等 问题答案: 我已经制作了一个MergeWithOutlines示例,该示例

  • 在我的项目中,我按书签拆分了一个pdf文件。应该创建一个新的pdf文件,其中包含拆分的页面和一些新的书签。 这将导致以下异常: 线程“main”java.io.ioException中的异常:COSStream已关闭,无法读取。也许它所附的PDDocument已经关闭了? 如果删除行,则新的pdf包含拆分的页面(没有例外),但显然不包含书签。我想我尝试添加书签的方式有问题。 我的代码: Stack

  • 内容 介绍 对象和属性 例子 API 参考:chrome.bookmarks 方法 create get getChildren getRecent getTree move remove removeTree search update 事件 onChanged onChildrenReordered onCreated onImportBegan onImportEnded onMoved o

  • 我正在使用PDFSharp为客户端的PDF文档的第一页添加书签。客户提供多个不同页面的PDF文档,每个PDF都是一个人的一张账单。客户让我把账号放在第一页的书签里,这样他们就可以把它们组合在一起,用书签作为识别账号的手段处理一个PDF文件和许多账单。 我使用下面的代码成功地创建了书签,但是当客户端使用Ghost脚本将文件组合在一起时,所有书签都被分配到合并后的PDF上的第1页。当他们组合来自另一个

  • 我正在从PDF中删除一个带有书签链接的页面。删除页面后,指向此页面的书签链接不再有效。 我没有从PDFBox api中找到一种方法来完成这项工作 有没有办法删除这个书签?有没有办法删除全部书签? 提前感谢

  • 问题内容: 抱歉,我是PDF框的新手,正在寻找一种解决方案,该方案如何使用书签名称获取特定的pdf页面?像下面的代码片段一样,我试图循环所有页面,但卡住了将书签与我需要的页面链接。有人可以帮忙吗? 问题答案: 事实证明,在PDF中,页面目标不在书签的目标条目中,而是在书签的操作项中(是的,PDF使您可以通过两种方式来执行同一操作)。将此添加到您的代码: