Thursday, November 15, 2012

PdfBox LandScape Printing Problem and Solution

I tried to use Apache's PdfBox library to silently print documents from my applet.
No problem printing in "Portrait", as it was my printer's default setting...however pdfs containing Landscape pages wouldn't  print properly. The thing is that the orientation is always defined by the printer service and not by the Document itself.
It seems that there is a "problem" with PDPageable class, specifically with it's getPageFormat method.
So here is my temporary fix for this problem (temporary because I hope it will be fixed in following versions).
I must mention that my solution is based on Roberto Mazzola 's  findings and proposition
(see https://issues.apache.org/jira/browse/PDFBOX-985 for details)

Version I used : PdfBox-1.7.1 

Solution:
...
import org.apache.pdfbox.pdmodel.PDPageable;
...
public static class MyPDPageable extends PDPageable {
       PrinterJob myJob;
       PDDocument myDocument;
          public MyPDPageable() throws PrinterException{
              super(null);
             
          }
         
    public MyPDPageable(PDDocument document) throws PrinterException{
              super(document);
              this.myDocument = document;
     }
    public MyPDPageable(PDDocument document,PrinterJob job) throws PrinterException{
              super(document,job);
              this.myJob = job;
              this.myDocument = document;
    }
      @Override
      public PageFormat getPageFormat(int i) throws IndexOutOfBoundsException {
        PageFormat format = myJob.defaultPage();
        List<PDPage> allPages = myDocument.getDocumentCatalog().getAllPages();
        PDPage page = allPages.get(i); // can throw IOOBE
        Dimension media = page.findMediaBox().createDimension();
        Dimension crop = page.findCropBox().createDimension();
        // Center the ImageableArea if the crop is smaller than the media
        double diffWidth = 0.0;
        double diffHeight = 0.0;
        if (!media.equals(crop)) {
            diffWidth = (media.getWidth() - crop.getWidth()) / 2.0;
            diffHeight = (media.getHeight() - crop.getHeight()) / 2.0;
        }
        int vOrientation = PageFormat.PORTRAIT;
         if(media.getWidth() > media.getHeight())
                 vOrientation = PageFormat.LANDSCAPE;
       
        format.setOrientation(vOrientation);
        Paper paper = format.getPaper();
        if (vOrientation == PageFormat.LANDSCAPE) {
           paper.setImageableArea(
                   diffHeight, diffWidth, crop.getHeight(), crop.getWidth());
           paper.setSize(media.getHeight(), media.getWidth());
        } else {
           paper.setImageableArea(
                   diffWidth, diffHeight, crop.getWidth(), crop.getHeight());
           paper.setSize(media.getWidth(), media.getHeight());
        }
        format.setPaper(paper);
        return format;
    }    
      }
and then if I wanted to print
.... 
PDDocument doc = PDDocument.load(psStream,true);
 if (printService != null) {             
            PrinterJob pj =  PrinterJob.getPrinterJob();
            pj.defaultPage();
            pj.setCopies(Integer.parseInt(finalnumberOfCopies));
            pj.setPrintService(printService);
            pj.setPageable(new MyPDPageable(doc,pj));

            pj.print();
}
....
note that I don't use doc.silentPrint(pj) but pj.print() ...

It is a quick and dirty fix, obviously code could use some improvements..but it works for me...
Hope it will be useful and to somebody else..

6 comments:

  1. Replies
    1. thx ! I got a similar problem with pdfbox 1.8 and your solution helped me!

      Jeff

      Delete
    2. U r welcome Jeff! I am glad my post helped. Thanks for leaving a comment.

      Delete
  2. PDDocument doc = PDDocument.load(new URL(pdfUrl).openStream());
    doc.silentPrint(job);
    this worked forme when i try to run applet directly,
    when i try to integrate this code with html page it's doesn't work , is ther any solutions ??

    ReplyDelete
    Replies
    1. Hi there ... anonymous ;-)
      I think that in your case the problem could reside in the security restrictions of an applet. Try using signed applet to avoid such restrictions.

      Delete