【问题标题】:How to create a multi page TOC using itext?如何使用 itext 创建多页目录?
【发布时间】:2015-04-22 22:01:16
【问题描述】:

我需要在 PDF 中创建目录。它可以是 1 页或多页,具体取决于 PDF 中的页数。我了解到可以使用PdfStamperPdfActionPdfAnnotaion 来实现。

我目前正在合并多个文档,并为 JAVA 中的所有文档创建书签和目录。我已经摆脱了书签,但卡在了多页目录中。

另外,请解释这行代码 - link = new PdfAnnotation(copy, 36, ct.getYLine(), 559, y, action);... 我的理解是您传递的是单击链接(,36,ct.getYLine(),559,y,)后它将在页面上的矩形..因此我面临一个问题,即单击链接,如果页面大小与 US Letter Portrait 不同。

这里是sn-p-

int tocPages = 1;
Document tocDocument = new Document();
String tocFilename ="toc-filename";
Phrase tocPhrase =
            new Phrase("Table of Contents", new Font(Font.FontFamily.HELVETICA, 20, Font.BOLD, BaseColor.BLACK));
PdfWriter writer = PdfWriter.getInstance(tocDocument, new FileOutputStream(tocFilename));
tocDocument.open();
tocDocument.add(new Paragraph(tocPhrase));
PdfReader reader = new PdfReader(tocFilename);
page = copy.getImportedPage(reader, tocPages);
stamp = copy.createPageStamp(page);

float y = 770;
ColumnText ct = new ColumnText(stamp.getOverContent());
ct.setSimpleColumn(36, 36, 559, y);
for (Map.Entry<Integer, String> entry : toc.entrySet()) {
if (y <= 20) {
copy.addPage(page);
                copy.newPage(); //(tried with writer.newPage() and tocDocument.newPage(), not working )
               page = copy.getImportedPage(reader, ++tocPages);
            }
            p = new Paragraph(entry.getValue());
            p.add(new Chunk(new DottedLineSeparator()));
            p.add(String.valueOf(entry.getKey() + 1));
            ct.addElement(p);
            ct.go();
            action = PdfAction.gotoLocalPage("p" + entry.getKey(), false);
            link = new PdfAnnotation(copy, 36, ct.getYLine(), 559, y, action);
            stamp.addAnnotation(link);
            y = ct.getYLine();
        }
        ct.go();
        stamp.alterContents();
        copy.addPage(page);
        tocDocument.close();
        reader.close();          

com.itextpdf.text.exceptions.InvalidPdfException:未找到 PDF 标头签名。 在 com.itextpdf.text.pdf.PRTokeniser.getHeaderOffset(PRTokeniser.java:227) 在 com.itextpdf.text.pdf.PdfReader.getOffsetTokeniser(PdfReader.java:442) 在 com.itextpdf.text.pdf.PdfReader.(PdfReader.java:176) 在 com.itextpdf.text.pdf.PdfReader.(PdfReader.java:219) 在 com.itextpdf.text.pdf.PdfReader.(PdfReader.java:207) 在 com.itextpdf.text.pdf.PdfReader.(PdfReader.java:197)

行异常 - PdfReader reader = new PdfReader(tocFilename);

【问题讨论】:

  • 有一个例子回答了重复的问题Create Index File(TOC) for merged pdf using itext library in java。您还应该解释更多有关该过程的信息。您是否也需要创建一个大纲树(又名书签)。您参考PdfStamper,但不清楚如果您从头开始创建文档,为什么需要它。如果您想要一个准确的答案,请说明。
  • 让我重复一遍:如果您希望得到这个问题的答案,请说明您已经在做什么:您是从头开始创建文档吗?您是否尝试为现有文档创建目录(如果有,是否有书签)?你在合并文件吗?你是用 Java 还是 C# 编写代码?
  • 请将您的评论表述为一个真实的问题,提供足够的上下文,以便人们知道您的问题是关于什么的。
  • 你的问题不是独立的。我已经为其他人提供了必要的上下文来理解我的回答中的问题。

标签: itext


【解决方案1】:

你已经修改了你的问题,所以它变成了我对Create Index File(TOC) for merged pdf using itext library in java的回答中关于这个sn-p的问题

Paragraph p;
PdfAction action;
PdfAnnotation link;
float y = 770;
ColumnText ct = new ColumnText(stamp.getOverContent());
ct.setSimpleColumn(36, 36, 559, y);
for (Map.Entry<Integer, String> entry : toc.entrySet()) {
    p = new Paragraph(entry.getValue());
    p.add(new Chunk(new DottedLineSeparator()));
    p.add(String.valueOf(entry.getKey()));
    ct.addElement(p);
    ct.go();
    action = PdfAction.gotoLocalPage("p" + entry.getKey(), false);
    link = new PdfAnnotation(copy, 36, ct.getYLine(), 559, y, action);
    stamp.addAnnotation(link);
    y = ct.getYLine();
}
ct.go();

你的问题被简化为:请解释一下你的这行代码:

link = new PdfAnnotation(copy, 36, ct.getYLine(), 559, y, action);

我已经粘贴了完整的sn-p,因为这一行无法解释断章取义

在 sn-p 中,我们使用 PdfAnnotation 类创建链接注释。链接注释是页面上某处的区域,当点击时会触发一个动作。

在这种情况下会触发哪个操作?这就是action 对象的意义所在,在这种情况下,它跳转到由命名目的地 定义的本地页面。这与拥有&lt;a name="dest" /&gt;&lt;a href="#dest"&gt;Jump to a specific destination on the current page&lt;/a&gt; 的HTML 非常相似。

创建PdfAnotation 时,您始终需要PdfWriter 实例。在本例中,我们使用名为 copyPdfCopy 实例合并文档。由于PdfCopy 扩展了PdfWriter,我们可以将copy 实例作为参数传递。

最后,我们定义可点击区域。这始终是一个使用两个坐标定义的矩形:左下角坐标和右上角坐标。

在上面的 sn-p 中,我们使用ColumnText 添加段落。 ColumnText 允许您在添加内容后获取有关当前y 位置的信息。例如,当我们这样做时:

ct.addElement(p);
ct.go();

我们可以这样做来获取当前的 Y 坐标:

float y = ct.getYLine();

在我们的代码 sn-p 中,我们跟踪之前的 y 值(Y 位置之前我们添加了p),我们使用ct.getYLine() 的当前值来获得当前y 位置。

这样,我可以像这样定义左下角的坐标:

float llx = 36;
float lly = ct.getYLine();

而右上角的坐标是这样的:

float urx = 559;
float ury = y;

这些是我们构造链接注解时可以看到的值。

我对@9​​87654347@ 值进行了硬编码。它们基于这样一个事实,即我正在创建一个页面大小为 A4 且边距为半英寸的文档。 A-4 页面的宽度为 595 个用户单位。在左边,我有 36 个用户单位的边距;在右边我还有 36 个用户单位的边距,我必须从页面宽度中减去:595 - 36 = 559。

如果您有一个格式为 LETTER 的页面,则需要调整这些值。但是:最好根据现有页面的 MediaBox / CropBox 的实际值来计算它们。这样一来,当您意外遇到页面大小不同的文档时,您的代码仍然可以正常工作。

您可以在我对这个问题的回答中阅读有关 MediaBox 和 CropBox 的更多信息:How to get dimensions of each page of a pdf file

【讨论】:

  • @Kapil 也许如果您首先发布一个问题来解释您正在尝试做什么。您想创建一个 TOC,但基于什么?这在您的问题中绝对不清楚。错误的问题 = 错误的答案。
  • 有什么区别?当页面超过一页时,什么不起作用?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-05
  • 2021-10-28
  • 2011-07-18
  • 2019-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多