A Demonstration: Using the Java[tm] Print Service API
by Rajesh Ramchandani and CK Prasad
(June 2002)
We want to hear from you! Please send us your
FEEDBACK.
The following demonstration may contain actual software programs in source
code form. This source code is made available for developers to use as needed,
pursuant to the terms and conditions of this license.
You can download this demo and its corresponding files
here.
Summary
The Java[tm] SDK 1.4 introduced a new set of Unified Print APIs
that use the extendable, industry standard attribute set specified
in the Internet Printing Protocol (IPP) 1.1 from the IETF (Internet Engineering
Task Force). This article explains the new Java[tm] Print Service
API with the help of a Demo Print Service Servlet for server side printing,
along with a sample application for end user printing.
Document Structure
Overview of Java[tm]
Print Service API Software
Java Print Service APIs are based on the proposed Unified
Printing Model in JSR 6. The Unified Printing Model will allow
printing on all platforms requiring a Java Print API, including platforms
requiring a small footprint, such as a Java[tm] 2 Micro Edition (J2ME[tm]) platform, but will
still support the current Java 2 Print Service API, which allows printing of all
Java 2D[tm] API graphics. The Java[tm] Print Service ("JPS") API maintains
backward compatibility with the previous Java[tm] platform AWT print API, so
applications written to print 2D graphics, text and images can still print
using the new JPS API.
Some of the interesting features that the Java Print Service API offers
are:
-
Printer Discovery: Both the client and server side applications
can programmatically do Printer Discovery and find suitable printers
that can print the Print Job with user specified Print Job attributes.
There are several Print Job attributes that one can specify. For example,
number of sides for printing - single-sided or double-sided, chromaticity
(color or monochrome), media size (A4, legal, letter etc.), Print Job name
(specify a name for any printing job) etc.
-
Print Job Attributes as Objects: Implementations of standard IPP
attributes are included in the JPS API as objects.
-
Print Job Attribute Classes: Applications can extend the attributes
included with the JPS API.
-
Service Provider Interface: Third parties can plug in their
own Print Services using the Service Provider Interfaces. Using the vendor
provided SPI, Print Service can be implemented most effectively
and efficiently.
Java Print Service
Architecture
JPS API is provided in the following packages:
javax.print
This is the main JPS API package which provides the following functionality:
The JPS API provides an API like PrintServiceLookup() to look up printers
that can satisfy all the requested attributes for the print job. For example,
if the requested print job is a GIF image file and needs to be printed
on A4 size paper and in color, then PrintServiceLookup() is used to search
for the relevant printers that can satisfy all if not most of the
requested attributes of the print job, instead of just sending the print
job to any printer. If Fidelity attribute is set to true, then only the
Print Services that can satisfy all the print job attributes will be returned.
The javax.print package provides a StreamPrintService API to
send print data to an output stream that an application can use to
convert print data from one format to another. For example,
use the StreamPrintService API to send 2D graphics images to an output
stream.
-
Specify the Print Data Format
JPS provides the DocFlavor class to specify what print data
format is associated with the Print Service. The print data format includes
JPEG images, GIF images, text files, HTML files, PDF files, Postscript files,
etc. There are APIs to discover what data formats a print service or a
printer can print directly, instead of using another software to convert
the print data to a format that the printer understands. For example, a
printer may be able to print PDF files or image files directly when sent
from a InputStream.
DocFlavor can handle client-formatted print data (where the user
knows the type of the file to be printed) or service-formatted print
data (which lets the print service decide and interpret the print data format).
DocFlavor requires two parameters:
-
A MIME type which tells the Print Service how to interpret the print data.
For example, to print an HTML page, the MIME type is set to "text/html;
charset=utf-16".
-
A representation Java[tm] platorm class indicating to the print service how the print
data is sent to the printer. For example, class java.io.InputStream can
be specified if the print data is sent to the Print Service through a stream.
It is always a good idea to query the Print Service for the supported print
data format of DocFlavors before sending the print data to a print service.
Once a suitable Print Service is located with the PrintServiceLookup()
method using the set of requested attributes, a print job is created by
the DocPrintJob class from the Print Service. This print job is then sent
to the printer for printing. The DocPrintJob class provides the print method
for sending the print job to the printer. To print 2D graphics, JPS
provides the PrinterJob class that can be utilized by an application.
javax.print.arrtibute
and javx.print.attribute.standard
These two JPS packages help to specify print job attributes, print request
attributes and Document attributes. Let us look at each of these attribute sets below:
Print Request attributes are the attributes an application specifies,
describing characteristics of the whole print job and
all the docs in the print job. For example, number of copies for a print
job, chromaticity or number of sides to print on.
Each of the attributes can be applied to the print request and
must implement the PrintRequestAttribute interface. For example, class
Copies implements PrintRequestAttribute and provides some constants like
SINGLE_DOCUMENT, SINGLE_DOCUMENT_NEW_SHEET, etc. describing the number
of copies to print and whether to use new sheets or to collate them.
The set of print request attributes (like any other attributes) can
be applied to a print job using a Hash implementation of PrintRequestAttributeSet -
that is, HashPrintRequestAttributeSet.
Print Job attributes report the status of the Print Job or some other characteristics
of the Print Job. Print Service uses PrintJobAttributeSet to report the
status of the Print Job - for example, JobState. JobState is a printing
attribute class, an enumeration that identifies the current state of a
Print Job. Class JobState defines standard job state values. Now however,
the Print Service does not have to report all the Print Job attributes.
Print Service attributes report the status of Print Service or some other
characteristics of Print Service. A Print Service instance adds a number
of PrintServiceAttributes to a Print Service's
attribute set to report the Print Service's status. For example,
PagesPerMinute class reports number of pages a printer can print
nominally. This is a just an informative number and is not a guarantee.
Class PrinterState provides a enumeration describing the current
state of the printer.
DocAttributeSet specifies the characteristics of the individual doc and
print job settings to be applied to an individual doc or print data. For
example, class PrintQuality is an enumeration that specifies the print
quality used by the printer. The print quality specified can be DRAFT (lowest
print quality , HIGH (highest print quality ), NORMAL (normal print quality).
javax.print.event
JPS contains classes allowing the applications to register
for events on the print jobs (PrintJobAttributeSet) and Print Service
(PrintServiceAttributeSet). The event listener model is the same
as AWT's event listener model.
-
Print Service Event Listener
An application can use the addPrintServiceAttributeListener() method to add
a listener to the Print Service and to monitor the status changes in the Print
Service attributes.
The PrintServiceAttributeListener.attributeUpdate() method is called
when Print Service attributes change. The service uses the PrintServiceAttributeListener
interface to decide which events it supports. Inside the attributeUpdate()
method, the client application can determine which Print Service (getPrintService)
triggered the event and also the set of a Print Service attributes that
changed (PrintServiceAttributeSet).
Client applications can use PrintJobAttributeListener or PrintJobListener
to monitor changes in the Print Job attributes. These listeners are installed
on DocPrintJob and one may be preferred over the other depending on the
events to be monitored.
-
PrintJobAttributeListener
Class PrintJobAttributeEvent encapsulates an event a Print Service
reports to let the client know that one or more printing attributes for
a Print Job have changed. One example of this event being triggered
is when the JobState attribute changes from PROCESSING to
PROCESSING_STOPPED.
This event listener is easier to use than PrintJobAttributeListener
as it delivers higher level messages, unlike the PrintJobAttributeListener.
These messages contain details on individual attributes and may not be
very useful to the client application. For example, a client application
is more interested in knowing the progress of a print job sent to the printer. The
PrintJobListener interface provides methods like printDataTransferCompleted(),
printJobFailed(), etc., which can be implemented in the client application
to report and take appropriate actions on receiving events from the DocPrintJob.
Java
Print Service Sample Print Servlet and Standalone Print Application
Now that we have seen the overview of JPS and are familiar
with the JPS architecture, let us take a look at how these APIs can
be used to write the following client application. We will discuss the
code from the sample Print application here, but most of the code
applies to the sample Print Servlet too, barring a few changes that Servlet
requires with respect to InputStream and OutputStream. You can download
the source code for Print Application
and
Print
Servlet from here. The Servlet prints out the names of the methods
and the respective JPS API when the code executes. Please read the
README file provided with the source code for instructions
on how to set up Tomcat and other required JAR files to run
this demo servlet. The client application is a standalone application and
will work as is.
Let us get familiar with the steps required to send a print job
to the Print Service or to a printer.
-
Specify a DocFlavor type depending on the file type that is to be printed.
The DocFlavor can be specified by the client when the file type is
known, or it can be a service-formatted DocFlavor which will depend on the
Print Service to determine the file type or print data type. In our sample
Print application, we will specify the DocFlavor type and use text file
for printing, although it would be a good idea to query the Print Service
for the supported DocFlavors before sending the print data.
-
Build an attribute set, typically PrintRequestAttributeSet, to specify
the user specified print job attributes. For our sample application, we
will read a .props file from the current directory to determine the user
specified attributes. A sample .props file
is provided with the source code.
-
Discover the Print Services that satisfy the requested attribute
set of the specified DocFlavor. In our sample Print application, we use
the compromiseAttribute() method, which eliminates one attribute at a time
from the user specified attribute set until we find an appropriate Print
Service. In case there are no Print Services that can satisfy the user
specified attributes, the client exits with message "No Printers Found".
-
Add any Print Service or Print Job Listeners to the Print Service(s) depending
on what events the application is interested in.
-
Create a Print Job or Doc from the Print Service.
-
Finally, call the print() method on the DocPrintJob to send the print data
to the printer.
Let's look at the main method in our sample Print application:
public static void main(String args[]) {
Print p = new Print(); //Create a Print object
p.getDocProperties(); //Get Document properties from a .props file
p.loadFile("file.txt"); //Load the file using inputStream
p.setRequest(); //All Attributes are in place
// Let's see if the specified file type is a supported file type
if(!(p.isSupportedFileType(filetypestr))) {
System.out.println("File Type is not supported");
System.exit(1);
}
//Calling this method sets the appropriate DocFlavor.
//In case of text file it is set to
//DocFlavor.INPUT_STREAM.TEXT_PLAIN_US_ASCII;
p.setDocFlavor();
//Lets get a list of Print Services that satisfy our request
//attributes for given DocFlavor
PrintService[] ps = p.getPrintServices();
if(ps.length == 0) {
System.out.println("No printers found, exiting!");
System.exit(1);
}
//Let's query the Print Service to see what DocFlavors
//are supported
DocFlavor[] sdf = ps[0].getSupportedDocFlavors();
System.out.println("Supported DocFlavors are: ");
for( int r = 0; r < sdf.length; r++ ) {
System.out.println(sdf[r].toString());
}
//Let's add some PrintServiceAttributeListener and
//monitor the events.
p.addPrintAListener(ps);
//Let's print all the Print Services we discovered
//List of All print Services available
PrintService[] plist = p.listPrinters();
System.out.println("Printers are: ");
for(int i=0; i < plist.length; i++ ) {
System.out.println(plist[i]);
}
//Let's print the Default Print Service configured
PrintService defp = p.getDefaultPrintService(); /
System.out.println("Default printer is " + defp.getName() );
//Create a Print Job from the selected print Service
DocPrintJob pj = p.createDocPrintJob(ps[0]);
//Add a PrintJobAttrubuteListener to the selected
//Print Service and monitor print progress
p.addPrintJListener(pj);
//Create a Doc object before calling print()
Doc d = p.createFinalDoc();
//Print the Doc to the selected Print Service
printJob(pj,d);
}
The .props file specified the following attributes
for the print job:
CHROMATICITY:monochrome
COPIES:1
DESTINATION:
FIDELITY:false
JOBNAME:Java Printing Servlet
JOBSHEETS:standard
MEDIA:iso-a4
MEDIAPRINTABLEAREA:
FILETYPE:text
FILENAME:file.txt
MEDIA:A4
SIDES:DUPLEX
ORIENTATION:Potrait
FILENAME:file.txt
Here's the output from the Print application (condensed to show only
relevant information):
File Type is text
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
No Print Services found, will compromise attribute
javax.print.attribute.standard.OrientationRequested
Printer name is: pomona
Supported Print Service attribute categories are:
class javax.print.attribute.standard.Chromaticity
Color, Monochrome
class javax.print.attribute.standard.Copies
1-1000
class javax.print.attribute.standard.Destination
file:out.ps
class javax.print.attribute.standard.Fidelity
true
class javax.print.attribute.standard.JobName
Java Printing
class javax.print.attribute.standard.JobSheets
, standard
class javax.print.attribute.standard.Media
, tabloid, ledger, na-legal, executive,
iso-a3, iso-a4, iso-a5, iso-b4, iso-b5class javax.print.attribute.standard.MediaPrintableArea
Printer name is: pomona
Supported DocFlavors are:
application/postscript; class="[B"
application/postscript; class="java.io.InputStream"
application/postscript; class="java.net.URL"
image/gif; class="[B"
image/gif; class="java.io.InputStream"
image/gif; class="java.net.URL"
image/jpeg; class="[B"
image/jpeg; class="java.io.InputStream"
image/jpeg; class="java.net.URL"
image/png; class="[B"
image/png; class="java.io.InputStream"
image/png; class="java.net.URL"
text/plain; charset="utf-16"; class="[C"
text/plain; charset="utf-16"; class="java.io.Reader"
text/plain; charset="utf-16"; class="java.lang.String"
text/plain; charset="iso646-us"; class="[B"
text/plain; charset="utf-8"; class="[B"
text/plain; charset="utf-16"; class="[B"
text/plain; charset="utf-16be"; class="[B"
text/plain; charset="utf-16le"; class="[B"
text/plain; charset="us-ascii"; class="[B"
text/plain; charset="iso646-us"; class="java.io.InputStream"
text/plain; charset="utf-8"; class="java.io.InputStream"
text/plain; charset="utf-16"; class="java.io.InputStream"
text/plain; charset="utf-16be"; class="java.io.InputStream"
text/plain; charset="utf-16le"; class="java.io.InputStream"
text/plain; charset="us-ascii"; class="java.io.InputStream"
text/plain; charset="iso646-us"; class="java.net.URL"
text/plain; charset="utf-8"; class="java.net.URL"
text/plain; charset="utf-16"; class="java.net.URL"
text/plain; charset="utf-16be"; class="java.net.URL"
text/plain; charset="utf-16le"; class="java.net.URL"
text/plain; charset="us-ascii"; class="java.net.URL"
application/x-java-jvm-local-objectref; class="java.awt.print.Pageable"
application/x-java-jvm-local-objectref; class="java.awt.print.Printable"
application/octet-stream; class="[B"
application/octet-stream; class="java.net.URL"
application/octet-stream; class="java.io.InputStream"
No. of print Services Found is : 1
Printers are:
Unix Printer : pomona
Default Print service is Unix Printer : pomona
Default printer is pomona
Data xfer completed !
All events DONE
Now let us look at some of the important methods in our Print application.
The Javadoc[tm] software for the Print application is available
here.
The first step is to set the DocFlavor depending on the type of
file we are printing. In the Print application, we check the FILETYPE property
and create the DocFlavor in the setDocFlavor() method.
/**
* Sets Document
Flavor Type (DocFlavor) which will be used by
* PrintServiceLookup
and also to create a Doc for Print Service.
* @param
none
* @return
void
*/
public void setDocFlavor()
{
--
--
--
if (filetypestr.equalsIgnoreCase( "text") || filetypestr.equalsIgnoreCase(
"txt")) {
myFlavor = DocFlavor.INPUT_STREAM.TEXT_PLAIN_US_ASCII;
}
if (filetypestr.equalsIgnoreCase( "html")) {
System.out.println("setDocFlaovr is setting HTML....");
myFlavor = DocFlavor.INPUT_STREAM.TEXT_HTML_US_ASCII ;
}
--
--
--
Our next step is to create an attribute set from the user requested print
job attributes. We do this in the getDocProperties() and the setRequest()
methods. The getDocProperties() method reads the .props file and setRequest()
adds the requested attributes to the HashPrintrequestAttributeSet. The
HashPrintRequestAttributeSet is passed on to PrintServiceLookup()
to discover the suitable Print Services.
/**
* Creates the
request for print service with specified attributes
*/
public void setRequest()
{
aset = new HashPrintRequestAttributeSet();
/* Set Chromaticity */
if (chromaticity != null ) {
if(chromaticity.equalsIgnoreCase( "monochrome")) {
aset.add(Chromaticity.MONOCHROME);
}
if(chromaticity.equalsIgnoreCase( "color")) {
aset.add(Chromaticity.COLOR);
}
} else {
aset.add(Chromaticity.MONOCHROME);
}
/* Set number of Copies */
if(copies == null ) {
aset.add(new Copies(1));
}
if (copies != null ) {
aset.add(new Copies(Integer.parseInt(copies)));
}
--
--
--
Our third step is to determine the Print Services that can satisfy our PrintRequestAttributeSet
for the chosen DocFlavor. In the Print application, we implement a compromiseAttribute()
method to compromise one attribute from the HashPrintrequestAttributeSet()
until we find some Print Service. The compromiseAttribute() method removes
one attribute at a time from the user specified attribute set until a suitable
Print Service is discovered. The logic used in this method is very
simple and may not be satisfactory for some applications.
We use the getPrintServices() method in our Print application.
--
--
services = PrintServiceLookup.lookupPrintServices(myFlavor, aset);
if(services.length > 0 ) {
break;
}else {
System.out.println(" No Print Services found,
will compromise attribute");
compromiseAttribute(i); //remove one attribute
}
--
--
--
Once we determine the Print Services, we can install Print Service and
PrintJob Attribute Listeners.
/**
* Add PrintAttributeListener
to the service
* This monitors
the changes in Print Service Attributes
* @param
Print service
* @return
void
*/
public void addPrintAListener(PrintService
service) {
PrintAListener pAListener = new PrintAListener();
service.addPrintServiceAttributeListener(pAListener);
}
/**
* Add PrintAttributeListener
to the all Print services
* This monitors
the changes in Print Service Attributes
* @param
array of print services
* @return
void
*/
public void addPrintAListener(PrintService[]
service) {
PrintAListener pAListener = new PrintAListener();
for(int i = 0; i < service.length; i++) {
System.out.println("Adding AListener to
service number: " + i);
service[i].addPrintServiceAttributeListener(pAListener);
}
}
Our fourth step is to create a print job from the selected Print Service.
This is how we do it in our sample Print application:
/**
* Creates a Print
Job from one of the Print Services
* @param
printservice (usually returned from getPrintServices()
* @return
DocprintJob
*/
public DocPrintJob createDocPrintJob(PrintService
ps) {
DocPrintJob job = ps.createPrintJob();
return job;
}
And finally, we create a Doc object and send it to the Print Service using
print() method of DocPrintJob.
/**
* Create a Doc
for printing. Will use SimpleDoc for now
* @param
none
* @return
Doc
*/
public Doc createFinalDoc()
{
System.out.println(" parameters are : "+ fin + myFlavor );
myDoc = new SimpleDoc(fin, myFlavor, null);
return myDoc;
}
/**
* Prints the Print
Job to the specified printService
* @param
DocPrintJob
*
Doc
* @return
void
*/
public static void printJob(DocPrintJob
dpj, Doc d) {
DocPrintJob ljob = dpj;
Doc ld = d;
try {
--
--
--
ljob.print(ld,aset);
System.out.println("printed ljob");
--
--
}catch(PrintException pe) {
System.out.println("Got Printer Exception pe:"
+pe.getMessage());
System.out.println("Cause of pe is " + pe.getCause());
pe.printStackTrace();
--
--
--
Please refer to the source code and Javadoc software
for the Print application for more information on each of the methods used
here.
JPS Demo Print Servlet:
You can download the Print
Servlet source code from here. The Print Servlet code uses the Print object
itself and outputs the names of the API as it executes them. For quick
reference to the API follow the links on the servlet
outputs.
Please see the Servlet
README for an explanation
on the JPS Servlet source code, install requirements and instructions to
set up the servlet to run with Tomcat servlet container.
What's More on JPS:
What this document and sample Print application do not cover is printing
2D graphics and using Service-formatted print data. Please refer
to the JPS User's Guide for examples on how to work with these two items.
Excellent References for JPS:
DOC ID #791
|