Main Page

Previous Next

Printing in Java

Printing is always a messy business – inevitably so, because you have to worry about tedious details such as the size of a page, the margin sizes, and how many pages you're going to need for your output. As you might expect, the process for printing an image is different from printing text and you may also have the added complication of several printers with different capabilities being available, so with certain types of documents you need to select an appropriate printer. The way through this is to take it one step at a time. Let's understand the general principles first.

There are five packages dedicated to supporting printing capabilities in Java. These are:




Defines classes and interfaces that enable you to determine what printers are available and what their capabilities are. It also enables you to identify types of documents to be printed.


Defines classes and interfaces supporting the definition of sets of printing attributes. For example, you can define a set of attributes required by a particular document when it is printed, such as color output and two-sided printing, for instance.


Defines classes that identify a set of standard printing attributes.


Defines classes that identify events that can occur while printing and interfaces that identify listeners for printing events.


Defines classes and attributes for expediting the printing of 2D graphics and text.

The first four of these packages make up what is called the Print Service API that was added to Java in SDK 1.4. This allows printing on all Java platforms and has facilities for discovering and using multiple printers with varying capabilities. Since in all probability you have just a single printer available, we will concentrate in the first instance on understanding the classes and interfaces defined in the java.awt.print package that carry out print operations on a given printer, and stray into classes and interfaces from the other packages when necessary.

There are four classes in the java.awt.print package and we will be using all of them eventually:


An object of this class type controls printing to a particular print service (such as a printer or fax capability).


An object of this class type defines the size and orientation of a page that is to be printed.


An object of this class type defines the size and printable area of a sheet of paper.


An object of this class type defines a multipage document where pages may have different formats and require different rendering processes.

The PrinterJob class here drives the printing process. Don't confuse this with the PrintJob class in the java.awt package – this is involved in the old printing process introduced in Java 1.1 and the PrinterJob class now supersedes this. A PrinterJob class object provides the interface to a printer in your environment, and you use PrinterJob class methods to set up and initiate the printing process for a particular document. You start printing off one or more pages in a document by calling the print() method for the PrinterJob object.

A PageFormat object encapsulates information about a page, such as its dimensions, margin sizes, and orientation. An object of type Paper describes the characteristics of a physical sheet of paper that will be part of a PageFormat object. A Book object encapsulates a document consisting of a collection of pages that are typically processed in an individual way. We will be getting into the detail of how you work with these a little later in this chapter.

There are three interfaces in the java.awt.print package:


Implemented by a class to print a single page.


Implemented by a class to print a multipage document where each page may be printed by a different Printable object.


Declares a method for obtaining a reference to the PrinterJob object for use in a method that is printing a page.

When you print a page, an object of a class that implements the Printable interface determines what is actually printed. Such an object is referred to as a page painter.

The Printable interface only defines one method, print(), which is called by a PrinterJob object when a page should be printed. Therefore the print() method does the printing of a page. Note that we have mentioned two print() methods one defined in the PrinterJob class that you call to starting the printing process, and another declared in the Printable interface that you implement in your class that is to do the printing legwork for a single page.

The printing operation you must code when you implement the print() method declared in the Printable interface works through a graphics context object that provides the means for writing data to your printer. The first argument that is passed to your print() method when it is called by a PrinterJob object is a reference of type Graphics that represents the graphics context for the printer. The object that it references is actually of type Graphics2D, which parallels the process you are already familiar with for drawing on a component. Just as with writing to the display, you use the methods defined in the Graphics and Graphics2D classes to print what you want, and the basic mechanism for printing 2D graphics or text on a page is identical to drawing on a component. The Graphics object for a printer also happens to implement the PrinterGraphics interface (not to be confused with the PrintGraphics interface in the java.awt package!) that declares just one method, getPrinterJob(). You call this method to obtain a reference to the object that is managing the print process. You would do this if you need to call PrinterJob methods to extract information about the print job, such as the job name or the user name.

A class that implements the Pageable interface defines an object that represents a set of pages to be printed rather than a single page. You would implement this interface for more complicated printing situations where a different page painter may print each page using an individual PageFormat object. It's the job of the Pageable object to supply information to the PrinterJob object about which page painter and PageFormat object should be used to print each page. The Pageable interface declares three methods:


Returns the number of pages to be printed as type int, or the constant value Pageable.UNKNOWN_NUMBER_OF_PAGES if the number of pages is not known.

getPageFormat(int pageIndex)

Returns the PageFormat object describing the size and orientation of the page specified by the argument. An exception of type IndexOutOfBoundsException will be thrown if the page does not exist.

getPrintable(int pageIndex)

Returns a reference to the Printable object responsible for printing the page specified by the argument. An exception of type IndexOutOfBoundsException will be thrown if the page does not exist.

A Book object also encapsulates a document that consists of a number of pages, each of which may be processed individually for printing. The difference between this and an object of a class that implements Pageable is that you can add individual pages to a Book object programmatically, whereas a class implementing Pageable encapsulates all the pages. We will look at how both of these options work later in this chapter.

Creating and Using PrinterJob Objects

Because the PrinterJob class encapsulates and manages the printing process for a given printer that is external to the JVM, you can't create an object of type PrinterJob directly using a constructor. You can obtain a reference to a PrinterJob object for the default printer on a system by calling the static method getPrinterJob() that is defined in the PrinterJob class:

PrinterJob printJob = PrinterJob.getPrinterJob(); // For the default printer

The object printJob provides the interface to the default printer, and controls each print job that you send to it.

A printer is encapsulated by a PrintService object, and you can obtain a reference of type PrintService to the object encapsulating the printer that will be used by a PrinterJob object by calling its getPrintService() method:

PrintService printer = printJob().getPrintService();

You can query the object that is returned for information about the capabilities of the printer and the kinds of documents it can print but we won't divert down that track for the moment. One point you should keep in mind is that sometimes there may not be a printer available on the machine on which your code is running. In this case the getPrintService() method will return null, so it's a good idea to call the method and test the reference returned, even if you don't want to obtain details of the printer.

If there are multiple print services available, such as several printers or perhaps a fax capability, you can obtain an array of PrintService references for them by calling the static lookupPrintServices() method in the PrinterJob class. For example:

PrintServices[] printers = PrinterJob.lookupPrintServices();

The printers array will have one element for each print service that is available. If there are no print services available, the array will have zero length. If you want to select a specific printer for the PrinterJob object to work with, you just pass the array element corresponding to the print service of your choice to the setPrintService() method for the PrinterJob object. For example:


The if statement checks that there are some print services before we attempt to set the print service. Without this we could get an IndexOutOfBoundsException exception if the printers array has no elements.

Displaying a Print Dialog

When you want to provide the user with control over the printing process, you can display a print dialog by calling the printDialog() method. This displays the modal dialog that applies to your particular print facility. There are two versions of the printDialog() method for a PrinterJob object. The version without arguments will display the native dialog if the PrinterJob object is printing on a native printer, so we'll look at that first, and return to the other version later.

If the dialog is closed using the button that indicates printing should proceed, the printDialog() method will return true, otherwise it will return false. The method will throw an exception of type HeadlessException if there is no display attached to the system. Thus to initiate printing you can call the printDialog() method and, if it returns true, call the print() method for the PrinterJob object. Note that the print() method will throw an exception of type PrinterException if an error in the printing system causes the operation to be aborted.

Of course, the PrinterJob object can have no prior knowledge of what you want to print so you have to call a method to tell the PrinterJob object where the printed pages are coming from before you initiate printing. The simplest way to do this is to call the setPrintable() method, and pass a reference to an object of a class that implements the Printable interface as the argument.

In Sketcher, the obvious candidate to print a sketch is the SketchView object (a reference to which is stored in the view member of the application object). Thus we could allow sketches to be printed by making the SketchView class implement the Printable interface. That done, we could then set the source of the printed output just by passing a reference to the view to the setPrintable() method for a PrinterJob object. You might consider the SketchModel object to be a candidate to do the printing, but printing is not really related to a sketch, any more than plotting or displaying on the screen is. The model is the input to the printing process, not the owner of it. It is generally better to keep the model dedicated to encapsulating the data that represents the sketch.

Starting the Printing Process

Let's use what we now know about printing to add some code to the actionPerformed() method in the FileAction class. This will handle the event for the printAction object in the SketchFrame class:

      if(name.equals(printAction.getValue(NAME))) {
        // Get a printing object
        PrinterJob printJob = PrinterJob.getPrinterJob(); 
        PrintService printer = printJob.getPrintService();
        if(printer == null) {
                                        "No default printer available.",
                                        "Printer Error",
        // The view is the page source

        if(printJob.printDialog()) {              // Display print dialog
                                                  // If true is returned...
          try {
            printJob.print();                            // then print
          } catch(PrinterException pe) {
                                          "Error printing a sketch.",
                                          "Printer Error",

The code here obtains a PrinterJob object and, after verifying that we do have a printer to print on, sets the view as the printable source. The expression for the second if will display a print dialog, and if the return value from the printDialog() method call is true, we call the print() method for the printJob object to start the printing process. This is one of two overloaded print() methods defined in the PrinterJob class. We will look into the other one when we try out the alternative printDialog() method.

You will need to add three more import statements to the file:

import javax.print.PrintService;
import java.awt.print.PrinterJob;
import java.awt.print.PrinterException;

The SketchFrame class still won't compile at the moment, as we haven't made the SketchView class implement the Printable interface yet.

Printing Pages

The class object that you pass to the setPrintable() method is responsible for all the detail of the printing process. This class must implement the Printable interface, which implies defining the print() method in the same class. We can make the SketchView class implement the Printable interface like this:

import javax.swing.*;               // For various Swing components
import java.awt.*;
import java.util.*;                 // For Observer, Observer, Iterator
import java.awt.event.*;            // For events
import javax.swing.event.MouseInputAdapter;
import java.awt.geom.Line2D;
import java.awt.print.*;

class SketchView extends JComponent
                 implements Observer, Constants, ActionListener,
JavaScript Editor
 Java Tutorials Free JavaScript Editor