Adding text at absolute positions (iText 5)

In listing 3.1, you wrote "SOLD OUT" on top of a poster of the movie that is opening the film festival. You used methods such as setTextRenderingMode(), setTextMa-trix(), and so on, but it’s not easy to create a complete document using these low-level methods. It’s easier to use convenience methods that do part of the work for you. They’ll demand fewer lines and reduce the complexity of your code.

Convenience method: PdfContentByte.showTextAligned()

There was a lot of math involved when you added the words "SOLD OUT" in listing 3.1. You had to calculate the sine and the cosine of the rotation angle. You had to measure the length of the String "SOLD OUT" to determine the (x,y) coordinates so that the text was more or less centered. This length doesn’t depend solely on the characters in the String; you also needed to know which font was used to render it. The words "SOLD OUT" will have a different length in Helvetica than the same String in Times-Roman, even if the same font size is used. MEASURING A STRING

You can calculate the length of a String if you have an instance of the BaseFont class that will be used to draw the glyphs. In listing 3.7, you’ll measure the length of the String "Foobar Film Festival" using the getWidthPoint() method. This will return a width in points. For example, when you use the getWidthPoint() method with the font Helvetica and a font size of 12 pt, the resulting length is 108.684 pt. When you use the font program times.ttf with the same font size, the result is 100.572 pt. That’s a difference of 0.11 in.


Listing 3.7 FoobarFilmFestival.java

Listing 3.7 FoobarFilmFestival.javaListing 3.7 FoobarFilmFestival.java

Note that the Chunk object also has a getWidthPoint() method. You could use it to measure the width of a Chunk in points. While you’re at it, you could also measure the ascent and descent of the String.

ASCENT AND DESCENT OF THE STRING

The ascent is the space needed by a glyph above the baseline, and the descent is the space below the baseline. In listing 3.8, you’ll calculate the ascent and descent of the font Helvetica using the getAscentPoint() and getDescentPoint() methods.

Listing 3.8 FoobarFilmFestival.java

Listing 3.8 FoobarFilmFestival.java

You can calculate the height of a String by subtracting the descent from the ascent.

NOTE The font size isn’t the height of any specific glyph; it’s an indication of the vertical space used by a line of text.

Looking at figure 3.7, you might assume that the font size is about 9 pt, but you would be wrong. The ascent for the String in Helvetica is 8.328. You need to add the descent to the ascent, but the value of the descent is unusually low in this example because it doesn’t include glyphs like "g", "j", "p", "q", or "y", which have a descent that exceeds -0.18.

Measuring and positioning text: width, ascent, and descent of a String

Figure 3.7 Measuring and positioning text: width, ascent, and descent of a String

The word "Foobar" is magnified so you can see the descent caused by the rounding of the glyphs "o", "b", and "a". In the same screenshot, you can see that the same String was added a couple of times more, at absolute positions relative to a grid of lines.

POSITIONING A STRING

Now that you know how to measure the length of a String, you can compute the (x,y) coordinates and use them to change the text matrix so that the text is right aligned or centered (listing 3.9). Fortunately, there’s an easier way to achieve this. You can use the setTextAligned() method, which will act as a shorthand notation for two types of transformations: a translation and a rotation. You’ve already seen the result in figure 3.7.

Listing 3.10 FoobarFilmFestival.java

Listing 3.10 FoobarFilmFestival.java

The result indicates that the kerned version measures 107.664 pt; the String without kerning measures 108.684 pt.

Listing 3.9 FoobarFilmFestival.java

Listing 3.9 FoobarFilmFestival.java

O The text starts at position x = 400; the baseline y = 788. The text ends at position x = 400; the baseline y = 752. The text is centered at position x = 400, y = 716. O The text is centered at position x = 400, y = 680, and rotated 30 degrees.

The showTextAlignedKerned() method in listing 3.9 shows the same String, but it takes into account the kerning of the glyphs that were used.

KERNING

Kerning is the process of adjusting the space between the glyphs in a proportional font. When taking advantage of kerning, you can save some space between a series of specific glyph combinations. For instance, you can move the glyphs of the word "AWAY" closer to each other, due to the shapes of the A and the W. In listing 3.9, the kerned version of the String "Foobar Film Festival" is only slightly shorter than the nonkerned version. Here you compute the width of this String measured in points.

ADDING TEXT TO THE TIMETABLE

Returning to the film festival timetable, you can now add the names of the movie theaters and the screens, the time slot information, and the information about the day and date. For instance, you can add the text "Day 5" and "2011-10-16" shown in figure 3.5.

Listing 3.11 MovieTextInfo.java

Listing 3.11 MovieTextInfo.java

Note that the sanityCheck() method mentioned in section 3.1.2 that checks for unbalanced saveState() and restoreState() sequences also looks for possible problems related to text state:

■ Unbalanced beginText() and endText() combinations

■ Text state operators outside a beginText() and endText() sequence

■ Text for which you forgot to set a font and size

The showTextAligned() examples you’ve worked with so far have been very straightforward, but wouldn’t it be nice if you could create a Phrase object, and add that object at an absolute position? That’s what you’ll do in the next section with the show-TextAligned() method of the ColumnText object.

Convenience method: ColumnText.showTextAligned()

Next, instead of adding a String after selecting a BaseFont object and a font size, as you did in listing 3.9, you create a Phrase containing text in a certain Font.

Listing 3.12 FoobarFilmFestival.java

Listing 3.12 FoobarFilmFestival.java

Apart from the fact that you no longer need beginText() and endText() sequences, there seems to be little difference, but you’ll need fewer lines of code and less math for more complex Phrases.

POSITIONING A PHRASE

A Phrase can be composed of a series of Chunk objects. By adding a Phrase at an absolute position, you can easily switch fonts, font sizes, and font colors. iText will calculate the offset of every Chunk inside the Phrase, and change the text state accordingly for you.

In the next step, you draw a big "P" with a white font color on top of all the colored rectangles that represent preview screenings that are reserved for the press.

Listing 3.13 MovieTextInfo.java

Listing 3.13 MovieTextInfo.java

Although you’re working with high-level objects here, it’s possible to change their text state by setting Chunk attributes that we haven’t discussed yet.

CHUNKS: SCALING, SKEWING, RENDERING MODE

On the left side in figure 3.8, you can see the result of listing 3.12: the String "Foobar Film Festival" is added at an absolute position using different alignment options and different angles.

Adding text with ColumnText.showTextAligned()

Figure 3.8 Adding text with ColumnText.showTextAligned()

On the right side of figure 3.8, the same String is added left aligned, but the text is scaled or skewed, or the rendering mode was changed.

Listing 3.14 FoobarFilmFestival.java

Listing 3.14 FoobarFilmFestival.java

You can change the width of a Chunk with the setScaling() method. In the top right of figure 3.8, the words "Foobar Film Festival" are scaled to 50 percent of their width, but the height of the glyphs is preserved. This means that the aspect ratio of the letters is changed. You have to be careful not to exaggerate the scaling. At some point, your text will become almost illegible.

The setSkew() method expects two parameters. With the first parameter, you change the angle of the baseline. That’s what happened in the second line on the right side of figure 3.8: the angle of the baseline is changed to 15 degrees. The second parameter can be used to define the angle between the characters and the baseline. The third line on the right in figure 3.8 looks as if an italic font were used. In reality, the glyphs were skewed 25 degrees.

NOTE If you have to use a font for which you can’t find the corresponding font with italic or oblique style, you can use setSkew(0, 25) to simulate italics.

Finally, there’s the setTextRenderMode() method. These are possible values for the first parameter:

■ PdfContentByte.TEXT_RENDER_MODE_FILL—This is the default rendering mode; the glyph shapes are filled, not stroked.

■ PdfContentByte.TEXT_RENDER_MODE_STROKE—This causes the glyphs to be stroked, not filled. This is shown in figure 3.8: the letters are hollow.

■ PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE—This changes the text state so that the glyphs are filled and stroked. This state was used in figure 3.1 to cover existing content with the words "SOLD OUT" in white letters with red contours.

■ PdfContentByte.TEXT_RENDER_MODE_INVISIBLE—This will make all the text that is added invisible. The text will be there, but it won’t be visible.

Two extra parameters define the line width and the color that will be used to stroke the glyph. If you pass a null value for the stroke color, the fill color (defined in the Chunk’s Font object) will be used. The final line in figure 3.8 looks as if a bold font were used.

TIP If you have to use a font for which you can’t find the corresponding font with bold style, you could use setTextRenderMode(PdfContentByte .TEXT_ RENDER_MODE_FILL_STROKE, 0.5f, null) to simulate bold.

These attributes also work if you’re adding Chunks with document.add().

The timetable for the film festival is almost finished, as you can see in figure 3.9.

Timetable without movie titles

Figure 3.9 Timetable without movie titles

The only bits of information missing in the figure are the movie titles. It would be nice if you could add the text to the rectangles without having to scale the text or downsize the font size if the title is too long to fit the width of the rectangle. This can’t be done with the showTextAligned() method, to which you only pass a single set of (x,y) coordinates; you need an instance of the ColumnText object instead.

Next post:

Previous post: