Your approach
As you have found out yourself,
if I set immediateFlush
to false when creating the document then I do not get this issue.
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.
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.
An alternative approach
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();
}
}
}
(inner class in CreateSpecificFooters)
You can use it like this:
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
)
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.
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.
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.