Working with the ColumnText object (iText 5)

In this section, you’ll learn about the different ways to use the ColumnText object: text mode if you only use Chunks and Phrases, composite mode if you want to use other types of high-level objects as well.

In listing 3.13, you wrote a showMovieInfo() method. If a screening was reserved for the press, you marked the corresponding time block of the movie with a white, uppercase "P". You could try adding the movie title the same way, but the showText-Aligned() method isn’t able to wrap text. You also can’t use newlines in Strings or Chunks using any version of this method.

Let’s extend the previous example and reuse almost all of its methods. The only change involves the drawMovieInfo() method.

Listing 3.15 MovieCalendar.java

Listing 3.15 MovieCalendar.java

The result is shown in figure 3.10. Now you’re ready to go to the film festival!

We aren’t finished discussing the ColumnText object yet. This example worked out fine because you were able to fit the content inside the rectangles reserved for the screenings. But what would have happened if the text didn’t fit? Also, you’ve been adding a Phrase object to the column to display its contents at an absolute position. Can you add other objects such as Paragraphs, Lists, and Images with the ColumnText object? The answer to the first question will be explained in section 3.3.1; the answer to the second question is "yes," but not in text mode, only in composite mode.


The finished timetable, now with the movie titles

Figure 3.10 The finished timetable, now with the movie titles

Using ColumnText in text mode

In section 2.3.3, you created PDF documents with movie information that was organized in Paragraphs. Suppose you want to repeat this exercise, but now you want to organize the same information in columns, as is shown in figure 3.11.

Movie information, organized in columns

Figure 3.11 Movie information, organized in columns

Instead of the setSimpleColumn() method from listing 3.15, which can be used for a single Phrase, you’ll use addText() to add a series of Phrases and Chunks.

ADDING CONTENT WITH ADDTEXT()

Take a look at the code used to produce the columns shown in figure 3.11.

Listing 3.16 MovieColumns1.java

MovieColumns1.java

O Just as in listing 3.15, you create a ColumnText object, passing a PdfContentByte object as a parameter.

You add content (Phrase and/or Chunk objects) to this column with the addText() method.

You set the properties for the text that will be rendered, such as the alignment, the leading, extra space between paragraphs, and special indentations. You perform some initializations. The linesWritten parameter informs you about the number of lines that have been written. The column variable (keeping track of the column number) and the status of the ColumnText object are more important. You want all the content added to the ColumnText object to be rendered, so you invoke the go() method in a loop as long as the ColumnText.NO_MORE_TEXT bit isn’t set in the status value.

You define the dimensions of the column where the next block of text will be added. In this case, COLUMNS is a two-dimensional array, containing two sets of four values (one rectangle for each column). You also define the Y position; that’s the vertical start position of the text in the column.

H Lines of text are written as soon as you invoke the go() method. Text that didn’t fit the current column remains in the ColumnText object. The content that was rendered is consumed; it’s no longer present in the ColumnText object.

Each time a column is written, you have to switch to the next column. If there are no more columns on the current page, you have to go to a newPage(). In this example, you’re changing the properties of the text. The setAlignment() method is similar to the Paragraph method with the same name. It takes the same parameters: Element.ALIGN_LEFT, Element.ALIGN_RIGHT, Element.ALIGN_JUSTIFIED, and Element.ALIGN_JUSTIFIED_ALL. The setLeading() method comes in two flavors: in listing 3.16, you define an absolute leading of 0 pt and a relative leading of 1.2. The resulting leading will be 0 + 1.2 x 12 pt (the font size) = 14.4 pt. In listing 3.17, you’ll use the other setLeading() method to define a leading of 14 pt.

Let’s examine the properties that can be set for the text that has to be rendered.

COLUMNTEXT PROPERTIES

Although you aren’t using Paragraph objects here (when in text mode, Paragraphs are treated as Phrase objects) the setExtraParagraphSpace() method gives you a means to help the reader distinguish different paragraphs in a visual way. In listing 3.16, you tell iText to add 6 pt whenever a new portion of text is started on a new line. Another visual aid can be provided with the setFollowingIndent() method—this sets the left indentation of the lines that follow the first line. Listing 3.17 shows its counterpart: set-Indent() can be used to change the indentation of the first line. There’s also a set-RightIndent() method.

In section 2.2.4, you learned how to change the character/space ratio at the PdfWriter level for all the basic building blocks at once. With ColumnText, it’s possible to change the character/space ratio in a more fine-grained way. See the setSpaceChar-Ratio() method in listing 3.17.

ADDING CONTENT IN SMALL PORTIONS

In listing 3.16, you filled a ColumnText object with all the movie information that is present in the movie database. Then you rendered all that content until the Column-Text object had no more data.

There are 120 movies in the database, so at some point you have a ColumnText object that contains 120 Phrase objects. Maybe it’s better to invoke go() more frequently to avoid the memory building up in the ColumnText object.

Listing 3.17 MovieColumns2.java

Listing 3.17 MovieColumns2.javatmp17C115_thumb

You now invoke the go() method in the same loop that is used to add movies to the ColumnText object. The content is consumed immediately; if it doesn’t fit the current column, it’s added to the next one.

This brings us to the next question: what if you want to keep all the information about a specific movie together in one column? What if you don’t want the information to be split into two parts? ADDING CONTENT IN SIMULATION MODE

To answer the previous questions, you use a special go() method introduced here.

Listing 3.18 MovieColumns3.java

Listing 3.18 MovieColumns3.javatmp17C117_thumb

Listing 3.18 is almost identical to listing 3.17, except that you now invoke the go() method twice. The first time go() is simulated: nothing is added for real, but the content is consumed. If all the content of the column is gone, you need to go back to the initial Y position obtained with the getYLine() method, fill the column a second time, and then perform go() for real. If the content wasn’t entirely consumed, you have to switch to the next column and add the entire portion of movie information there.

NOTE The getYLine() method returns the Y position on the page after the last line was written, either for real or in simulation mode. You can use this method to find out the height that is needed to show the content, given a certain column width. If you want to center text vertically inside a column, you can add the text in simulation mode first to determine the height that is needed; then you can compute the offset like this: (available height – needed height) / 2. You can use this offset when adding the column for real.

It’s important to notice that you don’t use addText() just before the second go(). Instead you use setText(). The setText() method removes all the unconsumed text that may still be present in the column; otherwise you’d add portions of the movie information twice.

NOTE The basic building blocks discussed in topic 2 can be reused. They can be added more than once to the same, or to a different, document. This isn’t true for the ColumnText object. Each ColumnText object belongs to a specific PdfWriter, and it can’t be used more than once; the go() method consumes its content.

This technique is also used in the PDF shown in figure 3.12. There was space available in the first column of the page to the right, but the content was added to the second column to keep it together.

This screenshot also demonstrates another feature that is available when in text mode: irregular columns. As you can see, the columns in figure 3.12 are no longer rectangular. The border of each column is defined as a polygon, resulting in an irregular shape, so the text flows around the boxes.

IRREGULAR COLUMNS

Setting irregular columns is possible by using a variation on the original example in listing 3.16.

Irregular columns

Figure 3.12 Irregular columns

Listing 3.19 MovieColumns4.java

Listing 3.19 MovieColumns4.javaListing 3.19 MovieColumns4.java

The drawRectangles() method draws the squares that are shown in figure 3.12. This example is almost identical to the previous one, except that you no longer use the setSimpleColumn() method, but setColumns(). The parameters RIGHT and LEFT look like this:

tmp17C-121_thumb

LEFT contains the coordinates of the line that is used for the left border of the two columns. RIGHT defines the right borders.

Using irregular columns isn’t allowed in composite mode.

Using ColumnText in composite mode

So far, you’ve only used Phrase and Chunk objects and added them to a ColumnText object using the methods addText() and setText(). In this section, you’ll add other building blocks using the addElement() method. Invoking the addElement() method on the ColumnText object automatically switches you from text mode to composite mode.

ADDING CONTENT WITH ADDELEMENT()

Figure 3.13 shows a page in landscape format with four columns defined. Image, Paragraph, List, and Chunk objects have been added to it.

Columns in composite mode

Figure 3.13 Columns in composite mode

Listing 3.20 shows how the content was added to the ColumnText object.

Listing 3.20 ColumnsMovies1.java

Listing 3.20 ColumnsMovies1.java

This addContent() method is used in this bit of code O which doesn’t differ that much from the listings in the previous section demonstrating text mode.

Listing 3.21 ColumnsMovies1.java

Listing 3.21 ColumnsMovies1.java

Again, you’re using the go() method twice—once in simulation mode, and once for real—to keep the information about a movie together in the same column. Line C is important because it makes sure the content is added only once! Omit this line, and you’ll notice that part of the content is added twice.

PROPERTIES OF THE COLUMNTEXT OBJECT VERSUS ELEMENT PROPERTIES

As soon as you start using addElement(), all the content that was added in text mode previously and that hasn’t been rendered yet will be cleared. ColumnText properties, such as the leading and the alignment, will be ignored. Instead, the properties of the Elements that were added will be used.

Figure 3.14 shows four columns with Paragraphs that are centered, right-aligned, and justified.

Alignment in composite mode

Figure 3.14 Alignment in composite mode

You can reuse listing 3.21 to create the result in figure 3.14 by just changing the add-Content() method from listing 3.20 to what is shown next.

Listing 3.22 ColumnsMovies2.java

Listing 3.22 ColumnsMovies2.javaListing 3.22 ColumnsMovies2.java

It’s not possible to create irregular columns in composite mode, but you could work around this by adding the content in small portions, changing the column definition after every go().

The difference between text mode and composite mode will also matter in the next topic when you create PdfPCell objects, but first we’ll return to the movie timetable. We won’t change the content. The result will look identical to the PDF shown in figures 3.5 and 3.10, but you’ll learn how to reduce the file size by reusing data that is added multiple times.

Next post:

Previous post: