Adding page events to PdfWriter (iText 5)

After the intermezzo about page boundaries, containing some self-glorifying examples, it’s time to return to the real topic of this topic: page events. We have already discussed seven methods of the PdfPageEvent interface; four more methods involving the document and its pages remain:

■ onOpenDocument() —Triggered when a document is opened. This is a good place to initialize variables that will be needed throughout the document.

■ onStartPage() —Triggered when a new page is started. Use this method for initializing variables or for setting parameters that are page-specific. Do not use this method to add content.

■ onEndPage() —Triggered just before starting a new page and before closing the document. This is the best place to add a header, a footer, a watermark, and so on.

■ onCloseDocument() —Triggered just before the document is closed. This is the ideal place for any finalizations and to release resources (if necessary).

Let’s use these methods to solve common issues that are often mentioned in mailing-list questions. For instance, how can you add a page header while creating a PDF document.

Adding a header and a footer

Let’s return to the topic and Section example from section 2.3.2. You’ll make two small changes: you’ll define an art box, and you’ll add an event to the writer. This event, an instance of the HeaderFooter class, will add a header and a footer to the document as shown in figure 5.11.


First, take a look at the footer: you want to add page numbers that start with "page 1" every time a new topic begins. The text should be put under the actual content of the page, as a centered String. As for the header, you want it to alternate between the topic title aligned to the left, and the String "Movie history" aligned to the right. The following implementation of the PdfPageEvent interface meets these requirements.

These values are important primarily for the PDF consumer. Setting these boundaries doesn’t have any effect on the way iText creates the document. For instance, setting the art box doesn’t affect the page margins.

Adding headers and footers using page events

Figure 5.11 Adding headers and footers using page events

Listing 5.19 MovieHistory2.java

Listing 5.19 MovieHistory2.javaListing 5.19 MovieHistory2.java

There are no surprises in this code sample. You define two member variables:

■ header—An array with two Phrase objects. One is set in onOpenDocument(), and it’s valid for the full document. The other varies depending on the current topic.

■ pagenumber—A custom page number that is reset to 1 every time a new topic starts. It’s augmented in the onStartPage() method.

No content is added in the page event until a page has been completed. The header and footer are written to the direct content in the onEndPage() method. The parameters writer and document are to be used in the same way as done in section 5.2.

Note that you ask the writer for the art box rectangle using the getBoxSize() method. You use this rectangle to position the header and the footer. This will only work if you’ve defined that specific page boundary between steps 2 and 3 in the PDF creation process. Otherwise, the getBoxSize() method will return null.

FAQ Why is it not advised to add content in the onStartPage() method? You’ll remember from section 5.2.4 that iText ignores newPage() calls when the current page is empty. This method is executed—or ignored—when you call it explicitly from your code, but it’s also invoked implicitly from within iText on multiple occasions. It’s important that it’s ignored for empty pages; otherwise you’d end up with plenty of unwanted new pages that are unintentionally left blank. If you add content in an onStartPage() method, there’s always a risk of having unwanted pages. Consider it more safe to reserve the onEndPage() method for adding content.

In the next example, you’ll put the page number in the header, and you’ll add the total number of pages.

Solving the "page X of Y" problem

An example of a "page X of Y" header is shown in figure 5.12.

Retrieving the value of X is easy. You have access to the PdfWriter object in the onEndPage() method, so you can get the page number with getPageNumber(). But how can you retrieve the value for Y? There’s no way of knowing the total number of pages when the headers for the first pages are written. You only know the value of Y for sure when iText has finished writing the last page.

 Solving the page X of Y problem with page events

Figure 5.12 Solving the page X of Y problem with page events

There are two ways to solve this problem. One solution will be discussed in the next topic. It involves creating the PDF in two passes. You add the content in the first pass and the header or footer in a second pass. The other solution involves a PdfTemplate object and page events.

When we discussed form XObjects in section 3.4.2, I explained that iText only writes a PdfTemplate to the OutputStream when you explicitly use the releaseTem-plate() method. Otherwise the object is kept in memory until you close the Document. This opens possibilities: you can add a template to page 1, and wait until the final page to write content to this template. Even if the content stream of the first page has already been sent to the OutputStream, the content added to the template afterwards will still be shown on the first page.

Listing 5.20 MovieCountries1.java

Listing 5.20 MovieCountries1.javaListing 5.20 MovieCountries1.java

When the document is opened, you create a template with a size of 30 pt x 16 pt O. This time, you use a table with one row and three columns to draw the header. In the first cell, you add the text for the header. In this example, you’re listing movies by country, so you’ll let the header reflect the name of the country. This name is set using the setHeader() setter method.

In the second cell, you add "page X of" where X is the value returned by writer.get-PageNumber(). The third cell is special: you add the template created in the onOpen-Document() method, wrapped in an Image ©. No content has been added to this template yet—it’s just an empty canvas. It isn’t until the onCloseDocument() method is invoked that you add the page number of the final page to this small canvas ©.

NOTE When the document is closed, the newPage() method is triggered to perform finalizattions on the current page. When newPage() is called, the page number is augmented, so you need to use (writer.getPageNumber() – 1) if you want to add the total number of pages in the onCloseDocument() method.

In the previous example, you added a header and footer with the showTextAligned() method. This example demonstrates that it’s sometimes more interesting to use PdfPTable and writeSelectedRows(). You can define a bottom border for each cell so that the header is underlined. This is the most elegant way to add headers and footers, because the table mechanism allows you to position and align lines, images, and text.

Another common requirement when creating documents is to add a watermark.

Adding a watermark

The next example extends the previous one. The main difference is one extra feature, demonstrated in figure 5.13: we’ve added a watermark.

Adding a watermark using page events

Figure 5.13 Adding a watermark using page events

The code to create this document is almost identical to the code used in the previous example. You only need to add one extra page event, the Watermark class.

Listing 5.21 MovieCountries2.java

Listing 5.21 MovieCountries2.java

If your watermark is an image, you have options: you can add it with the PdfContent-Byte.addImage() method, or you can wrap it in a ColumnText object, or you can put it inside a cell in a table.

NOTE If you add an Image in a page event, be sure you create the Image object only once, such as in the event’s constructor or in the onOpenDocu-ment() method. If you create the Image object in onStartPage() or onEnd-Page(), your PDF will become bloated: you risk adding the same byte sequence over and over again. This will cost you not only in performance, but also in file size.

We’ll conclude this topic with one more example, introducing functionality that creates a document that can be displayed as a presentation, similar to a PowerPoint presentation.

Creating a slideshow

When you read a PDF document on screen, you usually hit a key, click a button, or use a scrollbar to go to the next page. But you can also let the viewer go to the next page automatically after a number of seconds, define a transition, or both.

In the example, you’ll set the viewer preferences to "Full Screen" mode O, because you want to use the PDF as a presentation.

Listing 5.22 MovieSlideShow.java

Listing 5.22 MovieSlideShow.java

There are two new special methods in onStartPage():

setDuration()—This method’s parameter defines for how many seconds the page is shown. If no duration is defined, user input is expected to go to the next page.

■ setTransition()—This method expects a Transition object. The main constructor of this class takes two parameters: a transition type and a value for the duration of the transition. Don’t confuse this with the value for the page duration.

There are different groups of transition types:

■ Dissolve—The old page gradually dissolves to reveal a new one (DISSOLVE).

■ Glitter—Similar to dissolve, except that the effect sweeps across the page in a wide band: diagonally (DGLITTER), from top to bottom (TBGLITTER), or from left to right (LRGLITTER).

■ Box—A rectangular box sweeps inward from the edges (INBOX) or outward from the center (OUTBOX).

■ Split—The lines sweep across the screen horizontally or vertically, depending on the value that was passed: SPLITHIN, SPLITHOUT, SPLITVIN, or SPLITVOUT.

■ Blinds—Multiple lines, evenly spaced across the screen, sweep in the same direction to reveal the new page horizontally (BLINDH) or vertically (BLINDV).

■ Wipe—A single line sweeps across the screen from one edge to the other: from top to bottom (TWIPE), from bottom to top (BWIPE), from right to left (RLWIPE), or from left to right (LRWIPE).

If you don’t specify a type, BLINDH is used. The default duration of a transition is 1 second. This is a nice example showing how onStartPage() can be used to set page parameters, because you need to set the transition and duration for every page.

With this example, we’ve covered all the methods of the PdfPageEvent interface. It’s high time for a short summary of this topic, and of part 1 as a whole.

Summary

After a short introduction to what you can do with PDF, we started with the basic mechanics of iText’s PDF creation process. You created several "Hello World" examples that demonstrated the famous "five steps" that were used in every example of part 1.

Two topics dealt with building blocks offered by iText, allowing you to create PDF documents using high-level concepts. In topic 2, you learned about Chunks, Phrases, Paragraphs, Lists, ListItems, Anchors, Images, topics, and Sections. topic 4 was dedicated entirely to the PdfPTable and PdfPCell objects.

Topic 3 explained how to add content at a lower level: you added lines, shapes, and text to different direct content layers. You also discovered two other important objects: using the ColumnText object, you added high-level objects at absolute positions; with PdfTemplate you learned how to reuse content as an XObject.

You’ve made good use of that knowledge in this topic. First you found a way to extend the functionality of PdfPTable and PdfPCell using table and cell events. Then you learned how to use the PdfPageEvent interface. Initially you added custom features to Chunk, Paragraph, topic, and Section objects. After an intermezzo about pagination, involving reordering pages, adding blank pages, and defining page boundaries, you used a second series of page events to solve a number of common issues: adding headers and footers, adding "page X of Y" to every page, adding watermarks, and even defining a duration and a transition for each page.

Now that you’ve finished part 1, you’re ready to start writing a prototype application that creates PDF documents from scratch. If you need to integrate this prototype into a web application, you’ll have to read further in part 3. That’s where you’ll learn how to generate a PDF document using Java servlet technology. Also, when you want to make your application production-ready, you’ll probably want to know more about using special fonts, about protecting your documents, and so on. Part 3 will teach you these essential iText skills.

But first, we’ll take a look at another aspect of iText programming. In part 2, you’ll learn how to manipulate existing PDF documents: how to import pages from one PDF document into another, how to stamp content on an existing PDF document, how to split one PDF into smaller PDFs, how to combine different PDFs into one large document, and so on. You’ll also learn how to fill out interactive forms using iText.

Next post:

Previous post: