title: Java printing: creating a PageFormat with minimum acceptable margin
body: <p>I'm trying to get the PageFormat correct when I print. Below is an example program that shows my dilemma: I get a different result when I use <code>printJob.setPrintable(printable)</code> than when I use <code>printJob.setPageable(book)</code> when I create a <code>Book</code> object using the default PageFormat from the Print job.</p>
  
  <p>When I run it, and click "Print", then "Print using Book", I see this console output:</p>
  
  <pre><code>doPrint(false)
  printing on 612.000000x792.000000 paper, imageable area=588.960000x768.960000
  printing on 612.000000x792.000000 paper, imageable area=588.960000x768.960000
  printing on 612.000000x792.000000 paper, imageable area=588.960000x768.960000
  doPrint(true)
  printing on 612.000000x792.000000 paper, imageable area=468.000000x648.000000
  printing on 612.000000x792.000000 paper, imageable area=468.000000x648.000000
  </code></pre>
  
  <p>What gives? The default page format when using <code>Book</code> sucks and uses 1" margin on each side of the page; the "real" page format only needs about 1/6" margin on each side.</p>
  
  <p>example program here:</p>
  
  <pre><code>package com.example.printing;
  
  import java.awt.BasicStroke;
  import java.awt.Dimension;
  import java.awt.Graphics;
  import java.awt.Graphics2D;
  import java.awt.event.ActionEvent;
  import java.awt.geom.Rectangle2D;
  import java.awt.print.Book;
  import java.awt.print.PageFormat;
  import java.awt.print.Paper;
  import java.awt.print.Printable;
  import java.awt.print.PrinterException;
  import java.awt.print.PrinterJob;
  
  import javax.swing.AbstractAction;
  import javax.swing.BoxLayout;
  import javax.swing.JButton;
  import javax.swing.JFrame;
  import javax.swing.JPanel;
  
  public class PrintRectangles extends JFrame 
  {
      static final int Nrectangles = 3;
  
      static class RectangleThingy extends JPanel implements Printable
      {
          @Override public int print(Graphics graphics, 
                  PageFormat pageFormat, 
                  int pageIndex)
              throws PrinterException 
          {
              describePageFormat(pageFormat);
              if (pageIndex &gt; 0) {
                  return(NO_SUCH_PAGE);
              } 
              else {
                  Graphics2D g2d = (Graphics2D)graphics;
                  g2d.translate(pageFormat.getImageableX(), 
                          pageFormat.getImageableY());
                  double w = pageFormat.getImageableWidth();
                  double h = pageFormat.getImageableHeight();
                  final int N = (Nrectangles - 1) / 2;
                  final double spacing = 7.2; // 1/10 inch 
                  g2d.setStroke(new BasicStroke(0.1f));
                  for (int i = -N; i &lt;= N; ++i)
                  {
                      double dx = i*spacing;
                      Rectangle2D r = new Rectangle2D.Double(
                              dx, dx, w-2*dx, h-2*dx
                              );
                      g2d.draw(r);
                  }
                  Rectangle2D rthick = new Rectangle2D.Double(
                          0, 0, w, h
                          );
                  g2d.setStroke(new BasicStroke(1.0f));
                  g2d.draw(rthick);
                  return(PAGE_EXISTS);
              }
          }
  
          private void describePageFormat(PageFormat pageFormat) {
              System.out.println(String.format("printing on %fx%f paper, imageable area=%fx%f",
                      pageFormat.getWidth(),
                      pageFormat.getHeight(),
                      pageFormat.getImageableWidth(),
                      pageFormat.getImageableHeight()
                      ));
  
          }
      }
  
      static private class PrintPreviewPanel extends JPanel
      {
          final private Printable p;
          final private PageFormat pageFormat;
          public PrintPreviewPanel(Printable p, PageFormat pf)
          {
              this.p = p;
              this.pageFormat = pf;
          }
          @Override public Dimension getPreferredSize() {
              return new Dimension((int)this.pageFormat.getWidth(), 
                      (int)this.pageFormat.getHeight());
          }
          @Override protected void paintComponent(Graphics g) {
              super.paintComponent(g);
              try {
                  p.print(g, this.pageFormat, 0);
              } catch (PrinterException e) {
                  e.printStackTrace();
              }
          }       
      }
  
      public PrintRectangles(String title) {
          super(title);
          JPanel panel = new JPanel();
          setContentPane(panel);
          panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
          JButton printButton = new JButton("Print");
          JButton printUsingBookButton = new JButton("Print using Book");
          JButton printPreviewButton = new JButton("Print preview");
          panel.add(printButton);
          panel.add(printUsingBookButton);
          panel.add(printPreviewButton);
          printButton.addActionListener(new AbstractAction("print") {
              @Override public void actionPerformed(ActionEvent e) {
                  doPrint(false);
              }           
          });
          printUsingBookButton.addActionListener(new AbstractAction("printUsingBook") {
              @Override public void actionPerformed(ActionEvent e) {
                  doPrint(true);
              }           
          });
          printPreviewButton.addActionListener(new AbstractAction("printPreview") {
              @Override public void actionPerformed(ActionEvent e) {
                  doPrintPreview();
              }           
          });
      }
  
      protected void doPrint(boolean useBook) {
          RectangleThingy rectangleThingy = new RectangleThingy();
          System.out.println("doPrint("+useBook+")");
          try
          {
              PrinterJob printJob = PrinterJob.getPrinterJob();
              PageFormat pageFormat = printJob.getPageFormat(null);
              if (useBook)
              {
                  Book book = new Book();
                  book.append(rectangleThingy, pageFormat);               
                  printJob.setPageable(book);
              }
              else
              {
                  printJob.setPrintable(rectangleThingy);             
              }
              if (printJob.printDialog())
                  printJob.print();
          } 
          catch(PrinterException pe) {
              System.out.println("Error printing: " + pe);
          }
      }
  
      protected void doPrintPreview() {
          RectangleThingy rt = new RectangleThingy();
          JFrame frame = new JFrame("print preview");
  
          // hack for now -- how do we get this from the printer?
          Paper paper = new Paper();
          double dotsperinch = 72;
          double margin = 0.125*dotsperinch;
          double w = 8.5*dotsperinch;
          double h = 11*dotsperinch;
          paper.setImageableArea(margin, margin, w-2*margin, h-2*margin);
          paper.setSize(w, h);
          PageFormat pfmt = new PageFormat();
          pfmt.setPaper(paper);
          frame.setContentPane(new PrintPreviewPanel(rt, pfmt));
          frame.pack();
          frame.setVisible(true);
      }
  
      public static void main(String[] args) {
          new PrintRectangles("PrintRectangles").start();
      }
  
      private void start() {
          setDefaultCloseOperation(EXIT_ON_CLOSE);
          pack();
          setVisible(true);
      }
  }
  </code></pre>