Printing in Java

I recently added the ability to print from one of my Java Swing apps. Overall it was similar to the OS/2 print system: The system gives you an abstract "graphics" space whose dimensions correspond to the printed page. You scribble in it as you like, then submit it for printing. The system handles everything else.

There was one annoying complexity that one could view as a design limitation. When printing, Java calls you page by page. If at the end of a page you have more to print, you return PAGE_EXISTS, else you return NO_SUCH_PAGE. But this means that when you reach the end of a page, your application must remember its exact state before it exits, so when Java calls it for the next page, it picks up right where it left off.

On the surface, this seems to require the application to be reentrant. Java calls you, you print to the end of the page, you exit. Java calls you again, you pick up right where you left off before. An Icon or Ruby type "suspend" capability would have been very useful here. But no such capability exists in Java, so I came up with a different (and simpler solution).

I created an abstract model of the stuff being printed. At the root is a PrintBatch, which is a list of PrintLines, each of which is a list of PrintItems. When the print is first requested, the system "prints" the entire document by creating and filling in an instance of this abstract model. When it's done, we have defined the document that will be printed. Its definiton is abstract and entirely independent of the print system. During printing we will render it to the print system's graphics space (the printed page).

When printing, when we get to the end of a page all we have to remember is:

  • Whether we printed anything on this page
  • What is the next line we haven't yet printed (for the next page).
  • Now to make matters worse, when printing, Java may call your app multiple times for the same page. For example I found it usually (but not always) calls for each page twice. I found that doing the same thing each time satisfied Java - no duplicate pages or missing data. However, this added a bit of complexity to the print program. To handle this I created a simple Map with integer keys & values. Keys are page numbers, values are the starting line for that page.

    So the algorithm became:

  • Produce the complete abstract document (up front, before being asked for any page).
    Set current line to zero.
  • When asked to print page N:
  • Check the Map to see if we have a starting line for page N.
    If we do, use it.
    If we do not, use the current line.
  • Render the abstract document to the graphics space line by line until the page is full.
  • Add K=(page + 1), V=(line + 1) to the Map, so when we're called for the next page we will start at the right place.
  • Printing in OS/2 was easier. The system calls you one time, giving you the graphics space representing the printed page. You print away and when you reach the end of a page, you make one of two calls:

  • If you are done, you tell the system that.
  • If you aren't done, you ask the system for a new page and it gives you a new graphics space (virtual page) to scribble on.
  • This simpler system eliminates the reentrancy that Java's print system requires, which made it simpler and easier for applications to print. That said, it isn't all that hard to print in Java. In fact it is pretty easy.