Wednesday, June 30, 2010

iText - Internal Hyperlinks with PdfContentByte

What do you do if you're using PdfContentBytes for maximum pdf content control and want to create an internal hyperlink?

Lets imagine we add one line of text to a document. It will be positioned 200pt below the top of the page and indented 70pt from the left side of the document. I'm going to assume you know all this already and this is by no means the complete code.

float yPos = reader.getPageSize(1).getHeight() - 200;
float xPos = 70.0f;
...
PdfContentByte cb = writer.getDirectContent();
...
cb.showTextAligned(PdfContentByte.ALIGN_LEFT, "This is some text", xPos,yPos, 0);
...

This might look something like this:



Now we want to be able to click on that text and jump to another page within the document. We can use the "setAction" method to achieve this.


This works by overlaying an interactive rectangle over the text (or whatever else) where the user can click and be taken to a defined destination. This destination can be within the same document, as we are interested in here, or in an external pdf file.

//go to page one
cb.setAction(PdfAction.gotoLocalPage(1, new PdfDestination(PdfDestination.FIT,0), writer), xPos, yPos, reader.getPageSize(1).getWidth() - 70, yPos+15);

setAction() defines the action to be taken (gotoLocalPage) and overlay an interactive rectangle whose bottom corner starts at (xPos, yPos) and whose top right corner sits 70pts away from the right side of the document and 15pts above the bottom of the text. (Note that the text size is 15.0 so this is a good height to choose for the overlay).


PdfAction.gotoLocalPage() specifies the pages to go to, which part of the page to show on the screen after jumping and the writer to be used. In this example we go to page 1.

We use a new PdfDestination to specify which part of the page to show after the jump. PdfDestination.FIT shows the page with its contents sized to fit the document window horizontally and vertically.

It's worth mentioning what the other PdfDestination view options are.

FIT = Fits the document window horizontally and vertically
FITB = Fits the bounding box of the contents (no margins)
FITH = Fits horizontally, with a definable parameter for the vertical coordinate of the page's top edge
FITBH = Same as above but fits the bounding box i.e no margins
FITV = Fits the document window vertically, with a definable parameter for the horizontal coordinate of the left edge of the page
FITBV = Same as above but fits the bounding box

This is very important - make sure to use PdfDestination.XYZ,-1,-1,0 if you want to keep the view etc the same when clicking on links. It will break and be off by one page otherwise :/ Depending on the view you are using - continuous, show one page.

In-depth iText information can be found in Bruno Lowagie's excellent book iText In Actionhttp://www.manning.com/lowagie2/




Saturday, June 26, 2010

iText - Opening a Document

Before manipulating a pdf document you have to "open" it first.

Document document = new Document();
document.open();

It is very important to do any changes to page size or margins etc. BEFORE opening the document. Otherwise, the defaults will be used for the first page and any alterations will only be effective from the second page onwards.

//Correct
Document document = new Document(PageSize.A5, 32 ,76 ,100, 200);
document.open();

//Wrong
Document document = new Document();
document.open();
document.setMargins(32, 76, 100, 200);

The reason for this is many document types keep meta data including page setup and version information in the file header which is created before the document itself.

The meta data of a PDF document can be set by using the accessor methods within the Document class. This includes:

  • addTitle("Title")
  • addSubject("Subject")
  • addKeywords("Metadata, iText")
  • addCreator("Lilpinkthings Program")
  • addAuthor("Lilpinkthing")
  • addHeader("ValidFrom, 0")

Don't change the producer and creation date meta values. They will tell you what version of iText was used to create the file if support is needed later on. These values must be set before opening the document.

It is possible also possible to tell a PdfWriter which pdf version to use - the default is 1.4.

Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("Output.pdf");
writer.setPdfVersion(PdfWriter.VERSION_1_6);
document.open();

Again this MUST be called before opening the document, because it goes into the header file.

This gives us 6 basic steps of PDF creation which look like this:
  1. Create a Document
  2. Create a PdfWriter and associate the Document and an OutputStream with it
  3. Set all meta data, version and page setup information
  4. Open the Document
  5. Add content
  6. Close the document
Code Example:
Document document = new Document(); //step 1
PdfWriter.getInstance(document, new FileOutputStream("Output.pdf") ); //step 2
document.addKeywords("hello, world, pdf, iText"); //step 3
document.addAuthor("lilpinkthing"); //step 3
document.open(); //step 4
document.add(new Paragraph("Hello there")); //step 5
document.close(); //step 6

The document.close() line here is VERY important. If you forget to add this you will probably get some crazy error similar to "There was an error opening this document. The file is damaged and could not be repaired." Don't panic, just close the document.

In-depth iText information can be found in Bruno Lowagie's excellent book iText In Action http://www.manning.com/lowagie2/




iText - Choosing a Writer

Allegedly there are several classes derived from the abstract DocWriter class which can be used for different kinds of output files - pdf, rtf and html. However, using iText 5.0.2 it seems that both the .rft and the .html options have been removed. So let's focus on pdf creation for now.

You can either write a document to file, memory or the output stream (e.g. stdout).

Document document = new Document();

// write to file
PdfWriter.getInstance(document, new FileOutputStream("Output.pdf");

//write to memory
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
PdfWriter.getInstance(document, buffer);

//write to System.out
PdfWriter.getInstance(document, System.out);

Note that when using the file output or System.out the document is outputted directly but with the ByteArrayOutputStream it is buffered for use at a later date. To retrieve it, the reader needs to be passed the ByteArrayOutputStream cast as a ByteArray.

//read from memory
PdfReader reader = new PdfReader(buffer.toByteArray());

This is extremely useful when you need to do multiple passes on a pdf document. For example, you may want to read in multiple pdfs and write page numbers for the new combined document on each page. The pdfs can be read in, merged together and stored in memory then the whole new document can be manipulated and annotated. This saves writing an intermediary file to disk.

In-depth iText information can be found in Bruno Lowagie's excellent book iText In Action http://www.manning.com/lowagie2/

iText - Page Setup

First things first. There are 72 points to 1 inch. This means that a document with a page size of 216pt x 720pt is actually 3 inches wide and 10 inches in height.

The lower-left corner of the page is used as the origin of the coordinate system when manipulating margins, rectangles etc.

Page sizes for documents can be defined using the Rectangle class.

Rectangle pagesize = new Rectangle(216f, 720f);
Document document = new Document(pagesize);

When using a Rectangle for page size, it allows the background color of the pages to be specified.

pagesize.setBackgroundColor(new BaseColor(0x64, 0x95, 0xed); //using rectangle from before

Be careful here. The signature for setBackgroundColor expects a BaseColor, from com.itextpdf.text.BaseColor not a Color from java.awt.Color.

Page sizes can also be set by specifying one of the static constants using the PageSize class.

Document document = new Document(PageSize.LETTER);

In addition, the pagesize object can be used to make the pages landscape.

PageSize.Letter.rotate(); or
Document document = new Document(pagesize.rotate()); //using the rectangle from before

PageSize can also be used to define the margins of the page. The margins are left, right, top and bottom respectively in the signature.

PageSize.A5, 36, 72, 108, 180) // 36 left, 72 right, 108 top, 180 bottom

Margin mirroring can be called on the document to mirror the margins on odd and even pages.

Document document = new Document(PageSize.A5, 36, 72, 108, 180) ;
document.setMarginMirroring(true);


In-depth iText information can be found in Bruno Lowagie's excellent book iText In Action http://www.manning.com/lowagie2/