iText 7 - Другой нижний колонтитул на последней странице - Пустой указатель в PdfDictionary - PullRequest
0 голосов
/ 08 мая 2018

Я пытаюсь реализовать нижний колонтитул в 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 голосов
/ 09 мая 2018

Ваш подход

Как вы сами выяснили,

, если при создании документа я установил значение immediateFlush в ложь, тогда я не получу эту проблему.

Причина в том, что 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)

Вы, возможно, заметили, что в отличие от моего первоначального комментария

вы можете дать этому слушателю какой-то метод, чтобы сообщить ему, что следующее событие будет для специальногоcase.

Я здесь не сигнализирую, что «следующее событие наступит» как особенное, а вместо этого «следующее событие наступит для данного объекта страницы».Причина в том, что 7-страничные события iText запускаются с некоторой задержкой, поэтому, когда я даю сигнал, как указано выше, событие для страницы до последней, возможно, еще не было инициировано.

Кстати, я основалэтот пример в классе примеров iText com.itextpdf.samples.sandbox.events.TextFooter.Класс сэмплов VariableHeader в том же пакете указывает на то, что моя первоначальная мысль (, сигнализирующая, что «следующее событие наступит» должно быть особенным ) должна была сработать.Разница, однако, заключается в том, что в этом примере страницы переключаются путем подачи в документ AreaBreak объектов.Это, кажется, вызывает события рано.В целом, однако, вы не можете сказать, когда страницы должны переключаться, поэтому в общем случае вы должны также предоставить прослушивателю событий страницу, чтобы убедиться, что она имеет правильную страницу для обработки.

0 голосов
/ 08 мая 2018

Я раньше не использовал itext, но javadoc для onCloseDocument в itext5 говорит:

Вызывается, когда документ закрыт. Обратите внимание, что этот метод вызывается с номером страницы, равным последней странице плюс один.

Может быть, вам просто нужно изменить цикл с

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

до

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

? (обратите внимание, что я <... вместо я <=) </p>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...