ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Web Server Java -- Servlets and JSP
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11

Generating PDF from a Servlet

Problem:
You want to make a printer-friendly document using a format like Adobe PDF.

Solution:
Use response.setContentType("application/pdf") and a third-party Java API that can generate PDF.

Discussion

Portable Document Format (PDF) is a file format created by Adobe Systems Inc. PDF gives you full control over how your document looks, much more so than HTML, XML, or even Java's printing routines (see Chapter 12). Adobe Acrobat is a set of programs for reading and writing PDF. Adobe itself does not publish a Java API for generating PDF from scratch, but it does publish the file format specification (Adobe Portable File Format Specification) and explicitly gives everyone permission to write software to generate and/or process PDF files. PDF is a good fit for processing by an object-oriented language like Java, as it's an object-based text format. As a result, there are several PDF APIs available for Java, both free and commercial:

  • Sitraka/KL Group (http://www.klg.com/) has a PDF API as well as charting and other widgets, and JProbe, a leading tuning tool.

  • StyleWriterEE (see http://www.inetsoftcorp.com/).

  • PDFLib GmbH (http://www.pdflib.com/pdflib/) produces PDFLib. PDFLib is mostly in C, with a Java wrapper; it also has bindings for several other popular languages.The source code is distributed, making it very cross-platform. It's free for noncommercial use; for commercial use, a small licensing fee is required.

  • ReportLab (http://www.reportlab.org/) is not for Java yet; it's entirely written in Python. You could probably use it within JPython. Watch for the possibility of future versions, though.

  • Finally, since I couldn't decide which of these alternatives to use, I just went ahead and wrote my own, SPDF.

Go to http://www.pdfzone.com/ and look in the Toolbox section for others.

Like Perl, SPDF has several names. Perl on a good day is the Practical Extraction and Report Language, but on a bad day it's the Purely Eclectic Rubbish Lister. A true geek has to admire that kind of whimsy. SPDF can be the Simple PDF API, but it can also be the Stupid PDF API. Mostly the latter, I fear. Example 18-8 is a simple servlet that takes the user's name from the HTML form in Figure 18-6 and generates a custom-made shopping coupon with the customer's info imprinted into it, and a unique serial number (for which a Date object provides a cheap stand-in here) to prevent multiple uses of the coupon. I suspect there is a real window of opportunity for such coupons in conjunction with online web sites and large discount retail stores. Unfortunately, I'm too busy writing this book to exploit this marvelous opportunity, so I'll just release the source code to SPDF. If you get rich from it, send me some of the money, OK?

Screen shot.
Figure 18-6. PDF coupon servlet.

I'm not showing the source code for SPDF in this book, as the present version is pretty crude. No font support. No graphics. Single-page documents. It may be released, however; check out http://www.darwinsys.com/freeware/spdf.html if you're interested. Think of SPDF as the Standby PDF API, which you can use while you decide which of the other PDF APIs you really want to use.

When you click on the "Get Yours" button, the servlet is run, generating a PDF file and sending it back to the browser. My Unix version of Netscape tries to save it to disk since I don't have Acrobat loaded; the filename MyCoupon.pdf is provided by the Content-disposition header that I added to the response object. See Figure 18-7.

Screen shot.
Figure 18-7. PDF coupon servlet save dialog.

My test MS-Windows system's copy of Netscape has Acrobat installed, and will run Acrobat as a Netscape Plug-in to display it; see Figure 18-8.

Screen shot.
Figure 18-8. PDF coupon in Acrobat Reader.

The basic SPDF API uses a PDF object to represent one PDF file. The PDF object has methods to set various things, to add pages to the object (and Page has methods to add text strings, moveTo operations, and others), and finally to write the file. Example 18-8 is the servlet that responds to the coupon request shown in Figure 18-6.


Example 18-8: PDFCouponServlet.java

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.darwinsys.spdf.*;
 
/** Simple PDF-based Coupon Printer Servlet
*/
public class PDFCouponServlet extends HttpServlet {
  public void doGet(HttpServletRequest request,
    HttpServletResponse response) throws IOException {
 
    PrintWriter out = response.getWriter(  );
    response.setContentType("application/pdf");
 
    // Tell browser to try to display inline, but if not,
    // to save under the given filename.
    response.setHeader("Content-disposition",
      "inline; filename=\"MyCoupon.pdf\"");
 
    PDF p = new PDF(out);
    Page p1 = new Page(p);
    p1.add(new MoveTo(p, 100, 600));
    p1.add(new Text(p,
      "This coupon good for one free coffee in the student lounge."));
    String name = request.getParameter("name");
    if (name == null)
      name = "unknown user";
    p1.add(new Text(p,
      "Printed for the exclusive use of " + name));
    p1.add(new Text(p,
      "by Ian Darwin's PDFCoupon Servlet and DarwinSys SPDF software"));
    p1.add(new Text(p, "at " + new Date().toString(  )));
    p.add(p1);
    p.setAuthor("Ian F. Darwin");
 
    // Write the PDF file page
    p.writePDF(  );
  }
}


Most of the Java PDF APIs are roughly similar. Example 18-9 is the Terms servlet rewritten using PDFLib to generate a fancier PDF document with the same information as the HTML version.


Example 18-9: TermsServletPDF.java

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import com.pdflib.*;
 
/** Output the dictionary in fancy(?) PDF.
* This version uses "PDFlib", from PDFLib.GmbH (www.pdflib.com).
*/
public class TermsServletPDF extends HttpServlet {
  /** A printwriter for getting the response. */
  PrintWriter out;
 
  /** Handle the get request. */
  public void doGet(HttpServletRequest request,
    HttpServletResponse response) throws ServletException {
 
    try {
 
      out = new PrintWriter(response.getOutputStream( ));
 
      int font;
      pdflib p = new pdflib( );
 
      if (p.open_file("") == -1) {
        warning(response, "Couldn't create in-memory PDF file", null);
        return;
      }
 
      p.set_info("Title", "Dictionary Project");
      p.set_info("Author", "Ian F. Darwin, ian@darwinsys.com");
      p.set_info("Creator", "www.darwinsys.com/dictionary");
 
      p.begin_page(595, 842);
 
      font = p.findfont("Helvetica", "host", 0);
 
      p.setfont(font, 14);
 
      // for now just use one term from the Iterator
      Iterator e = new TermsAccessor("terms.txt").iterator( );
      Term t = (Term)e.next( );
      p.set_text_pos(50, 700);
      p.show("Term: ");
      p.continueText(t.term);
      p.set_text_pos(70, 666);
      p.show("Definition: ");
      p.continueText(t.definition);
      p.end_page( );
 
      p.close( );
 
      byte[] data = p.get_buffer( );
 
      response.setContentType("application/pdf");
      response.getOutputStream( ).write(data);
    } catch (IOException e) {
      warning(response, "pdflib IO error:", e);
      return;
    } catch (Exception e) {
      warning(response, "pdflib error:", e);
    }
}


The end of the servlet in Example 18-10 demonstrates a way to provide a user-friendly wrapper around the occasional exception traceback. Method warning( ) can also be used to print a generic error message without a traceback by passing null as the exception argument.


Example 18-10: TermsServletPDF error handling

  /** Generic error handler. Must call before any use of "out" */
  protected void warning(HttpServletResponse response,
    String error, Exception e) {
    response.setContentType("text/html");
    try {
      PrintWriter out = response.getWriter( );
    } catch (IOException exc) {
      // egad - we can't tell the user a thing!
      System.err.println("EGAD! IO error " + exc +

" trying to tell user about " + error + " " + e);
      return;
    }
   out.println("<H1>Error</h2>");
    out.print("<P>Oh dear. You seem to have run across an error in ");
    out.print("our dictionary formatter. We apologize for the inconvenience");
    out.print("<P>Error message is ");
    out.println(error);
 
    if (e != null) {
      out.print("<P>Exception is: ");
      out.println(e.toString( ));
      out.print("Traceback is: ");
      out.print("<PRE>");
      e.printStackTrace(out);
      out.print("</PRE>");
    }
    System.out.print("DictionaryServletPDF: ");
    System.out.println(error);
    if (e != null) {
      System.out.println(e.toString( ));
    }
  }
}

Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11

Next Pagearrow