Open source Java projects: SwingLabs PDF Renderer

View and render PDF content from your Java programs

1 2 3 4 5 Page 4
Page 4 of 5

Application example: PDFViewer

User space
The coordinates of the various graphics objects that contribute to a page in a PDF document are specified in a device-independent coordinate system known as user space. This is analogous to working with the various methods in the java.awt.Graphics2D class, where coordinates are also specified in user space. Prior to version 1.6 of the PDF specification, each unit along a page's x and y axes had a length of 1/72 inch. Although version 1.6 and later versions let you specify a different default length by defining a UserUnit entry in a document's page dictionary, I assume that documents adhere to a 1/72 inch unit length in this article (for simplicity).

Listing 3 presents the source code to an application that uses getBBox() with the second getImage() method to render an arbitrary page from an arbitrary document to an Image, which is subsequently displayed on the screen.

Listing 3. PDFViewer.java

// PDFViewer.java

import java.awt.*;
import java.awt.geom.*;

import java.io.*;

import java.nio.*;
import java.nio.channels.*;

import javax.swing.*;

import com.sun.pdfview.*;

public class PDFViewer extends JFrame
{
   static Image image;

   public PDFViewer (String title)
   {
      super (title);
      setDefaultCloseOperation (EXIT_ON_CLOSE);

      JLabel label = new JLabel (new ImageIcon (image));
      label.setVerticalAlignment (JLabel.TOP);

      setContentPane (new JScrollPane (label));

      pack ();
      setVisible (true);
   }

   public static void main (final String [] args) throws IOException
   {
      if (args.length < 1 || args.length > 2)
      {
          System.err.println ("usage: java PDFViewer pdfspec [pagenum]");
          return;
      }

      int pagenum = (args.length == 1) ? 1 : Integer.parseInt (args [1]);
      if (pagenum < 1)
          pagenum = 1;

      RandomAccessFile raf = new RandomAccessFile (new File (args [0]), "r");
      FileChannel fc = raf.getChannel ();
      ByteBuffer buf = fc.map (FileChannel.MapMode.READ_ONLY, 0, fc.size ());
      PDFFile pdfFile = new PDFFile (buf);

      int numpages = pdfFile.getNumPages ();
      System.out.println ("Number of pages = "+numpages);
      if (pagenum > numpages)
          pagenum = numpages;

      PDFPage page = pdfFile.getPage (pagenum);
              
      Rectangle2D r2d = page.getBBox ();

      double width = r2d.getWidth ();
      double height = r2d.getHeight ();
      width /= 72.0;
      height /= 72.0;
      int res = Toolkit.getDefaultToolkit ().getScreenResolution ();
      width *= res;
      height *= res;

      image = page.getImage ((int) width, (int) height, r2d, null, true, true);

      Runnable r = new Runnable ()
                   {
                       public void run ()
                       {
                          new PDFViewer ("PDF Viewer: "+args [0]);
                       }
                   };
      EventQueue.invokeLater (r);
   }
}

PDFViewer takes one or two command-line arguments. The first argument is the name of a PDF document, and the optional second argument is the number of the page to render -- the page number defaults to 1 if the second argument isn't present. If you specify a page number less than 1, the number is clamped to 1. Similarly, if this number exceeds the number of document pages, this number is clamped to the page count.

After retrieving the appropriate PDFPage object via getPage(), PDFViewer retrieves the page's bounding box via getBBox(). It converts the box's width and height from user-space coordinates to equivalent screen device-space coordinates. This width and height, along with the bounding box are passed to getImage() to render the entire page.

Invoke the following command to compile PDFViewer.java:

javac -cp PDFRenderer-2008_05_18.jar PDFViewer.java

Assuming a Windows platform, invoke the following command to try out PDFViewer with Adobe's pdf_reference.pdf document -- this document contains the pdf specification:

java -cp PDFRenderer-2008_05_18.jar;. PDFViewer pdf_reference.pdf

This command line tells PDFViewer to render this document's first page (by default) -- specify a page number after pdf_reference.pdf to render another page. Figure 2 reveals the rendering result.

PDF Viewer renders a page at the same 100% zoom level as rendered by Adobe Acrobat.
Figure 2. PDF Viewer renders a page at the same 100% zoom level as rendered by Adobe Acrobat. (Click to enlarge.)

Render to arbitrary graphics contexts

PDF Renderer makes it possible to render page content to an arbitrary graphics context (perhaps to a printer graphics context). Accomplish this task with the help of the com.sun.pdfview.PDFRenderer class and its public PDFRenderer(PDFPage page, Graphics2D g, Rectangle imgbounds, Rectangle2D clip, Color bgColor) constructor:

  • page identifies the PDF content to render
  • g identifies the graphics context onto which the PDF content will be rendered
  • imgbounds identifies the pixel dimensions of the rendered content
  • clip identifies that portion (in user space) of the page to render -- pass null if you want to render the entire page
  • bgColor identifies the color of the rendered content's background -- pass null if you don't want to render a background

After creating a PDFRenderer instance, invoke its inherited public void run() method to perform the rendering operation. If the PDFPage is being obtained on a background thread, you'll need to invoke PDFPage's public void waitForFinish() method before invoking run(), to ensure that the page is completely loaded prior to rendering.

1 2 3 4 5 Page 4
Page 4 of 5