I Text 7 Building Blocks
I Text 7 Building Blocks
iText Software
This book is for sale at http://leanpub.com/itext7buildingblocks
This version was published on 2016-08-27
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
2016 iText Software
Contents
Before we start: Overview of the classes and interfaces . . . . . . . . . . . . . . . . . . .
Chapter 1: Introducing the PdfFont class
Creating a PdfFont object . . . . . . .
Embedding a font . . . . . . . . . . .
Choosing the appropriate encoding . .
Font properties . . . . . . . . . . . . .
Reusing styles . . . . . . . . . . . . .
Summary . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
11
14
19
22
23
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
24
24
31
36
40
42
46
48
51
52
55
57
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
58
60
66
67
69
70
74
75
76
81
92
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
93
93
97
98
102
105
107
112
114
118
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
119
119
121
123
126
127
133
135
140
142
146
151
152
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
154
154
156
157
160
164
166
166
167
171
172
177
180
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
181
181
184
187
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
191
195
196
198
202
204
206
209
215
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
216
216
217
218
CONTENTS
This is the second tutorial in the iText 7 series. In the first tutorial, iText 7: Jump-Start Tutorial, we
discussed a series of examples that explained the core functionality of iText 7. In this book, well focus
on the high-level building blocks that were introduced in the first chapter of that tutorial: Introducing
basic building blocks. In that chapter, we created PDFs using objects such as Paragraph, List, Image
and Table, but we didnt go into detail. This tutorial is the extended version of that chapter. In this
tutorial, youll discover which building blocks are available and how they all relate to each other.
Throughout the book, well use the following symbols:
The information sign indicates interesting extra information, for instance about different
options for a parameter, different flavors of a method, and so on.
The question mark will be used when this information is presented in the form of a question
and an answer.
The bug highlights an Exception that gets thrown if you make a common mistake.
The triangle with the exclamation point warns for functionality that was introduced at a
later stage in the development of iText 7.
The key indicates functionality that is new in iText 7, or at least very different from what
developers were used to in iText 5 or earlier versions.
All the examples of this book along with the resources needed to build them, are available online at
the following address: http://developers.itextpdf.com/content/itext-7-building-blocks/examples
http://developers.itextpdf.com/content/itext-7-jump-start-tutorial
http://developers.itextpdf.com/content/itext-7-jump-start-tutorial/chapter-1-introducing-basic-building-blocks
http://developers.itextpdf.com/content/itext-7-building-blocks/examples
At the top of the hierarchy, we find the IPropertyContainer interface. This interface defines
methods to set, get, and delete properties. This interfaces has two direct subinterfaces: IElement
and IRenderer. The IElement interface will be implemented by objects such as Text, Paragraph
and Table. These are the objects that well add to a document, either directly or indirectly. The
IRenderer interface will be implemented by objects such as TextRenderer, ParagraphRenderer and
TableRenderer. These renderers are used internally by iText, but we can subclass them if we want
to tweak the way an object is rendered.
The IElement interface has two subinterfaces of its own. The ILeafElement interface will be
implemented by building blocks that cant contain any other elements. For instance: you can add a
Text or an Image element to a Paragraph object, but you cant add any object to a Text or an Image
element. Text and Image implement the ILeafElement interface to reflect this. Finally, theres the
LargeElement interface that allows you to render an object before youve finished adding all the
content. Its implemented by the Table class, which means that you add a table to a document
before youve completed adding all the Cell objects. By doing so, you can reduce the memory use:
all the table content that can be rendered before the content of the table is completed, can be flushed
from memory.
The IPropertyContainer interface is implemented by the abstract ElementPropertyContainer
class. This class has three subclasses; see figure 0.2.
The Style class is a container for all kinds of style attributes such as margins, paddings and rotation.
It inherits style values such as widths, heights, colors, borders and alignments from the abstract
ElementPropertyContainer class.
The RootElement class defines methods to add content, using either an add() method or a
showTextAligned() method. The Document object will add this content to a page. The Canvas object
doesnt know the concept of a page. It acts as a bridge between the high-level layout API and the
low-level kernel API.
Figure 0.3 gives us an overview of the AbstractElement implementations.
All classes derived from the AbstractElement class implement the IElement interface. Text, Image,
Tab and Link also implement the ILeafElement interface. The ILargeElement interface is only
implemented by the Table class. The basic building blocks make it very easy for you to create tagged
PDF. Tagged PDF is a requirement for PDF/A, a standard for long-term preservation of document,
and, PDF/UA, an accessibility standard. A properly tagged PDF includes semantic information about
all the relevant content.
An ordinary PDF can show a human reader content that is organized as a table. This table
is rendered using a bunch of text snippets and lines. To a machine, the table isnt more than
that: text positioned at arbitrary places, lines drawn at arbitrary places. A seeing person can
detect rows and columns and understand which rows are actually header or footer rows
and which rows are body rows. There is no simple way for a machine to do this. When a
machine detects a text snippet, it doesnt know if that text snippet is part of a paragraph,
part of a title, part of a cell, or part of something else. When a PDF is tagged, it contains a
structure tree that allows a machine to understand the structure of the content. Some text
will be marked as part of a cell in a header row, other text will be marked as the caption
of the table. All real content will be tagged. Other content, such as lines between rows and
columns, running headers, page numbers, will be marked as an artifact.
In iText, we have introduced the IAccessibleElement interface. It is implemented by all the basic
building blocks that contain real content: Text, Link, Image, Paragraph, Div, List, ListItem, Table,
Cell, LineSeparator. If we define a PdfDocument as a tagged PDF using the setTagged() method,
iText will create a structure tree so that a Table is properly tagged as a table, a List properly tagged
as a list, and so on. There is no real content in a Tab or an AreaBreak, which is why these classes
dont implement that interface. Its just white space; a tab and an area break dont even need to be
marked as an artifact.
In this tutorial, we wont create tagged PDF; iText will just render the content to the document
using the appropriate IRenderer implementation. Figure 0.4 shows an overview of the IRenderer
implementations.
When you compare figure 0.4 with 0.3, youll discover that each AbstractElement and each
RootElement has its corresponding renderer. We wont discuss figure 0.4 in much detail. The concept
of renderers will become clear the moment we start making some examples.
10
The MT in the names of the Actual Font refers to the vendor of the fonts: the Monotype Imaging
Holdings, Inc. These are fonts shipped with Microsoft Windows. If youd open the same file on a
Linux machine, other fonts would be used as actual fonts. This is typically what happens when you
dont embed fonts. The viewer searches the operating system for the fonts that are needed to present
the document. If a specific font can be found, another font will be used instead.
Traditionally, there are 14 fonts that every PDF viewer should be able to recognize and
render in a reliable way: four Helvetica fonts (normal, bold, oblique, and bold-oblique),
four Times-Roman fonts (normal, bold, italic, and bold-italic), four Courier fonts (normal,
bold, oblique, and bold-oblique), Symbol and Zapfdingbats. These fonts are often referred
to as the Standard Type 1 fonts. Not every viewer will use that exact font, but it will use a
font that looks almost identical.
To create the PDF shown in figure 1.1, we used three of these fonts: we defined two fonts explicitly;
one font was defined implicitly. See the Text_Paragraph example.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-1#1822-c01e01_text_paragraph.java
1
2
3
4
5
6
7
8
9
10
11
In line 1, we create a PdfDocument using a PdfWriter as parameter. These are low-level objects that
will create PDF output based on your content. Were creating a Document instance in line 2. This
is a high-level object that will allow you to create a document without having to worry about the
complexity of PDF syntax.
In lines 5 and 6, we create a PdfFont using the PdfFontFactory. In the FontConstants object, youll
find a constant for each of the 14 Standard Type 1 fonts. In line 7, we create a Text object with the
title of Stevensons short story and we set the font to TIMES_BOLD. In line 8, we create a Text object
with the name of the author and we set the font to TIMES_ROMAN. We cant add these Text objects
straight to the document, but we add them to a BlockElement, more specifically a Paragraph, in line
9.
Between the title and the author, we add " by " as a String object. Since we didnt
define a font for this String, the default font of the Paragraph is used. In iText, the default
font is Helvetica. This explains why we see the font Helvetica listed in the font overview
in figure 1.1.
In line 10, we add the paragraph to the document object; we close the document object in line 11.
We have created our first Jekyll and Hyde PDF using fonts that arent embedded. As a result, slightly
different fonts can be used when rendering the document. We can avoid this by embedding the fonts.
Embedding a font
iText supports the Standard Type 1 fonts, because the io-jar contains the Adobe Font Metrics (AFM)
files of those 14 fonts. iText cant embed these 14 fonts because the PostScript Font Binary (PFB)
files are proprietary. They cant be shipped with iText because iText Group doesnt have a license to
do so. We are only allowed to ship the metrics files.
In the Text_Paragraph_Cardo example, we use three fonts of the Cardo font family. These are fonts
that were released under the Summer Institute of Logistics (SIL) Open Font License (OFL). The result
is shown in figure 1.2.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-1#1823-c01e02_text_paragraph_cardo.java
12
First we need the path to the font programs for the three Cardo fonts: Cardo-Regular.ttf, CardoBold.ttf and Cardo-Italic.ttf:
1
2
3
4
5
6
In line 1 to 3 of the following snippet, we use these paths as the first parameter of the createFont()
method. The second parameter is a Boolean indicating whether or not we want to embed the font.
13
1
2
3
4
5
6
7
8
9
Line 4 to 6 are identical to what we had before, but in line 7, we change the default font of the
Paragraph to italic. This explains why " by " was written in italic in figure 1.2 and why the font
Helvetica no longer appears in the font list. In line 7, we add the Paragraph to the Document instance.
Figure 1.3 shows what would happen if we dont embed the fonts.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-1#1824-c01e03_text_paragraph_nocardo.java
1
2
3
14
The constants REGULAR, BOLD and ITALIC refer to the correct Cardo .ttf files, but we omitted the
parameter that tells iText to embed the font. Incidentally, the Cardo fonts arent present on my PC.
Adobe Reader replaced them with Adobe Sans MM. As you can see, the result doesnt look nice. If
you dont use any of the standard Type 1 fonts, you should always embed the font.
The problem is even worse when you try to create PDFs in different languages. In figure 1.4, we try
to add some text in Czech, Russian and Korean. The Czech text looks more or less OK, but well
soon discover that there one character missing. The Russian and Korean text is invisible.
Not embedding the font isnt the only problem here. We also need to define the appropriate encoding.
15
Instead of Cp1250 and Cp1251, we could also use Unicode for the Czech and Russian text. Actually,
when we store hard-coded text in source code, it is preferred to store Unicode values.
1
2
3
4
5
6
7
8
9
10
11
16
Well use the values CZECH. RUSSIAN and KOREAN in our next couple of examples.
Using the correct encoding isnt sufficient to solve every font problem you might encounter. In the
Czech_Russian_Korean_Wrong example, we create the Paragraph objects like this:
1
2
3
4
5
6
7
This wont work because we didnt use the correct encoding, but also because we didnt define a
font that supports Russian and Korean. We fix this problem in the Czech_Russian_Korean example
by embedding the free font FreeSans for the Czech and Russian translation of the title. Well use
a Hancom font HCR Batang for the Korean text.
1
2
Well use these paths as the first parameter for the PdfFont constructor. We pass the desired encoding
as the second parameter. The third parameter indicates that we want to embed the font.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-1#1836-c01e04_czech_russian_korean_wrong.java
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-1#1837-c01e05_czech_russian_korean_right.java
1
2
3
4
5
6
7
8
9
10
17
When we look at the Fonts panel in the document properties, we notice that FreeSans is mentioned
twice. That is correct: weve added the font once with the encoding Cp1250 and once with the
encoding Cp1251, In the Czech_Russian_Korean_Unicode example, well create one composite font,
freeUnicode, for both languages, Czech and Russian.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-1#1844-c01e06_czech_russian_korean_unicode.java
1
2
3
4
5
6
7
8
9
10
18
PdfFont freeUnicode =
PdfFontFactory.createFont(FONT, PdfEncodings.IDENTITY_H, true);
document.add(new Paragraph().setFont(freeUnicode)
.add(CZECH).add(" by Robert Louis Stevenson"));
document.add(new Paragraph().setFont(freeUnicode)
.add(RUSSIAN).add(" by Robert Louis Stevenson"));
PdfFont fontUnicode =
PdfFontFactory.createFont(HCRBATANG, PdfEncodings.IDENTITY_H, true);
document.add(new Paragraph().setFont(fontUnicode)
.add(KOREAN).add(" by Robert Louis Stevenson"));
Figure 1.6 shows the result. The page looks identical to what we saw in figure 1.5, but now the PDF
only contains one FreeSans font with Identity-H as encoding.
Using Unicode is one of the requirements of PDF/UA and of certain flavors of PDF/A for reasons of
accessibility. With custom encodings, it isnt always possible to know which glyphs are represented
by each character.
In the next series of font examples, well experiment with some font properties such as font size,
font color, and rendering mode.
19
Font properties
Figure 1.7 shows a screen shot of yet another PDF with the Jekyll and Hyde title. This time, the
default font Helvetica is used, but weve defined different font sizes.
The font size is set with the setFontSize() method. This method is defined in the abstract class
ElementPropertyContainer, which means that we can use it on many different objects. In the
FontSize example, we use the method on Text and Paragraph objects:
1
2
3
4
5
6
We set the font size of the newly created Paragraph to 8 pt. This font size will be inherited by all the
objects that are added to the Paragraph, unless the objects override that default size. This is the case
for title1 for which we defined a font size of 12 pt and for title2 for which we defined a font size
of 16 pt. The content added as a String (" by ") and the content added as a Text object for which
no font size was defined inherit the font size 8 pt from the Paragraph to which they are added.
In iText 5, it was necessary to create a different Font object if you wanted a font with a
different size or color. We changed this in iText 7: you only need a single PdfFont object.
The font size and color is defined at the level of the building blocks. We also made it possible
for elements to inherit the font, font size, font color and other properties from the parent
object.
In previous examples, weve worked with different fonts from the same family. For instance, weve
created a document with three different fonts from the Cardo family: Cardo-Regular, Cardo-Bold,
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-1#1846-c01e07_fontsize.java
20
and Cardo-Italic. For most of the Western fonts, youll find at least a regular font, a bold font, an
italic font, and a bold-italic font. It will be more difficult to find bold, italic and bold-italic fonts for
Eastern and Semitic languages. In that case, youll have to mimic those styles as is done in figure
1.8. If you look closely, you see that different styles are used, yet weve only defined a single font in
the PDF.
Lets take a look at the BoldItalic example to find out how this was done.
1
2
3
4
5
6
In lines 1 to 3, we use the methods setItalic() and setBold(). The setItalic() method wont
switch from a regular to an italic font. Instead, it will skew the glyphs of the italic font in such a
way that it looks as if they are italic. The setBold() font will change the render mode of the text
and increase the stroke width. Lets introduce some color to show what this means.
Figure 1.9 shows the text using different colors and different rendering modes.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-1#1847-c01e08_bolditalic.java
21
A font program contains the syntax to construct the path of each glyph. By default the path is painted
using a fill operator, not drawn with a stroke operation, but we can change this default.
In line 1, we change the font color to blue using the setFontColor() method. This changes
the fill color for the paint operation that fills the paths of all the text.
In line 2-4, we dont define a font color, which means the text will be painted in black.
Instead we define a stroke color using the setStrokeColor() method, and we change the
text rendering mode to FILL_STROKE with the setTextRenderingMode() method. As a result
the contours of each glyph will be drawn in green. Inside those contours, well see the default
fill color black.
We dont change any of the defaults in line 5. This Text object will simply inherit the font size
of the Paragraph, just like all of the other Text objects.
In line 6-8, we change the stroke color to red and we use the setStrokeWidth() to 0.5 user
units. By default, the stroke width is 1 user unit, which by default corresponds with 1 point.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-1#1848-c01e09_colorrendering.java
22
There are 72 user units in one inch by default. We also change the text rendering mode to
STROKE which means the text wont be filled using the default fill color. Instead, well only see
the contours of the text.
Mimicking bold is done by setting the text rendering mode to FILL_STROKE and by increasing the
stroke width; mimicking italic is done by using the setSkew() method that will be discussed in
chapter 3. Although this approach works relatively well, the setBold() and setItalic() method
should only be used as a last resort when its really impossible to find the appropriate fonts for the
desired styles. Mimicking styles makes it very hard if not impossible for parsers extracting text
from PDF to detect which part of the text is rendered in a different style.
Reusing styles
If you have many different building blocks, it can become quite cumbersome to define the same
style over and over again for each separate object. See for instance figure 1.10 where parts of the
text the title of a story are written in 14 pt Times-Roman, but other parts the names of the main
characters are written in 12 pt Courier with red text on a light gray background.
We could define the font family, font size, font color and background for each separate Text object
that is added to the title Paragraph, but in the ReusingStyles example, we use the Style object to
define all the different styles at once.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-1#1889-c01e10_reusingstyles.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
23
In line 1-3, we define a normal style; in line 4-7, we define a code style Courier is often used when
introducing code snippets in text. In line 8-13, we compose a Paragraph using different Text objects.
We set the style of each of these Text objects to either normal, or code.
The Style object is a subclass of the abstract ElementPropertyContainer class, which is the
superclass of all the building blocks we are going to discuss in the next handful of chapters. It
contains a number of setters and getters for numerous properties such as fonts, colors, borders,
dimensions, and positions. You can use the addStyle() method on every AbstractElement subclass
to set these properties in one go.
Being able to combine different properties in one class, is one of the many new features in
iText 7 that can save you many lines of code when compared to iText 5.
The Style class is about much more than fonts. You can even use it to define padding and margin
values for BlockElement building blocks. But lets not get ahead of ourselves, the BlockElement class
will be discussed in chapters 4 and 5.
Summary
In this chapter, weve introduced the PdfFont class and we talked about font programs, embedding
fonts and using different encodings. This allowed us to show the title of a short story by Robert
Louis Stevenson in different languages: English, Czech, Russian, and Korean. We also looked at font
properties such as font size, font color, and rendering mode. We even discovered how to mimic styles
in case we cant find the font program to render text in italic or bold.
Theres much more that could be said about fonts, but well leave that for a separate tutorial.
In the next chapter, well create a PDF with the full story while we discuss the RootElement
implementations Document and Canvas.
25
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1891-c02e01_canvasexample.java
26
Looking at this example, its not hard to understand the use case. Suppose that you need to add
content on a specific page at a specific rectangular location. You create a Canvas object passing that
page and that rectangle as a parameter, and you can add that content to that object. The content will
be rendered inside the boundaries of that rectangle.
It is important to understand that all the content that doesnt fit the rectangle will be cut. See figure
2.2.
In this snippet, we add the exact same content as before, but instead of new Rectangle(36, 650,
100, 100), we reduced the height from 100 to 50: new Rectangle(36, 750, 100, 50). As a result,
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1892-c02e02_canvascut.java
27
the text no longer fits the rectangle. The part that says Mr. Hyde by Robert Louis Stevenson got
lost. No exception gets thrown because this is expected behavior.
iText 7.0.0 was rewritten from scratch. Weve waited with the release of the first iText 7
version until we were 99% sure of the API. We wanted to avoid significant changes to the
API in later versions. Nevertheless, were constantly improving the library, hence you will
notice that some functionality described in the tutorials will only work in the current or
SNAPSHOT version of iText 7. Whenever this is the case, Youll see a Warning call out
like this one. The CanvasCut example weve just discussed wont work as described in iText
7.0.0. Youll need iText 7.0.1 to get the behavior described in this tutorial.
Text getting cut without warning isnt always what you want. In some cases, you need to know if
the content fit the rectangle or not. For instance, in figure 2.3, we have defined a larger rectangle to
which weve added the Paragraph as many times as possible.
Weve added the Paragraph three times, because we can fit it inside the rectangle almost two and a
half times. How did we know this? Lets take a look at the CanvasRepeat example.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1893-c02e03_canvasrepeat.java
28
We introduce a member-variable full that indicates if the rectangle was completely filled or not.
Each time a child is added to the renderer, we check the status of the FULL property. This status can
be null, false or true. If its true, there is no more space left to add content. We also added an
isFull() method for our convenience.
1
2
3
4
5
6
7
8
9
10
11
12
The Rectangle we define in line 1 is larger than what we had before. Line 3 and 4 are new. We
create an instance of our custom renderer and we declare this renderer to the Canvas object. In line
11 and 12, we add the Paragraph as many times as possible as long as the Canvas weve defined isnt
completely full.
29
One might wonder why we are adding the border of the rectangle using the
low-level rectangle menu. The abstract RootElement extends the abstract
ElementPropertyContainer class. The ElementPropertyContainer class defines methods
such as setBorder() and setBackgroundColor(), but these methods cant be used because
setting a border or a background isnt implemented for Canvas, nor for Document. Not
every method defined in ElementPropertyContainer makes sense for all of its subclasses.
For instance: it doesnt make sense to implement the setFont() method for an Image
object. You can check which methods are implemented for the Canvas and Document class
in Appendix C.
In figure 2.4, we created a document with two pages, but theres something special about it: we added
content under the existing content of the first page after we added content to the second page.
The first part of the code, is identical to what we had in the first example: we define a first page
and a rectangle, we create a Canvas instance with this page and this rectangle. Then we define a
Paragraph and we add this Paragraph to the canvas. The following code snippet taken from the
CanvasReturn example shows how we create a second page and add some content to that page.
1
2
3
4
We add a new page to the document with the addNewPage() method (line 1). We create a new
PdfCanvas object with that page (line 2) and a new Canvas object using that new PdfCanvas, the
http://developers.itextpdf.com/content/itext-7-building-blocks/c-rootelement-methods
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1894-c02e04_canvasreturn.java
30
PdfDocument and the Rectangle we used for the first page (line 3). We add a Paragraph to that new
Canvas.
In line 1, we create a PdfPage instance for the first page using the getFirstPage() method.
The getFirstPage() method is a custom version of the getPage() method. The getPage()
method allows you to get access to any page that was created before as long as the
PdfDocument hasnt been closed.
31
Being able to go back to a previous page and to add content to that page is one of the
new, powerful features in iText 7. The architecture of iText 5 didnt allow us to change the
content of completed pages. This is one of the many reasons why we decided to rewrite
iText from scratch.
So far, we have been using the Canvas class to add content to a PdfCanvas. In chapter 7, well
discover another use case: you can also create a Canvas to add content to a PdfFormXObject.
A form XObject is an object that is external to any page content stream. It represents a
stream of PDF content that can be referred to more than once from the same page, or from
different pages. Its a stream of reusable PDF syntax. The Canvas objects allows you to
create that PDF syntax without any hassle.
Its high time that we create a PDF with the full Jekyll and Hyde story instead of merely adding the
title and the author to a page. Well use the Document class to achieve this.
Figure 2.5: Text file with the Jekyll and Hyde story
Well convert this txt file to a PDF multiple times in the next handful of examples. Well start by
creating the PDF shown in figure 2.6.
http://gitlab.itextsupport.com/itext7/samples/raw/develop/publications/highlevel/src/main/resources/txt/jekyll_hyde.txt
32
The JekyllHydeV1 example is very simple. You dont need any new functionality that hasnt been
discussed before:
1
2
3
4
5
6
7
8
In line 1, we create the low-level PdfDocument object. In line 2, we create the high-level Document
instance. We create a BufferedReader to read the txt file in line 3. We read every line in the text file
in a loop in lines 4 to 7. In line 6, we wrap every line inside a Paragraph object, which we add to the
Document object. In line 8. we close the document. The result is a 42-page PDF with the full story of
The Strange Case of Dr. Jekyll and Mr. Hyde.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1895-c02e05_jekyllhydev1.java
33
While this result is already nice, we can do better. The first thing that jumps to the eye in figure
2.7 is the fact that we changed the alignment. Instead of the default left alignment, the text is now
justified on both sides of the page. If you take a closer look, youll also notice that weve introduced
hyphenation.
For the JekyllHydeV2 example, we copied the first example, and we added the following lines:
1
2
document.setTextAlignment(TextAlignment.JUSTIFIED)
.setHyphenation(new HyphenationConfig("en", "uk", 3, 3));
We used the setTextAlignment() to change the default alignment at the Document level. We
used the setHyphenation() method to define the hyphenation rules. In this case, we created a
HyphenationConfig object to treat the text as British English. When splitting a word, we indicated
that we want at least 3 characters before the hyphenation point and at least 3 characters after the
hyphenation point. This means that the word elephant cant be hyphenated as e-lephant because
e is shorter than 3 characters; we need to split the word like this instead: ele-phant. The word
attitude cant be hyphenated as attitu-de because de is shorter than 3 characters, in this case
we need something like atti-tude.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1896-c02e06_jekyllhydev2.java
34
Changing defaults at the Document level, such as the default alignment, the default
hyphenation, or even the default font, wasnt possible in iText 5. You had to define all
of these properties at the level of the separate building blocks. In iText 7, we introduced
the inheritance of properties. The default font is still Helvetica, but we can now define a
different font at the Document level.
Figure 2.8 shows our third attempt to convert the txt file to a PDF. We changed the font from 12 pt
Helvetica to 11 pt Times-Roman. As a result, the page count was reduced from 42 pages to only 34.
When we look at the JekyllHydeV3 example, we see that two different fonts are used:
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1897-c02e07_jekyllhydev3.java
1
2
3
4
5
6
7
35
Times-Roman is used as the default font, but we also define Helvetica-Bold for the titles. The txt file
was conceived in such a way that the first line of the text file is the title of the book. Every other title
in the story is preceded by an empty line. Every line that isnt a title, is a full paragraph. Knowing
this, we can adapt the loop that reads the text file line by line.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
This code snippet is a tad more complex than what we had before, but lets walk through it step by
step:
We introduce a Boolean title (line 4) which we initalize as true because we know that the
first line in the text file is a title. We create a Paragraph for each line (line 6) and we use
36
the setKeepTogether() method because we dont want iText to distribute paragraphs over
different pages (line 7). If a Paragraph doesnt fit the current page, it will be forwarded to
the next page unless the Paragraph doesnt fit the next page either. In that case will be split
anyway: part of it will be added to the current page and the rest will be forwarded to the next
page or pages.
If value of title is true, we change the default font that was defined at the Document level as
11 pt Times-Roman to 12 pt Helvetica-Bold. We know that the next line in the txt file will be
normal content, so we set the value of title to false (line 9-11). For normal lines, we change
the indentation of the first line so that we can easily distinguish the different paragraphs in
the text (line 12-14).
If the current line is an empty String, we define a bottom margin of 12 (line 16) and we
change the value of title back to true (line 17), because we know that the next line will be
a title; for all other the lines, we reduce the bottom margin of the Paragraph to 0 (line 20).
Once all the properties for the Paragraph are set, we add it to the Document (line 22).
As you could tell from figure 2.8, iText has rendered the text to PDF page by page in quite a nice
way. Now suppose that we want to render the text in two columns, organized side by side on one
page. In that case, we need to introduce a DocumentRenderer instance.
37
To achieve this, we used the ColumnDocumentRenderer class. This is a subclass of the DocumentRenderer class that is used by default. The JekyllHydeV4 example explains how the ColumnDocumentRenderer is created and applied.
1
2
3
4
5
6
7
8
9
We define an array of Recangle objects, and we use that array to create a ColumnDocumentRenderer
object. We use the setRenderer() method to tell the Document to use this renderer instead of the
default DocumentRenderer instance.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1898-c02e08_jekyllhydev4.java
38
While we were at it, we applied a small change to the code that parses the text:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
In line 5, we create an AreaBreak object. This is a layout object that terminates the current content
area and creates a new one. In this case, we create an AreaBreak of type NEXT_AREA and we introduce
it before the start of every new chapter. The effect of this area break is shown in figure 2.10.
39
Without the AreaBreak, the chapter INCIDENT AT THE WINDOW would have started in the left
column of page 19, right after the content of the previous chapter. By introducing the AreaBreak,
the new chapter now starts in a new column. If we had used an AreaBreak of type NEXT_PAGE, a new
page would have been started; see figure 2.11.
40
Instead of skipping to the next column, iText now skips to the next page.
By default, the newly created page will have the same page size as the current page. If
you want iText to create a page of another size, you can use the constructor that accepts a
PageSize object as a parameter. For instance: new AreaBreak(PageSize.A3).
Theres also an AreaBreak of type LAST_PAGE. This AreaBreakType is to be used when switching
between different renderers.
If we look closely at the JekyllHydeV6 example, we see that we swith renderers two times.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
41
23
24
25
42
document.add(p);
document.close();
}
We add a long Paragraph to the first page (line 4-9). As we didnt define any renderer, the default
DocumentRenderer is used. We introduce a page break (line 10) and change the renderer to a
ColumnDocumentRenderer with two columns. Right after we set this new renderer, we introduce
an AreaBreak that jumps to the last page. Why is this necessary?
Whenever you create a new DocumentRenderer, iText starts returns to the top of the
document that is: from the first page. This allows you to use different renderers on the
same document next to each other on the same page. If that is needed, well have to instruct
iText not to flush the content to the OutputStream; otherwise we wont have access to
previous pages. In this case, we dont need to change anything on previous pages. We just
want to switch to another renderer on the next page. Introducing a page break that goes to
the last page will avoid that new content overwrites old content.
43
Lets return to the example in which we converted text to a PDF document with two columns, more
specifically to the example in which we introduced page breaks before every new chapter. These
page breaks result in different pages having only one column. As we can tell from figure 2.11, this
column is on the right side of the page.
Now suppose that we want to move these solitary columns to the middle of the page as shown in
figure 2.13.
We cant tell in advance when this situation will occur. We parse the text line by line, and we dont
know what the next line will bring us when we add a Paragraph to the document. It could be another
Paragraph or a LineBreak. This means that we shouldnt render the content right away. If we did, we
couldnt move it to the middle if a chapter ends somewhere in the left column. We need to postpone
flushing. We can do so in the renderer as demonstrated in the JekyllHyderV7 example.
In this example, we took the code of the ColumnDocumentRenderer class and we adapted it to our
specific needs.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1901-c02e11_jekyllhydev7.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
44
43
44
45
}
}
Rectangle[] columns = {
new Rectangle(offSet, offSet, columnWidth, columnHeight),
new Rectangle(
offSet + columnWidth + gutter, offSet, columnWidth, columnHeight)};
DocumentRenderer renderer = new MyColumnRenderer(document, columns);
document.setRenderer(renderer);
We define an array with two Rectangle objects. We use this array to create an instance of our
custom MyColumnRenderer object. We use this instance as the renderer for our Document. The rest
46
of our code is identical to what we had before: we set the default values for the Document; then we
parse the text file and we add content while doing so.
If we would close the document object after adding all the content, wed end up with a document
that consists of nothing but empty pages. In our renderer, we jump from area to area, and we create
new page after new page, but we arent rendering anything because the flushSingleRenderer()
method is never called. We have to trigger this method ourselves, and we can do so like this:
1
2
renderer.flush();
document.close();
When we flush() the renderer, all the content weve been adding without flushing will be rendered.
The flushSingleRenderer() method will be called as many times as there are objects added to the
Document. Every time its called on a page marked as a single-column page, the content will be
moved to the right so that the column appears in the middle of the page.
This is one of the more complex examples in this book. Writing your own RootRenderer
implementation isnt easy, but this functionality gives you a lot of power to create PDF
documents the way you want to, as opposed to the way iText wants to.
Lets continue with a couple of examples in which we use the immediateFlush parameter when
creating a Document instance.
47
The first line of text says This document has 34 pages. From previous examples, we know that
were building a document as we go, reading a text file line by line. When we parse the first lines
of text, there is no way we can predict how many pages will be needed for the full document. How
did we guess that wed end up with 34 pages?
Truth be told, we didnt have to guess; we used a little trick. The JekyllHydeV8 example reveals the
magic we used. We created a Document instance with the immediateFlush parameter set to false.
1
The first object we add to this document is some text saying This document has {totalpages} pages.
1
2
3
4
As you can see, we used a placeholder {totalpages} for the total number of pages. We created a
TextRenderer instance and added this renderer as the next renderer for the Text object. We wrap
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1902-c02e12_jekyllhydev8.java
48
the Text object in a Paragraph and add this paragraph to the document. Then we add all story of Dr.
Jekyll and Mr. Hyde. Because of the fact that immediateFlush is false, no text will be rendered until
at the very last moment. This very last moment could be when we close the document, in which
case the first line would still read This document has {totalpages} pages.
Obviously, thats not what we want. We want to change {totalpage} into the actual number of
pages before the text is rendered. This can be achieved using the TextRenderer object.
1
2
3
4
5
6
In line 1-2, we change the String This document has {totalpages} pages. to This document has
34 pages. As you can see, we can retrieve the original content of the Text object from the renderer
and we replace the placeholder with pdf.getNumberOfPages(). In line 3-4, we change the text of
the TextRenderer and we add this altered text renderer to the Text object.
If we would close the document after line 4, the PDF would still show This document has
{totalpages} pages. For the change to take effect, we need to re-layout the document. This is done
using the relayout() method in line 5. Only after the layout has been recreated, we can close the
document, as is done in line 6.
In iText 5, we could have achieved more or less the same result by adding a placeholder
with fixed dimensions. Once the complete document was rendered, we could then fill out
the total number of pages on the placeholder. We will use the same approach with iText 7
in chapter 7, but iText 7 now also provides an alternative solution by allowing us to change
the content of a Text object and then recreate the layout.
Changing the content of a Text object is still somewhat complex. There are many cases where we
dont need to recreate the layout. In those cases, the complexity can be reduced substantially as
demonstrated in the next example.
49
To achieve this, we used a much easier approach than what we did in the previous example. Lets
take a look at the JekyllHydeV9 example.
Once more, we tell the Document that it shouldnt flush its content immediately.
1
After adding the complete text of the short story by Robert Louis Stevenson, we loop over every
page in the document and we add a Paragraph to each page.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1903-c02e13_jekyllhydev9.java
50
1
2
3
4
5
6
7
8
int n = pdf.getNumberOfPages();
Paragraph footer;
for (int page = 1; page <= n; page++) {
footer = new Paragraph(String.format("Page %s of %s", page, n));
document.showTextAligned(footer, 297.5f, 20, page,
TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0);
}
document.close();
The showTextAligned() method can be used to add text at an absolute position on any page, using a
specific horizontal and vertical alignment with respect to the chosen coordinate, and using a specific
angle.
In this case, we loop over all the pages (from 1 to 34) and we add a line of text centered vertically
and horizontally at position x = 297.5f and y = 20 on every page. We didnt need to change the
layout of any of the content that was already added, hence we dont need to use the relayout()
method. All of the content is rendered at the moment we close() the document.
This example only works if you set immediateFlush to false. If you forget setting this
parameter, youll encounter the following exception:
Exception in thread main java.lang.NullPointerException
com.itextpdf.kernel.pdf.PdfDictionary.get(PdfDictionary.java)
at
This exception occurs because you are trying to change the contents of a page dictionary
that has already been flushed to the OutputStream. iText still has a reference to that page
dictionary, but the dictionary as such is no longer there, hence the NullPointerException.
51
The PDF shown in figure 2.16 was created with the ShowTextAligned example.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Paragraph title = new Paragraph("The Strange Case of Dr. Jekyll and Mr. Hyde");
document.showTextAligned(title, 36, 806, TextAlignment.LEFT);
Paragraph author = new Paragraph("by Robert Louis Stevenson");
document.showTextAligned(author, 36, 806,
TextAlignment.LEFT, VerticalAlignment.TOP);
document.showTextAligned("Jekyll", 300, 800,
TextAlignment.CENTER, 0.5f * (float)Math.PI);
document.showTextAligned("Hyde", 300, 800,
TextAlignment.CENTER, -0.5f * (float)Math.PI);
document.showTextAligned("Jekyll", 350, 800,
TextAlignment.CENTER, VerticalAlignment.TOP, 0.5f * (float)Math.PI);
document.showTextAligned("Hyde", 350, 800,
TextAlignment.CENTER, VerticalAlignment.TOP, -0.5f * (float)Math.PI);
document.showTextAligned("Jekyll", 400, 800,
TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0.5f * (float)Math.PI);
document.showTextAligned("Hyde", 400, 800,
TextAlignment.CENTER, VerticalAlignment.MIDDLE, -0.5f * (float)Math.PI);
In line 1 and 3, we create two Paragraph objects. We add these objects to the current page using the
showTextAligned() method.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1904-c02e14_showtextaligned.java
52
In line 2, we add the Paragraph at position x = 36; y = 806 and we align the content to
the left of this coordinate. We didnt define a vertical alignment. The default VerticalAlignment.BOTTOM will be used, which means that the coordinate will be considered as the bottom
coordinate of the content.
In line 4-5, we add the content at the exact same coordinate, but we define a different value
for the vertical alignment: VerticalAlignment.TOP. Now the coordinate is considered as the
top coordinate of the content.
In lines 6 to 17, we add text as a String instead of as a Paragraph. We also introduce rotation values
of 90 degrees (0.5f * (float)Math.PI) and -90 degrees
In lines 6-9, we add two names at the same coordinate, but with a different rotation angle.
We do the same in lines 10-13. Notice the difference between the apparent order in which the
names Jekyll and Hyde appear depending on the value of the VerticalAlignment (as we
introduce a rotation of 90 degrees, vertical becomes horizontal, and vice-versa).
In lines 14-17, we add both names at the same coordinate with a different angle, but with
VerticalAlignment.MIDDLE. The names are written on top of each other and have become
almost illegible.
This example demonstrates the different variations of showTextAligned() methods. Theres also a
showTextAlignedKerned() method, but we need to learn more about using iText 7 add-ons before
we can use that method in an example.
53
Many developers arent aware of the implications of using AGPL software. This can be
very annoying for many different reasons. These are some examples of such annoyances:
Companies at the verge of getting funding or being acquired, fail the due diligence
process because they dont have a commercial license for their use of iText.
iText Group successfully sued a company for blatant abuse of our intellectual
property as an example proving that the AGPL can be enforced. The case was won in
about one and a half month. That was fast, but at iText Group, we all agree that there
are better ways to spend our time than by going to court because some company
wrongly assumes that open source software is software that is free of obligations
and free of charge.
Some companies ignore the implications of the AGPL license deliberately. This
leads to unfair competition between customers who buy a commercial license,
allowing us to invest in further development, and users who benefit from the further
development, refusing to contribute in any way.
To create more awareness and to avoid misunderstandings, we decided to make part of
iText closed source. Weve defined a series of valuable add-ons that wont be available as
open source software. We used to work with a dual licensing model and well continue
to do so, but now were also using the open core model. If developers want to use the
functionality that is only available in a closed source add-on, a commercial license will
have to be purchased.
The pdfCalligraph module (aka the typography jar) is one example of such a closed source addon. Weve spent a lot of time and effort into improving the typography. With pdfCalligraph, iText
finally supports Indic writing systems such as Devanagari and Tamil. iText now also supports special
features such as the visualization of vowels in Arabic. All of this functionality is available in a
separate typography jar.
You can use the pdfCalligraph add-on by introducing the following dependency:
1
2
3
4
5
6
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>typography</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
When importing a closed source add-on, you need a license-key in order to use that add-on. You need
the itext-licensekey jar to import that key into your code. This is the dependency for the license-key
jar:
http://itextpdf.com/itext7/pdfcalligraph
1
2
3
4
5
6
54
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-licensekey</artifactId>
<version>2.0.0</version>
<scope>compile</scope>
</dependency>
Loading the license key into your code is done like this:
1
LicenseKey.loadLicenseFile(new FileInputStream(KEY));
In my case, the KEY value is a constant with the path to my personal license key for using the
typography jar.
If you introduce an add-on, but you forget adding the line using the loadLicenseFile()
method, youll run into the following exception:
Exception
in
thread
main
java.lang.RuntimeException:
java.lang.reflect.InvocationTargetException
Caused
by:
com.itextpdf.licensekey.LicenseKeyException: License file not loaded.
If you try to load the license key, but its missing, the following exception will be thrown:
Exception in thread main java.io.FileNotFoundException:itextkey.xml (The
system cannot find the path specified)
If the key was found at this location, but it was corrupted, youll get this
LicenseKeyException:
Exception in thread main com.itextpdf.licensekey.LicenseKeyException:
Signature was corrupted.
If you are using a license key that is expired, youll get yet another message:
Exception in thread main com.itextpdf.licensekey.LicenseKeyException:
License expired.
55
These are the most common exceptions that can occur. Usually, a verbose message will tell you what
went wrong. In the next example, were going to use the typography jar to introduce kerning.
The kerning mechanism isnt that obvious in the title of Stevensons short story. The devil is in the
details: the . after Dr and Mr has been slightly moved in the kerned line. When kerning is active, the
font program is consulted for kerning information. In this case, the font program knows that when
a combination of r and . is encountered, the . should moved closer to the r.
The mechanism is easier to spot in the word "AWAY". In the kerned version, the A characters
move closer to the W on both sides. The distance between the A and the Y has also been reduced.
The ShowTextAlignedKerned example demonstrates how we used the showTextAlignedKerned()
method to achieve this.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1905-c02e15_showtextalignedkerned.java
1
2
3
4
5
6
7
8
56
document.showTextAligned(
"The Strange Case of Dr. Jekyll and Mr. Hyde", 36, 806, TextAlignment.LEFT);
document.showTextAlignedKerned(
"The Strange Case of Dr. Jekyll and Mr. Hyde", 36, 790,
TextAlignment.LEFT, VerticalAlignment.BOTTOM, 0);
document.showTextAligned("AWAY AGAIN", 36, 774, TextAlignment.LEFT);
document.showTextAlignedKerned("AWAY AGAIN", 36, 758,
TextAlignment.LEFT, VerticalAlignment.BOTTOM, 0);
The pdfCalligraph add-on is made an optional because improved typography requires more
extensive processing power to examine character combinations and to look up if the font program
contains kerning or ligature information for these combinations.
In iText 5, R2L script was supported, but only in the context of ColumnText and PdfPCell.
You had to change the writing system explicitly. Ligatures were supported, but only in
Arabic text. There was no support for Hindi or other Indic writing systems whatsoever.
With iText 7, its sufficient to add the typography jar to the CLASSPATH. As soon as iText
7 detects the pdfCalligraph add-on, The writing system will be automatically changed from
left to right (L2R) to right to left (R2L) if Hebrew or Arabic is detected. When Devanagari
or Tamil content is detected, ligatures will be made automatically.
All of this extra work may be overkill for straightforward English text, in which case you dont
really need the pdfCalligraph add-on.
We could continue with many more examples involving pdfCalligraph and typography, but well
leave that for another tutorial. This chapter was about the RootElement objects Canvas and Document,
and weve covered quite some ground.
57
Summary
In this chapter, we discussed the Canvas and the Document object, both subclasses of the abstract
RootElement class. We also made some examples with the corresponding RootRenderer classes,
CanvasRenderer and DocumentRenderer. While doing so, we discovered that we can easily render
content in columns using the ColumnDocumentRenderer. The column examples allowed us to learn
more about the AreaBreak object, which is a subclass of the abstract AbstractElement class.
We rendered the text of the short story The Strange Case of Dr. Jekyll and Mr. Hyde many times
tweaking different properties of the Document object. We learned that content is flushed to the
OutputStream as soon as possible by default, but that we can ask iText to postpone the rendering of
elements so that we can change their content or layout afterwards.
Finally, we discussed the mechanism of closed source add-ons for iText 7. These add-ons require
a license key that needs to be purchased from iText Software. Weve experimented with the
pdfCalligraph add-on also known as the typography jar. In the next chapter, well dig into the
ILeafElement implementations. Weve already used the Text object many times, but in the next
chapter, well also take a look at the Link, Tab and Image object.
59
Figure 3.1: A simple CSV file that will be used as data source
As you can see, this CSV file could be interpreted as a database table containing records that consist
of 6 fields:
1. An IMDB number the ID of an entry in the Internet Movie Database (IMDB) that was based
on the Jekyll and Hyde story by Robert Louis Stevenson.
2. A year the year the corresponding movie, short film, cartoon, or video was produced.
3. A title the title of the movie, short film, cartoon, or video.
4. Director or directors the director or directors who made the movie, short film, cartoon, or
video.
5. A country the country where the movie, short film, cartoon, or video was produced.
6. A run length the number of minutes of the movie, short film, cartoon, or video.
We will use the CsvTo2DList utilities class to read this CSV file, that was stored using UTF-8
encoding, into a two-dimensional List<List<String>> list.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/util#1919-csvto2dlist.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
60
In this chapter, well render this two-dimensional list to a PDF using Tab elements.
In line 1, we use our CsvTo2DList utilities class to create a resultSet of type List<List<String>>.
In line 2, we loop over the rows of this result set, and we create a Paragraph containing all the fields
in the record list. In-between, we add Tab objects.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1906-c03e01_jekyllhydetabsv1.java
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/util#1919-csvto2dlist.java
61
As you can see, weve added extra lines to show the default tab positions.
1
2
3
4
5
6
By default, each tab position is a multiple of 50 user units (which, by default, equals 50 pt), starting
at the left margin of the page. Those tab positions work quite well for the first three fields (IMDB,
Year, and Title), but the Director(s) field starts at different positions, depending on the length
of the Title field. Lets fix this and try to get the result shown in figure 3.3.
62
In the JekyllHydeTabsV2 example, we define specific tab positions using the TabStop class.
1
2
3
4
5
6
7
8
9
Weve stored 5 tab stops in a float array in line 1, we create a List of TabStop objects in line 2,
we loop over the different float values in line 4 and add the 5 tab stops to the TabStop list in line
5. While we are at it, we also draw lines that will show us the position of each tab stop, so that we
have a visual reference to check if iText positioned our content correctly.
The next code snippet is almost an exact copy of what we had before.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1907-c03e02_jekyllhydetabsv2.java
1
2
3
4
5
6
7
8
9
10
11
12
63
Line 4 is the only difference: we use the addTabStops() method to add the List<TabStop> object
to the Paragraph. The different fields are now aligned in such a way that the content starts at the
position defined by the tab stop; the tab stop is to the left of the content. We can change this alignment
as shown in figure 3.4.
1
2
3
4
5
6
7
8
9
64
We have 5 tabstops:
The first tab stop will center the Year at position 80; for this we use TabAlignment.CENTER.
The second tab stop will make sure that the title starts at position 120; for this we use
TabAlignment.LEFT.
The third tab stop will make sure that the name(s) of the director(s) ends at position 580; for
this we use TabAlignment.RIGHT.
The fourth tab stop will make sure that the country starts at position 590.
The fifth tab stop will align the content based on the position of the space character; for this
we use TabAlignment.ANCHOR and we define a tab anchor using the setTabAnchor() method.
If you look at the CSV file, you see that we dont have any space characters in the Run length field,
so lets add adapt our code and add " \'" to that field. See line 10 in the following snippet.
1
2
3
4
5
6
7
8
9
10
11
12
65
A tab leader is defined using a class that implements the ILineDrawer interface. We add a dotted line
between the IMDB id and the year, a solid line between the title and the director(s), and a dashed
line between the country and the run length.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1909-c03e04_jekyllhydetabsv4.java
66
You could implement the ILineDrawer interface to draw any kind of line, but iText ships
with three implementations that are ready to use: SolidLine, DottedLine, and DashedLine.
Each of these classes allows you to change the line width and color. The DottedLine class
also allows you to change the gap between the dots. In the next chapter, well also use these
classes to draw line separators with the LineSeparator class.
At first sight, using the Tab object seems to be a great way to render content in a tabular form, but
there are some serious limitations.
This PDF was made with the JekyllHydeV5 example. As you can see, this still looks quite nice,
apart from the fact that Country and Duration stick together on the first line.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1910-c03e05_jekyllhydetabsv5.java
67
The Tab functionality contained some errors in iText 7.0.0. Due to rounding errors, some
text wasnt aligned correctly in a seemingly random way. This problem was fixed in iText
7.0.1.
Another bug that was fixed in iText 7.0.1 is related to the SolidLine class. In iText 7.0.0,
the line width of a SolidLine was ignored.
When we scroll down in the document, we see a more serious problem when theres no sufficient
space to fit the title and the director next to each other. Director Charles Lamont pushes the country
to the Duration column and the number of minutes gets shown on a second row.
Figure 3.7: trying to fit the data on a page with portrait orientation
We can solve these problems by using the Table and Cell class to organize data in a tabular form.
These objects will be discussed in chapter 5 of this tutorial. For now, well continue with some more
ILeafElement implementations.
Adding links
In the previous examples, weve added the ID of the movie, short film, cartoon, or video as actual
content. This ID can help us find the movie on the Internet Movie Database (IMDB). In Figure 3.8,
we dont show the ID, but when we click on the title of a movie, we can jump to the corresponding
page on IMDB.
http://imdb.com
68
In line 5-6, we create a PdfAction object that links to an URL. This URL is composed of
http://www.imdb.com/title/tt/ and the IMDB ID. In line 7, we create a Link object using a String
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1911-c03e06_jekyllhydetabsv6.java
69
containing the title of the movie, and the PdfAction. As a result, you will be able to jump to the
corresponding IMDB page when clicking a title.
Interactivity in PDF is achieved by using annotations. Annotations arent part of the real
content. They are objects added on top of the content. In this case, a link annotation is used.
There are many other types of annotations, but thats outside the scope of this tutorial.
There are also many types of actions. For now, weve only used a URI action. Well use
some more in chapter 6.
The Link class extends the Text class. Appendix A lists a series of methods that are available for
the Link as well as for the Text class to change the font, to change the background color, to add
borders, and so on.
The first Text object shown in figure 3.9 is what text normally looks like. For the words Dr. Jekyll,
we defined a text rise. We scaled the word and horizontally. And we skewed the words Mr. Hyde.
The methods used to achieve this can be found in the TextExample example.
http://developers.itextpdf.com/content/itext-7-building-blocks/appendix/appendix-abstractelement-methods
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1920-c03e07_textexample.java
1
2
3
4
5
70
Introducing images
In 1996, Stephen Frears made a movie with Julia Roberts in the role of Mary Reilly, a maid in the
household of Dr. Jekyll. Lets take an image of the poster of this movie and add it to a document as
done in figure 3.10.
71
The code to achieve this, is very simple. See the MaryReillyV1 example.
1
2
3
4
5
6
7
8
9
10
11
12
We have the path to our image in line 1. The ImageDataFactory uses this path in line 9 to get the
image bytes and to convert them into an ImageData object that can be used to create an Image object.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1921-c03e08_maryreillyv1.java
72
In this case, we are passing a JPEG image, and we add that image straight to the document object in
line 10.
JPEG images are stored inside a PDF as-is. It isnt necessary for iText to convert the image
bytes into another image format. PNG for instance, isnt supported in PDF, hence iText
will have to convert each PNG image we pass into a compressed bitmap.
Images are stored outside the content stream of the page in an object named an image XObject.
XObject stands for eXternal Object. The bytes of the image are stored in a separate object outside
the content stream. Now suppose that we would add the same image twice as is done in figure 3.11.
When we compare the files mary_reilly_V2.pdf and mary_reilly_V3.pdf, they look exactly the
same to the naked eye. When we look at the file size of the files, we notice something strange:
The file marked as V2 has the same file size as the file marked as V1. In other words, the file
with two images has more or less the same file size as the file with a single image. This is
http://gitlab.itextsupport.com/itext7/samples/raw/develop/publications/highlevel/cmpfiles/chapter03/cmp_mary_reilly_V2.pdf
http://gitlab.itextsupport.com/itext7/samples/raw/develop/publications/highlevel/cmpfiles/chapter03/cmp_mary_reilly_V3.pdf
73
consistent with what we said before: the file is stored inside the document only once as an
external object. We refer to this XObject twice.
The file marked as V3 looks identical to the file marked as V2, but its file size is almost double
the size of the file marked as V2. Its as if the image bytes of our JPEG are added twice to the
PDF document.
The code we used to create the file marked as V2 can be found in the MaryReillyV2 example:
1
2
3
We create one img object; we add this image twice to the same document. As a result, the image is
shown twice, but the image bytes are stored in a single image XObject.
Now lets take a look at the MaryReillyV3 example.
1
2
3
4
In this snippet, we create two Image instances for the same image, and we add both of these
instances to the same document. Once more the image is shown twice, but now its also stored
twice (redundantly) inside the document.
Theres a direct relationship between an Image object in iText and an image XObject inside
the PDF. Every new Image object that is created and added to a document, results in a
separate image XObject inside the PDF. If you create two or more Image objects of the same
image, youll end up with a bloated PDF file with too many redundant image XObjects.
This is clearly something you want to avoid.
In these first examples, we added Image objects without defining a location. The first image was
added right under our first paragraph. The second image was added right under the first one. We
can also choose to add the Image at specific coordinates.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1925-c03e09_maryreillyv2.java
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1926-c03e10_maryreillyv3.java
74
In this example, we define the position and the size of the image in the Image constructor. We define
the position as x = 320; y = 750, and we define a width of 50 user units (which is, by default, a
width of 50 pt). The height of the image will be adjusted accordingly, preserving the aspect ratio of
the image.
The second PDF was created using the MaryReillyV5 example.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1929-c03e11_maryreillyv4.java
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1930-c03e12_maryreillyv5.java
1
2
3
75
In this case, we use the setFixedPosition() method to define the position and size of the image.
Note that we use the UnitValue to define that 50 is a value expressed in pt. The other option is to
define the width as a percentage.
There are different variations available for the Image constructor and the setFixedPosition()
method. For instance, you can also define a page number as is done in the MaryReillyV6 example.
1
2
3
In this example, adding the image on page 2, triggers the creation of a new page. See figure 3.13.
If we had been adding the image on page 200, 199 new pages would have been added in order to
make sure that the image is actually on page 200. Im not sure if theres an actual use case for the
setFixedPosition() method that accepts a page number as a parameter when creating a document
from scratch, but that method can also be used when adding content to an existing document.
76
We create a PdfDocument instance using a PdfReader and a PdfWriter object. We use the PdfDocument instance to create a Document. We add an Image to that document using specific coordinates
and a specific width on page 1. The result is shown in figure 3.14.
1
2
3
4
77
As shown in figure 3.15, the image is now centered on the page (using the setHorizontalAlignment() method) and it takes 80% of the available width on the page (using the setWidthPercent()
method).
Note that iText will automatically scale the image to 100% of the available width when youre trying
to add an image that doesnt fit.
78
Resizing an image doesnt change anything to the original quality of the image. The number
of pixels in the image remains identical; iText doesnt change a single pixel in your image.
This doesnt mean the resolution doesnt change when you resize an image. If an image
is 720 pixels by 720 pixels and you render this image as a 720 pt by 720 pt image, the
resolution will be 72 dots per inch. If you change the dimension to 72 pt by 72 pt, you will
have a resolution of 720 dots per inch.
So far, weve been adding Image objects straight to the document. You can also add Image objects to
BlockElement objects. In the MaryReillyV9 example, we add an Image to a Paragraph.
1
2
3
4
5
79
We see that the leading has been adjusted automatically, but also that the image is somewhat big. The
Mary Reilly poster is 182 by 268 pixels in size. In this case, iText will use the same size in user units. As
a result, the image shown in figure 3.16 measures 182 by 268 pt. iText may scale images automatically
depending on the context. We already mentioned the situation where the image doesnt fit the width
of the page; in chapter 5, well see how images behave in the context of tables.
There are also different scale() methods that allow us to scale an image programmatically. In the
MaryReillyV10 example, we scale the image to 50% in X- as well as in Y-direction.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1935-c03e17_maryreillyv10.java
1
2
3
4
5
6
7
80
We also set a rotation angle of -30 degrees, which results in the PDF shown in figure 3.17.
These are the most common ways to change the dimensions of an Image object:
the scale() method: accepts two parameters. The first one is the factor that will be used in
the X-direction; the second one is the factor that will be used in the Y-direction. For instance:
if you pass a value of 1f for the X-direction and 0.5f for the Y-direction, the image will be as
wide as initially, but the height will be reduced to 50% of the original height.
the scaleAbsolute() method: also accepts two parameters. The first one is the absolute width
in user units; the second one is the absolute height in user units. For instance: if you use a value
of 72f for both the width and the height, the image will, by default, be rendered as an image
of 1 inch by 1 inch.
the scaleToFit() method: also accepts two parameters. Using the scaleAbsolute() method
can lead to awkward results if you dont take the aspect ratio of the image into account.
81
The first parameter of the scaleToFit() method defines the maximum width of the image;
the second one defined the maximum height. The image will be scaled preserving the aspect
ratio. This means that the resulting image may be smaller than expected.
So far, weve only been using JPEG images, but iText supports many other image types.
The RGB code for yellow is #FFFF00; the RGB code for blue is #0000FF. If we want to create an RGB
images that shows gradient from yellow to blue, we could create an image with 256 pixels that is
256 pixels wide and 1 pixel high. We could then loop from 0 (0x00) to 255 (0xFF) creating pixels that
vary from [Red = 255, Green = 255, Blue = 0] to [Red = 0, Green = 0, Blue = 255]. The total byte
size of that image would be the number of pixels multiplied with the number of values needed to
describe the color of each pixel. The following code snippet shows how this is done:
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1936-c03e18_imagetypes.java
1
2
3
4
5
6
7
8
9
10
82
In this snippet, we ask the ImageDataFactory to create the ImageData for an image of 256 pixels
by 1 pixel. We are using 3 components for each pixel. Each component is expressed using 8 bits
per component (bpc); thats 1 byte. The fourth parameter of the create() method is the data[].
The fifth parameter is an array we can use to define transparency. We dont need this parameter
in our simple example. We use the ImageData to create a new Image and we scale this image in the
Y-direction. If we didnt scale the image, wed only see a very thin line that is 1 user unit high.
Lets take this first batch of image files and see what happens what we add them to a Document.
83
1
2
3
4
5
6
7
public
public
public
public
public
public
public
static
static
static
static
static
static
static
final
final
final
final
final
final
final
String
String
String
String
String
String
String
TEST1
TEST2
TEST3
TEST4
TEST5
TEST6
TEST7
=
=
=
=
=
=
=
"src/main/resources/img/test/map.jp2";
"src/main/resources/img/test/butterfly.bmp";
"src/main/resources/img/test/hitchcock.png";
"src/main/resources/img/test/info.png";
"src/main/resources/img/test/hitchcock.gif";
"src/main/resources/img/test/amb.jb2";
"src/main/resources/img/test/marbles.tif";
JPEG / JPEG2000
The code to add a JPEG2000 image doesnt look any different than the code to add a JPEG image.
1
2
JPEG and JPEG200 are supported natively in PDF, this isnt the case for PNG.
84
The code for the page to the left looks like this:
1
2
3
4
5
6
7
8
9
// BMP
Image img2 = new Image(ImageDataFactory.create(TEST2));
img2.setMarginBottom(10);
document.add(img2);
// PNG
Image img3 = new Image(ImageDataFactory.create(TEST3));
img3.setMarginBottom(10);
document.add(img3);
// Transparent PNG
10
11
12
85
As you can see, were using the setMarginBottom() method for img2 and img3 to introduce 10 user
units of white space between the images. There is something special with img4; info.png is partly
transparent. We introduce a left border with a thickness of 6 user units. We see that border, because
the image is transparent. If the image were opaque, that border would have been invisible because
it would have been covered by the image.
Transparent images arent supported in PDF, at least not in the way youd expect. When
you add an image with transparent parts to a PDF, iText will add two images:
An opaque image: for instance, an image where the transparent part consists of black
pixels,
An image mask: this is an image with 1 component that defines the transparency.
A PDF viewer will use both images to compose the transparent image.
If the image mask has 1 bpc, we talk about a hard mask. The pixel of the opaque image
underneath the mask is either visible or invisible. If the image mask has more than 1 bpc,
we talk about a soft mask. The pixel underneath the mask can be partly transparent.
The same is true for background colors. We define a gray background for the first Hitchcock image
in the page on the right:
1
2
3
We only see this background, because hitchcock.gif is a GIF file with transparency. The second
Hitchcock image is added in a completely different way.
AWT images
If youre working in a Java environment, you may have to work with the AWT image class
java.awt.Image; iText also support these images.
1
2
3
4
5
6
86
java.awt.Image awtImage =
Toolkit.getDefaultToolkit().createImage(TEST5);
Image awt =
new Image(ImageDataFactory.create(awtImage, java.awt.Color.yellow));
awt.setMarginTop(10);
document.add(awt);
We read the hitchcock.gif image into a java.awt.Image object in line 1-2. We get an ImageData
object from the ImageDataFactory in line 4. The first parameter is the AWT image, the second
parameter defines the color that needs to be used for the transparent part (if there is any). You can
also add a Boolean as third parameter. If that parameter is true, the image will be converted to a
black and white image.
JBIG2 / TIFF
Figure 3.21 shows a JBIG2 image and a TIFF image.
1
2
3
4
5
6
87
// JBIG2
Image img6 = new Image(ImageDataFactory.create(TEST6));
document.add(img6);
// TIFF
Image img7 = new Image(ImageDataFactory.create(TEST7));
document.add(img7);
It isnt always that easy to convert the full JBIG2 or TIFF image to PDF though. A JBIG2 image and
a TIFF image can contain different pages. In that case, we need to loop over the pages and extract
every page as a separate image. The same is true for animated GIF images that consist of different
frames.
Figure 3.22 shows the different frames of an animated GIF that shows an animation of a fox jumping
over a dog.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-3#1937-c03e19_pagedimages.java
88
Animated GIFs arent supported in PDF, so you cant add the animation as-is to the document. We
can only add every frame to the document as a separate image. Thats what we do in the next code
snippet.
1
2
3
4
5
6
We create an URL object that uses the path to the file as input. We then create a List of ImageData
objects containing the ImageData of every frame in the animated GIF. Finally, we add each frame
as a separate Image to the Document.
The code to read the different pages from a JBIG2 and a TIFF file is more complex.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
89
// JBIG2
URL url2 = UrlUtil.toURL(TEST2);
IRandomAccessSource ras2 =
new RandomAccessSourceFactory().createSource(url2);
RandomAccessFileOrArray raf2 = new RandomAccessFileOrArray(ras2);
int pages2 = Jbig2ImageData.getNumberOfPages(raf2);
for (int i = 1; i <= pages2; i++) {
img = new Image(ImageDataFactory.createJbig2(url2, i));
document.add(img);
}
// TIFF
URL url3 = UrlUtil.toURL(TEST3);
IRandomAccessSource ras3 =
new RandomAccessSourceFactory().createSource(url3);
RandomAccessFileOrArray raf3 = new RandomAccessFileOrArray(ras3);
int pages3 = TiffImageData.getNumberOfPages(raf3);
for (int i = 1; i <= pages3; i++) {
img = new Image(
ImageDataFactory.createTiff(url3, true, i, true));
document.add(img);
}
document.close();
We first need to know the number of pages in the JBIG2 or TIFF file. This requires us to create
a RandomAccessFileOrArray object. With this object, we can ask the Jbig2ImageData or the
TiffImageData class for the number of pages in the JBIG2 or TIFF file. We can then loop over
the number of pages in that file, and we use the createJbig2() or createTiff() method to get the
ImageData object needed to create an Image.
Up until now, all the Image objects that we have created, resulted in an image XObject stored in the
PDF document. In the next example, well create a different type of XObject.
WMF / PDF
All the image types weve worked with so far were raster images. Raster images consist of pixels of a
certain color put next to each other in a grid. In the XObjectTypes example, we have the following
source files:
1
2
90
WMF is a vector image format. Vector images dont have pixels. They are made up of basic geometric
shapes such as lines and curves. These lines and curves are expressed as a mathematical equation,
which means that you can easily scale them without losing any quality.
The concept of resolution doesnt exist in the context of vector images. The resolution only
comes into play when you render the image to a device. The resolution of the device a
printer, a screen will determine the resolution you perceive when looking at the vector
image.
A PDF can contain raster images, and each of these raster images will have its own resolution, but
the PDF itself doesnt have a resolution. The content of the PDF is also made up of geometric shapes
defined using PDF syntax.
In figure 3.23, you see a WMF file representing a butterfly and a page from an existing PDF file that
were added to a Document using the Image object.
91
If you look inside this PDF file, you wont find any image XObject; instead youll discover two
form XObjects. A form XObject uses the same mechanism as an image XObject, except that a form
XObject doesnt consist of pixels; its a snippet of PDF syntax that is external to the page content.
If we want to add a WMF file to a Document using the Image class, we need to create a
PdfFormXObject first. The WmfImageData object will help us create the ImageData that is needed
to create this form XObject. We can use that xObject1 to create an Image instance.
1
2
3
4
PdfFormXObject xObject1 =
new PdfFormXObject(new WmfImageData(WMF), pdf);
Image img1 = new Image(xObject1);
document.add(img1);
We need to do something similar to import a page from an existing PDF file if we want to import
that page as if it were an image.
1
2
3
4
5
6
7
92
We start by creating a PdfReader object (line 1) and a PdfDocument based on that reader (line 2). We
obtain a PdfPage from that existing document (line 3), and we copy that page as a PdfXFormObject.
We can use that xObject2 to create an Image instance.
The content of the existing page will be added as if it were a vector image. All interactive
features that may exist in the original page, such as links, form fields, and other annotations,
will be lost.
This concludes the overview of the objects that implement the ILeafElement interface.
Summary
In this chapter, weve covered the building blocks that implement the ILeafElement interface. These
elements are atomic building blocks; they arent composed of other elements.
Tab is an element that allows you to put some space between two other building blocks,
either using white space, or by introducing a leader. You can also use the Tab element to align
an element.
Text is an element that contains a snippet of text using a single font, single font size, single
font color. Its the atomic text building block.
Link is a Text element for which we can define a PdfAction, for instance: an action that
opens a web site when we click on the text. Well discuss more examples of links and actions
in chapter 6.
Image is an element that can be used to create an image XObject so that you can use
raster images in your PDF. For reasons of convenience, we also allow developers to wrap
a PdfFormXObject inside an Image object so that they can use form XObjects using the same
functionality that is available for image XObjects.
We havent finished talking about these objects. Well continue using them in the chapters that
follow, starting with the next chapter that will discuss the Div, LineSeparator, Paragraph, List,
and ListItem object.
94
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
95
"http://www.imdb.com/title/tt%s", record.get(0));
Link movie = new Link(record.get(2), PdfAction.createURI(url));
div.add(new Paragraph(movie.setFontSize(14)))
.add(new Paragraph(String.format(
"Directed by %s (%s, %s)",
record.get(3), record.get(4), record.get(1))));
File file = new File(String.format(
"src/main/resources/img/%s.jpg", record.get(0)));
if (file.exists()) {
Image img = new Image(
ImageDataFactory.create(file.getPath()));
img.scaleToFit(10000, 120);
div.add(img);
}
document.add(div);
}
document.close();
}
As usual, we create a PdfDocument and a Document instance (line 2-3). We reuse the CSV file that was
introduced in the previous chapter, and we loop over all the movies listed in that CSV file, excluding
the header row (line 4-6). We create a new Div object (line 7) and we define the left border as a solid
border with a thickness of 2 user units (line 8), we set the left padding to 3 user units (line 9), and
we introduce a bottom margin of 10 user units (line 10). We add the title Paragraph to this Div (line
14), as well as a Paragraph with additional info (line 15 - 17). If we find a movie poster, we add it as
an Image (line 24). We add each Div to the document (line 26) and we close the document (line 28).
If we look at the bottom of the first page and at the top of the second page in Figure 4.1, we see
that the Div containing the information about the movie Dr. Jekyll and Mr. Hyde directed by John
S. Roberson, is distributed over two pages. The movie poster didnt fit on the first page, so it was
forwarded to the second page. Maybe this isnt the behavior we desire. Maybe we want to keep the
elements added to the same Div together as shown in figure 4.2.
96
We use only one extra method to achieve this; see the DivExample2 example.
1
2
3
4
5
By adding setKeepTogether(true), we tell iText to try to keep the content of a Div on the same
page. If the content of that Div fits on the next page, all the elements in the Div will be forwarded
to the next page. This is the case in figure 4.2 where the title and the info about the 1920 movie Dr.
Jekyll and Mr. Hyde directed by John S. Roberson is no longer added on the first page. Instead its
forwarded to the next page.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-4-examples-abstractelement-part-1#1963-c04e02_
divexample2.java
97
This approach wont work if the content of a Div doesnt fit on the next page. In that case, the
elements are distributed over the current page and subsequent pages as if the setKeepTogether()
method wasnt used. Theres a workaround in case you really want to keep one element on the same
page as the next element. Well look at an example demonstrating this workaround after weve
discussed the LineSeparator object.
98
We create a SolidLine object, passing a parameter that defines the thickness. We remember from
the previous chapter that SolidLine is one of the implementations of the ILineDrawer interface. We
set its color to red and we use this ILineDrawer to create a LineSeparator instance. In this case,
we define the width of the line using the setWidthPercent() method. We could also have used
the setWidth() method to define an absolute width expressed in user units. Finally, we set the top
margin to 5 user units.
In the LineSeparatorExample example, we add the ls object to our Div element containing
information about a movie.
1
div.add(ls);
There isnt much more to be said about LineSeparator. Just make sure that you use the right methods
to set properties. For instance: you cant change the color of a line at the level of the LineSeparator,
you have to set it at the level of the ILineDrawer. The same goes for the thickness of the line. Check
Appendix B to find out which AbstractElement methods are implemented for the LineSeparator
class, and which methods are ignored.
99
Wed like to avoid this kind of behavior. Wed like the title to be on the same page as the start of the
content of the chapter. We do a first attempt to fix this problem in the ParagraphAndDiv1 example.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
100
This example is very similar to the examples we made in chapter 2. The main difference is that we no
longer add the Paragraph objects straight to the Document. Instead, we store the Paragraph objects
in a Div object, and we add the Div object to the Document at the end of each chapter.
We could add .setKeepTogether(true) between line 15 and 16, but that wouldnt have any effect as
the full content of the Div doesnt fit on a single page. As documented before, the setKeepTogether()
method is ignored. Weve had long discussions at iText on how to solve this problem. We decided
that the most elegant way to avoid widowed objects consisted of introducing a setKeepWithNext()
method.
The setKeepWithNext() method was introduced in iText 7.0.1. You wont find it in the
very first iText 7 release. Were investigating if we could support the method for nested
objects. Were reluctant to do this because this could have a significant negative impact on
the overall performance of the library.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
101
We use a Paragraph added straight to the Document for the title (line 5); we create a Div to combine
the rest of the content in the chapter (line 9). We indicate that the Paragraph needs to be kept on the
same page as (the first part of) the Div by adding setKeepWithNext(true). The result is shown in
figure 4.5. The title SEARCH FOR MR. HYDE is now forwarded to the next page when compared
to figure 4.4.
102
The setKeepWithNext() method can be used with all other AbstractElement implementations,
except Cell. The method only works for elements added straight to the Document instance. It doesnt
work for nested objects such as a Cell that is always added to a Table and never straight to a
Document. In the case of our example, it wouldnt work if the title Paragraph was added to the Div
instead of to the Document.
103
The word leading is pronounced as ledding, and its derived from the word lead (the metal).
When type was set by hand for printing presses, strips of lead were placed between lines
of type to add space. The word originally referred to the thickness of these strips of lead
that were placed between the lines. The PDF standard redefines the leading as the vertical
distanced between the baselines of adjacent lines of text (ISO-32000-1, section 9.3.5).
104
The code of the ParagraphAndDiv3 example is identical to what we had in the previous example,
except for the following snippet.
1
2
3
4
5
6
div.add(
new Paragraph(line)
.setMarginBottom(0)
.setFirstLineIndent(36)
.setMultipliedLeading(1.2f)
);
When we add an object to a Document either directly or indirectly (e.g. through a Div), iText uses
the appropriate IRenderer to render this object to PDF. In the Before we start section of this
book, figure 0.4 shows an overview of the different renderers. Normal use of iText hardly ever
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-4-examples-abstractelement-part-1#1967-c04e05_
paragraphanddiv3.java
105
requires creating a custom renderer, but well take a look at one example in which we create a
MyParagraphRenderer extending the default ParagraphRenderer.
Lets take a look at the CustomParagraph example to see the difference between the two
approaches. The first Paragraph was added like this:
1
2
3
4
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-4-examples-abstractelement-part-1#1968-c04e06_
customparagraph.java
1
2
3
4
5
106
35
36
37
107
}
}
}
We extend the existing ParagraphRenderer class and we override one single method. We take
the original drawBackground() method from the AbstractRenderer class, and we replace the
rectangle() method with the roundRectangle() method (line 23). As you can see in line 24-29.
the dimension of the rectangle can be fine-tuned with extra space to the left, right, top, and bottom.
These values can be passed to the internal Background object by using a different flavor of the
setBackgroundColor() method that takes 4 extra float values (extraLeft, extraTop, extraRight,
and extraBottom).
Well conclude this chapter with some examples involving the List and ListItem class.
108
The ListTypes example shows how the first three lists are added.
1
2
3
4
5
6
7
8
9
10
11
12
java
109
In line 1, we create a list without specifying a type. By default, this will result in a list with hyphens
as list symbols. We add two list items the quick and dirty way in line 2-3; then we add the list to
the Document in line 4. We repeat these four lines many times, first we create a decimal list (line 5),
then we define an alphabetic list with lowercase letters (line 9).
The parameters we use to create different types of lists are stored in an enum. This ListNumberingType enumeration consists of the following values:
a, b, c, d, e,
ENGLISH_UPPER the list symbols are uppercase alphabetic letters (using the English alphabet):
A, B, C, D, E,
GREEK_LOWER the list symbols are lowercase Greek letters: , , , , ,
GREEK_UPPER the list symbols are uppercase Greek letters: , , , , ,
ZAPF_DINGBATS_1 the list symbols are bullets from the Zapfdingbats font, more specifically
characters in the range [172; 181].
ZAPF_DINGBATS_2 the list symbols are bullets from the Zapfdingbats font, more specifically
characters in the range [182; 191].
ZAPF_DINGBATS_3 the list symbols are bullets from the Zapfdingbats font, more specifically
characters in the range [192; 201].
ZAPF_DINGBATS_4 the list symbols are bullets from the Zapfdingbats font, more specifically
characters in the range [202; 221].
Obviously, we can also define our own custom list symbols, or we can use a combination of the
default list symbols (e.g. numbers) and combine them with a prefix or a suffix. Thats demonstrated
in figure 4.9.
110
The PDF in the screen shot of figure 4.9 was the result of the CustomListSymbols example. Well
examine this example snippet by snippet.
First we take a look at how we can introduce a simple bullet as list symbol, instead of the default
hyphen.
1
2
3
4
5
We create a List and we use the setListSymbol() method to change the list symbol. We can use any
String as list symbol. In our case, we want a single bullet. The Unicode value of the bullet character
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-4-examples-abstractelement-part-1#1970-c04e08_
customlistsymbols.java
111
is /u2022. If you examine the screen shot, you notice that the bullet is rather close to the content of
the list items. We can change this by defining an indentation using the setSymbolIndent() method
as is done in the next code snippet.
1
2
3
4
5
6
7
Here we set the list symbol to *, but we use a Text object instead of a String. and we set the font
to ZapfDingbats. We also change the font color to orange. This results in a list symbol that looks as
an orange pointing finger. In the next snippet, we use an Image object as a list symbol.
1
2
3
4
5
6
7
In line 1. we create an Image object; INFO contains the path to a blue info bullet. We scale the image
so that it measures 12 by 12 user units, and we pass the Image as a parameter of the setListSymbol()
method.
In the default list types, iText always added a dot after the list symbol of numbered lists: a., b., c.,
and so on. Maybe we dont want this dot. Maybe we want the list symbols to look like this: a-, b-,
c-, and so on. The following code snippet shows how to achieve this.
1
2
3
4
5
6
1
2
3
4
5
6
112
Not every numbered list needs to start with 1, i, a, and so on. You can also choose to start with a
higher number (or letter) using the setItemStartIndex() method. In the following code sample, we
start counting at 5.
1
2
3
4
5
Finally, well use the setListSymbolAlignment() to change the alignment of the labels. If you
compare the lowercase Roman numbers list in figure 4.8 with the one in figure 4.9, youll see a
difference in the way the list labels are aligned.
1
2
3
4
5
6
7
So far, weve always added list items to a list using Strings. These String values are changed into
ListItems internally.
The code of the ListItemExample example is very similar to the code of the Div examples.
1
2
3
4
5
6
7
8
9
10
113
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
114
Nested lists
In the final example of this chapter, well create nested lists as shown in figure 4.11.
115
The NestedLists example is rather artificial, so please bear with me. We start with an ordinary list,
named list. Thats the list with the hyphens as list symbol.
1
We create a numbered list list1 (line 1). This list will have two ListItems, liEL (line 5) and liEU
(line 11). We create a new List to be added to each of these list items respectively: listEL (line
2; lowercase English letters) and listEU (line 8, uppercase English letters). We add list items "Dr.
Jekyll" and "Mr. Hyde" to each of these lists (line 3-4; line 9-10).
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-4-examples-abstractelement-part-1#1972-c04e10_
nestedlists.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
116
When we look at figure 4.11, we see the hyphen, we see a numbered list with list symbols 1. and
2.. Nested inside these lists are two lists using the English alphabet (lower- and uppercase).
In the next snippet, we create an extra ListItem for list, more specifically li (line 1). We add four
lists to this ListItem: listGL (line 2), listGU (line 6), listRL (line 10), and listRU (line 14). These
lists are added one after the other (Greek lowercase, Greek uppercase, Roman numbers lowercase,
Roman number uppercase) to the list item with the default list symbol.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
117
Furthermore, we create a list listZ1 with numbered ZapfDingbats bullets. We add this list to a list
item named listZ1.
1
2
3
4
5
We create a second list listZ2 with a different set of ZapfDingbats bullets. We add this list to a list
item named listZ2.
1
2
3
4
5
We create a second list listZ3 with another set of ZapfDingbats bullets. We add this list to a list
item named listZ3.
1
2
3
4
5
We create a final list listZ4 with yet another set of ZapfDingbats bullets. We add this list to a list
item named listZ4.
1
2
3
4
5
1
2
3
4
5
118
listZ3.add(liZ4);
listZ2.add(liZ3);
listZ1.add(liZ2);
list.add(liZ1);
document.add(list);
The nested ZapfDingbats list is shown to the right in figure 4.11. As you can see, the different
list items are indented exactly the way one would expect. This concludes the first series of
AbstractElement examples.
Summary
In this chapter, we discussed the building blocks Div, LineSeparator, Paragraph, List, and
ListItem. We used Div to group other building blocks and LineSeparator to draw horizontal
lines. We fixed a problem with the chapter 2 examples we werent aware of: we learned how to
keep specific elements together on one page. We didnt go into detail regarding the IRenderer
implementations, but we looked at an example in which we changed the way a background is drawn
for a Paragraph. We created a custom ParagraphRenderer to achieve this. Finally, we created a
handful of List examples demonstrating different types of lists (numbered, unnumbered, straightforward, nested, and so on).
The next chapter will be dedicated entirely to tables, more specifically to the Table and Cell class.
My first table
Figure 5.1 shows a simple table that was created with iText 7.
The code to create this table is really simple; see the MyFirstTable example.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-5#2031-c05e01_myfirsttable.java
1
2
3
4
5
6
7
8
9
10
11
12
13
120
Figure 5.2 shows a variation of our first table. We changed the width of the table, its alignment, and
the width of the columns.
121
This was achieved by changing the constructor and by adding two extra lines; see the ColumnWidths example.
1
2
3
Instead of passing the number of columns to the Table constructor, we now pass an array with as
many elements as there are columns. Each element is a float value indicating the relative width of
the corresponding column. In this case, the first column will be twice as wide as the second and
third column.
We use the setWidthPercent() method so that the table takes 80% of the available width thats
the width of the page minus the width reserved for the left and right margin.
The default width percentage is 100%. Theres also a setWidth() method that allows you
to set the absolute width. Use this method if you prefer a value in user units over a width
that is relative to the available width.
122
We can change the alignment of the content of a Cell in different ways. The CellAlignment
example demonstrates the different options.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
123
table.addCell(cell);
document.add(table);
Once more we use the setHorizontalAlignment() method to define the horizontal alignment of
the table itself (line 3). Additionally, we use the setTextAlignment() method to change the default
alignment of the content of the Cell added to this table. By default, this content is aligned to the
left (TextAlignment.LEFT); we change the alignment to TextAlignment.CENTER (line 4). As a result,
"Cell with colspan 3" will be centered in the first cell we add (line 5).
We change the alignment of "Cell with rowspan 2" to TextAlignment.RIGHT for the second cell.
This time, we use the setTextAlignment() method at the level of the Cell (line 6-7). We complete
the two rows in this rowspan by adding four more cells without specifying the alignment. The
alignment is inherited from the table; their content is centered.
In line 12, we define a Cell for which we define the alignment at the level of the content.
In line 13, we add a Paragraph that is aligned to the left.
In line 14, we dont define an alignment for the Paragraph. The alignment is inherited from
the Cell. No alignment was defined at the level of the Cell either, so the alignment is inherited
from the Table. As a result, the content is centered.
In line 15, we add a Paragraph that is aligned to the right.
The next two cell demonstrate the vertical alignment and the setVerticalAlignment() method.
Content is aligned to the top by default (VerticalAlignment.TOP). In line 17-18, we create a Cell of
which the alignment is set to the middle (vertically: VerticalAlignment.MIDDLE). In line 20-21, the
content is bottom-aligned (VerticalAlignment.BOTTOM).
124
In this table, we are adding the same Paragraph to a table with 1 column; see the ColumnHeights
example.
1
2
3
Paragraph p =
new Paragraph("The Strange Case of\nDr. Jekyll\nand\nMr. Hyde")
.setBorder(new DashedBorder(0.3f));
We define a border of 0.3 user units for the Paragraph, so that we can clearly make the distinction
between the boundaries of the Paragraph and the borders of the Cell.
The first time, we add the Paragraph directly to the Table.
1
2
In this case, iText will determine the height in such a way that the content of the Paragraph fits the
Cell.
In the second row, we change the height of the Cell in such a way that the content wouldnt fit.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-5#2034-c05e04_columnheights.java
1
2
125
If iText would reduce the cell height to 16 user units, content would be lost. Usually this isnt
acceptable, so iText ignores the setHeight() method. Just like before, the height of the Cell is
determined by its content.
For the third row, we define a height that is much higher than needed.
1
2
The dashed line shows the space needed for the Paragraph. The full line is the border of the Cell.
When we look at the third row in figure 5.4, we see that theres quite some extra space between the
bottom boundary of the Paragraph and the bottom border of the Cell.
We can also set a rotation angle for the Cell. This is done in figure 6.5. The full block of the Paragraph
is rotated, and the height of the Cell adapts to the height that is necessary to render that rotated
block completely.
Rotating the content of a Cell is done using the setRotationAngle() method. The angle needs to
be expressed in Radians.
1
2
126
The space between the dashed border of the Paragraph and the border of the Cell is called the
padding. In the next example, well examine the difference between the margin and the padding.
Figure 5.6: the difference between the margin and the padding of a cell
Lets take a look at the CellMarginPadding example to see how this PDF was created.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
127
We set the background for the full table to orange in line 2. We add six cells to this table:
1. line 5-7: a cell with a green background, a margin of 5 user units and a padding of 10 user
units. Looking at the screen shot, we see that the margin is the space between the border of the
cell and the green rectangle the background. The padding is the space between the border of
that green rectangle and the content of the cell.
2. line 8-10: a cell with white text, a blue background, a top and bottom margin of 5 user units,
and a left padding of 30 user units. We dont see any orange ribbons to the left and the right.
We only see 5 user units of orange at the top and the bottom. The default margin of a Cell
is 0 user units. The text doesnt start immediately at the left. Theres 30 user units of space
between the left border and the text.
3. line 11-12: a cell with white text, a red background, and default values for the margin and the
padding. The text doesnt stick to the border because iText uses a default padding of 2 user
units.
4. line 13: a cell with default properties. This cell has no background color. Its orange because
of the background color of the table.
5. line 14-15: a cell with white text, a red background and a margin of 10 user units.
6. line 16-17: a cell with white text, a red background and a padding of 10 user units.
So far, we havent defined the border of any of the cells. The default border is a Border instance
define like this: new SolidBorder(0.5f). There is something special about cell borders that requires
more explanation.
128
16
17
18
129
Lets compare the code and the resulting table, shown in figure 5.8.
line 4-6: The first cell has a dashed border that is 0.5 user units wide. The border consists of a
complete rectangle.
line 7-10: For the second cell, we only defined a bottom border and a left border. A dotted line
is drawn to the left and at the bottom of the cell. The top and the right border are actually the
borders of other cells.
line 11-12: We introduce an orange dotted border that is 0.5 user units wide. Although we set
the border for the full cell, the top border isnt drawn as an orange dotted line. The top border
is part of the dashed border of our first cell; iText wont draw an extra border on top of that
already existing border.
line 13: We dont define a border. By default, a solid border of 0.5 user units is drawn. Two
borders were already defined previously, in the context of other, previously added cells. The
borders of those cells prevail.
line 14-15 and line 16-17: We define a solid bottom border that is 2 user units wide. The top
borders of both cells are already defined: they are also the bottom borders of the corresponding
cells in the previous row. The left and right borders arent defined anywhere; iText will use
the default border: a solid line of 0.5 user units.
130
One way to deal with borders would be to let every Cell, or more specifically every
CellRenderer, draw its own borders. In that case, the borders of adjacent cells would
overlap. For instance: the dashed border at the bottom of the cell in the first row would
overlap with the orange dotted top border of a cell in the second row. This is what happened
in previous versions of iText. The border of two adjacent cells often consisted of two
identical lines that overlapped each other. The extra line wasnt only redundant, it also
caused a visual side-effect in some viewers. Many viewers render identical content that
overlaps in a special way. In the case of overlapping text, a regular font looks as if it is bold.
In the case of overlapping lines, the line width looks thicker than defined. The line width
of two lines that are 0.5 user units wide and that are added at the exact same coordinates
is rendered with a width slightly higher than 0.5 user units. Although this difference isnt
always visible to the naked eye, we made the design decision to avoid this. All the borders
are drawn at the level of the Table. That is: at the level of the TableRenderer.
In the next example, we define a border for the table, while setting the borders of every cell to
Border.NO_BORDER.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
The result is shown in figure 5.9. The table has a border, but the cells dont have any inside borders.
131
Our design decision also has an impact on how we deal with custom renderers for cells. Suppose
that wed want to create cells with rounded borders. In that case, we could extend the CellRenderer
class and create a RoundedCornersCellRenderer like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In the previous chapter, weve used the setNextRenderer() method to replace the default ParagraphRenderer of a Paragraph by our custom renderer. We could do the same with every Cell we
create. In that case, wed have something like:
1
2
However, we dont like having to do this for every Cell we create. Its much easier to extend the
Cell class, overriding the makeNewRenderer() method.
1
2
3
4
5
6
7
8
9
10
11
12
132
We can now use the RoundedCornersCell object instead of the Cell object.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
We removed the border of the first cell in line 6. We didnt remove the borders of the other cells.
Looking at figure 5.10, we see that those cells have two borders.
133
This may be surprising: now that weve overridden the drawBorder() method of the CellRenderer,
why is iText still drawing that extra border? Weve already answered that question. We have made
the design decision to draw the borders at the level of the Table. The original drawBorder() method
in the CellRenderer class is empty. It doesnt draw any borders. If we want to use a custom border,
we can either do what weve done in line 6 for every cell we create. The better solution would be to
add setBorder(Border.NO_BORDER); to every RoundedCornersCell constructor.
In the next example, well add tables inside tables.
Nesting tables
Figure 5.11 shows two or four tables, depending on how you look at the screen shot. There are two
outer tables. Each of these tables has an inner table nested inside.
Lets examine the NestedTable example. This is how the first table was created:
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-5#2037-c05e07_nestedtable.java
1
2
3
4
5
6
7
8
9
10
11
134
We create a Table object named table. We add four Cell objects to this table, but one Cell object
is special. We created another Table object named inner and we added this table to the outer table
table using the addCell() method. If we look at figure 5.11, we see that theres a padding between
the border of the fourth cell and the border of the inner table. Thats the default padding of 2 user
units.
The second table was created in almost the exact same way as the first table. The main difference
can be found in the last line.
1
table.addCell(new Cell().add(inner).setPadding(0));
Instead of adding the nested table straight to the table object, we now create a Cell object to which
we add the inner table. We set the padding of this cell to 0. Now it looks as if the cell with content
"Cell with rowspan 1" has a rowspan of 2. This isnt the case. We have mimicked a rowspan of 2
by using a nested table.
If you look closely at the screen shot, you may see why you should avoid using nested
tables. Common sense tells us that nesting tables has a negative impact on the performance
of an application, but theres another reason why you might want to avoid using them in
the context of iText. As mentioned before, all cell borders are drawn at the Table level. In
this case, the border of the cell containing the nested table is drawn by the TableRenderer
of the outer table table. The border of the cells of the nested table are drawn by the
TableRenderer of the inner table inner. This results in overlapping lines, which may cause
an undesired effect. In some PDF viewers, the width of the overlapping lines may seem to
be wider than the width of each separate line.
Now lets switch to some examples that are less artificial. Lets convert our CSV file to a Table and
render it to PDF.
135
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-5#2038-c05e08_jekyllhydetablev1.java
136
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
137
We get our data from a CSV file (line 3) and we get the line containing the header information (line
4). Instead of using addCell(), we add each field in that line using the addHeaderCell() method.
This marks these cell as header cells: they will be repeated at the top of the page every time a new
page is started.
We also create footer cell that spans the six columns (line 8). We make this cell a footer cell by using
the addFooterCell() method (line 9). We also instruct the table to skip the last footer (line 10). This
way, the cell wont appear as a footer after the last row of the table. This is shown in figure 5.13.
There is also a way to skip the first header. See figure 5.14.
138
139
In this case, we had to use nested tables, because we have two types of headers. We have a header
that needs to be skipped on the first page. We also have a header that needs to appear on every page.
The JekyllHydeTableV2 example shows how its done.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-5#2039-c05e09_jekyllhydetablev2.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
140
Lines 1-12 should have no secrets to us. In lines 13-16, we use what weve learned when we discussed
nested tables to create an outer table with a second header. We use the setSkipFirstHeader()
method to make sure that header doesnt appear on the first page, only on subsequent pages.
Images in tables
Figure 5.15 demonstrates that we can also add images to a table. We can even make them scale so
that they fit the width of the cell.
141
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
142
We can add the image to a Cell using the add() method the same way weve added content to a
Cell before. We use the setAutoScaleWidth() method to tell the image that it should try to scale
itself to fit the width of its container, in this case the Cell to which it is added.
Theres also a setAutoScaleHeight() method if you want the images to scale automatically
depending on the available height, and a setAutoScale() method to scale the image based
on the width and the height.
Not scaling images can result in ugly tables; when the images are too large for the cell, they will
take up space from the adjacent cells.
143
When the content doesnt fit the page, the cell is split. The production year and title are on one
page, the director and the country the movie was produced in on the other page. This is the default
behavior when you write your code as done in the JekyllHydeTabV4 example.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-5#2041-c05e11_jekyllhydetablev4.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
144
You may want iText to do an effort to keep the content of a cell together on one page (if possible).
145
The PDF in the screen shot of figure 5.17 was created using the JekyllHydeTableV5 example. Theres
only one difference with the previous example. Weve added the following line of code after line 15:
1
cell.setKeepTogether(true);
The setKeepTogether() method is defined at the BlockElement level. Weve used that method
before in the previous chapter. Note that the setKeepWithNext() cant be used in this context,
because were not adding the Cell object directly to the Document.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-5#2042-c05e12_jekyllhydetablev5.java
146
Lets take a look at the JekyllHydeTableV6 example to see what this custom TableRenderer looks
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-5#2043-c05e13_jekyllhydetablev6.java
like.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
147
42
43
44
148
super.draw(drawContext);
}
}
We create constructors that are similar to the TableRenderer constructors (line 3-9), and we override
the getNextRenderer() method so that it returns an AlternatingBackgroundTableRenderer (line
10-14). We introduce a boolean variable named isOdd to keep track of the rows (line 2).
The draw() method is where we do our magic (line 15-43). We loop over the rows (line 17-19), and
we get the CellRenderer instances of all the cells in each row (line 20). We get the renderer of the
left cell and the right cell in each row (line 21-24), and we use those renderers to determine the
coordinates of the row (line 25-28). We draw the Rectangle based on those coordinates in a color
that depends on the alternating value of the isOdd parameter (line 29-40).
In the next code snippet, well create a table, and well declare the AlternatingBackgroundTableRenderer as the new renderer for that table.
1
2
3
4
Note that we have to define the RowRange. We take the number of elements in our resultSet after
having removed the header row. That gives us the number of actual rows that we are going to add
to the table, and to which we want to apply an alternating background.
Figure 5.19 shows another type of background. The width of the Title column represents four
hours; the colored bar in the Title cells represents the run length of the video. For instance: if the
colored bar takes half of the width of the cell, the run length of the movie is half of four hours; that
is: two hours.
149
150
The code for the custom CellRenderer to achieve this can be found in the JekyllHydeTable7
example.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Once more, we create a constructor (line 3-7) and we override the getNextRenderer() method
(line 8-12). We store the run length of the video in a runlength variable (line 2). We override
the drawBackground() method and we draw the background using the appropriate size and color
depending on the value of the runlength variable (line 13-32).
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-5#2044-c05e14_jekyllhydetablev7.java
151
Well conclude this example with a trick to keep the memory use low when creating and adding
tables to a document.
Suppose that we would create a Table object consisting of 3 header cells, 3 footer cells, and
3,000 normal cells, before adding this Table to a document. That would mean that at some point,
wed have 3,006 Cell objects in memory. That can easily lead to an OutOfMemoryException or an
OutOfMemoryError. We can avoid this by adding the the table to the document while we are still
adding content to the table. See the LargeTable example.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-5#2825-c05e15_largetable.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
152
The Table class implements the ILargeElement interface. This interface defines methods such as
setDocument(), isComplete() and flushContent() that are used internally by iText. When we use
the ILargeElement interface in our code, we only need to use the flush() and complete() method.
We start by creating a Table for which we set the value of the largeTable parameter to true (line
1). We add the Table object to the document before weve completed adding content (line 8). As we
marked the table as a large table, iText will use the setDocument() method internally so that the
table and the document know of each others existence. We add our 3,000 cells in a loop (line 9), but
we flush() the content every 50 rows (line 13-15). When we flush the content, we already render
part of the table. The Cell objects that were rendered are made available to the garbage collector
so that the memory used by those objects can be released. Once weve added all the cells, we use
the complete() method to write the remainder of the table that wasnt rendered yet, including the
footer row.
This concludes the chapter about tables and cells.
Summary
In this chapter, weve experimented with tables and cells. We talked about the dimensions and the
alignment of tables, cells, and cell content. We learned about the difference between the margin and
the spacing of a cell. We changed the borders of tables and cells using predefined Border objects
and using a custom CellRenderer implementation. We nested tables, repeated headers and footers,
changed the way tables are split when they dont fit a page. We extended the TableRenderer and
the CellRenderer class to implement special features that arent offered out-of-the-box. Finally, we
learned how to reduce the memory use when creating and adding a Table.
153
We could stop here, because weve now covered every building block, but well add two more
chapters to discuss some extra functionality that is useful when creating PDF documents using
iText.
URI actions
If you look at the AbstractAction class, you notice that it has a method named secAction(). When
you use this method on a building block, you can define actions that will be triggered when clicking
on its content. This is an alternative to using the Link object.
The setAction() method doesnt make sense for every building block. For instance: you
cant click an AreaBreak. Please consult the appendix to find out for which objects the
setAction() method can be used.
In figure 6.1, we see a PDF that is almost identical to the one we created in chapter 4 when we
rendered the entries in our CSV file to a PDF with a numbered list.
155
In the original example, we used a Link object so that you could jump to the corresponding IMDB
page when clicking the title. In the URIAction example, we make the complete ListItem clickable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
156
}
String url = String.format(
"http://www.imdb.com/title/tt%s", record.get(0));
li.setAction(PdfAction.createURI(url));
list.add(li);
}
document.add(list);
In line 21, we create a URI action using a link to IMDB and we set the action for the complete list
item using the setAction() method.
Named actions
Figure 6.2 shows links that are added to the first and the last page of a similar document. The link
on the first page is marked Go to last page; the link on the last page is marked Go to first page,
and thats exactly what the links do when you click them.
1
2
3
4
5
6
7
8
157
The createNamed() method accepts a PdfName as a parameter. You can use one of the following
values:
PdfName.FirstPage the action allows you to jump to the first page of the document.
PdfName.PrevPage the action allows you to jump to the previous page in the document.
PdfName.NextPage the action allows you to jump to the next page in the document.
PdfName.LastPage the action allows you to jump to the last page of the document.
You could create these names yourself, for instance new PdfName("PrevPage"), but its always better
to use the names that are predefined in the PdfName class.
iText wont check if you pass a parameter that corresponds to one of these four values,
because a PDF viewer may support additional, non-standard named actions. However, any
document using such a non-standard action isnt portable.
These named actions allow us to navigate through a document, but they are rather limited, arent
they? If we want to create a table of contents that allows us to jump to a specific page, we need a
GoTo action.
GoTo actions
Figure 6.3 shows the table of contents of the Jekyll and Hyde story. If wed click on a line, wed jump
to the corresponding page.
158
To achieve this, we keep track of the titles and the page numbers on which these titles appear. The
TOC_GoToPage example shows how.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
159
document.add(p);
toc.add(new SimpleEntry(line, pdf.getNumberOfPages()));
}
else {
p.setFirstLineIndent(36);
if (line.isEmpty()) {
p.setMarginBottom(12);
title = true;
}
else {
p.setMarginBottom(0);
}
document.add(p);
}
}
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
p = new Paragraph().setFont(bold).add("Table of Contents");
document.add(p);
toc.remove(0);
List<TabStop> tabstops = new ArrayList();
tabstops.add(new TabStop(580, TabAlignment.RIGHT, new DottedLine()));
for (SimpleEntry<String, Integer> entry : toc) {
p = new Paragraph()
.addTabStops(tabstops)
.add(entry.getKey())
.add(new Tab())
.add(String.valueOf(entry.getValue()))
.setAction(PdfAction.createGoTo(
PdfExplicitDestination.createFit(entry.getValue())));
document.add(p);
}
Most of the code repeats what weve done before to render the TXT file to a PDF, but these are the
new lines that interest us the most:
Line 6: we create an ArrayList named toc that will contain a series of SimpleEntry key-value
pair entries. The key is a String well use for the title. They value is an Integer well use for
the page number.
Line 17: each time we add a title to the document (line 10-19), we add a new SimpleEntry to
the toc list. We get the current page number using the getNumberOfPage() method.
Line 31-33: once the full text is added, we go to a new page. We add a Paragraph saying "Table
of Contents".
160
Line 34: we remove the first entry of the list, because thats the title of the book, not the title
of a chapter.
Line 35-36: we create a list of TabStop elements. We use a DottedLine as the tab leader.
Line 37-46: we loop over all the entries in our toc. We use the key of each entry as well as the
corresponding value to construct a Paragraph with the title and the page number as content.
We also use the page number to create a GoTo action that jumps to that specific page.
In line 43, we use the createGoTo() method with a PdfExplicitDestination object as a parameter.
The PdfExplicitDestination class extends the PdfDestination class. Well take a closer look at
these classes later on in this chapter. Whats more important right now, is that there are two problems
with this example, one problem is worse than the other.
1. The link jumps to another page in the document and shows this page in full. A more
elegant solution would be to jump to the start of the actual title. We could use a different
PdfExplicitDestination to achieve this (for instance createFitH() instead of createFit()).
2. The link doesnt always jump to the correct page. We store the page number of the last page
in the document at the moment we add the title. Thats the page number of the current page.
However, were also using the setKeepWithNext() method. This method forwards the title to
a new page if the first paragraph of the chapter doesnt fit the current page. In that case, our
TOC points at the wrong page, more specifically at the page just before the one we need.
Well fix these two problems in the next example. Instead of an explicit destination, well use named
destinations for a change.
Named destinations
Figure 6.4 looks almost identical to figure 6.3. The fact that the page numbers are now correct is the
only visible difference.
161
The other difference is that we now used named destinations. We create those destinations by using
the setDestination() method. This method is defined in the ElementPropertyContainer and can
be used on many building blocks (see appendix). In the TOC_GoToNamed example, we use it on
a Paragraph.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
162
p.setFont(bold).setFontSize(12)
.setKeepWithNext(true)
.setDestination(name)
.setNextRenderer(new UpdatePageRenderer(p, titlePage));
title = false;
document.add(p);
toc.add(new SimpleEntry(name, titlePage));
}
else {
p.setFirstLineIndent(36);
if (line.isEmpty()) {
p.setMarginBottom(12);
title = true;
}
else {
p.setMarginBottom(0);
}
document.add(p);
}
}
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
p = new Paragraph().setFont(bold)
.add("Table of Contents").setDestination("toc");
document.add(p);
toc.remove(0);
List<TabStop> tabstops = new ArrayList();
tabstops.add(new TabStop(580, TabAlignment.RIGHT, new DottedLine()));
for (SimpleEntry<String, SimpleEntry<String, Integer>> entry : toc) {
SimpleEntry<String, Integer> text = entry.getValue();
p = new Paragraph()
.addTabStops(tabstops)
.add(text.getKey())
.add(new Tab())
.add(String.valueOf(text.getValue()))
.setAction(PdfAction.createGoTo(entry.getKey()));
document.add(p);
}
Lets examine what is so different about this example when compared to the previous one.
Line 6: we create an ArrayList named toc that will contain a series of SimpleEntry key-value
pair entries. The key is a String that well use for a unique name. The value is no longer a
163
page number, but another SimpleEntry. The key of this second key-value pair will be the title
of the chapter; the value will be the corresponding page number.
Line 11: we create a unique name for every title: title00, title01, title03, and so on.
Line 12-13: we create a SimpleEntry named titlePage using the title as a key and the current
page number as the value. We know that this page number will be wrong in some cases. We
will use a custom ParagraphRenderer to update the page number.
Line 16: we use the unique name as a destination for the Paragraph using the setDestination() method.
Line 17: we create an UpdatePageRenderer that will serve as the renderer for the title
paragraph. We pass the titlePage entry as a parameter so that the renderer can update the
page number.
Line 20: we add a new SimpleEntry instance to the toc object. This entry contains the unique
name and another entry with the title and the page number.
Line 34-37: once the full text is added, we go to a new page. We add a Paragraph saying "Table
of Contents". Note that we define a destination named "toc" for that paragraph (line 36).
Line 38: we remove the first entry of the list, because thats the title of the book, not the title
of a chapter.
Line 39-40: we create a list of TabStop elements. We use a DottedLine as the tab leader.
Line 41-50: we loop over all the entries in our toc. We get the value of each entry (line 42)
to construct the content of each line in the table of contents: the title (line 45) and the page
number (line 47). We make the line clickable by adding a GoTo action that jumps to a location
in the document based on a name.
Summarized: we mark a building block using a unique name. Internally, iText will map that name
with a specific position aka an explicit destination in the document. Because of this, you can use
the createGoTo() method passing that name as a parameter to create a link to that specific building
block. We will even be able to use that name outside of the PDF document, but lets take a look at
the UpdatePageRenderer before we do so.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
164
}
}
The entry object contains a title and a page number. That page number could be wrong if the title
is moved to the next page. We can only know if that happens when the title paragraph is rendered.
Only at that moment, a layout decision will be made. The easiest way to update the page number
in the entry object, is to override the layout() method as is done in line 11.
Link link1 = new Link("Strange Case of Dr. Jekyll and Mr. Hyde",
PdfAction.createGoToR(
new File(TOC_GoToNamed.DEST).getName(), 1, true));
Link link2 = new Link("table of contents",
PdfAction.createGoToR(
new File(TOC_GoToNamed.DEST).getName(), "toc", false));
Paragraph p = new Paragraph()
.add("Read the amazing horror story ")
.add(link1.setFontColor(Color.BLUE))
.add(" or, if you're too afraid to start reading the story, read the ")
.add(link2.setFontColor(Color.BLUE))
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-6#2571-c06e05_remotegoto.java
12
13
165
.add(".");
document.add(p);
In line 2 and 3, we use the createGoToR() method to create a link to a remote PDF document.
The first parameter is the name of the file we created in the previous example. We expect it
to be in the same directory as the file we refer from.
The second parameter is the page number; we want the link to jump to the first page.
The third parameter indicates that we want to open the document in a new PDF viewer
window.
In line 5 and 6, we use another createGoToR() method to create a link to a named destination in
another document.
The first parameter is the name of the file we created in the previous example.
The second parameter is the name we used when we added the paragraph "Table of
Contents".
The third parameter indicates that we want to open the document in the current PDF viewer
window.
There are many other variations of the createGoToR() method, but they are all similar to one of the
two methods that were just explained.
Talking about HTML: you can use JavaScript in a PDF file that is very similar to the JavaScript youd
use in HTML. Many methods, such as methods that communicate with a server, are restricted, but
you also have some extra methods that are specific to PDF. For instance: the JavaScript inside a PDF
file has access to an app object that offers some functionality to communicate with the PDF viewer.
166
JavaScript actions
We wont go into detail regarding the JavaScript functionality in PDF, but well create a simple PDF
that shows an alert when you click a link; see figure 6.6.
We create the Link that allows us to trigger this alert in the JavaScript example.
1
2
3
4
5
6
7
In the next example, well use the same action, and well make it follow by another action.
Chained actions
Weve already used several create() convenience methods in the PdfAction class; weve experimented with createURI(), createGoTo(), createGoToR() and so on. If you consult the API
documentation for the PdfAction class, youll find many more, such as createGoToE() to go to an
embedded PDF file, createLaunch() to launch an application. All of these other methods are out of
scope in the context of this tutorial, but well look at one more action example, the ChainedActions
example. It explains how to chain actions.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-6#2572-c06e06_javascript.java
http://itextsupport.com/apidocs/itext7/latest/com/itextpdf/kernel/pdf/action/PdfAction.html
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-6#2573-c06e07_chainedactions.java
1
2
3
4
5
6
7
8
9
167
In line 1, we create the same JavaScript action as in the previous example. We chain a remote GoTo
action to this JavaScript action using the next() method in line 2. Now when we click the word
"here", a Boo alert will be triggered first; then another PDF will open in a new window.
The createSubmitForm() method is one of the many PdfAction methods we didnt discuss.
We mention it here because of a common use case for the next() method. It is not unusual
to validate fields that were filled in manually before submitting a form. This validation
could be done using JavaScript. The submit action could be the last action in a validation
chain.
While we were talking about actions, we mentioned the concept of destinations a couple of times.
We also explained that links are actually annotations. In the next couple of examples, well spend
some more time on these concepts.
Destinations
The PdfDestination class is the abstract superclass of the PdfExplicitDestination, the PdfStringDestination, and the PdfNamedDestination class. The PdfExplicitDestination class can
be used to create a destination to a specific page, using specific coordinates if needed. PdfStringDestination and PdfNamedDestination can be used to create a named destination.
168
Using a PDF string as name is also the default way used by iText when you use the setDestination()
method. Well discover another way to create named destinations once we discuss bookmarks, but
first, well create a couple of explicit destinations in the ExplicitDestinations example.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PdfDestination jekyll =
PdfExplicitDestination.createFitH(1, 416);
PdfDestination hyde =
PdfExplicitDestination.createXYZ(1, 150, 516, 2);
PdfDestination jekyll2 =
PdfExplicitDestination.createFitR(2, 50, 380, 130, 440);
document.add(new Paragraph()
.add(new Link("Link to Dr. Jekyll", jekyll)));
document.add(new Paragraph()
.add(new Link("Link to Mr. Hyde", hyde)));
document.add(new Paragraph()
.add(new Link("Link to Dr. Jekyll on page 2", jekyll2)));
document.add(new Paragraph()
.setFixedPosition(50, 400, 80)
.add("Dr. Jekyll"));
document.add(new Paragraph()
.setFixedPosition(150, 500, 80)
.add("Mr. Hyde"));
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-6#2574-c06e08_explicitdestinations.java
169
20
21
22
document.add(new Paragraph()
.setFixedPosition(50, 400, 80)
.add("Dr. Jekyll on page 2"));
Parameters
Description
createFit()
createFitB()
170
Method
Parameters
Description
createFitH()
top
createFitBH()
top
createFitV()
left
createFitBV()
left
createXYZ()
createFitR()
171
So far, weve created Link objects either by passing a PdfAction object as a parameter, or a
PdfDestination. Both these methods create a PdfLinkAnnotation. We could have created that
PdfLinkAnnotation ourselves and we could have passed that annotation as a parameter. This allows
us to add some extra flavor to the link.
Link annotations
There are two links in the document shown in figure 6.7. One is underlined; the other is marked by
a rectangle.
This line and rectangle shown in this screen shot are not part of the actual content of the PDF
document. They werent drawn using a sequence of moveTo(), lineTo(), and stroke() methods.
They are part of the link annotation, and they are drawn by the PDF viewer that renders annotations
on top of the existing content.
Also, when you would click the annotation, you would see a specific behavior. When clicking the
first link, the colors would be inverted. When clicking the second link, youd have a push-down
effect. See the Annotation example.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-6#2575-c06e09_annotation.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
172
PdfAction js = PdfAction.createJavaScript("app.alert('Boo!');");
PdfAnnotation la1 = new PdfLinkAnnotation(new Rectangle(0, 0, 0, 0))
.setHighlightMode(PdfAnnotation.HIGHLIGHT_INVERT)
.setAction(js).setBorderStyle(PdfAnnotation.STYLE_UNDERLINE);
Link link1 = new Link("here", (PdfLinkAnnotation)la1);
document.add(new Paragraph()
.add("Click ")
.add(link1)
.add(" if you want to be scared."));
PdfAnnotation la2 = new PdfLinkAnnotation(new Rectangle(0, 0, 0, 0))
.setDestination(PdfExplicitDestination.createFit(2))
.setHighlightMode(PdfAnnotation.HIGHLIGHT_PUSH)
.setBorderStyle(PdfAnnotation.STYLE_INSET);
Link link2 = new Link("next page", (PdfLinkAnnotation)la2);
document.add(new Paragraph()
.add("Go to the ")
.add(link2)
.add(" if you're too scared."));
173
the document. We only see it when we open the bookmarks panel in our PDF viewer, and we can
use it to easily navigate the document by collapsing items in a tree structure.
This tree structure is called an outline tree. Each branch and leaf of this tree is an outline object.
In iText, we create these objects using the PdfOutline class. In the TOC_OutlinesNames example,
we use named destinations to jump to each chapter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
174
.setKeepWithNext(true)
.setDestination(name);
title = false;
document.add(p);
}
else {
p.setFirstLineIndent(36);
if (line.isEmpty()) {
p.setMarginBottom(12);
title = true;
}
else {
p.setMarginBottom(0);
}
document.add(p);
}
}
We initialize a PdfOutline object in line 6. We create a unique name for each chapter title in line 11.
We use this name in line 15 as a destination, and pass it to the createOutline() method to create a
PdfOutline that will link to the corresponding destination.
1
2
3
4
5
6
7
8
9
10
11
12
13
If the outline object passed to the createOutline() method is null, were at the very beginning
of our story. We get the root outline from the PdfDocument and we add an outline to this root
object with the first title we encounter. This is the title of our novel THE STRANGE CASE OF
DR. JEKYLL AND MR. HYDE. We want this PdfOutline to be the parent of all the other titles.
We use the makeDestination() method using a PdfString object. This is equivalent to creating a
PdfStringDestination using a String instance. We do more or less the same for the other titles.
175
When we create a destination using the setDestination() method, iText creates an XYZ destination
using the top-left coordinate of the corresponding building block and a zoom factor of 100%. This
creates the awkward effect that we no longer see the margin when we click on one of the bookmarks.
We can fix this by creating explicit destinations. See figure 6.9.
We remember from the previous table of contents example for which we used explicit destinations
that its easy to point to the wrong page. Once again, well use a renderer to make sure we link to
the correct page. See the TOC_OutlinesDestinations example.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
176
.setKeepWithNext(true);
title = false;
document.add(p);
}
else {
p.setFirstLineIndent(36);
if (line.isEmpty()) {
p.setMarginBottom(12);
title = true;
}
else {
p.setMarginBottom(0);
}
document.add(p);
}
}
This code snippet is shorter than the previous one because we dont have to create a name and we
dont have to set that name as a destination. The main difference is in the createOutline() method.
It now looks like this:
1
2
3
4
5
6
7
8
9
10
11
We use the first title we encounter (when outline == null) as the top-level outline in the outline
tree. We create an OutlineRenderer to add the links to the kids of this top-level outline.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
177
In this case, we override the draw() method. We create a PdfOutline object with the top-level outline
as parent (line 18), and we use the top y coordinate of the area occupied by the Paragraph as the top
parameter for an explicit destination that fits the page horizontally (line 14-17) as the destination
for that newly created outline (line 19).
If you study both examples carefully, youll discover that the top-level outline of the example using
named destination can be clicked to jump to the title of the novel. This isnt the case in the example in
which we create explicit destinations: we only created destinations for the titles of the chapters, not
for the title of the novel. The PdfOutline objects in an outline tree dont need to be real bookmarks.
They dont have to point to a destination on a specific page in the document. They can point to
nowhere; they can also be used to trigger an action. Well make one more bookmark example to
demonstrate this. Additionally, well change the color and style of the elements in the bookmarks
panel.
178
When we open the bookmark panel, we see an outline tree of which all first-level elements are titles
of a movie, cartoon or video. These outlines are the parent of two kids:
1. One shows Link to IMDB in bold and blue. When we click that outline, an URI action is
triggered that brings us to the corresponding web page.
2. The other reads as More info: in italic. It is closed by default, but when we open it, we see
information in different colors about the director, the country where the movie is produced,
and its release data.
None of these PdfOutline objects point to a location in the document. The Outlines example
shows how this outline tree was built.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-6#2578-c06e12_outlines.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
179
180
We add a first child outline with as title Link to IMDB (line 10). We change the color of this
title to blue (line 11) and bold (line 12). We add a URI action that jumps to the movie page for
that specific movie on IMDB (line 13-15).
We add a second child outline with as title More info: (line 16). By default all the outlines
we create are open; in this case, we want the outline to be closed (line 17). We change the
style to italic (line 18). Finally we add three children to this outline: the director (line 19) as
red text (line 20), the country (line 21) as magenta text (line 22), and the year (line 23) as dark
gray text (line 24).
Finally, we close the PdfDocument (line 26).
This example shows how you can easily create an outline tree with different branches, branches of
branches, and leaves. It also shows how you can change the color and style of an element in the
outline tree, and how you can change the open or closed status of each outline element.
Summary
This chapter was all about interactive elements that help us navigate through and between
documents. We started by experimenting with a series of actions:
We took a close look at destinations, and how to create them using one of the subclasses of the
abstract PdfDestination class.
After we learned that links are stored inside a PDF as annotations, we looked at some bookmark
examples. We learned how to create an outline tree, and we used the setDestination() method to
jump to a destination inside the document, the setAction() method to trigger an action, and none
of these to create an inert hierarchical entry in the outline tree.
We already saw a glimpse of the next chapter, when we changed the page mode to make sure the
bookmarks panel was opened when opening the document. Viewer preferences will be one of the
topics well discuss next, but first well learn more about the concept of event handling.
182
In the EventHandlers example, we create four A6 pages to which we add content as if the page
is in portrait. We change the rotation of the page at the page level in an IEventHandler. As defined
in the ISO standard for PDF, the rotation of a page needs to be a multiple of 90. This leaves us four
possible orientations when we divide the rotation by 360: portrait (rotation 0), landscape (rotation
90), inverted portrait (rotation 180) and seascape (rotation 270).
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-7#2579-c07e01_eventhandlers.java
1
2
3
4
public
public
public
public
static
static
static
static
final
final
final
final
PdfNumber
PdfNumber
PdfNumber
PdfNumber
183
The default orientation will be portrait (line 2), but we can change this default using the setRotation() method (line 4-6). We override the handleEvent() method that is triggered when an
event occurs. We can get the PdfPage instance of the page on which the event is triggered from the
PdfDocumentEvent. This PdfPage object represents the page dictionary. One of the possible entries
of a page dictionary, is its rotation. We change this entry to the current value of rotation (line 9)
every time the event is triggered.
The following snippet shows how we can introduce this event handler in the PDF creation process.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
184
document.add(new AreaBreak());
document.add(new Paragraph("Dr. Jekyll"));
eventHandler.setRotation(SEASCAPE);
document.add(new AreaBreak());
document.add(new Paragraph("Mr. Hyde"));
document.close();
}
pdf.addEventHandler(
PdfDocumentEvent.END_PAGE,
new TextWatermark());
This choice for the END_PAGE event has an impact on the TextWatermark class.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-7#2580-c07e02_textwatermark.java
185
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
186
We create color objects (line 2) and a font (line 3) in the constructor (line 4-8), so that we can reuse
these objects every time the event is triggered.
The PdfDocumentEvent (line 11) gives us access to the PdfDocument (line 12) and the PdfPage (line 13)
on which the event was triggered. We get the current page number (line 14) and the page size (line
15) from the PdfPage. In this example, we will add all the content using low-level PDF functionality.
We need a PdfCanvas object to do this (line 16-17). We draw the background using a rectangle()
and fill() method (line 18-22). For pages with page number greater than 1 (line 23), We create
187
a text object marked by beginText() and endText() with two snippets of text that are positioned
using the moveText() method and added with showText() method (line 24-30).
As we add this content after the current page has been completed and right before a
new page is created, we have to be careful not to overwrite already existing content. For
instance: to create a colored background, we draw an opaque rectangle. If we do this
after we have added content to the page, this content wont be visible anymore: it will
be covered by the opaque rectangle. We can solve this by creating the PdfCanvas using
the page.newContentStreamBefore() method. This will allow us to write PDF syntax to a
content stream that will be parsed before the rest of the content of the page is parsed.
In iText 5, we used page events to add content when a specific event occurred. It was
forbidden to add content in an onStartPage() event. One could only add content to a page
using the onEndPage() method. This often led to confusion among developers who assumed
that headers needed to be added in the onStartPage() method, whereas footers needed to
be added in the onEndPage() method. Although this was a misconception, we fixed this
problem anyway. Actually, in the case of this example, it would probably be a better idea to
add the back ground in the START_PAGE event. We can use page.getLastContentStream()
to create the content stream needed for the PdfCanvas object.
In the next example, well add a header using a START_PAGE event and a footer using an END_PAGE
event. The footer will show the page number as well as the total number of pages.
188
The event handlers in the PageXofY example are added like this:
1
2
3
4
5
Instead of using low-level PDF operators to create the text object, we use the showTextAligned()
method that was introduced when we talked about the Canvas object. See for instance the
handleEvent implementation of the Header class.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-7#2581-c07e03_pagexofy.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
189
This time, we use the getLastContentStream() method (line 14). As we use this class to create a
START_PAGE event, the header will be the first thing that is written in the total content stream of the
page.
The Page X of Y footer confronts us with a problem weve already solved once in chapter
2. In the JekyllHydeV8 example, we wanted to add the total number of pages of the
document on the first page. However, at the moment we wrote that first page, we didnt
know the total number of pages in advance. We used a placeholder instead of the final
number, and we instructed iText not to flush any content to the OutputStream until all
pages were created. At that moment, we used a TextRenderer to replace the place holder
with the total number of pages, and we recreated the layout using the relayout() method.
There is one major disadvantage with this approach: it requires that we keep a lot of
content in memory before we flush it to the OutputStream. The more pages, the more well
risk an OutOfMemoryException. We can solve this problem by using a PdfFormXObject as
placeholder.
A form XObject is a snippet of PDF syntax stored in a separate stream that is external to the content
stream of a page. It can be referred to from different pages. If we create one form XObject as a
placeholder, and we add it to multiple pages, we have to update it only once, and that change will
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-2#1902-c02e12_jekyllhydev8.java
190
be reflected on every page. We can update the content of a form XObject as long as it hasnt been
written to the OutputStream. This is what well do in the PageXofY class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
191
Lines 14 to 21 are identical to what we had in the Header class. We create the Page X of part of the
footer in line 21 and 22. We add this Paragraph to the left of the coordinates x and y (line 24). We add
the placeholder at the coordinates x + space and y - descent. We release the Canvas, but we dont
release the placeholder yet. Once the complete document is generated, we call the writeTotal()
method, right before we close the document.
1
2
3
document.add(div);
event.writeTotal(pdf);
document.close();
In this writeTotal() method, we add the total number of pages at the coordinate x = 0; y =
descent (line 30-31). This way, the Page X of Y text will always be nicely aligned with x and
y as the coordinate that has Page X of to the left and Y to the right.
192
13
14
15
16
17
18
19
20
21
22
23
193
Note that we store the Image object as a member-variable; this way, we can use it as many times we
want and the bytes of the image will be added to the PDF document only once.
Creating a new Image instance of the same image in the handleEvent would result in a
bloated PDF document. The same image bytes would be added to the document as many
times as there are pages. This was already explained in chapter 3.
We also reuse the PdfExtGState object. This is a graphics state object that is external to the content
stream. We use it to set the fill opacity to 20%.
In this example, we use a mix of PdfCanvas and Canvas. We use PdfCanvas to save, change, and
restore the graphics state. We use Canvas to add the image resized to the dimensions of the page.
In this example, we didnt want the background image to appear for the table of contents. See figure
7.5.
194
We achieve this by removing the event handler, right before we add the table of contents.
1
2
3
4
5
6
7
8
9
10
11
We can remove a specific handler, using the removeEventHandler() method. We can remove all
handlers using the removeAllHandlers() method. Thats what were going to do in the next example.
195
The AddRemovePages example uses the INSERT_PAGE event to add content to the inserted page,
and the REMOVE_PAGE method to write something to the System.out. At some point, we remove all
handlers.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
196
In this example, we have an AddPageHandler (line 18-28) and a RemovePageHandler (line 29-35). We
declare these handlers to the PdfDocument as INSERT_PAGE and REMOVE_PAGE event respectively (line
5-8). The AddPageHandler will be triggered only once, when we add a new page (line 9). The remove
page will be triggered four times. We remove all pages from page 9 to the total number of pages. We
do this by removing page 9 over and over again (line 12), until no pages are left. As soon as weve
removed page 12, we remove all handlers (line 13-14), which means that the event is triggered after
we removed pages 9, 10, 11, and 12.
In the next example, were going to define page labels.
Page labels
Figure 7.7 shows a document with 38 pages. In the toolbar above the document, Adobe Acrobat
shows that were on page i or page 1 of 38. We have opened the Page Thumbnails panel to see a
thumbnail for each page. We see that the first three pages are number i, ii, iii. Then we have 34 pages
numbered from 1 to 34. Finally, we have a page with page label TOC.
197
These page labels arent part of the actual content. For instance: you wont see them when you print
the document. They are only visible in the PDF viewer that is: if the PDF viewer supports page
labels. The PDF in figure 7.7 was created using the PageLabels example.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
198
.add("Table of Contents").setDestination("toc");
document.add(p);
page = pdf.getLastPage();
page.setPageLabel(null, "TOC", 1);
... // add table of contents
document.close();
We change the page label style three times in this code snippet:
1. We create the first page in line 2 and we set the page label style for this page to LOWERCASE_ROMAN_NUMERALS. We dont define a prefix. All the pages that follow this first page will be
numbered like this: i, ii, iii, iv, v, until we change the page label style. This happens in line 9.
2. In line 8, we get the last page that was added so far, and we change the page labels to DECIMAL_ARABIC_NUMERALS in line 9. Once more we dont define a prefix, and we tell the current page
to start the page count with 1. We didnt really have to do this, because the page count always
restart when you change the page labels. You can use this method if you dont want that to
happen. For instance: we could pass 4 instead of 1 if we want the first page that follows the
pages with Roman numerals to be page 4.
3. In line 17, we change the page label style to null. This means that no numbering will be used,
even if we pass a value for the first page after the page label change. In this case, we do pass
a prefix. Thats the page label we see when we reach the table of contents of our document.
The prefix can be combined with a page number, for instance if you have Arabic numerals for
page numbers and the prefix is X-, then the pages will be numbered as X-1, X-2, X-3,
and so on.
In this example, we had to manually open the Page Thumbnails panel to see the thumbnail overview
of all the pages. We could have instructed the document to open that panel by default. In the next
example, well change the page display and the page mode.
199
The PageLayoutPageMode example is identical to the previous example, except for the following
lines.
1
2
pdf.getCatalog().setPageLayout(PdfName.TwoColumnRight);
pdf.getCatalog().setPageMode(PdfName.UseThumbs);
We get the catalog from the PdfDocument. The catalog is also known as the root dictionary of the
PDF file. Its the first object that is read when a parser reads a PDF document.
We can set the page layout for the document with the setPageLayout() method using one of the
following parameters:
PdfName.SinglePage Display one page at a time.
PdfName.OneColumn Display the pages in one column.
PdfName.TwoColumnLeft Display the pages in two columns, with the odd-numbered pages
on the left.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-7#2585-c07e07_pagelayoutpagemode.java
200
PdfName.TwoColumnRight Display the pages in two columns, with the odd-numbered pages
on the right.
PdfName.TwoPageLeft Display the pages two at a time, with the odd-numbered pages on
the left.
PdfName.TwoPageRight Display the pages two at a time, with the odd-numbered pages on
the right.
We can set the page mode for the document with the setPageMode() method using one of the
following parameters
We havent discussed optional content yet, nor attachments. Thats something well save for another
tutorial.
When we use PdfName.FullScreen, the PDF will try to open in full screen mode. Many viewers
wont do this without showing a warning first.
The warning shown in figure 7.9 was triggered by the PDF created with the FullScreen example.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-7#2586-c07e08_fullscreen.java
1
2
3
4
5
201
pdf.getCatalog().setPageMode(PdfName.FullScreen);
PdfViewerPreferences preferences = new PdfViewerPreferences();
preferences.setNonFullScreenPageMode(
PdfViewerPreferencesConstants.USE_THUMBS);
pdf.getCatalog().setViewerPreferences(preferences);
In this example, we also create a PdfViewerPreferences instance (line 2). We set the viewer
preference that tells the viewer what to do when we exit full screen mode. The possible values
for the setNonFullScreenPageMode() are:
PdfViewerPreferencesConstants.USE_NONE No panel is opened when we return from full
screen mode.
PdfViewerPreferencesConstants.USE_OUTLINES The bookmarks panel is visible, showing
the outline tree.
PdfViewerPreferencesConstants.USE_THUMBS A panel with pages visualized as thumbnails
is visible.
PdfViewerPreferencesConstants.USE_OC The panel with the optional content structure is
open.
We used PdfViewerPreferencesConstants.USE_THUMBS which means that we see the PDF as shown
in figure 7.10.
202
Lets take a look at some other viewer preferences that are available in the PDF specification.
Viewer preferences
When we open the PDF shown in figure 7.11, we dont see a menu bar, we dont see a tool bar, we
see the title of the document in the top bar, and so on.
203
The ViewerPreferences example shows us which viewer preferences have been set for this
document.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
204
All of these preferences expect true or false as a parameter; false being the default value if no
preference is chosen.
Line 4: with setFitWindow(), we tell the viewer to resize the documents window to fit the
size of the first displayed page.
Line 5: with setHideMenubar(), we tell the viewer to hide the menu bar; that is the bar with
menu items such as File, Edit, View,
Line 6: with setHideToolbar(), we tell the viewer to hide the tool bar; that is the bar with the
icons that give us direct access to some features also available through the menu items.
Line 7: with setHideWindowUI(), we tell the viewer to hide all user interface elements such
as scroll bars and other navigational controls.
Line 8: with setCenterWindow(), we tell the viewer to position the documents window in the
center of the screen.
Line 9: with setDisplayDocTitle(), we tell the viewer to show the title of the document in
the title bar.
Setting the title in the title bar requires that we define a title in the metadata. We do this in line
11-12. Well have a closer look at metadata in a moment.
You can also use the PdfViewerPreferences class to define the predominant reading order of text
using the setDirection() method, the view area using the setViewArea() and setViewClip()
method. We wont do that in this tutorial, well skip to some printer preferences.
Printer preferences
The mechanism of viewer preferences can also be used to set some printer preferences. For
instance: we can select the area that will be printed by default using the setPrintArea() and
the setPrintClip() method. Specific printer settings can be selected using the setDuplex() and
setPickTrayByPDFSize() method. You can select a default page range that needs to be printed using
the setPrintPageRange() method.
Figure 7.12 shows the default settings in the Print Dialog after using the setPrintScaling() and
setNumCopies() method.
205
The values in the screen shot correspond with the code of the PrinterPreferences example.
1
2
3
4
5
Although PDF viewers nowadays offer many print-scaling options, the PDF specification only
allows you to choose between NONE (no print scaling; the actual size of the document is preserved)
and APP_DEFAULT (the default scaling of the viewer application). We set the number of copies to 5,
which is reflected in the Print Dialog.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-7#2588-c07e10_printerpreferences.java
206
As you can see, setting a printer preference doesnt enforce the preference that is chosen.
For instance, if we set the number of copies to 5, a user can easily change this to any
other number in the dialog. In PDF 2.0, an extra viewer preference, named /Enforce will
be introduced. Its value will be an array. A PDF 2.0 viewer should check the entries and
enforce all the viewer preferences that are present in that array. The Draft specification only
provides a way to enforce the print scaling so far. We havent implemented this /Enforce
preference in iText yet, but well do so as soon as the PDF 2.0 standard aka ISO-32000-2 is
released.
Once in a while, we get the question if its possible to set a viewer preference to open a document
at a certain page. Its possible to jump to a specific page when opening a document, but that isnt
achieved using a viewer preference. We need an open action to do this.
Figure 7.13: Document opens on last page and page says goodbye when we leave it
When we go to the first page, the document shows another alert: This is where it starts!
Finally, when we close the document, it says: Thank you for reading.
207
208
Figure 7.15: The document says Thank you for reading upon closing it
The action that jumps to the last page is an open action; all the other actions are additional actions
with respect to events triggered on the document or on a page. The Actions example shows us
what its all about.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
209
Lets start with the open action (line 3-4). This action is added to the catalog using the setOpenAction() method. This method accepts an instance of the PdfDestination class in this case a link to
a named destination or of the PdfAction class.
The next action is an additional action for the document (line 5-6). This action is also added to the
catalog, using the setAdditionalAction() method. The second parameter has to be a PdfAction
object. The first parameter is one of the following names:
PdfName.WC which stands for Will Close. This action will be performed right before closing
a document.
PdfName.WS which stands for Will Save. This action will be performed right before saving a
document. Note that this will only work for viewers that allow you to save a document; and
that save isnt the same as save as in this context.
PdfName.DS which stands for Did Save. This action will be performed right after saving a
document. Note that this will only work for viewers that allow you to save a document; and
that save isnt the same as save as in this context.
PdfName.WP which stands for Will Print. This action will be performed right before printing
a document.
PdfName.DP which stands for Did Print. This action will be performed right after printing a
document.
The next two additional actions are actions that are added to a PdfPage object (line 7-8; line 11-12).
The parameters of this setAdditionalAction() method are again an instance of the PdfAction class
as the second parameter, but the first parameter has to be one of the following names:
PdfName.O the action will be performed when the page is opened, for instance when a user
navigates to it from the next or previous page, or by clicking a link. If this page is the first
page that opens when opening a document, and if theres also an open action, the open action
will be triggered first.
PdfName.C the action will be performed when the page is closed, for instance when a user
navigates away from it by going to the next or previous page, or by clicking a link that moves
away from this page.
There are more types of additional actions, especially in the context of interactive forms. Those
actions are out of the scope of this tutorial and will be discussed in a tutorial about forms. Well
finish this chapter by looking at some writer properties.
Writer properties
In one of the previous examples, we added some metadata to a PdfDocumentInfo object. We obtained
this object from the PdfDocument using the getDocumentInfo() method. This PdfDocumentInfo
210
object corresponds with the info dictionary of the PDF; thats a dictionary containing metadata
in the form of key-value pairs. This is how metadata was originally stored inside a PDF, but soon
it turned out that it was a better idea to store metadata as XML inside a PDF file. This is shown in
figure 7.16.
The XML is added as an uncompressed stream, which allows software that doesnt understand PDF
syntax to extract the XML stream anyway and interpret it. The format of the XML is defined in the
eXtensible Metadata Platform (XMP) standard. This standard allows much more flexibility than a
simple key-value pair dictionary.
XMP metadata
When you create a document, you can create your own XMP metadata using the XMPMeta class and
then add this metadata to the PdfDocument by passing it as a parameter with the setXmpMetadata()
method. But you can also ask iText to create the metadata automatically, based on the entries in the
info dictionary. Thats what we did in the Metadata example.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-7#2590-c07e12_metadata.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
211
We create a WriterProperties object (line 4) that is used as a second parameter of the PdfWriter
class (line 3). We use the addXmpMetadata() method (line 5) to instruct iText to create an XMP stream
based on the metadata added to the PdfDocumentInfo object: the title (line 8), the author (line 9), the
subject (line 10), the keywords (line 11), and the creator application (line 12). The producer, creation
time and modification time are set automatically. You cant change them.
iText 5 generated PDF 1.4 files by default. In some cases, this version was changed automatically when you used specific functionality, For instance: when using full compression,
the version was changed to PDF 1.5. Full compression means that the cross-reference table
and possibly some indirect objects will be compressed. That wasnt possible in PDF 1.4.
iText 7 creates PDF 1.7 files (ISO-32000-1) by default. In the previous example, we changed
the version to PDF 1.6 using the setPdfVersion() method on the WriterProperties.
Compression
In one of the event handler examples, we created a document with an image as background. The
size of this PDF was 134 KB. In figure 7.17, you see another version of a document with the exact
same content. The size of that PDF is only 125 KB.
212
This difference in size is caused by the way some content is stored inside the PDF. For small
files, without many objects, the effect of full compression wont be significant. All content streams
with PDF syntax are compressed by default by iText. Starting with PDF 1.5, more objects can be
compressed, but that doesnt always make sense. A fully compressed file can count more bytes than
an ordinary PDF 1.4 file if the PDF consists of only a dozen objects. The effect is more significant
the more objects are needed in the PDF. If you plan to create large PDF files with many pages and
many objects, you should take a look at the Compressed example.
1
2
Once again, we use the WriterProperties object, now in combination with the setFullCompressionMode() method. Theres also a setCompressionLevel() method that allows you to set a
compression level ranging from 0 (best speed) to 9 (best compression), or you can set it to the default
value -1.
Well conclude this chapter with a small encryption example.
Encryption
There are two ways to encrypt a PDF file. You can encrypt a PDF file using the public key of a
public/private key pair. In that case, the PDF can only be viewed by the person who has access to
the corresponding private key. This is very secure.
Using passwords is another way to encrypt a file. You can define two passwords for a PDF file: an
owner password and a user password.
If a PDF is encrypted with an owner password, the PDF can be opened and viewed without that
password, but some permissions can be in place. In theory, only the person who knows the owner
password can change the permissions.
The concept of protecting a document using only an owner password is flawed. Many tools,
including iText, can remove an owner password if theres no user password in place.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-7#2591-c07e13_compressed.java
213
If a PDF is also encrypted with a user password, the PDF cant be opened without a password. Figure
7.18 shows what happens when you try to open such a file.
The document only opens when we pass one of the two passwords, the user password in which case
the permissions will be in place, or the owner password in which case we can change the permissions.
As we can see in the Encrypted example, the passwords and the permissions were defined using
WriterProperties.
http://developers.itextpdf.com/content/itext-7-building-blocks/examples/chapter-7#2592-c07e14_encrypted.java
1
2
3
4
5
6
7
214
We define a user password and an owner password as a byte array (line 1-2). Those are the first
two parameters of the setStandardEncryption() method. The third parameter can be used to
define permissions. In our example, we allow printing and document assembly that is: splitting
and merging. Finally, we define the encryption algorithm: AES 256.
The possible values for the permissions are:
You can also add one of the following extra parameters to the encryption algorithm using an or
(|) operation:
215
DO_NOT_ENCRYPT_METADATA if you want to avoid that the metadata will also be encrypted.
Encrypting the metadata doesnt make sense if you want the metadata to be accessible for
your document management system, but be aware that this option is ignored when using
40-bit ARC encryption.
EMBEDDED_FILES_ONLY if you want to encrypt the embedded files only, and not the actual
PDF document. For instance: if the PDF document itself is a wrapper for other documents,
such as a cover note explaining that its not possible to open the document without having
the right credentials at hand immediately. In this case the PDF is an unencrypted wrapper for
encrypted documents.
All of these parameters can also be used for the setPublicKeyEncryption() method, in which case
the first parameter is an array of Certificate objects, the second parameter an array with the
corresponding permissions, and the third parameter the encryption mode that is: the encryption
algorithm and the option to not encrypt the metadata and to encrypt only the embedded files.
With this last example, we have given away the punch line of The Strange Case of Dr. Jekyll and
Mr. Hyde: Dr. Jekyll has a secret: he changes into Mr. Hyde. But you probably already knew that
after all the Jekyll and Hyde examples weve made in this book.
Summary
In this final chapter of the iText 7: Building Blocks, we have covered the IEventHandler functionality that allows us to take action when specific events such as starting, ending, inserting, and
removing a page occur. We looked at viewer preferences that allowed us to tell PDF viewers how
to present the document when we open it. We could also use the mechanism of viewer preferences
to set some printer preferences. Finally, we looked at some writer properties. We discussed these
properties in the context of metadata, compression, and encryption.
Weve covered a lot of ground in this tutorial. You should now have a clear understanding of the basic
building blocks that are available when you want to create a document from scratch. You also know
how to establish document interactivity (actions) and navigation (links, destinations, bookmarks).
You know the mechanism to handle events, and you can set viewer preferences and writer properties.
Youre all set to create some really cool PDFs.
Obviously, this is not the definitive guide on iText. In future tutorials, well also take a look under
the hood, examining the PDF syntax that is used to create the content stream of a page and the
structure of a PDF document. Well spend a tutorial on forms, how to create them and how to
use forms as templates. Well create a tutorial on manipulating existing documents by adding and
removing content. Well also update the old tutorial on digital signatures. We dont have an ETA on
those tutorials, but make sure to check our Books page on a regular basis.
THE END
http://developers.itextpdf.com/books
Appendix
A: AbstractElement methods
Method
addStyle()
setHyphenation()
setSplitCharacters()
setHorizontalAlignment()
setTextAlignment()
setHeight()
setWidth()
setWidthPercent()
setBorder()
setBorderLeft()
setBorderRight()
setBorderTop()
setBorderBottom()
setBackgroundColor()
setFont()
setFontSize()
setFontColor()
setBold()
setItalic()
setLineThrough()
setUnderline()
setTextRenderingMode()
setStrokeColor()
setStrokeWidth()
setCharacterSpacing()
setWordSpacing()
setFontKerning()
setFontScript()
setBaseDirection()
setRelativePosition()
setFixedPosition()
setAction()
setDestination()
Text / Link
Image
Tab
AreaBreak
Yes
Yes
Yes
No
No
No
No
No
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Typ
Typ
Typ
Yes
No
Yes
Yes
Yes
No
No
Yes
No
Yes
Yes
Yes
Yes*
Yes*
Yes*
Yes*
Yes*
Yes*
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
Yes
Yes
Yes
Yes
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
No
217
Appendix
In the Image column, some methods have an asterisk next to Yes. The asterisk means that you may
not notice that the method works because the image isnt transparent. For instance: it doesnt make
sense to set a background color for an opaque image: the image covers the background completely.
The same is true for borders.
B: BlockElement methods
Method
Paragraph
addStyle()
Yes
setHyphenation()
Yes
setSplitCharacters()
Yes
setHorizontalAlignment() Yes
setVerticalAlignment() No
setRotationAngle()
Yes
setTextAlignment()
Yes
setKeepTogether()
Yes
setKeepWithNext()
Yes*
setHeight()
Yes
setWidth()
Yes
setWidthPercent()
Yes
setMargin()
Yes
setMargins()
Yes
setMarginLeft()
Yes
setMarginRight()
Yes
setMarginTop()
Yes
setMarginBottom()
Yes
setPadding()
Yes
setPaddings()
Yes
setPaddingLeft()
Yes
setPaddingRight()
Yes
setPaddingTop()
Yes
setPaddingBottom()
Yes
setBorder()
Yes
setBorderLeft()
Yes
setBorderRight()
Yes
setBorderTop()
Yes
setBorderBottom()
Yes
setBackgroundColor()
Yes
setFont()
Yes
setFontSize()
Yes
setFontColor()
Yes
setBold()
Yes
setItalic()
Yes
Div
List
Table
Cell
LineSeparator
Yes
Yes
Yes
Yes
No
Yes
Yes
Yes
Yes*
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
No
Yes
Yes
Yes
Yes*
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
No
No
Yes
Yes
Yes*
No
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
No
No
No
No
No
No
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
No*
Yes
Yes
Yes
Yes
No*
Yes
No*
No*
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes**
Yes**
Yes**
Yes**
Yes**
Yes
Yes
Yes
Yes
Yes
Yes
Yes
No
No
Yes
No
No
No
No
Yes*
No
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
No
No
No
No
No
No
No
No
No
No
No
Yes
No
No
No
No
No
218
Appendix
Method
Paragraph
Div
List
Table
Cell
LineSeparator
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Typ
Typ
Typ
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Typ
Typ
Typ
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Typ
Typ
Typ
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Typ
Typ
Typ
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Typ
Typ
Typ
Yes
Yes
Yes
Yes
No
No
No
No
No
No
No
No
No
No
No
No
No
Yes
Yes
setLineThrough()
setUnderline()
setTextRenderingMode()
setStrokeColor()
setStrokeWidth()
setSpacingRatio()
setCharacterSpacing()
setWordSpacing()
setFontKerning()
setFontScript()
setBaseDirection()
setRelativePosition()
setFixedPosition()
setAction()
setDestination()
Theres an asterisk added to the Yes value of setKeepWithNext() because this method only works
for objects added directly to the Document. It wont work for nested objects.
Theres an asterisk added to the No value for some methods of the Cell method because these
methods cant be used when the Cell is part of a Table. But you can also use a Cell outside the
context of a Table. In that case, you can define the width and the horizontal alignment. There are
two asterisks next to the Yes for the border methods of the Cell. Borders are drawn at the level of
the Table. Changing the border of a Cell changes a line in the grid of the Table, but you cant use
the setBorder() methods if you are using a Cell outside a Table.
C: RootElement methods
Method
setHyphenation()
setSplitCharacters()
setHorizontalAlignment()
setTextAlignment()
setHeight()
setWidth()
setWidthPercent()
setBorder()
setBorderLeft()
setBorderRight()
setBorderTop()
setBorderBottom()
setBackgroundColor()
Document
Canvas
Yes
Yes
No
Yes
No
No
No
No
No
No
No
No
No
Yes
Yes
No
Yes
No
No
No
No
No
No
No
No
No
219
Appendix
Method
setFont()
setFontSize()
setFontColor()
setBold()
setItalic()
setLineThrough()
setUnderline()
setTextRenderingMode()
setStrokeColor()
setStrokeWidth()
setCharacterSpacing()
setWordSpacing()
setFontKerning()
setFontScript()
setBaseDirection()
setRelativePosition()
setFixedPosition()
setDestination()
showTextAligned()
showTextAlignedKerned()
add(BlockElement)
add(Image)
add(AreaBreak)
setMargins()
setLeftMargin()
setRightMargin()
setTopMargin()
setBottomMargin()
Document
Canvas
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Typ
Typ
Typ
No
No
No
Yes
Typ
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Yes
Typ
Typ
Typ
No
No
No
Yes
Typ
Yes
Yes
No
No
No
No
No
No