If you've been working with the Java platform for some time, you're probably as frustrated as I am by the endless succession of changes to the print API. Merlin brings yet another set of print capabilities and techniques to the Java platform. Dubbed the Java Print Service API, this latest iteration has been working its way through the Java Community Process since 1999. Fortunately, the new API is a positive addition and should represent the final significant change to printing support for some time.
Printing with the new Print Service API involves a three-part process of discovery,
specification, and printing. An optional fourth part involves notification as a
printing task progresses. All the classes and interfaces we'll be working
with in this article are in the javax.print package or one of its
subpackages (see Resources).
The first step to executing a print job is to identify the printer or
set of printers you want to print to. Printer objects are called print services, and
the identification process is called a lookup. The support class for
the lookup task is aptly named PrintServiceLookup. To look up
the print service, you would use one of the three methods shown in Listing 1.
Listing 1. Looking up a print service
public static final PrintService
lookupDefaultPrintService()
public static final PrintService[]
lookupPrintServices(DocFlavor flavor, AttributeSet attributes)
public static final MultiDocPrintService[]
lookupMultiDocPrintServices(DocFlavor[] flavors,
AttributeSet attributes)
|
Each of the three methods shown is used for a different task:
-
lookupDefaultPrintService()returns the default print service.
-
lookupPrintServices()returns the set of printers that support printing a specific document type (such as GIF) with a specific set of attributes (such as two sided).
-
lookupMultiDocPrintServices()provides support for printing multiple documents at once.
After you've located the print service you want to use, you need to create a print job.
You'll later send output to this job. The PrintService returned
by the lookup can be used to create the job with its createPrintJob() method, as shown here:
PrintService printService =
PrintServiceLookup.lookupDefaultPrintService();
DocPrintJob job = printService.createPrintJob();
|
In addition to specifying where to print, you must specify the format of your
print documents. This is where the DocFlavor class
(or one of its subclasses) will come in handy. The DocFlavor
class is used to identify the MIME (Multipurpose Internet Mail Extensions) type of the object you want to print. The MIME type describes how electronic data
should be interpreted. You may have run across MIME types when working with e-mail
and attachments, but the MIME specification describes a more
general-purpose mechanism for identifying data forms.
Merlin provides seven subclasses of DocFlavor as
inner classes of itself to define formats. These classes can be broken up into
three subsets of MIME types: byte-oriented, character-oriented, and
service-oriented. The byte-oriented flavors are:
-
BYTE_ARRAY -
INPUT_STREAM -
URL
The character-oriented ones are:
-
CHAR_ARRAY -
READER -
STRING
And the service-oriented inner class is
SERVICE_FORMATTED.
Inner classes within inner classes
Each flavor type supports its own set of MIME types. These MIME types are defined, yet again, as inner classes. There are 19 byte-oriented flavors, as follows:
-
AUTOSENSE -
GIF -
JPEG -
PCL -
PDF -
PNG -
POSTSCRIPT -
TEXT_HTML_HOST -
TEXT_HTML_US_ASCII -
TEXT_HTML_UTF_16 -
TEXT_HTML_UTF_16BE -
TEXT_HTML_UTF_16LE -
TEXT_HTML_UTF_8 -
TEXT_PLAIN_HOST -
TEXT_PLAIN_US_ASCII -
TEXT_PLAIN_UTF_16 -
TEXT_PLAIN_UTF_16BE -
TEXT_PLAIN_UTF_16LE -
TEXT_PLAIN_UTF_8
The character-oriented streams are less dramatic, providing just two formats:
-
TEXT_HTML -
TEXT_PLAIN
The service-oriented streams includes three formats:
-
PAGEABLE -
PRINTABLE -
RENDERABLE_IMAGE
You configure the flavor as shown here (in this example, as a PNG image):
DocFlavor flavor = DocFlavor.INPUT_STREAM.PNG; |
Specifying the print attributes
When printing, you can specify attributes that describe how you want to print a document. Example attributes include number of copies, which pages to print, and the document image type (for example, landscape versus portrait). To specify attributes, you need to work with one of two classes:
-
DocAttributeSetspecifies the characteristics for a single document. -
PrintRequestAttributeSetspecifies the characteristics of a single print job.
To specify the attributes for a print run, you create an instance of the
appropriate set (DocAttributeSet or
PrintRequestAttributeSet) and fill it with the
desired attributes for your print run. Reasonable defaults will be configured
for those attributes you don't specify. The javax.print.attribute
package holds about 70 different attributes, each of which is defined as a separate class.
Each attribute works with one or more of the attribute sets. In addition to the two
attribute sets described in this article, two other sets are available for
querying information. The javax.print.attribute package
provides a common place to look for all the types.
Below we see a print run that uses the PrintRequestAttributeSet
and prints five copies of an object:
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet(); pras.add(new Copies(5)); |
Be sure to look in the javax.print.attribute package
for a list of all the available attributes.
The Doc interface provides the data to the print job. The
implementor of the interface is the SimpleDoc class. With a single
constructor, you provide the content as the first parameter, the flavor as the second,
and the attributes as the third. The constructor is as follows:
public SimpleDoc(Object printData, DocFlavor flavor, DocAttributeSet attributes) |
That leaves the question of the data. And the answer depends on the DocFlavor.
If you specified a flavor of DocFlavor.INPUT_STREAM,
then the data would be identified by its InputStream.
If your flavor was DocFlavor.BYTE_ARRAY, then the data
would be a byte array (byte [ ]).
So, to print a PNG image file, you would use the syntax shown in Listing 2.
Listing 2. Setting the content
DocFlavor flavor = DocFlavor.INPUT_STREAM.PNG; String filename = ...; FileInputStream fis = new FileInputSteam(filename); DocAttributeSet das = new HashDocAttributeSet(); Doc doc = new SimpleDoc(fis, flavor, das); |
Once you've identified the printer and specified the output format,
attributes, and content, the only thing left to do is print. The actual print job
is executed through the print() method of the
DocPrintJob, retrieved from
the PrintService, as shown in Listing 3.
Listing 3. A print job
DocPrintJob job = ...; PrintRequestAttributeSet pras = ...; Doc doc = ...; job.print(doc, pras); |
By calling print() you trigger the
mechanism to send your content to the print service in a separate thread.
One thing noticeably absent so far is any mention of the print dialog. The dialog is the system popup that allows you to configure printer attributes graphically. Figure 1 shows a print dialog for the new Java Print Service API.
Figure 1. Printer dialog

Interestingly, the printer dialog's default behavior has changed
with the new API: by default the dialog is not shown. So we must use
the ServiceUI class to create a print
dialog like the one shown above.
The ServiceUI class provides a single method to display the printer selection dialog:
printDialog(GraphicsConfiguration gc, int x, int y, PrintService[] services, PrintService defaultService, DocFlavor flavor, PrintRequestAttributeSet attributes) |
You then use the returned PrintService to obtain the
DocPrintJob to print with, as shown in Listing 4.
Listing 4. Printing with dialog
String filename = ...;
PrintRequestAttributeSet pras = ...;
DocFlavor flavor = ...;
PrintService printService[] =
PrintServiceLookup.lookupPrintServices(flavor, pras);
PrintService defaultService =
PrintServiceLookup.lookupDefaultPrintService();
PrintService service = ServiceUI.printDialog(null, 200, 200,
printService, defaultService, flavor, pras);
if (service != null) {
DocPrintJob job = service.createPrintJob();
FileInputStream fis = new FileInputStream(filename);
DocAttributeSet das = new HashDocAttributeSet();
Doc doc = new SimpleDoc(fis, flavor, das);
job.print(doc, pras);
}
|
We'll close with a working example that will let you to try out the new Print
Service API's capabilities. The following code essentially combines all
our prior code examples into one runnable program. When you run the program,
be sure to pass the name of a PNG image file on the command line. If you'd
rather print a different format, just change the DocFlavor.
Listing 5. A printing example
import javax.print.*;
import javax.print.attribute.*;
import java.io.*;
public class Printing {
public static void main(String args[]) throws Exception {
String filename = args[0];
PrintRequestAttributeSet pras =
new HashPrintRequestAttributeSet();
DocFlavor flavor = DocFlavor.INPUT_STREAM.PNG;
PrintService printService[] =
PrintServiceLookup.lookupPrintServices(flavor, pras);
PrintService defaultService =
PrintServiceLookup.lookupDefaultPrintService();
PrintService service = ServiceUI.printDialog(null, 200, 200,
printService, defaultService, flavor, pras);
if (service != null) {
DocPrintJob job = service.createPrintJob();
FileInputStream fis = new FileInputStream(filename);
DocAttributeSet das = new HashDocAttributeSet();
Doc doc = new SimpleDoc(fis, flavor, das);
job.print(doc, pras);
Thread.sleep(10000);
}
System.exit(0);
}
}
|
In the next installment of Magic with Merlin, you'll learn how to print the contents of
a screen or component. We'll also discuss event handling tasks related to printing, and
I'll show you how to remove the sleep() call from your
printing operations.
Note: Just because the PDF DocFlavor exists doesn't mean you can use it to print a PDF file. When you lookup the PrintService for the PDF flavor, it will report unsupported flavor. That means there is no print service for the flavor. Sun doesn't provide one for PDF files. To the best of my knowledge, nobody else makes one available either. Until such a time as someone does, you can't print PDF files using the new Print Service API
- Learn how to use the complete Java Print Service API. See the formal
documentation from Sun Microsystems.
- The JSR-006 Unified
Printing API offers a glimpse into the origins of the new printing service.
- If you don't know much about design patterns, you can start learning with
David Gallardo's "Design patterns 101" tutorial, exclusively from developerWorks.
- Peruse John Zukowski's complete collection of tips for working with Merlin.
- You'll find hundreds of articles about every aspect of Java programming in
the developerWorks
Java technology zone.

John Zukowski does strategic Java consulting for JZ Ventures, Inc.. His latest book is "Java Collections" (Apress, May 2001).



