java - 如何使用iText从书签在PDF文件中创建目录页?

标签 java pdf itext

我需要在PDF中创建一个到表格内容的页面。我将创建PDF阅读书签。

对于iText,我使用:

tmp = SimpleBookmark.getBookmark (reader);


使用此PDF进行测试:

Download file PDF

返回此MAP:

[{Action = GoTo, Named = __ WKANCHOR_2, Title = Secretariat Teste0}, {Action = GoTo, Named = __ WKANCHOR_4, Title = Secretariat TestBook1}, {Action = GoTo, Named = __ WKANCHOR_6, Title = Secretariat Test2}, {Action = GoTo , Named = __ WKANCHOR_8 ...


没有页码。

如何显示带有标题和页码的表格内容?

我想证明一下:

content of table

最佳答案

请阅读以下问题的答案:Java: Reading PDF bookmark names with itext

它说明了如何使用SimpleBookmark方法获取轮廓树的标题(这是PDF规范中“书签”的调用方式)。

public void inspectPdf(String filename) throws IOException, DocumentException {
    PdfReader reader = new PdfReader(filename);
    List<HashMap<String,Object>> bookmarks = SimpleBookmark.getBookmark(reader);
    for (int i = 0; i < bookmarks.size(); i++){
        showTitle(bookmarks.get(i));
    }
    reader.close();
}

public void showTitle(HashMap<String, Object> bm) {
    System.out.println((String)bm.get("Title"));
    List<HashMap<String,Object>> kids = (List<HashMap<String,Object>>)bm.get("Kids");
    if (kids != null) {
        for (int i = 0; i < kids.size(); i++) {
            showTitle(kids.get(i));
        }
    }
}


然后阅读以下问题的答案:Set inherit Zoom(action property) to bookmark in the pdf file

您会看到HashMap<String, Object>不仅包含键为"Title"的条目,而且还可以包含键为"Page"的条目。书签指向页面时就是这种情况。该值将是一个明确的目的地。它由页码,一个值(例如FitFitHFitBXYZ)以及一些标记位置的参数组成。

如果查看CreateOutlineTree示例,您会发现还可以将书签提取为XML文件:

public void createXml(String src, String dest) throws IOException {
    PdfReader reader = new PdfReader(src);
    List<HashMap<String, Object>> list = SimpleBookmark.getBookmark(reader);
    SimpleBookmark.exportToXML(list,
            new FileOutputStream(dest), "ISO8859-1", true);
    reader.close();
}


这是我写的关于iText的书的屏幕截图,其中显示了您在书签条目中可以期望的键:

enter image description here

从该表可以看出,链接也可以表示为命名目的地。在这种情况下,您将不会获得页码,而只会获得一个名称。要获取页码,您需要提取命名目的地列表。此列表将为您提供与指定目的地相对应的显式目的地。

这本书和official documentation中也对此进行了说明。

有了标题和页码(使用了根据上述指针编写的代码进行检索)后,您可以使用PdfStamperinsertPage()方法将页面插入PDF文件。您可以使用ColumnText将TOC放在这些页面上,也可以为TOC创建单独的PDF并将其与原始PDF合并。请参阅How to add a cover/PDF in a existing iText document以了解有关这两种技术的更多信息。

您还将从此示例中受益:Create Index File(TOC) for merged pdf using itext library in java

至于标题和页码之间的虚线,这是使用分隔符(更具体地说是虚线分隔符)完成的。您应该先阅读以下问题:iTextSharp - Is it possible to set a different alignment in the same cell for text

然后阅读以下问题:How to Generate Table-of-Figures Dot Leaders in a PdfPCell for the Last Line of Wrapped Text(或此问题It is possible with itext 5 which at the end of a paragraph justified the remaining space is filled with scripts?

请注意,您的问题实际上是题外话。它被表述为“家庭作业”问题。它邀请人们在您的位置做您的工作。既然您拥有了所需的所有元素,那么您应该可以自己进行这项工作。如果未成功,则应编写一个主题“堆栈溢出”问题。在这个问题中,您将展示自己的尝试并说明遇到的技术问题。

更新:

您与以下大纲树共享了一个文档:

enter image description here

如您所见,书签是使用命名目标定义的,例如/__WKANCHOR_2/__WKANCHOR_4等。从/字符可以看出,名称存储为PDF名称对象(PDF 1.1),而不存储为PDF字符串对象(自1.2起)。最新的PDF标准建议使用PDF字符串对象而不是PDF名称对象,您可能需要询问PDF生成软件的供应商以更新软件,使其符合最新的PDF标准的建议。

不过,我们可以轻松地获得与命名目的地相对应的显式目的地。它们存储在根字典的/Dests条目中:

enter image description here

当您查看目的地的方式时,您会看到另一个应报告给wkhtmltopdf的问题。让我们看一下ISO标​​准告诉我们有关目的地使用的语法的信息:

enter image description here

页码的概念在PDF中不存在。使用页面词典描述页面,并且页面编号是从页面在页面树中的位置得出的。在页面树中遇到的第一页是页面1,遇到的第二页是页面2,依此类推。

在您的示例中,复制目标的定义如下:[9/XYZ 30.2400000 524.179999 0][9/XYZ 30.2400000 231.379999 0]等。

错了ISO标准说,数组中的第一个值必须是间接引用。间接引用的格式为9 0 R,而不是9。我看了看文档的结构,发现wkhtmltopdf使用页码-1而不是间接引用。如果我查看/__WKANCHOR_2,则它指的是[0/XYZ 30.240000 781.459999 0],而0应该指向第1页。由于Adobe Reader可以容忍糟糕的软件,因此它可以在Adobe Reader中使用,但是由于该文件违反了ISO- 32000年,iText不知道如何处理那些误导性的目的地,至少,便捷类SimpleNamedDEstination不知道如何处理它。

幸运的是,iText是一个非常通用的库,可让您深入了解PDF。在这种情况下,我们只需要更深一层。代替SimpleNamedDestination.getNamedDestination(reader, true),我们可以使用以下方法:

HashMap<String, PdfObject> names = reader.getNamedDestinationFromNames();
for (Map.Entry<String, PdfObject> entry: names.entrySet()) {
    System.out.print(entry.getKey());
    System.out.print(": p");
    PdfArray arr = (PdfArray)entry.getValue();
    System.out.println(arr.getAsNumber(0).intValue() + 1);
}
reader.close();


该方法的输出为:

__WKANCHOR_w: p7
__WKANCHOR_y: p7
__WKANCHOR_2: p1
__WKANCHOR_4: p1
__WKANCHOR_16: p9
__WKANCHOR_14: p8
__WKANCHOR_18: p9
__WKANCHOR_1s: p13
__WKANCHOR_a: p2
__WKANCHOR_1q: p13
__WKANCHOR_1o: p12
__WKANCHOR_12: p8
__WKANCHOR_1m: p12
__WKANCHOR_e: p3
__WKANCHOR_10: p7
__WKANCHOR_1k: p12
__WKANCHOR_c: p3
__WKANCHOR_1i: p11
__WKANCHOR_i: p4
__WKANCHOR_8: p2
__WKANCHOR_g: p3
__WKANCHOR_1g: p11
__WKANCHOR_6: p1
__WKANCHOR_1e: p10
__WKANCHOR_m: p5
__WKANCHOR_1c: p10
__WKANCHOR_k: p4
__WKANCHOR_q: p5
__WKANCHOR_1a: p9
__WKANCHOR_o: p5
__WKANCHOR_u: p6
__WKANCHOR_s: p6


如果我们检查__WKANCHOR_2,我们会看到它正确指向页面1。我检查了轮廓中的最后一个链接,它指向名称为__WKANCHOR_1s的命名目的地,实际上:它应该链接到页面13。

您的问题就是“垃圾进垃圾出”问题的明显例子。您的工具所生成的PDF违反了ISO的PDF标准,因此您浪费大量时间试图找出问题所在。但是,更糟糕的是:由于别人的过失,您让我失去了时间。

关于java - 如何使用iText从书签在PDF文件中创建目录页?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39027586/

相关文章:

Java - 递归地交替循环颜色

java - 如何从现有的 ORACLE 序列中为实体生成 ID?

java - JAVA 如何将构造函数中的值传递给另一个类中的 setter?

html - UIMarkupTextPrintFormatter 从不渲染 base64 图像

java - 段落从右到左书写

java - 强制将 a 从父类型解析为子类型?

ubuntu - 如何在 Ubuntu 终端中确定 PDF 文档的页面大小?

java - iText 7、htmlPDF 2 - DefaultFontProvider 的并行使用

java - 在 Itext 中使用 XMLWorkerHelper 时未设置图像宽度和高度

java - 如何在java中的itext pdf库中为段落添加边框?