iText 7 - 最后一页上的不同页脚 - PdfDictionary中的空指针

时间:2018-05-08 14:30:40

标签: footer itext7

我正在尝试在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,那么我就不会遇到此问题。

这样做是否有重大影响?不利因素?

2 个答案:

答案 0 :(得分:2)

您的方法

正如你自己发现的那样,

  

如果我在创建文档时将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 test testDifferentLastPageFooter

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

  

你可以给那个事件监听器一些方法,告诉它下一个事件将是特殊情况。

我在这里没有发出信号&#34;接下来要发生的事件&#34;是特别的,而是#34;给定页面对象的下一个事件&#34;。原因是iText 7页面事件被触发了一些延迟,所以当我给出上面的信号时,最后一页之前的页面事件可能还没有被触发。

顺便说一下,我将此示例基于iText示例类com.itextpdf.samples.sandbox.events.TextFooter。同一个包中的样本类VariableHeader似乎表明我原来的想法(信号&#34;下一个要发生的事件&#34;要特殊)应该有效。不过,不同之处在于,在该示例中,通过将AreaBreak个对象提供给文档来切换页面。这似乎很早就引发了事件。但是,一般情况下,您无法确定何时切换页面,因此通常您还应该向事件监听器提供页面以验证它是否具有正确处理的正确页面。

答案 1 :(得分:0)

之前我没有使用过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&lt; ...而不是i&lt; =)