Events for basic building blocks (iText 5)

When you add a basic building block to a Document instance, it’s translated into PDF syntax and written to a PDF file by a PdfWriter object. In this process, there’s an important class you’ll hardly ever need to address directly: PdfDocument. This class is responsible for examining the high-level objects. It’s the invisible rope tying the document to the writer.

The PdfDocument class is also responsible for firing the page events defined by the PdfPageEvent interface. This interface has 11 methods that can be divided into two groups:

■ Methods that involve basic building blocks—These are similar to the tableLayout() and cellLayout() methods discussed in the previous section, but instead of tables, they involve Chunks, Paragraphs, topics, and Sections. These methods will be discussed in this section.

■ Methods that involve the document and its pages—These are called when the document is opened or closed, or when a page starts or ends. We’ll discuss these methods in section 5.4.

The onGenericTag() method is without any doubt the most powerful method in the first category.

Generic Chunk functionality

When we discussed the Chunk object in section 2.2.1, there was an example (shown in figure 2.3) where we displayed country codes using a white font on a black background. This example demonstrated the setBackground() method. Figure 5.5 does something similar, but instead of a rectangular background, you draw a filmstrip for the year, and a blue ellipse for the link to the IMDB.


Page events for Chunks and Paragraphs

Figure 5.5 Page events for Chunks and Paragraphs

There are no standard methods to draw special backgrounds for Chunks, but you can write your own custom Chunk functionality by implementing the onGenericTag() method of the PdfPageEvent interface.

Listing 5.8 MovieYears.java

Listing 5.8 MovieYears.javaListing 5.8 MovieYears.java

Instead of O, you could have written GenericTags implements PdfPageEvent, but then you’d need to implement all the methods defined in the PdfPageEvent interface. Here you’re only interested in the onGenericTag() method, so it’s easier to extend the PdfPageEventHelper class. This class contains nothing but empty implementations of the interface’s methods. In this example, you override one specific method, and you can safely ignore the other methods.

The code in listing 5.8 won’t be executed unless you declare the event to a writer. The onGenericTag() method will never be invoked if you don’t define generic tags for Chunks.

Listing 5.9 MovieYears.java

Listing 5.9 MovieYears.java

Before we study the mechanisms used in this code, let’s look at the parameters passed to the onGenericTag() method:

■ writer—The PdfWriter object to which the event is added.

■ pdfDocument—Not the Document object to which the Paragraph is added. This is a PdfDocument that is created internally when you create a PdfWriter instance. Use this object just for read-only purposes!

■ rect—Rectangle defining the boundaries of the Chunk for which a generic tag is set.

■ text—The String passed to the Chunk with the setGenericTag() method.

In listing 5.9 you’re tagging the Chunks representing the year with a generic tag named "strip". When the content is written to the page, the onGenericTag() method is invoked. In the page event implementation, the onGenericTag() method looks at the text, and calls the strip() method to draw a filmstrip over the year.

NOTE If a Chunk is split over multiple lines, the onGenericTag() method will be invoked as many times as there are lines. Every line will have its own Rectangle.

The same happens for the IMDB links: the text "ellipse" corresponds with the ellipse() method. You’re using this page event to achieve more or less the same goals as with table and cell events: to add special shapes. But there’s more.

In listing 5.8, you’ll also find a countYear() method. This method is invoked because you’re setting the year as a generic tag for the movie titles. A list of these years and the number of times each year occurs is kept in the member variable years. Here is what you can do with this TreeMap.

Listing 5.10 MovieYears.java

Listing 5.10 MovieYears.java

You start a new page and remove the page events from the writer by setting the page events to null. You don’t want any of the page events to be active, and figure 5.5 shows that GenericTags wasn’t the only event used in this example— you also used a Paragraph event to draw extra lines. You don’t want these lines to appear when you create an overview of the years for which you have a film in the database, along with the number of times each year occurs. This overview is shown in figure 5.6.

Counting movies using the generic tag functionality

Figure 5.6 Counting movies using the generic tag functionality

Listing 5.9 was far from complete—the lines in figure 5.5 were added using another type of page event. The following line actually came right after GenericTags was set:

tmp17C218_thumb

ParagraphPositions is an example of how to create events for Paragraph objects.

Listing 5.11 MovieYears.java

Listing 5.11 MovieYears.java

There are two page event methods involving paragraphs. The first two parameters of these methods, writer and pdfDocument, have the same meaning as the onGeneric-Tag() parameters with the same names. I repeat: use pdfDocument for read-only purposes. In this example, you use pdfDocument to get the values of the left and right margins of the page. An extra parameter named paragraphPosition gives you access to a Y coordinate.

These are the two page event methods:

onParagraph()—Called before a Paragraph is rendered. The paragraphPosition passed to the method is the Y coordinate of the baseline of the first line of the Paragraph, augmented with its leading.

onParagraphEnd()—Called after a Paragraph is rendered. The paragraphPosition is the Y coordinate of the baseline of the last line of the Paragraph.

There are also page events involving topic and Section.

Topic and Section events

You can use topic and Section events for the same reasons you use Paragraph events: to retrieve a Y position and use that coordinate to draw lines or shapes. This is what’s done in figure 5.7.

The ParagraphPositions class creates Paragraph events.

Page events for topics and Sections

Figure 5.7 Page events for topics and Sections

As you know, using topic and Section automatically creates an outline tree, visible in the bookmarks pane of Adobe Reader. In the next example, you’ll use page events to create a table of contents that can be printed. See figure 5.8.

Next, you’ll reuse the example from section 2.3.2, but add a page event implementation for events that are triggered when a topic, a Section, or both, starts or ends.

Page events for topics and Sections: reordering pages

Figure 5.8 Page events for topics and Sections: reordering pages

Listing 5.12 MovieHistory1.java

Listing 5.12 MovieHistory1.java

In this example, you’re adding Paragraphs with the content of the topic and Section titles to a list, and you’re using the depth of the Sections to define an indentation. You can create a table of contents if you add all the Paragraphs in this list to the Document. You’ll find this table of contents (TOC) on the last pages of the document. The TOC entries are stored only after topics and Sections are rendered. You can’t add the TOC up front.

If you want the document to start with the TOC on the first page, you’ll need to find a way to reorder the pages before the Document is closed.

Page order and blank pages

Before we look at the code to reorder pages, you have to know that pages in a PDF document are usually organized in a page tree with different branches and leaves.

LINEAR PAGE MODE

By default, iText creates a balanced tree, because using such a tree optimizes the performance of viewer applications. The simplest page tree structure consists of a single node that references all of the document’s page objects directly.

Reordering pages with iText is only possible if you tell PdfWriter to create this simple structure. To do so, you need to add the following line before opening the document:

tmp17C223_thumb

After opening the document, you add all the content. In this case, the content consists of a series of topics.

REORDERING PAGES

Once the content is added, you can reorder the pages.

Listing 5.13 MovieHistory1.java

Listing 5.13 MovieHistory1.java

Let’s examine this code step by step. O You start on a new page, and you store the current page number. That’s where the table of contents starts before reordering the pages. In this example, the TOC starts on page 27.

You add the TOC. That’s the list of Paragraphs you’ve created in the page event. You need to start a new page before you can count the number of pages that need to be reordered. You obtain this value by calling the reorderPages() method a first time with null as the parameter. In this example, the total number of pages is 30. You create an array of int values that will be used to map the new page index to the old page number. The new page with index 0—the new page 1—will be the old page with number toc. In this example, the first page will be the old page 27. The TOC consists of 4 pages. The new page with index 4—that is, page 5—was originally page 1. Creating the new order is a matter of doing some simple math. © Once this mapping is done, you invoke reorderPages() a second time with the new order as the parameter.

You could replace the two lines marked with © with the following line:

tmp17C225_thumb

But experience has taught me that this can cause exceptions if the current page is empty.

You may wonder if using document.newPage() won’t result in an unnecessary extra blank page at the end of the document. The answer is no: iText ignores docu-ment.newPage() if the current page is empty. iText never adds a blank page to a document unintentionally.

ADDING A BLANK PAGE

If adding a blank page is a requirement, you have to tell iText explicitly about this.

Listing 5.14 NewPage.java

Listing 5.14 NewPage.java

In O, you add a Paragraph to page 1.

With document.newPage() ©, you go to page 2, but you don’t add anything to this page: you immediately ask for another new page. Since nothing was added to page 2, © will be ignored: the second Paragraph © will be added on page 2.

Page 2 is no longer empty, so © will take you to page 3. You don’t add any content to page 3, but with © you tell iText that the current page should not be treated as an empty page.

© takes you to page 4, and that’s where the third Paragraph © will be added.

The example about creating a TOC using topic and Section events led us somewhat astray and resulted in a discussion about pages. We’ll talk about the second category of page events in section 5.4, but first we’ll take a closer look at the boundaries of a page.

Next post:

Previous post: