iText 7-最后一页上的页脚不同-PdfDictionary中的空指针 [英] iText 7 - Different footer on last page - Null pointer in PdfDictionary

查看:267
本文介绍了iText 7-最后一页上的页脚不同-PdfDictionary中的空指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to implement a footer in iText7, the footer should be different on the last page of the document, I have added an event handler which is called when the document is closed but trying to then loop over the pages causes a null pointer exception:

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分为两页,但是当我添加更多内容并且页面自然增加时,我会遇到此异常,类似地,我现在尝试通过插入多个区域中断,我得到相同的异常.

Initially I thought this worked as I was inserting a single area break to get the pdf to split over two pages, however when I add more content and the pages increase naturally they I get this exception, similarly I have now tested by trying to insert multiple area breaks and I get the same exception.

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

Note* I have found that if I set immediateFlush to false when creating the document then I do not get this issue.

这样做是否会对性能造成重大影响?

Is there a major performance hit\downside to doing this?

推荐答案

您的方法

您发现自己了

Your approach

As you have found out yourself,

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

if I set immediateFlush to false when creating the document then I do not get this issue.

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

The cause is that itext usually attempts to write data to the output stream and free memory as early as possible to not leave too big a memory footprint. Thus, your approach

我添加了一个事件处理程序,该事件处理程序在文档关闭时尝试调用,然后试图在页面上循环

I have added an event handler which is called when the document is closed [...] trying to then loop over the pages

在该循环期间,通常将尝试操作已刷新内容并将其从页面对象中删除的页面.

during that loop usually will try and manipulate pages whose contents have already been flushed and removed from the page object.

immediateFlush设置为false可以防止这种情况.不利的一面是您使用了更多的内存,因为现在更多的pdf都保留在内存中,直到结束.

Setting immediateFlush to false prevents this. The down side is that you use more memory because now much more of the pdf is kept in memory until the end.

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

An alternative approach would use the normal END_PAGE event listener pattern. You can give such an event listener some method to signal to it which page will be the last. When the event listener is triggered, it can check whether it is currently processing that page, and in that case it can draw an alternative footer, e.g. like this:

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();
        }
    }
}    

(您可以像这样使用它:

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();

(您可能已经注意到,与我最初的评论相反

You may have observed that in contrast to my original comment

您可以为该事件侦听器提供一些方法,以告知该事件下一个事件将用于特殊情况.

you can give that event listener some method to signal to it that the next event to come will be for the special case.

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

I here do not signal "the next event to come" to be special but instead "the next event to come for the given page object". The cause is that iText 7 page events are triggered with some delay, so when I give the signal as above, the event for the page before the last might not have been triggered yet.

顺便说一句,我将此示例基于iText样本类com.itextpdf.samples.sandbox.events.TextFooter.同一包中的示例类VariableHeader似乎表明我的原始想法(发信号通知即将发生的下一个事件"很特别)应该起作用.但是,不同之处在于,在该示例中,页面是通过将AreaBreak对象输入文档来切换的.这似乎很早触发了事件.但是,一般而言,您无法确定何时切换页面,因此,通常,您还应该为事件侦听器提供该页面,以验证其具有正确处理的正确页面.

By the way, I based this example on the iText samples class com.itextpdf.samples.sandbox.events.TextFooter. The samples class VariableHeader in the same package appears to indicate that my original thought (signaling "the next event to come" to be special) should have worked. The difference is, though, that in that example pages are switched by feeding AreaBreak objects to the document. This seems to trigger events early. In general, though, you cannot tell when pages shall switch, so in general you should also give the event listener the page to verify it has the correct page to treat specially.

这篇关于iText 7-最后一页上的页脚不同-PdfDictionary中的空指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆