Skip to main content

skip to main content

developerWorks  >  Java technology  >

Magic with Merlin: Printing in JDK 1.4, Part 1

The new Java Print Service API offers a happy ending to the Java printing saga

developerWorks
Document options

Document options requiring JavaScript are not displayed

Discuss


Rate this page

Help us improve this content


Level: Introductory

John Zukowski (jaz@zukowski.net)JZ Ventures, Inc.

01 Mar 2002

It seems like every new release of the Java platform since 1.1 has brought changes to the platform's printing support framework. Merlin is no exception. In this month's Magic with Merlin, John Zukowski begins a two-part discussion of the latest capabilities.

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).

Locating a printer

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();



Back to top


Specifying the output format

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;



Back to top


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:

  • DocAttributeSet specifies the characteristics for a single document.
  • PrintRequestAttributeSet specifies 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.



Back to top


Setting the content

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);



Back to top


Printing

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.



Back to top


Printer dialog

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
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);
  }




Back to top


A working example

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



Resources



About the author

Photo of John Zukowski

John Zukowski conducts strategic Java consulting with JZ Ventures, Inc. and serves as the resident guru for a number of jGuru's community-driven Java FAQs. His latest books are Java Collections and Definitive Guide to Swing for Java 2 (2nd ed.) from Apress. Contact John at jaz@zukowski.net.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top