Generating PDF with iText

The following section discusses how to use the iText library to generate PDF.

Installing iText

iText is simply a class library, so there's no need to configure the runtime as with Velocity or XMLC. However, we will need to include the binaries in WARs using it. The /lib/runtime/itext-pdf directory of our sample app includes the single JAR file required - iText.jar. Again, the sample app's Ant build script will copy iText.jar to the /WEB-INF/lib directory of WAR distribution units it builds.

Implementing the View Interface for PDF Generation with iText

As with XMLC, we'll need one implementation of our View interface for each view that generates PDF. The basic steps involved in generating PDF using iText involve getting a com.lowagie.text.pdf.PdfWriter instance, adding document content objects to it, and closing the PdfWriter to output the content:

 PdfWriter.getInstance(document, response.getOutputStream());;
 // Output model data

However, this simple approach won't work with Internet Explorer, which needs to know the content length of the generated PDF. Hence in a web app we must use the more verbose code shown below to perform the same basic steps. We also need to give the requested URL a .pdf extension. Although the AbstractPdfView sets the content type to app/pdf, which should be sufficient, Internet Explorer doesn't always respect this unless we change the extension.


The use of a .pdf extension means that we must also remember to map the relevant .pdf URL to our controller servlet. Failure to define all necessary mappings in the web.xml deployment descriptor is a common cause of frustration with any MVC framework. If the framework's controller servlet never gets a request in the first place, it may be unclear where the problem lies.

Again, the framework uses the Template Method design pattern, providing a convenient superclass for iText PDF views. The com.interface21.web.servlet.view.pdf.AbstractPdfView abstract class takes care of creating an iText document and writing the complete document to the response in a final implementation of the renderMergedOutputModel() method. It conceals from app code the complexity of generating the content length and dealing with Internet Explorer's failure to honor the returned content type. Note that we also set the page size:

 protected final void renderMergedOutputModel(Map model,
 HttpServletRequest request, HttpServletResponse response)
 throws ServletException, IOException {
 Document document = new Document(PageSize.A4);
 try {
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 PdfWriter writer = PdfWriter.getInstance(document, baos);
 PdfWriter.AllowPrinting |

Now we've created a new document, we need to invoke app code to write content to it. The following line invokes a protected template method whose definition we'll see below:

 buildPdfDocument(model, document, request, response);

Finally, we output the data to the HttpServletResponse, taking care to set the content length using the Servlet API:

 document. close() ;
 response. setContentLength (baos. size());
 ServletOutputStream out = response. getOutputStream();
 baos. writeTo(out) ;
 out. flush();
 catch (DocumentException ex) {
 throw new ServletException("Error creating PDF document", ex);

Subclasses of AbstractPdfView must implement the following protected abstract method to write model data to the iText PDF Document passed as the second parameter. The request and response objects won't normally be used, but we include them in case the view needs locale information or needs to write a cookie:

 protected abstract void buildPdfDocument (Map model,
 Document pdfDoc,
 HttpServletRequest request,
 HttpServletResponse response)
 throws DocumentException;

Defining PDF Views for Use in an app

All we need to do to define a PDF view is to specify the app-specific subclass in /WEB-INF/classes/, as follows:

 showReservation. class=
 com. wrox. expertj2ee. ticket. web. pdfviews. ShowReservationViewExample