iText Tutorial SourceForge.net Logo iText, a Free Java-PDF library
by
Bruno Lowagie
[Home] [Previous] [TOC] [Next] [PDF]

Part III: Advanced iText

Chapter 10: Absolute Positioning of Graphics and Text


PdfContentByte
Uptil now, we have used Simple iText, we have added text and images, chapters and sections, lists and tables,... without bothering about layout. iText took care of dividing the text into pages and positioning every word, sentence, paragraph on a page. But sometimes we don't want this automatic formatting. Sometimes we want to put some graphic or some text at some exact position on a page. To achieve this, we are going to use the class PdfContentByte.

Remark:
the class PdfContentByte can only be used in combination with one PdfWriter object. It can't be used for HTML or any other format.

So instead of what was said in Chapter 1, it's not sufficient to invoke the method getInstance on class PdfWriter, you must actually have a PdfWriter-object. You can obtain this object by using the method getDirectContent() on this writer-object.
Example:
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("test.pdf"));
PdfContentByte cb = writer.getDirectContent();


Remark:
When you add high level objects, such as Table, 2 different PdfContentByte-objects are used internally: one for text and one for graphics (for instance the borders or the background of a cell). The text is drawn on top of the graphics.
When you use a PdfContentByte-object directly using the getDirectContent()-method, everything you add to this object, is written on top of the text and graphics. If you want to avoid this and if you want to add content under the internal graphics and text PdfContentByte-objects, you have to use the method getDirectContentUnder().
Summarized: when a page is finished, 4 layers are drawn on top of eachother in this order:
  1. the PdfContentByte you can retrieve and with the method getDirectContentUnder()
  2. the internal PdfContentByte-object that contains the graphics of high level objects
  3. the internal PdfContentByte-object that contains the text of high level objects
  4. the PdfContentByte you can retrieve and with the method getDirectContent()


Simple Graphics
In example 1, some simple graphics are drawn. We use methods such as moveTo and lineTo to move to a certain position on the page and draw a line to another position. We use methods such as setLineWith and setLineDash to alter the appearance of the line.
Example:
cb.setLineWidth(10f);
cb.moveTo(100, 700);
cb.lineTo(200, 800);
cb.stroke();


Remark:
When you change the properties such as color, linewidth,... these changes will been taken into account on the moment you call one of the stroke-methods. In the example with the triangle, we set the color to green, but before we stroke the triangle we change it to red. The resulting triangle will be red, not green.

As you can see in the resulting PDF some other methods such as rectangle and circle are used. Please consult the welldocumented API of class PdfContentByte and the PDF Reference manual for more info.


Using the java.awt.Graphics2D-object
Allthough the previous section was called 'Simple Graphics', you need to know how PDF graphics are constructed. But maybe you're not really in to PDF; maybe you're more familiar with JAVA. No problem! You can create a PdfTemplate (see the section on Templates) and ask this template to create a java.awt.Graphics2D-object:

PdfContentByte cb = writer.getDirectContent();
PdfTemplate tp = cb.createTemplate(w, h);
Graphics2D g2 = tp.createGraphics(w, h, mapper);

In the Graphics2D-example (see also resulting PDF), the code of an example in SUN's Graphics2D tutorial was copied literally. As you can see, everything that was written to the Graphics2D-object was translated to PDF syntax. This is especially useful if you have existing applications generating graphics for the screen, printer, or some image file. You can have your existing code generating PDF with very little effort.
Remark: You can also write directly to the page, without using a PdfTemplate:

PdfContentByte cb = writer.getDirectContent();
cb.saveState();
cb.concatCTM(1, 0, 0, 1, 50, 400);
Graphics 2D g2 = cb.createGraphics(500, 300);
// draw things to g2
g2.dispose();
cb.restoreState();


Text
When you want to write text to the contentbyte, you have to use the methods beginText() and endText(). You also have to set the font and size. As in the graphics example, there are lots of methods that can be used to write and position text, but the ones you will need the most are the method showTextAligned and the method showText in combination with setTextMatrix.
Example 1:
BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);  cb.beginText();
cb.setFontAndSize(bf, 12);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, text + "This text is centered", 250, 700, 0);
cb.endText();

Example 2:
BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
cb.beginText();
cb.setFontAndSize(bf, 12);
cb.setTextMatrix(100, 400);
cb.showText("Text at position 100,400.");
cb.endText();

Please take a look at example 2 and its resulting PDF.


Templates (Form XObjects)
When we discussed the HeaderFooter object in Chapter 4, we defined a piece of information that was added on each page. In fact, this piece of information was written to the file on every new page. This is not a very economic solution. It is better to add this piece of information only once to the document (as a form XObject) and repeat only its visualization. To achieve this, we are going to use templates.

Creation of a PdfTemplate
The best way to create a PdfTemplate, is to invoke the method createTemplate on the PdfContentByte-object:
PdfTemplate template = cb.createTemplate(500, 200);
In this case, the width of the template is 500, the height 200.
With this template, we can do the same things as we did with the PdfContentByte.
template.moveTo(0, 200);
template.lineTo(500, 0);
template.stroke();
template.beginText();
BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
template.setFontAndSize(bf, 12);
template.setTextMatrix(100, 100);
template.showText("Text at the position 100,100 (relative to the template!)");
template.endText();


Adding a template to the document
You can add a template at an absolute position like this:
cb.addTemplate(template, 0, 400);
But you can also do some funny things with Templates, such as rotate or scale them:
// rotate the template 90 degrees
cb.addTemplate(template, 0, 1, -1, 0, 500, 200);
// scale the template to 50%
cb.addTemplate(template, .5f, 0, 0, .5f, 100, 400);
// scale the template to 200%
cb.addTemplate(template, 2, 0, 0, 2, -200, 400);

This is demonstrated in example 3 (see Chap1003.pdf).

The Transformation Matrix:
If you want to translate, scale or rotate images or text, you need to use a Transformation Matrix. In the code you are asked for the transformation matrix parameters a, b, c, d, e and f. But what do these parameters exactly mean?
This is the Transformation Matrix:
[ a b 0 ]
c d 0
e f 1
With the parameters e and f, you can specify a translation. The following matrix moves everything e pixels in x-direction and f pixels in y-direction.
[ 1 0 0 ]
0 1 0
e f 1
You can use the a and d parameter to scale. The following matrix doubles everything in x-direction and triples everything in y-direction:
[ 2 0 0 ]
0 3 0
0 0 1
If you want to rotate something, you have to change a, b, c and d. With angle equals to the rotation angle in radians, you have a matrix like this:
[ Math.cos(angle) Math.sin(angle) 0 ]
-Math.sin(angle) Math.cos(angle) 0
0 0 1
There is one serious caveat when you rotate an object: the coordinate of the rotation pivot is (0, 0). If you rotate something, you have to watch out that it is not rotated 'off' your page. you may have to perform a translation to keep the object on the page. Of course you can combine translation (tX, tY), scaling (sX, sY) rotation (angle) in one matrix:
[ sX * Math.cos(angle) sY * Math.sin(angle) 0 ]
-sX * Math.sin(angle) sY * Math.cos(angle) 0
tX tY 1
So you will have to use these parameters:
  • a = sX * Math.cos(angle);
  • b = sY * Math.sin(angle);
  • c = -sX * Math.sin(angle);
  • d = sY * Math.cos(angle)
  • e = tX;
  • f = tY;


Page x of y
In some cases, you may want to add information that is not available yet at the moment you write a certain page to the outputstream. For instance: on the first page of a document, you don't know how many pages the document will count in total. You only know the total number of pages once you have finished generating the entire document. This is not a problem when you use templates. In example 3, we added information to the template before we added the template to the contentbyte. This is not necessary. We can add information to a template at any moment because iText adds the form XObjects at the end of the PDF file (when the close-method of the document is called).
Example 4 shows how 4 pages are created first and how the total number of pages is added afterwards (Chap1004). This example is very simple and not very useful rightnow, but in Chapter 12, we will refine this example.


Columns
Earlier in this chapter, you have learned how to put text on an absolute position. In casu: we were able to enter a coordinate where iText had to start writing text. If we wanted to know the position where the text ended, we had to do some calculations.
Now we want to add some text within a given rectangle. We want the text to wrap automatically when the right limit of the rectangle is reached. All text that doesn't fit in the rectangle isn't shown. We can achieve this using class ColumnText.

An example:
To show a certain phrase, centered in a rectangle between the coordinates (100, 300) and (200, 500), we write some code like this:
PdfContentByte cb = writer.getDirectContent();
ColumnText ct = new ColumnText(cb);
ct.setSimpleColumn(phrase, 60, 300, 100, 500, 15, Element.ALIGN_CENTER);
ct.go();

If you take a look at example 5 (Chap1005.pdf), you immediately notice that this functionality can be used to draw some complex tables without using the Table-object.

Another example:
It isn't necessary to add the text all at once. You can first define the rectangle, then add some text and finally show the column with the go-method:
PdfContentByte cb = writer.getDirectContent();
ColumnText ct = new ColumnText(cb);
ct.setSimpleColumn(60, 300, 100, 500, 15, Element.ALIGN_CENTER);
ct.addText(phrase1);
ct.addText(phrase2);
ct.addText(phrase3);
ct.go();

This is demonstrated in example 6; the resulting PDF of this example is identical to the result of example 5.

Multiple columns
Of course, if there is more text than space in the rectangle, we don't want to loose the text that didn't fit. Maybe we want this text to be shown in another column. That's why we are going to take a look at the return value of the go-method. If this return value has the flag 'NO_MORE_COLUMN' turned on, there wasn't enough room for the text in the column. If all the text was shown, the flag 'NO_MORE_TEXT' is on.
Please take a close look at example 7 and see how we obtain a result like this.

Irregular columns
It is also possible to define an non rectangular area with to visualize columns. With the method setColumns, we can define a left and right boundary for the text:
float[] left = {70,790, 70,60};
float[] right = {300,790, 300,700, 240,700, 240,590, 300,590, 300,106, 270,60};
ct.setColumns(left, right);

The left border is a straight line, but the right border is irregular. The effect of this functionality can lead to very nice layouts: see Chap1008.pdf and the code that was used to generate this example.


PdfPTable (by Paulo Soares)
We already briefly mentioned the PdfPTable object in Chapter 5. Now we are going to discuss some more features of this object.
You can create a PdfPTable in 3 different ways:

PdfPTable(float[] relativeWidths);
PdfPTable(int numColumns);
PdfPTable(PdfPTable table);


You can set some parameters for this table, such as the width of the table, widths of the columns, the horizontal alignment,... You can add cells with these methods:

public void addCell(PdfPCell cell);
public void addCell(PdfPTable table);
public void addCell(Phrase phrase);
public void addCell(String text);


This is all very similar to the Table-object, except for cellpadding and cellspacing. These parameters are set on the level of each individual cell. Of course you can define the default behaviour of a Cell; to change the defaults of a cell, just use the method getDefaultCell() and invoke one or more methods of class PdfPCell on it (you can set alignments, padding, borders, colors, even a minimum height).
Important remark: With PdfPTable you can change the colspan of a Cell, but you can't change the rowspan! Internally a PdfPTable is a stack of independent rows. Supporting rowspan would involve a massive restructuring of the class PdfPTable. It is not expected that this will happen in the near future. You can work around this problem by using nested tables.

You can add a PdfPTable to a document as we did in Chapter 5, but you can also add the table on an absolute position of the current page:

public float writeSelectedRows(int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte canvas);

The parameter rowStart is the number of the row you want to start with, rowEnd is the last row you want to show (if you want to show all the rows, use -1). xPos and yPos are the coordinates of the table and canvas is the PdfContentByte-object.
In example 9 (Chap1009), we add a simple table at position (100, 600):

table.writeSelectedRows(0, -1, 100, 600, writer.getDirectContent());

With PdfPTables, you can't set a rowspan and/or colspan. You can work around this by using nested tables. See example 10 (Chap1010.pdf).

Finally example 11 and example 12 show you how PdfPTable can be used in combination with templates and columns (Chap1011.pdf and Chap1012.pdf).


SpotColors and Patterns (by Phillip Pan)
The use of spotcolors is demonstrated in example 13 (Chap1013.pdf). example 14 (Chap1014.pdf) and 15 (Chap1015.pdf) demonstrate the use of patterns. This will be documented in the future.


[Top] [Previous] [TOC] [Next] [PDF]
Page Updated: $Date: 2003/06/25 07:36:35 $
Copyright © 2000, 2001 by Bruno Lowagie
Adolf Baeyensstraat 121, 9040 Gent, BELGIUM,
tel +00 32 92 28 10 97 mailto:itext-questions@lists.sourceforge.net