Print a PDF Document in Java

For some time now I have searched for a way to print a PDF document from Java code. Up to now, I have had to shell out and invoke Acrobat from the command line but this hack has been painful to support in the field because of the multiple version of Acrobat and its quirks.

A new PDF Renderer project has recently been released on java.net which can in addition to rendering and viewing a PDF document, it can be used to print a PDF document. If you download the PDF Renderer you can run the jar to start a sample PDF viewer application which can print PDF documents. All the code listed below is inspired from the PDF Renderer sample application.

The following code uses classes from the PDF Renderer library and standard Java. Like usual, I have omitted all import statements.

// Create a PDFFile from a File reference
File f = new File("c:\\juixe\\techknow.pdf");
FileInputStream fis = new FileInputStream(f);
FileChannel fc = fis.getChannel();
ByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
PDFFile pdfFile = new PDFFile(bb); // Create PDF Print Page
PDFPrintPage pages = new PDFPrintPage(pdfFile);

// Create Print Job
PrinterJob pjob = PrinterJob.getPrinterJob();
PageFormat pf = PrinterJob.getPrinterJob().defaultPage();
pjob.setJobName(f.getName());
Book book = new Book();
book.append(pages, pf, pdfFile.getNumPages());
pjob.setPageable(book);

// Send print job to default printer
pjob.print();

One critical class required to print a PDF document using PDF Renderer is the PDFPrintPage. The PDFPrintPage implements the Printable print method. The PDFPrintPage is included in the PDFRenderer.jar, but in the source distribution it is found under the demos project. I have included the source to PDFPrintPage print method here…

class PDFPrintPage implements Printable {
  private PDFFile file;
  PDFPrintPage(PDFFile file) {
    this.file = file;
  }

  public int print(Graphics g, PageFormat format, int index) 
    throws PrinterException {
    int pagenum = index + 1;

    // don't bother if the page number is out of range.
    if ((pagenum >= 1) && (pagenum < = file.getNumPages())) {
      // fit the PDFPage into the printing area
      Graphics2D g2 = (Graphics2D)g;
      PDFPage page = file.getPage(pagenum);
      double pwidth = format.getImageableWidth();
      double pheight = format.getImageableHeight();

      double aspect = page.getAspectRatio();
      double paperaspect = pwidth / pheight;

      Rectangle imgbounds;

      if (aspect>paperaspect) {
        // paper is too tall / pdfpage is too wide
        int height= (int)(pwidth / aspect);
        imgbounds= new Rectangle(
          (int)format.getImageableX(),
          (int)(format.getImageableY() + ((pheight - height) / 2)),
          (int)pwidth,
          height
        );
      } else {
        // paper is too wide / pdfpage is too tall
        int width = (int)(pheight * aspect);
        imgbounds = new Rectangle(
          (int)(format.getImageableX() + ((pwidth - width) / 2)),
          (int)format.getImageableY(),
          width,
          (int)pheight
        );
      }

      // render the page
      PDFRenderer pgs = new PDFRenderer(page, g2, imgbounds, null, null);
      try {
        page.waitForFinish();
        pgs.run();
      } catch (InterruptedException ie) {}

      return PAGE_EXISTS;
    } else {
      return NO_SUCH_PAGE;
    }
  }
}

A final note, PDF Renderer does require Java 1.5 or greater.