Java Pro Programming: Printing
Learn how to use the print service API
SummaryBy Brett Spell
In this article, an excerpt from Pro Java Programming (Apress, June 2005), Brett Spell explains step-by-step how to locate print services, create a print job, create an implementation of theDoc
interface that describes the data you want to print, and initiate printing. (4,500 words; July 25, 2005)
Printer-friendly version | Mail this to a friend
Advertisement | |
|
PrintJob
in the java.awt package
, but the printing capabilities supported by that class were somewhat crude and unreliable. When Java 1.2 (or "Java 2") was introduced, it included a completely separate mechanism (called the Java 2D printing API) for printing designed around PrinterJob
and other classes and interfaces defined in the new java.awt.print
package. This rendered the PrintJob
-based printing mechanism (also known as AWT printing) largely obsolete, although PrintJob
has never been deprecated and, at least of this writing, is still technically a supported class. Additional changes were made in J2SE 1.3 when PrintJob
's capabilities expanded to allow the setting of job and page attributes using the appropriately named JobAttributes
and PageAttributes
classes within the java.awt
package. With the release of J2SE 1.3, the printing capabilities were reasonably robust, but some problems still existed aside from the confusion associated with having two completely separate printing facilities. For one thing, both facilities used an implementation of the java.awt.Graphics
class for rendering the content to be printed, which meant anything that needed to be printed had to be rendered as a graphical image. In addition, the newer and generally more robust PrinterJob
facility provided only limited support for setting attributes associated with the job. Finally, neither facility provided a way to programmatically select the target printer.
The biggest change in Java's printing capabilities to date came with the release of J2SE 1.4, when the Java print service API was introduced. This third implementation of printing support in Java addressed the limitations that were just described using an implementation of the PrintService
and DocPrintJob
interfaces defined in the javax.print
package. Because this new API represents a superset of the functionality defined by the two older printing facilities, it's the one you should normally use and will be the focus of this article.
At a high level, the steps involved in using the Java print service API are straightforward:
- Locate print services (printers), optionally limiting the list of those returned to the ones that support the capabilities your application needs. Print services are represented as instances of
PrintService
implementations. - Create a print job by calling the
createPrintJob()
method defined in thePrintService
interface. The print job is represented by an instance ofDocPrintJob
. - Create an implementation of the
Doc
interface that describes the data you want to print. You also have the option of creating an instance ofPrintRequestAttributeSet
that describes the printing options you want. - Initiate printing by calling the
print()
method defined in theDocPrintJob
interface, specifying theDoc
you created in the previous step and thePrintRequestAttributeSet
or a null value.
You'll now examine each of these steps and see how to achieve them.
Note |
---|
Within this article, I'll use the terms printer and print service interchangeably because, in most cases, a print service is nothing more than a representation of a physical printer. The more generic print service reflects that the output can theoretically be sent to something other than a printer. For example, a print service might not print the output at all but instead write it to a disk file. In other words, all printers are represented by a print service, but not every print service necessarily corresponds to a physical printer. In practice, though, it's likely you'll almost always send your content to a printer, which is why I'll sometimes use the simpler printer term instead of the more technically accurate print service. |
Locating print services
You locate a printer using one of three static methods defined in the PrintServiceLookup
class. The simplest of the three methods is lookupDefaultPrintService()
, and, as its name implies, it returns a reference to the service that represents your default printer:
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
Although this method is simple and convenient, using it to select which printer to send output to means you're implicitly assuming that the user's default printer will always be able to support the capabilities your application needs in order to be able to print its output correctly. In practice, you'll typically want to select only those printers that are able to handle the type of data you want to print and that support the features your application needs, such as color or two-sided printing. To retrieve the list of all defined printers or to retrieve a list that's limited to printers supporting certain capabilities, you'll want to use one of two other static methods defined in PrintServiceLookup
: either lookupPrintServices()
or lookupMultiDocPrintServices()
.
The lookupPrintServices()
method accepts two parameters: an instance of DocFlavor
and an instance of some implementation of the AttributeSet
interface. As you'll see shortly, you can use both of these to limit the list of printers returned by the method, but lookupPrintServices()
allows you to specify a null value for either or both of the two parameters. By specifying a null value for both parameters, you're effectively requesting that the method return a PrintService
instance for every printer that's available. At this point, you haven't really examined the methods defined in PrintService
, but one of them is the getName()
method, which returns a String
representing the name of the printer. You can display a list of all printers available on your system by compiling and running code like this:
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
for (int i = 0; i < services.length; i++) {
System.out.println(services[i].getName());
}
For example, if you have access to printers named Alpha, Beta, and Gamma that are attached to a server named PrintServer, running the previous code produces this output:
\\PrintServer\Alpha
\\PrintServer\Beta
\\PrintServer\Gamma
Now let's examine the parameters you can pass to the lookupPrintServices()
method and see how they allow you to limit the printers returned to those with only certain capabilities.
DocFlavor
The first parameter you can specify on a call to lookupPrintServices()
is an instance of the DocFlavor
class, which describes the type of data to be printed and how that data is stored. In most cases, it won't be necessary for you to create a new instance of DocFlavor
because Java includes many predefined instances, allowing you to simply pass a reference to one of those instances to lookupPrintServices()
. However, let's look at the DocFlavor
constructor and methods to understand how an instance is used by a print service.
The two arguments required when creating an instance of DocFlavor
are both String
instances, with one representing a MIME (Multipurpose Internet Mail Extensions) type and the other being the name of a representation class. The MIME type is used by a DocFlavor
to describe the type of data to be printed. For example, if you're printing a gif file, you'll need to use a DocFlavor
that has a MIME type of image/gif. Similarly, you might use a MIME type of text/plain if you're printing text information or text/html for an HTML document.
Representation class
While the MIME type describes the type of data to be printed, the representation class describes how that data is to be made available to the print service. DocFlavor
includes seven static inner classes, with each one corresponding to a representation class and each one corresponding to a different way of encapsulating the data that's to be printed.
Table 1 shows the names of the static inner classes defined within DocFlavor
and their corresponding representation classes. Note that aside from SERVICE_FORMATTED
(which I'll discuss in detail later), each one is described as being associated with either "binary" or "character" data. In reality, the distinction is somewhat artificial because character data is really just a specialized form of binary data, in this case, referring to binary data that contains only human-readable characters and perhaps some formatting characters such as tabs, carriage returns, and so on. However, the distinction is important because it reflects that character-oriented representation classes aren't appropriate for storing binary data that's to be printed.
For example, you wouldn't store a representation of a gif image in a character array or a String
, and you wouldn't make it accessible through a Reader
implementation. On the other hand, since "character" data is just a specialized type of binary data, it's entirely appropriate to store text information in a byte array or make it accessible through an InputStream
or via a URL.
Table 1. DocFlavor's predefined representation classesInner class name Representation class Data type BYTE_ARRAY
[B (byte[])
Binary CHAR_ARRAY
[C (char[])
Character INPUT_STREAM
java.io.InputStream
Binary READER
java.io.Reader
Character SERVICE_FORMATTED
java.awt.print.Pageable or java.awt.print.Printable
Other STRING
java.lang.String
Character URL
java.net.URL
Binary
Each of these static inner classes defined within DocFlavor
corresponds to a particular representation class, but remember that I said each DocFlavor
instance encapsulates both a representation class and a MIME type that identifies the type of data to be printed. To access an instance of DocFlavor
that corresponds to both the representation class and the MIME type of the content you want to print, you'll need to reference an inner class within one of the inner classes listed in Table 1. For example, let's suppose you want to print a gif file that's available on the Web through a URL. In this case, the obvious choice for the representation class is java.net.URL
, which is associated with the static class named URL
that's defined within DocFlavor
. If you browse the documentation for that inner class, you'll find that it, in turn, defines a number of static inner classes, each one corresponding to a particular MIME type representing data types commonly supported by printers. Table 2 shows the inner classes defined within DocFlavor.URL
and their corresponding MIME types.
Table 2. The DocFlavor.URL inner classesStatic inner classes MIME type AUTOSENSE
application/octet-stream GIF
image/gif JPEG
image/jpeg PCL
PCL application/vnd-hp.PCL PDF
application/pdf PNG
image/png POSTSCRIPT
application/postscript TEXT_HTML_HOST
text/html TEXT_HTML_US_ASCII
text/html;charset=us-ascii TEXT_HTML_UTF_16
text/html;charset=utf-16 TEXT_HTML_UTF_16BE
text/html;charset=utf-16be TEXT_HTML_UTF_16LE
text/html;charset=utf-16le TEXT_HTML_UTF_8
TEXT_HTML_UTF_8 text/html;charset=utf-8 TEXT_PLAIN_HOST
text/plain TEXT_PLAIN_US_ASCII
text/plain;charset=us-ascii TEXT_PLAIN_UTF_16
text/plain;charset=utf-16 TEXT_PLAIN_UTF_16BE
text/plain;charset=utf-16be TEXT_PLAIN_UTF_16LE
text/plain;charset=utf-16le TEXT_PLAIN_UTF_8
text/plain;charset=utf-8
Since you'll print a gif image that's available through a URL, you can access an appropriate DocFlavor
instance using this code:
DocFlavor flavor = DocFlavor.URL.GIF;
This code creates a reference to the static instance of DocFlavor
that has a representation class of java.net.URL
and a MIME type of image/gif.
The classes listed in Table 2 are defined within the DocFlavor.URL
class, but what about the other six inner classes defined within DocFlavor
? Again, I'll defer a discussion of SERVICE_FORMATTED
until later, but, as for the classes associated with binary data types, all three (BYTE_ARRAY
, INPUT_STREAM
, and URL
) include inner classes with the names shown in Table 2. So, for example, if you had loaded the gif data into a byte array, you might instead choose to use code like this:
DocFlavor flavor = DocFlavor.BYTE_ARRAY.GIF;
Just as the three DocFlavor
inner classes associated with binary data types include their own inner classes, the three associated with character data types include a different set of inner classes, as shown in Table 3.
Table 3. CHAR_ARRAY, READER, and STRING Static inner class MIME type TEXT_HTML
text/html;charset=utf-16 TEXT_PLAIN
text/plain;charset=utf-16
So, for example, if you wanted to print plain text data that's stored in an instance of String
, you could use code like the following:
DocFlavor flavor = DocFlavor.STRING.TEXT_PLAIN;
Similarly, if the text data represented an HTML document and you wanted to have the data printed as it would appear within a Web browser, you could use the following:
DocFlavor flavor = DocFlavor.STRING.TEXT_HTML;
Choosing the right printer
Remember that the discussion of DocFlavor
began with a desire to make sure the printer you use actually supports the type of data that's to be printed and the delivery mechanism (representation class) you intend to use. This might seem like an unnecessary step, but, in reality, you may be surprised at which document types a given printer supports. For example, the text-oriented types just described might seem as though they'd be the simplest ones to support, so, if your application is printing plain or HTML text, you might be tempted to simply select the first available print service and send the output to that printer. As it turns out, though, many printers don't support the text-based representation classes, and, if you attempt to send output to a printer that doesn't support the DocFlavor
you select, an exception will be thrown like the following:
Exception in thread "main" sun.print.PrintJobFlavorException: invalid flavor
at sun.print.Win32PrintJob.print(Win32PrintJob.java:290)
at PrintTest.main(PrintTest.java:11)
Now that you've seen how to obtain a reference to a DocFlavor
and I've discussed the importance of selecting a printer that supports the selected flavor, I'll show how you can use it to make sure you use a printer that supports the flavor you need. As I discussed earlier, the lookupPrintServices()
allows you to specify a DocFlavor
as its first argument, and, if you specify a non-null value, the method will return only the PrintService
instances that correspond to printers that support the specified DocFlavor
. For example, the following code will retrieve an array that identifies all printers on your system that can print gif images that are referenced via a URL:
DocFlavor flavor = DocFlavor.URL.GIF;
PrintService[] services = PrintServiceLookup.lookupPrintServices(flavor, null);
Alternatively, if your application has already retrieved a reference to a PrintService
and you want to determine whether it supports a particular flavor, you can call the isDocFlavorSupported()
method. In the following code segment, a reference to the default printer is obtained, and an error message will be displayed if it's not able to print a gif image retrieved via a URL:
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
DocFlavor flavor = DocFlavor.URL.GIF;
if (!service.isDocFlavorSupported(flavor)) {
System.err.println("The printer does not support the appropriate DocFlavor");
}
AttributeSet
As you've now seen, a DocFlavor
describes the data to be printed and can be used to ensure that a PrintService
supports the corresponding type of data. However, your application may also need to select a printer based upon the features that the printer supports. For example, if you're printing a graph that uses different colors to convey information, you might want to see if a given service supports color printing and, if not, either prevent the printer from being used or render a representation of the graph that doesn't rely on colors.
Characteristics such as the ability to print in color, to print on both sides of a page, or to use different orientations (portrait or landscape) are referred to as a printer's attributes, and the javax.print.attribute
package contains many classes and interfaces you can use to describe those attributes. One of those interfaces is AttributeSet
, which was mentioned earlier as the second parameter that can be specified on a call to lookupPrintServices()
. As you might expect, an implementation of AttributeSet
represents a collection of attributes, and specifying a non-null value on the call to lookupPrintServices()
will result in only print services being returned that support those attributes. In other words, if you specify both a DocFlavor
and an AttributeSet
on a call to lookupPrintServices()
, the method will return only those printers that support both the specified flavor and the appropriate attributes.
Attribute
Given that an AttributeSet
is a collection of attributes, the obvious question is, how do you go about specifying the attribute values that should make up that collection? The javax.print.attribute
package also includes an interface named Attribute
, and, as you'll see shortly, you create the collection of attributes by adding instances of Attribute
to an AttributeSet
by calling the add()
method. Reviewing the documentation for the Attribute
interface reveals that a large number of implementations are defined within the javax.print.attribute.standard
package, and it's those classes you'll use. Before you see how that's done, it's helpful to review the other interfaces in the javax.print.attribute
package along with their implementations.
Attribute roles
So far, I've described attributes as capabilities of a print service, and while that's largely true, it's really something of an oversimplification, at least in terms of how Java supports attributes. For each different attribute, Java associates it with one or more "roles," and the attribute is valid only in the context of the role(s) with which it's assigned. In other words, various places within the Java print service attributes are used, and not every attribute is valid within every context.
To better understand this, consider the OrientationRequested
and ColorSupported
implementations of Attribute
that are defined within the javax.print.attribute.standard
package. The OrientationRequested
attribute is one you can specify when creating a document to be printed and allows you to specify the orientation (such as portrait or landscape) that should be used when printing the document. In contrast, ColorSupported
is an attribute that can be returned when you call the getAttributes()
method of the PrintService
interface. In other words, OrientationRequested
is an attribute you use to pass information to the print service, and ColorSupported
is one that the print service uses to provide you with information about the printer's abilities. You can't specify ColorSupported
as an attribute when creating a document to be printed because the printer's ability to print in color isn't something your application is able to control.
Interfaces and implementations
When you first look at the interfaces and classes defined in the javax.print.attribute
package, it may appear to present a confusing list of choices when it comes to the interfaces and classes defined there. Aside from the Attribute
and AttributeSet
interfaces and the HashAttributeSet
class that implements AttributeSet
, the javax.print.attribute
package has four sets of subinterfaces and classes, as shown in Table 4 and Figure 1.
Table 4. Interfaces and classes defined within the javax.print.attribute packageAttribute subinterface AttributeSet subinterface AttributeSet subclass DocAttribute
DocAttributeSet
HashDocAttributeSet
PrintJobAttribute
PrintJobAttributeSet
HashPrintJobAttributeSet
PrintRequestAttribute
PrintRequestAttributeSet
HashPrintRequestAttributeSet
PrintServiceAttribute
PrintServiceAttribute PrintServiceAttributeSet
HashPrintServiceAttributeSet
Figure 1. The class hierarchy of a portion of the javax.print.attribute package. Click on thumbnail to view full-sized image.
|
So why do you need all these various interfaces and implementations, particularly since the more generalized Attribute
, AttributeSet
, and HashAttributeSet
are provided? The answer is that these specializations are defined to ensure that only the appropriate attributes are used within the role(s) where they're valid. For example, I mentioned that one place where you can use attributes is when creating a document that's to be printed and that some attributes such as ColorSupported
aren't valid within that context. When creating such a document, you'll use the DocAttributeSet
interface (or more specifically, its HashDocAttributeSet
implementation), and the implementation will allow you to add only attributes that implement the DocAttribute
interface. The four different types of roles are as follows:
- Doc: Specified when creating a document that's to be printed to describe how the document should be printed
- PrintJob: Attributes returned from the print job to describe the state of the job
- PrintRequest: Attributes passed to the print job when a request is made to initiate printing
- PrintService: Returned by a
PrintService
to describe the capabilities of the service
To see how this works, let's create an instance of a DocAttributeSet
and then attempt to set both the OrientationRequested
and ColorSupported
attributes for that AttributeSet
. The HashDocAttributeSet
defines a no-argument constructor, so you can create an instance easily as follows:
DocAttributeSet attrs = new HashDocAttributeSet();
Now that you've created the AttributeSet
, you can call its add()
method and pass to it instances of Attribute
implementations. If you examine the documentation for the OrientationRequested
class, you'll see it includes references to a number of static OrientationRequest
instances, with each one corresponding to a document orientation, such as portrait or landscape. To specify the orientation you want, all you need to do is pass a reference to the appropriate static instance to the add()
method as follows:
DocAttributeSet attrs = new HashDocAttributeSet();
attrs.add(OrientationRequested.PORTRAIT);
The ColorSupported
class is slightly different but equally simple to use, and it defines two static instances: one that indicates that color printing is supported and another that indicates it's not supported. You can attempt to add a ColorSupported
attribute to the DocAttributeSet
with code like this:
DocAttributeSet attrs = new HashDocAttributeSet();
attrs.add(OrientationRequested.PORTRAIT);
attrs.add(ColorSupported.SUPPORTED);
As mentioned earlier, it's not appropriate to specify whether to support color printing because this isn't something an application is allowed to control. In other words, the ColorSupported
attribute isn't valid within the context of a set of document attributes, and, as a result, attempting to run the previous code will cause a ClassCastException
to be thrown when it attempts to add the ColorSupported
attribute.
To understand how this works, remember that each AttributeSet
subinterface (in this case, DocAttributeSet
) has a corresponding Attribute
subinterface (DocAttribute
) and an implementation class (HashDocAttributeSet
). When an attempt is made to add an attribute, the implementation class tries to cast the Attribute
parameter to the corresponding subinterface type, which, in turn, ensures that only attributes appropriate for that context can be added successfully.
In this case, the add()
method of HashDocAttributeSet
is first called with an instance of OrientationRequested
, and it successfully casts that object to a DocAttribute
, because, as Figure 2 shows, OrientationRequested
implements that interface. In contrast, however, passing an instance of ColorSupported
fails because ColorSupported
doesn't implement DocAttribute
.
Figure 2. The class hierarchy of a portion of the javax.print.attribute package. Click on thumbnail to view full-sized image.
|
As this example illustrates, the four different groups of interfaces and classes shown in Table 4 ensure that only the appropriate attributes are used within the appropriate context. Notice that a great deal of overlap occurs between roles and the various attributes, so many of the attributes are associated with more than one role. For example, many of the attributes implement both PrintJobAttribute
and PrintRequestAttribute
because many of the attributes that are maintained and provided to you by a print job correspond to attributes you can specify when you request that printing be initiated. You can, for instance, both specify the job name by adding it to a PrintRequestAttributeSet
and retrieve the name of the job during printing by retrieving it from a PrintJobAttributeSet
. As a result, the JobName
attribute class implements both PrintRequestAttribute
and PrintJobAttribute
.
AttributeSet and HashAttributeSet
You've now seen why the four groups of subclasses exist, but what about the base AttributeSet
interface and the HashAttributeSet
superclass? AttributeSet
/HashAttributeSet
is used in situations where you can't assume that only attributes associated with a single role will need to be stored in a collection. Remember that earlier I mentioned that the lookupPrintServices()
method allows you to specify an AttributeSet
parameter that will limit which print services are returned. On the surface it might appear that it'd be better to require that an instance of PrintServiceAttributeSet
be specified, but many of the attributes you might want to specify don't implement PrintServiceAttribute
.
Let's assume you want the lookupPrintServices()
method to retrieve only services that support both color printing and landscape printing. Those attributes correspond to the ColorSupported
and OrientationRequested
attributes, respectively, but notice that those two attribute classes don't share a common role: ColorSupported
is a PrintServiceAttribute
, and OrientationRequested
is associated with all three of the other roles (Doc
, PrintRequest
, and PrintJob
), as shown in Figure 2. What this means is that there's no single role-specific AttributeSet
interface/class that can contain both a ColorSupported
attribute and a Sides
attribute.
The way to create an AttributeSet
that contains both an OrientationRequested
and a ColorSupported
instance is to simply use an instance of the generic HashAttributeSet
. Unlike its subclasses, it doesn't limit you to adding attributes associated with a particular role, so you can successfully execute the following code:
AttributeSet attrs = new HashAttributeSet();
attrs.add(ColorSupported.SUPPORTED);
attrs.add(OrientationRequested.LANDSCAPE);
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, attrs);
Printer selection via user interface
Up to this point, I've assumed that the printer to be used would be selected programmatically by the application. In practice, however, it's more common to simply display a dialog and allow the user to select which printer to use when printing the output. Fortunately, Java makes it easy to do just that by using the static printDialog()
method in the ServiceUI
class defined within the javax.print
package.
Aside from the location of the dialog to be displayed, the only parameter values that must be specified on the call to printDialog()
are these:
- An array of
PrintService
instances from which the user can choose. - The default
PrintService
. - An instance of
PrintRequestAttributeSet
. This is used to populate the dialog that's displayed, and it returns any changes that were made by the user before the dialog was dismissed.
To illustrate how this works, you can use the following simple code segment to display a print dialog:
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
PrintService svc = PrintServiceLookup.lookupDefaultPrintService();
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
PrintService selection = ServiceUI.printDialog(
null, 100, 100, services, svc, null, attrs);
When run, the code produces a dialog like the one shown in Figure 3.
Figure 3. The printer dialog
|
As this code illustrates, the value returned from the printDialog()
method is an instance of PrintService
that identifies which printer the user selected or null if the user canceled the printer dialog. In addition, the PrintRequestAttributeSet
is updated to reflect any changes made by the user through the dialog, such as the number of copies to be printed.
By using the printDialog()
method, you can allow users to select which printer their output will be sent to, providing the kind of functionality that users have come to expect from professional applications.
Creating a print job
This is the simplest step involved in printing, because once you've obtained a reference to a PrintService
, all you need to do is call its createPrintJob()
method like so:
PrintService service;
.
.
.
DocPrintJob job = service.createPrintJob();
As indicated in the code, the value returned from createPrintJob()
is an instance of DocPrintJob
, an object that allows you to control and monitor the status of the printing operation. To initiate printing, you'll call the DocPrintJob
object's print()
method, but, before you do so, you'll need to define the document to be printed and optionally a PrintRequestAttributeSet
. You've already seen how to construct and populate an AttributeSet
, so I won't review that step; instead, you'll see how you go about defining the document to be printed.
Defining the document to print
The next step in printing is to define the document that's to be printed, which is done by creating an instance of an implementation of the Doc
interface defined in the javax.print
package. Each instance of Doc
has two mandatory attributes and an optional one:
- An
Object
that represents the data to be printed - An instance of
DocFlavor
that describes the type of data to print - An optional
DocAttributeSet
containing attributes to use when printing the document
Reviewing the documentation for the Doc
interface reveals that the javax.print
package includes an implementation of the interface named SimpleDoc
, which has a constructor that takes three arguments that match the three attributes described previously. To see how to construct an instance of SimpleDoc
, let's assume you want to print two copies of a gif image that's stored at http://www.apress.com/ApressCorporate/supplement/1/421/bcm.gif.
All that's needed to construct a SimpleDoc
instance that describes the document to be printed is to create a URL that points to the image, obtain a reference to the appropriate DocFlavor
, and pass those two objects to the SimpleDoc
constructor as follows:
URL url = new URL(
"http://www.apress.com/ApressCorporate/supplement/1/421/bcm.gif");
DocFlavor flavor = DocFlavor.URL.GIF;
SimpleDoc doc = new SimpleDoc(url, flavor, null);
Initiating printing
The final step involved in printing is to call the DocPrintJob
's print()
method, passing it the Doc
object that describes the data to be printed and optionally an instance of PrintRequestAttributeSet
. For the sake of simplicity, I'll assume the default printer supports the flavor and attributes you need, in which case you could use the following code to print two copies of the gif file referenced in the previous example:
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
DocPrintJob job = service.createPrintJob();
URL url = new URL(
"http://www.apress.com/ApressCorporate/supplement/1/421/bcm.gif ");
DocFlavor flavor = DocFlavor.URL.GIF;
Doc doc = new SimpleDoc(url, flavor, null);
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
attrs.add(new Copies(2));
job.print(doc, attrs);
Note that, in some cases, printing is performed asynchronously, in which case, the call to print()
may return before printing has actually completed
No comments:
Post a Comment