【问题标题】:iText 7 - Different footer on last page - Null pointer in PdfDictionaryiText 7 - 最后一页的不同页脚 - PdfDictionary 中的空指针
【发布时间】:2018-10-18 13:14:01
【问题描述】:

我正在尝试在iText7 中实现页脚,文档最后一页的页脚应该不同,我添加了一个事件处理程序,该处理程序在文档关闭时调用,然后尝试循环遍历页面导致空指针异常:

java.lang.NullPointerException
at com.itextpdf.kernel.pdf.PdfDictionary.get(PdfDictionary.java:482)
at com.itextpdf.kernel.pdf.PdfDictionary.get(PdfDictionary.java:152)
at com.itextpdf.kernel.pdf.PdfPage.newContentStream(PdfPage.java:777)
at com.itextpdf.kernel.pdf.PdfPage.newContentStreamBefore(PdfPage.java:212)

这是我要实现的代码:

private void onCloseDocument(
        final PdfDocument document, final float xpos, final float ypos)
{

    for (int i = 1; i <= document.getPdfDocument().getNumberOfPages(); i++)
    {
        final PdfPage page = document.getPdfDocument().getPage(i);
        final PdfCanvas canvas =
                new PdfCanvas(page.newContentStreamBefore(), page.getResources(), document.getPdfDocument());
        canvas.beginText();
        canvas.setFontAndSize(document.getFont(), FONT_SIZE);
        canvas.setLeading(14.4F);

        canvas.moveText(xpos, ypos);

        if (i == document.getPdfDocument().getNumberOfPages())
        {
            canvas.showText("Last page");
        }
        else
        {
            canvas.showText("Another page");
        }

        canvas.endText();
        canvas.stroke();
        canvas.release();
    }
}

最初我认为这很有效,因为我插入了一个单独的区域分隔符以使 pdf 分成两页,但是当我添加更多内容并且页面自然增加时,我得到了这个异常,同样我现在已经通过尝试进行了测试插入多个区域中断,我得到相同的异常。

注意* 我发现如果在创建文档时将 immediateFlush 设置为 false,则不会出现此问题。

这样做是否会对性能造成重大影响\不利因素?

【问题讨论】:

  • “这样做是否会对性能造成重大影响\缺点?” - 缺点是您使用更多内存,因为更多的 pdf 文件保存在内存中,直到结束。通常,itext 会尝试将数据写入输出流并尽早释放内存,以免留下太大的内存占用,将immediateFlush 设置为false 可以防止这种情况发生。
  • 我想了很多,有问题的 pdf 永远不会超过 5-6 页,所以我想这将取决于 NFT。您是否知道一种更合适的基于 Java 的方法来解决特定页面上不同的页脚问题?
  • "你知道更合适的基于 java 的方法吗..." - 如果你使用普通的事件监听器生成页眉和页脚,你可以给那个事件监听器某种方法向它发出信号,下一个事件将针对特殊情况。然后你可以在你的代码中触发这个方法,在适当的时候添加内容。
  • 你有类似例子的链接吗?这是我尝试过的,但无法判断它是结束页面事件中的最后一页或插入页面事件侦听器,所以我在这里错过了一些东西。我已经有一个 n 页脚的工作页 x,但无法调整它来检测最后一页。我将有两个页脚,第 x 页,共 n 页,然后一个页脚在除最后一页之外的所有页面上都相同。
  • 查看我的答案,它包含一个概念验证。

标签: footer itext7


【解决方案1】:

你的方法

如你所见,

如果我在创建文档时将 immediateFlush 设置为 false,则不会出现此问题。

原因是 itext 通常会尝试将数据写入输出流并尽早释放内存,以免留下太大的内存占用。因此,您的方法

我添加了一个事件处理程序,当文档关闭时调用该处理程序 [...] 尝试然后循环浏览页面

在该循环期间,通常会尝试和操作内容已从页面对象中刷新和删除的页面。

immediateFlush 设置为false 可以防止这种情况发生。不利的一面是您会使用更多内存,因为现在更多的 pdf 文件会一直保存在内存中。

另一种方法

另一种方法是使用普通的END_PAGE 事件侦听器模式。您可以为此类事件侦听器提供一些方法来通知它哪个页面将是最后一页。当事件监听器被触发时,它可以检查它当前是否正在处理该页面,在这种情况下它可以绘制一个替代页脚,例如像这样:

class TextFooterEventHandler implements IEventHandler {
    Document doc;
    PdfPage lastPage = null;

    public TextFooterEventHandler(Document doc) {
        this.doc = doc;
    }

    @Override
    public void handleEvent(Event event) {
        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfCanvas canvas = new PdfCanvas(docEvent.getPage());
        Rectangle pageSize = docEvent.getPage().getPageSize();
        canvas.beginText();
        try {
            canvas.setFontAndSize(PdfFontFactory.createFont(StandardFonts.HELVETICA_OBLIQUE), 5);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (lastPage == docEvent.getPage()) {
            canvas.moveText((pageSize.getRight() - doc.getRightMargin() - (pageSize.getLeft() + doc.getLeftMargin())) / 2 + doc.getLeftMargin(), pageSize.getTop() - doc.getTopMargin() + 10)
                .showText("VERY SPAECIAL LAST PAGE HEADER")
                .moveText(0, (pageSize.getBottom() + doc.getBottomMargin()) - (pageSize.getTop() - doc.getTopMargin()) - 20)
                .showText("VERY SPAECIAL LAST PAGE FOOTER")
                .endText()
                .release();
        } else {
            canvas.moveText((pageSize.getRight() - doc.getRightMargin() - (pageSize.getLeft() + doc.getLeftMargin())) / 2 + doc.getLeftMargin(), pageSize.getTop() - doc.getTopMargin() + 10)
                .showText("this is a header")
                .moveText(0, (pageSize.getBottom() + doc.getBottomMargin()) - (pageSize.getTop() - doc.getTopMargin()) - 20)
                .showText("this is a footer")
                .endText()
                .release();
        }
    }
}    

CreateSpecificFooters 中的内部类)

你可以这样使用它:

PdfDocument pdfDoc = new PdfDocument(new PdfWriter(new File("differentLastPageFooter.pdf")));
Document doc = new Document(pdfDoc);
TextFooterEventHandler eventHandler = new TextFooterEventHandler(doc);
pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, eventHandler);
for (int i = 0; i < 12; i++) {
    doc.add(new Paragraph("Test " + i + " Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."));
}

// the currently last page is also the last page of the document, so inform the event listener 
eventHandler.lastPage = pdfDoc.getLastPage();
doc.close();

(CreateSpecificFooters 测试testDifferentLastPageFooter)

您可能已经观察到与我原来的评论相反

你可以给那个事件监听器一些方法来通知它下一个事件将是针对特殊情况的。

我在这里并不表示“下一个事件即将到来”是特殊的,而是“给定页面对象的下一个事件”。原因是 iText 7 页面事件的触发有一定的延迟,所以当我发出上述信号时,最后一个页面之前的事件可能尚未触发。

顺便说一下,我的这个例子是基于 iText 示例类 com.itextpdf.samples.sandbox.events.TextFooter。同一个包中的示例类VariableHeader 似乎表明我最初的想法(表示“下一个事件即将到来”是特殊的)应该有效。但是,不同之处在于,在该示例中,通过将AreaBreak 对象提供给文档来切换页面。这似乎很早就触发了事件。但是,一般情况下,您无法判断页面何时切换,因此通常您还应该为事件侦听器提供页面以验证它是否有正确的页面进行特殊处理。

【讨论】:

  • 这非常有帮助,非常感谢您花时间回答这些问题并提供示例,非常感谢!
【解决方案2】:

我之前没用过itext,但是javadoc for onCloseDocument in itext5说:

文档关闭时调用。 请注意,调用此方法时,页码等于最后一页加一。

是不是你只需要从

改变你的循环
for (int i = 1; i <= document.getPdfDocument().getNumberOfPages(); i++)
{

for (int i = 1; i < document.getPdfDocument().getNumberOfPages(); i++)
{

? (注意 i

【讨论】:

  • onCloseDocument() 只是我写的一个私有方法,它实际上不是iText7 的一部分
  • 另外澄清一下,您的建议不起作用。
猜你喜欢
  • 1970-01-01
  • 2019-09-25
  • 2023-03-24
  • 2013-11-18
  • 1970-01-01
  • 2020-09-08
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
相关资源
最近更新 更多