Printing


The original Java Development Kit had no support for printing at all. It was not possible to print from applets, and you had to get a third-party library if you wanted to print in an application. JDK 1.1 introduced very lightweight printing support, just enough to produce simple printouts, as long as you were not too particular about the print quality. The 1.1 printing model was designed to allow browser vendors to print the surface of an applet as it appears on a web page (which, however, the browser vendors have not embraced).

JDK 1.2 introduced the beginnings of a robust printing model that is fully integrated with 2D graphics, and JDK 1.3 provided minor improvements. JDK 1.4 adds important enhancements, such as discovery of printer features and streaming print jobs for server-side print management.

In this section, we show you how you can easily print a drawing on a single sheet of paper, how you can manage a multipage printout, and how you can benefit from the elegance of the Java 2D imaging model and easily generate a print preview dialog box.

NOTE

The Java platform also supports the printing of user interface components. We do not cover this topic because it is mostly of interest to implementors of browsers, screen grabbers, and so on. For more information on printing components, see http://java.sun.com/developer/onlineTraining/Programming/JDCBook/render.html.


Graphics Printing

In this section, we tackle what is probably the most common printing situation: to print a 2D graphic. Of course, the graphic can contain text in various fonts or even consist entirely of text.

To generate a printout, you take care of these two tasks:

  • Supply an object that implements the Printable interface.

  • Start a print job.

The Printable interface has a single method:

 int print(Graphics g, PageFormat format, int page) 

That method is called whenever the print engine needs to have a page formatted for printing. Your code draws the text and image that are to be printed onto the graphics context. The page format tells you the paper size and the print margins. The page number tells you which page to render.

To start a print job, you use the PrinterJob class. First, you call the static getPrinterJob method to get a print job object. Then set the Printable object that you want to print.

 Printable canvas = . . .; PrinterJob job = PrinterJob.getPrinterJob(); job.setPrintable(canvas); 

CAUTION

The class PrintJob handles JDK 1.1-style printing. That class is now obsolete. Do not confuse it with the PrinterJob class.


Before starting the print job, you should call the printDialog method to display a print dialog box (see Figure 7-34). That dialog box gives the user a chance to select the printer to be used (in case multiple printers are available), the page range that should be printed, and various printer settings.

Figure 7-34. A cross-platform print dialog box


You collect printer settings in an object of a class that implements the PrintRequestAttributeSet interface to the printDialog method. The JDK provides a HashPrintRequestAttributeSet class for that purpose.

 HashPrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet(); 

Add attribute settings and pass the attributes object to the printDialog method.

The printDialog method returns true if the user clicked OK and false if the user canceled the dialog box. If the user accepted, call the print method of the PrinterJob class to start the printing process. The print method may throw a PrinterException. Here is the outline of the printing code:

 if (job.printDialog(attributes)) {    try    {       job.print(attributes);    }    catch (PrinterException exception)    {       . . .    } } 

NOTE

If you must support JDK 1.2 or 1.3, modify your program to display the native print dialog box instead of the cross-platform dialog box that was added in JDK 1.4. See the sidebar at the end of this section for details.


During printing, the print method of the PrinterJob class makes repeated calls to the print method of the Printable object associated with the job.

Because the job does not know how many pages you want to print, it simply keeps calling the print method. As long as the print method returns the value Printable.PAGE_EXISTS, the print job keeps producing pages. When the print method returns Printable.NO_SUCH_PAGE, the print job stops.

CAUTION

The page numbers that the print job passes to the print method start with page 0.


Therefore, the print job doesn't have an accurate page count until after the printout is complete. For that reason, the print dialog box can't display the correct page range and instead displays a page range of "Pages 1 to 1." You will see in the next section how to avoid this blemish by supplying a Book object to the print job.

During the printing process, the print job repeatedly calls the print method of the Printable object. The print job is allowed to make multiple calls for the same page. You should therefore not count pages inside the print method but always rely on the page number parameter. There is a good reason why the print job may call the print method repeatedly for the same page. Some printers, in particular dot-matrix and inkjet printers, use banding. They print one band at a time, advance the paper, and then print the next band. The print job may use banding even for laser printers that print a full page at a timeit gives the print job a way of managing the size of the spool file.

If the print job needs the Printable object to print a band, then it sets the clip area of the graphics context to the requested band and calls the print method. Its drawing operations are clipped against the band rectangle, and only those drawing elements that show up in the band are rendered. Your print method need not be aware of that process, with one caveat: It should not interfere with the clip area.

CAUTION

The Graphics object that your print method gets is also clipped against the page margins. If you replace the clip area, you can draw outside the margins. Especially in a printer graphics context, the clipping area must be respected. Call clip, not setClip, to further restrict the clipping area. If you must remove a clip area, then make sure to call getClip at the beginning of your print method and restore that clip area.


The PageFormat parameter of the print method contains information about the printed page. The methods getWidth and getHeight return the paper size, measured in points. One point is 1/72 of an inch. (An inch equals 25.4 millimeters.) For example, A4 paper is approximately 595 by 842 points, and U.S. letter-size paper is 612 by 792 points.

Points are a common measurement in the printing trade in the United States. Much to the chagrin of the rest of the world, the printing package uses point units for two purposes. Paper sizes and paper margins are measured in points. And the default unit for all print graphics contexts is one point. You can verify that in the example program at the end of this section. The program prints two lines of text that are 72 units apart. Run the example program and measure the distance between the baselines. They are exactly 1 inch or 25.4 millimeters apart.

The getWidth and getHeight methods of the PageFormat class give you the complete paper size. Not all of the paper area is printable. Users typically select margins, and even if they don't, printers need to somehow grip the sheets of paper on which they print and therefore have a small unprintable area around the edges.

The methods getImageableWidth and getImageableHeight tell you the dimensions of the area that you can actually fill. However, the margins need not be symmetrical, so you must also know the top-left corner of the imageable area (see Figure 7-35), which you obtain by the methods getImageableX and getImageableY.

Figure 7-35. Page format measurements


TIP

The graphics context that you receive in the print method is clipped to exclude the margins, but the origin of the coordinate system is nevertheless the top-left corner of the paper. It makes sense to translate the coordinate system to start at the top-left corner of the imageable area. Simply start your print method with

 g.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); 


If you want your users to choose the settings for the page margins or to switch between portrait and landscape orientation without setting other printing attributes, then you can call the pageDialog method of the PrinterJob class:

 PageFormat format = job.pageDialog(attributes); 

NOTE

One of the tabs of the print dialog box contains the page setup dialog box (see Figure 7-36). You may still want to give users an option to set the page format before printing, especially if your program presents a "what you see is what you get" display of the pages to be printed. The pageDialog method returns a PageFormat object with the user settings.

Figure 7-36. A cross-platform page setup dialog box



Example 7-12 shows how to render the same set of shapes on the screen and on the printed page. A subclass of JPanel implements the Printable interface. Both the paintComponent and the print methods call the same method to carry out the actual drawing.

 class PrintPanel extends JPanel implements Printable {    public void paintComponent(Graphics g)    {       super.paintComponent(g);       Graphics2D g2 = (Graphics2D) g;       drawPage(g2);    }    public int print(Graphics g, PageFormat pf, int page)       throws PrinterException    {       if (page >= 1) return Printable.NO_SUCH_PAGE;       Graphics2D g2 = (Graphics2D) g;       g2.translate(pf.getImageableX(), pf.getImageableY());       drawPage(g2);       return Printable.PAGE_EXISTS;    }    public void drawPage(Graphics2D g2)    {       // shared drawing code goes here       . . .    }    . . . } 

CAUTION

JDK 1.2 does not set the paint to black when setting up a printer graphics context. If your pages come out all white, add a line g2.setPaint(Color.black) at the top of the print method. This problem has been fixed in JDK 1.3.


This example displays and prints the same image as Example 7-6 on page 498, namely, the outline of the message "Hello, World" that is used as a clipping area for a pattern of lines (see Figure 7-22 on page 497).

Click the Print button to start printing, or click the Page setup button to open the page setup dialog box. Example 7-12 shows the code..

Using Native Print Dialog Boxes

Prior to JDK 1.4, the printing system used the native print and page setup dialog boxes of the host platform. To show a native print dialog box, call the printDialog method with no parameters. (There is no way to collect user settings in an attribute set.) Figure 7-37 shows a Windows print dialog box.

Figure 7-37. A Windows print dialog box


One potential advantage of using the native print dialog box is that some printer drivers have special features that are not accessible through the cross-platform dialog box. At any rate, to support JDK 1.2 and 1.3, show the native dialog boxes.

To show a native page setup dialog box, you pass a default PageFormat object to the pageDialog method. The method clones that object, modifies it according to the user selections in the dialog bpx, and returns the cloned object.

 PageFormat defaultFormat = printJob.defaultPage(); PageFormat selectedFormat = printJob.pageDialog(defaultFormat); 

Figure 7-38 shows a page setup dialog box for the Windows operating system.

Figure 7-38. A Windows page setup dialog box



Example 7-12. PrintTest.java

[View full width]

   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.awt.font.*;   4. import java.awt.geom.*;   5. import java.awt.print.*;   6. import java.util.*;   7. import javax.print.*;   8. import javax.print.attribute.*;   9. import javax.swing.*;  10.  11. /**  12.    This program demonstrates how to print 2D graphics  13. */  14. public class PrintTest  15. {  16.    public static void main(String[] args)  17.    {  18.       JFrame frame = new PrintTestFrame();  19.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  20.       frame.setVisible(true);  21.    }  22. }  23.  24. /**  25.    This frame shows a panel with 2D graphics and buttons  26.    to print the graphics and to set up the page format.  27. */  28. class PrintTestFrame extends JFrame  29. {  30.    public PrintTestFrame()  31.    {  32.       setTitle("PrintTest");  33.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  34.  35.       canvas = new PrintPanel();  36.       add(canvas, BorderLayout.CENTER);  37.  38.       attributes = new HashPrintRequestAttributeSet();  39.  40.       JPanel buttonPanel = new JPanel();  41.       JButton printButton = new JButton("Print");  42.       buttonPanel.add(printButton);  43.       printButton.addActionListener(new  44.          ActionListener()  45.          {  46.             public void actionPerformed(ActionEvent event)  47.             {  48.                try  49.                {  50.                   PrinterJob job = PrinterJob.getPrinterJob();  51.                   job.setPrintable(canvas);  52.                   if (job.printDialog(attributes))  53.                      job.print(attributes);  54.                }  55.                catch (PrinterException e)  56.                {  57.                   JOptionPane.showMessageDialog(PrintTestFrame.this, e);  58.                }  59.             }  60.          });  61.  62.       JButton pageSetupButton = new JButton("Page setup");  63.       buttonPanel.add(pageSetupButton);  64.       pageSetupButton.addActionListener(new  65.          ActionListener()  66.          {  67.             public void actionPerformed(ActionEvent event)  68.             {  69.                PrinterJob job = PrinterJob.getPrinterJob();  70.                job.pageDialog(attributes);  71.             }  72.          });  73.  74.       add(buttonPanel, BorderLayout.NORTH);  75.    }  76.  77.    private PrintPanel canvas;  78.    private PrintRequestAttributeSet attributes;  79.  80.    private static final int DEFAULT_WIDTH = 300;  81.    private static final int DEFAULT_HEIGHT = 300;  82. }  83.  84. /**  85.    This panel generates a 2D graphics image for screen display  86.    and printing.  87. */  88. class PrintPanel extends JPanel implements Printable  89. {  90.    public void paintComponent(Graphics g)  91.    {  92.       super.paintComponent(g);  93.       Graphics2D g2 = (Graphics2D) g;  94.       drawPage(g2);  95.    }  96.  97.    public int print(Graphics g, PageFormat pf, int page)  98.       throws PrinterException  99.    { 100.       if (page >= 1) return Printable.NO_SUCH_PAGE; 101.       Graphics2D g2 = (Graphics2D) g; 102.       g2.translate(pf.getImageableX(), pf.getImageableY()); 103.       g2.draw(new Rectangle2D.Double(0, 0, pf.getImageableWidth(), pf .getImageableHeight())); 104. 105.       drawPage(g2); 106.       return Printable.PAGE_EXISTS; 107.    } 108. 109.    /** 110.       This method draws the page both on the screen and the 111.       printer graphics context. 112.       @param g2 the graphics context 113.    */ 114.    public void drawPage(Graphics2D g2) 115.    { 116.       FontRenderContext context = g2.getFontRenderContext(); 117.       Font f = new Font("Serif", Font.PLAIN, 72); 118.       GeneralPath clipShape = new GeneralPath(); 119. 120.       TextLayout layout = new TextLayout("Hello", f, context); 121.       AffineTransform transform = AffineTransform.getTranslateInstance(0, 72); 122.       Shape outline = layout.getOutline(transform); 123.       clipShape.append(outline, false); 124. 125.       layout = new TextLayout("World", f, context); 126.       transform  = AffineTransform.getTranslateInstance(0, 144); 127.       outline = layout.getOutline(transform); 128.       clipShape.append(outline, false); 129. 130.       g2.draw(clipShape); 131.       g2.clip(clipShape); 132. 133.       final int NLINES =50; 134.       Point2D p = new Point2D.Double(0, 0); 135.       for (int i = 0; i < NLINES; i++) 136.       { 137.          double x = (2 * getWidth() * i) / NLINES; 138.          double y = (2 * getHeight() * (NLINES - 1 - i)) 139.             / NLINES; 140.          Point2D q = new Point2D.Double(x, y); 141.          g2.draw(new Line2D.Double(p, q)); 142.       } 143.    } 144. } 


 java.awt.print.Printable 1.2 

  • int print(Graphics g, PageFormat format, int pageNumber)

    renders a page and returns PAGE_EXISTS, or returns NO_SUCH_PAGE.

    Parameters:

    g

    The graphics context onto which the page is rendered

     

    format

    The format of the page to draw on

     

    pageNumber

    The number of the requested page



 java.awt.print.PrinterJob 1.2 

  • static PrinterJob getPrinterJob()

    returns a printer job object.

  • PageFormat defaultPage()

    returns the default page format for this printer.

  • boolean printDialog(PrintRequestAttributeSet attributes)

  • boolean printDialog()

    open print dialog boxes to allow a user to select the pages to be printed and to change print settings. The first method displays a cross-platform dialog box, the second a native dialog box. The first method modifies the attributes object to reflect the user settings. Both methods return TRue if the user accepts the dialog box.

  • PageFormat pageDialog(PrintRequestAttributeSet attributes)

  • PageFormat pageDialog(PageFormat defaults)

    display page setup dialog boxes. The first method displays a cross-platform dialog box, the second a native dialog box. Both methods return a PageFormat object with the format that the user requested in the dialog. The first method modifies the attributes object to reflect the user settings. The second method does not modify the defaults object.

  • void setPrintable(Printable p)

  • void setPrintable(Printable p, PageFormat format)

    set the Printable of this print job and an optional page format.

  • void print()

  • void print(PrintRequestAttributeSet attributes)

    prints the current Printable by repeatedly calling its print method and sending the rendered pages to the printer, until no more pages are available.


 java.awt.print.PageFormat 1.2 

  • double getWidth()

  • double getHeight()

    return the width and height of the page.

  • double getImageableWidth()

  • double getImageableHeight()

    return the width and height of the imageable area of the page.

  • double getImageableX()

  • double getImageableY()

    return the position of the top-left corner of the imageable area.

  • int getOrientation()

    returns one of PORTRAIT, LANDSCAPE, REVERSE_LANDSCAPE. Page orientation is transparent to programmers since the page format and graphics context settings automatically reflect the page orientation.

Multiple-Page Printing

In practice, you usually shouldn't pass a raw Printable object to a print job. Instead, you should obtain an object of a class that implements the Pageable interface. The Java platform supplies one such class, called Book. A book is made up of sections, each of which is a Printable object. You make a book by adding Printable objects and their page counts.

 Book book = new Book(); Printable coverPage = . . .; Printable bodyPages = . . .; book.append(coverPage, pageFormat); // append 1 page book.append(bodyPages, pageFormat, pageCount); 

Then, you use the setPageable method to pass the Book object to the print job.

 printJob.setPageable(book); 

Now the print job knows exactly how many pages to print. Then, the print dialog box displays an accurate page range, and the user can select the entire range or subranges.

CAUTION

When the print job calls the print methods of the Printable sections, it passes the current page number of the book, and not of each section, as the current page number. That is a huge paineach section must know the page counts of the preceding sections to make sense of the page number parameter.


From your perspective as a programmer, the biggest challenge about using the Book class is that you must know how many pages each section will have when you print it. Your Printable class needs a layout algorithm that computes the layout of the material on the printed pages. Before printing starts, invoke that algorithm to compute the page breaks and the page count. You can retain the layout information so you have it handy during the printing process.

You must guard against the possibility that the user has changed the page format. If that happens, you must recompute the layout, even if the information that you want to print has not changed.

Example 7-13 shows how to produce a multipage printout. This program prints a message in very large characters on a number of pages (see Figure 7-39). You can then trim the margins and tape the pages together to form a banner.

Figure 7-39. A banner


The layoutPages method of the Banner class computes the layout. We first lay out the message string in a 72-point font. We then compute the height of the resulting string and compare it with the imageable height of the page. We derive a scale factor from these two measurements. When printing the string, we magnify it by that scale factor.

CAUTION

To lay out your information precisely, you usually need access to the printer graphics context. Unfortunately, there is no way to obtain that graphics context until printing actually starts. In our example program, we make do with the screen graphics context and hope that the font metrics of the screen and printer match.


The getPageCount method of the Banner class first calls the layout method. Then it scales up the width of the string and divides it by the imageable width of each page. The quotient, rounded up to the next integer, is the page count.

It sounds like it might be difficult to print the banner since characters can be broken across multiple pages. However, thanks to the power of the Java 2D API, this turns out not to be a problem at all. When a particular page is requested, we simply use the translate method of the Graphics2D class to shift the top-left corner of the string to the left. Then, we set a clip rectangle that equals the current page (see Figure 7-40). Finally, we scale the graphics context with the scale factor that the layout method computed.

Figure 7-40. Printing a page of a banner


This example shows the power of transformations. The drawing code is kept simple, and the transformation does all the work of placing the drawing at the appropriate place. Finally, the clip cuts away the part of the image that falls outside the page. In the next section, you will see another compelling use of transformations, to display a print preview.

Print Preview

Most professional programs have a print preview mechanism that lets you look at your pages on the screen so that you won't waste paper on a printout that you don't like. The printing classes of the Java platform do not supply a standard "print preview" dialog box, but it is easy to design your own (see Figure 7-41). In this section, we show you how. The PrintPreviewDialog class in Example 7-13 is completely genericyou can reuse it to preview any kind of printout.

Figure 7-41. A print preview dialog box


To construct a PrintPreviewDialog, you supply either a Printable or a Book, together with a PageFormat object. The surface of the dialog box contains a PrintPreviewCanvas. As you use the Next and Previous buttons to flip through the pages, the paintComponent method calls the print method of the Printable object for the requested page.

Normally, the print method draws the page context on a printer graphics context. However, we supply the screen graphics context, suitably scaled so that the entire printed page fits inside a small screen rectangle.

 float xoff = . . .; // left of page float yoff = . . .; // top of page float scale = . . .; // to fit printed page onto screen g2.translate(xoff, yoff); g2.scale(scale, scale); Printable printable = book.getPrintable(currentPage); printable.print(g2, pageFormat, currentPage); 

The print method never knows that it doesn't actually produce printed pages. It simply draws onto the graphics context, thereby producing a microscopic print preview on the screen. This is a compelling demonstration of the power of the Java 2D imaging model.

Example 7-13 contains the code for the banner printing program and the print preview dialog box. Type "Hello, World!" into the text field and look at the print preview, then print the banner.

Example 7-13. BookTest.java

[View full width]

   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.awt.font.*;   4. import java.awt.geom.*;   5. import java.awt.print.*;   6. import java.util.*;   7. import javax.print.*;   8. import javax.print.attribute.*;   9. import javax.swing.*;  10.  11. /**  12.    This program demonstrates the printing of a multipage  13.    book. It prints a "banner," by blowing up a text string  14.    to fill the entire page vertically. The program also  15.    contains a generic print preview dialog box.  16. */  17. public class BookTest  18. {  19.    public static void main(String[] args)  20.    {  21.       JFrame frame = new BookTestFrame();  22.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  23.       frame.setVisible(true);  24.    }  25. }  26.  27. /**  28.    This frame has a text field for the banner text and  29.    buttons for printing, page setup, and print preview.  30. */  31. class BookTestFrame extends JFrame  32. {  33.    public BookTestFrame()  34.    {  35.       setTitle("BookTest");  36.  37.       text = new JTextField();  38.       add(text, BorderLayout.NORTH);  39.  40.       attributes = new HashPrintRequestAttributeSet();  41.  42.       JPanel buttonPanel = new JPanel();  43.  44.       JButton printButton = new JButton("Print");  45.       buttonPanel.add(printButton);  46.       printButton.addActionListener(new  47.          ActionListener()  48.          {  49.             public void actionPerformed(ActionEvent event)  50.             {  51.                try  52.                {  53.                   PrinterJob job = PrinterJob.getPrinterJob();  54.                   job.setPageable(makeBook());  55.                   if (job.printDialog(attributes))  56.                   {  57.                      job.print(attributes);  58.                   }  59.                }  60.                catch (PrinterException e)  61.                {  62.                   JOptionPane.showMessageDialog(  63.                      BookTestFrame.this, e);  64.                }  65.             }  66.          });  67.  68.       JButton pageSetupButton = new JButton("Page setup");  69.       buttonPanel.add(pageSetupButton);  70.       pageSetupButton.addActionListener(new  71.          ActionListener()  72.          {  73.             public void actionPerformed(ActionEvent event)  74.             {  75.                PrinterJob job = PrinterJob.getPrinterJob();  76.                pageFormat = job.pageDialog(attributes);  77.             }  78.          });  79.  80.       JButton printPreviewButton = new JButton("Print preview");  81.       buttonPanel.add(printPreviewButton);  82.       printPreviewButton.addActionListener(new  83.          ActionListener()  84.          {  85.             public void actionPerformed(ActionEvent event)  86.             {  87.                PrintPreviewDialog dialog = new PrintPreviewDialog(makeBook());  88.                dialog.setVisible(true);  89.             }  90.          });  91.  92.       add(buttonPanel, BorderLayout.SOUTH);  93.       pack();  94.    }  95.  96.    /**  97.       Makes a book that contains a cover page and the  98.       pages for the banner.  99.    */ 100.    public Book makeBook() 101.    { 102.       if (pageFormat == null) 103.       { 104.          PrinterJob job = PrinterJob.getPrinterJob(); 105.          pageFormat = job.defaultPage(); 106.       } 107.       Book book = new Book(); 108.       String message = text.getText(); 109.       Banner banner = new Banner(message); 110.       int pageCount = banner.getPageCount((Graphics2D)getGraphics(), pageFormat); 111.       book.append(new CoverPage(message + " (" + pageCount + " pages)"), pageFormat); 112.       book.append(banner, pageFormat, pageCount); 113.       return book; 114.    } 115. 116.    private JTextField text; 117.    private PageFormat pageFormat; 118.    private PrintRequestAttributeSet attributes; 119. } 120. 121. /** 122.    A banner that prints a text string on multiple pages. 123. */ 124. class Banner implements Printable 125. { 126.    /** 127.       Constructs a banner 128.       @param m the message string 129.    */ 130.    public Banner(String m) 131.    { 132.       message = m; 133.    } 134. 135.    /** 136.       Gets the page count of this section. 137.       @param g2 the graphics context 138.       @param pf the page format 139.       @return the number of pages needed 140.    */ 141.    public int getPageCount(Graphics2D g2, PageFormat pf) 142.    { 143.       if (message.equals("")) return 0; 144.       FontRenderContext context = g2.getFontRenderContext(); 145.       Font f = new Font("Serif", Font.PLAIN, 72); 146.       Rectangle2D bounds = f.getStringBounds(message, context); 147.       scale = pf.getImageableHeight() / bounds.getHeight(); 148.       double width = scale * bounds.getWidth(); 149.       int pages = (int)Math.ceil(width / pf.getImageableWidth()); 150.       return pages; 151.    } 152. 153.    public int print(Graphics g, PageFormat pf, int page) 154.       throws PrinterException 155.    { 156.       Graphics2D g2 = (Graphics2D)g; 157.       if (page > getPageCount(g2, pf)) 158.          return Printable.NO_SUCH_PAGE; 159.       g2.translate(pf.getImageableX(), pf.getImageableY()); 160. 161.       drawPage(g2, pf, page); 162.       return Printable.PAGE_EXISTS; 163.    } 164. 165.    public void drawPage(Graphics2D g2, PageFormat pf, int page) 166.    { 167.       if (message.equals("")) return; 168.       page--; // account for cover page 169. 170.       drawCropMarks(g2, pf); 171.       g2.clip(new Rectangle2D.Double(0, 0, pf.getImageableWidth(), pf .getImageableHeight())); 172.       g2.translate(-page * pf.getImageableWidth(), 0); 173.       g2.scale(scale, scale); 174.       FontRenderContext context = g2.getFontRenderContext(); 175.       Font f = new Font("Serif", Font.PLAIN, 72); 176.       TextLayout layout = new TextLayout(message, f, context); 177.       AffineTransform transform = AffineTransform.getTranslateInstance(0, layout .getAscent()); 178.       Shape outline = layout.getOutline(transform); 179.       g2.draw(outline); 180.    } 181. 182.    /** 183.       Draws 1/2" crop marks in the corners of the page. 184.       @param g2 the graphics context 185.       @param pf the page format 186.    */ 187.    public void drawCropMarks(Graphics2D g2, PageFormat pf) 188.    { 189.       final double C = 36; // crop mark length = 1/2 inch 190.       double w = pf.getImageableWidth(); 191.       double h = pf.getImageableHeight(); 192.       g2.draw(new Line2D.Double(0, 0, 0, C)); 193.       g2.draw(new Line2D.Double(0, 0, C, 0)); 194.       g2.draw(new Line2D.Double(w, 0, w, C)); 195.       g2.draw(new Line2D.Double(w, 0, w - C, 0)); 196.       g2.draw(new Line2D.Double(0, h, 0, h - C)); 197.       g2.draw(new Line2D.Double(0, h, C, h)); 198.       g2.draw(new Line2D.Double(w, h, w, h - C)); 199.       g2.draw(new Line2D.Double(w, h, w - C, h)); 200.    } 201. 202.    private String message; 203.    private double scale; 204. } 205. 206. /** 207.    This class prints a cover page with a title. 208. */ 209. class CoverPage implements Printable 210. { 211.    /** 212.       Constructs a cover page. 213.       @param t the title 214.    */ 215.    public CoverPage(String t) 216.    { 217.       title = t; 218.    } 219. 220.    public int print(Graphics g, PageFormat pf, int page) 221.       throws PrinterException 222.    { 223.       if (page >= 1) return Printable.NO_SUCH_PAGE; 224.       Graphics2D g2 = (Graphics2D)g; 225.       g2.setPaint(Color.black); 226.       g2.translate(pf.getImageableX(), pf.getImageableY()); 227.       FontRenderContext context = g2.getFontRenderContext(); 228.       Font f = g2.getFont(); 229.       TextLayout layout = new TextLayout(title, f, context); 230.       float ascent = layout.getAscent(); 231.       g2.drawString(title, 0, ascent); 232.       return Printable.PAGE_EXISTS; 233.    } 234. 235.    private String title; 236. } 237. 238. /** 239.    This class implements a generic print preview dialog. 240. */ 241. class PrintPreviewDialog extends JDialog 242. { 243.    /** 244.       Constructs a print preview dialog. 245.       @param p a Printable 246.       @param pf the page format 247.       @param pages the number of pages in p 248.    */ 249.    public PrintPreviewDialog(Printable p, PageFormat pf, int pages) 250.    { 251.       Book book = new Book(); 252.       book.append(p, pf, pages); 253.       layoutUI(book); 254.    } 255. 256.    /** 257.       Constructs a print preview dialog. 258.       @param b a Book 259.    */ 260.    public PrintPreviewDialog(Book b) 261.    { 262.       layoutUI(b); 263.    } 264. 265.    /** 266.       Lays out the UI of the dialog. 267.       @param book the book to be previewed 268.    */ 269.    public void layoutUI(Book book) 270.    { 271.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 272. 273.       canvas = new PrintPreviewCanvas(book); 274.       add(canvas, BorderLayout.CENTER); 275. 276.       JPanel buttonPanel = new JPanel(); 277. 278.       JButton nextButton = new JButton("Next"); 279.       buttonPanel.add(nextButton); 280.       nextButton.addActionListener(new 281.          ActionListener() 282.          { 283.             public void actionPerformed(ActionEvent event) 284.             { 285.                canvas.flipPage(1); 286.             } 287.          }); 288. 289.       JButton previousButton = new JButton("Previous"); 290.       buttonPanel.add(previousButton); 291.       previousButton.addActionListener(new 292.          ActionListener() 293.          { 294.             public void actionPerformed(ActionEvent event) 295.             { 296.                canvas.flipPage(-1); 297.             } 298.          }); 299. 300.       JButton closeButton = new JButton("Close"); 301.       buttonPanel.add(closeButton); 302.       closeButton.addActionListener(new 303.          ActionListener() 304.          { 305.             public void actionPerformed(ActionEvent event) 306.             { 307.                setVisible(false); 308.             } 309.          }); 310. 311.       add(buttonPanel, BorderLayout.SOUTH); 312.    } 313. 314.    private PrintPreviewCanvas canvas; 315. 316.    private static final int DEFAULT_WIDTH = 300; 317.    private static final int DEFAULT_HEIGHT = 300; 318. } 319. 320. /** 321.    The canvas for displaying the print preview. 322. */ 323. class PrintPreviewCanvas extends JPanel 324. { 325.    /** 326.       Constructs a print preview canvas. 327.       @param b the book to be previewed 328.    */ 329.    public PrintPreviewCanvas(Book b) 330.    { 331.       book = b; 332.       currentPage = 0; 333.    } 334. 335.    public void paintComponent(Graphics g) 336.    { 337.       super.paintComponent(g); 338.       Graphics2D g2 = (Graphics2D)g; 339.       PageFormat pageFormat = book.getPageFormat(currentPage); 340. 341.       double xoff; // x offset of page start in window 342.       double yoff; // y offset of page start in window 343.       double scale; // scale factor to fit page in window 344.       double px = pageFormat.getWidth(); 345.       double py = pageFormat.getHeight(); 346.       double sx = getWidth() - 1; 347.       double sy = getHeight() - 1; 348.       if (px / py < sx / sy) // center horizontally 349.       { 350.          scale = sy / py; 351.          xoff = 0.5 * (sx - scale * px); 352.          yoff = 0; 353.       } 354.       else // center vertically 355.       { 356.          scale = sx / px; 357.          xoff = 0; 358.          yoff = 0.5 * (sy - scale * py); 359.       } 360.       g2.translate((float)xoff, (float)yoff); 361.       g2.scale((float)scale, (float)scale); 362. 363.       // draw page outline (ignoring margins) 364.       Rectangle2D page = new Rectangle2D.Double(0, 0, px, py); 365.       g2.setPaint(Color.white); 366.       g2.fill(page); 367.       g2.setPaint(Color.black); 368.       g2.draw(page); 369. 370.       Printable printable = book.getPrintable(currentPage); 371.       try 372.       { 373.          printable.print(g2, pageFormat, currentPage); 374.       } 375.       catch (PrinterException e) 376.       { 377.          g2.draw(new Line2D.Double(0, 0, px, py)); 378.          g2.draw(new Line2D.Double(px, 0, 0, py)); 379.       } 380.    } 381. 382.    /** 383.       Flip the book by the given number of pages. 384.       @param by the number of pages to flip by. Negative 385.       values flip backwards. 386.    */ 387.    public void flipPage(int by) 388.    { 389.       int newPage = currentPage + by; 390.       if (0 <= newPage && newPage < book.getNumberOfPages()) 391.       { 392.          currentPage = newPage; 393.          repaint(); 394.       } 395.    } 396. 397.    private Book book; 398.    private int currentPage; 399. } 


 java.awt.print.PrinterJob 1.2 

  • void setPageable(Pageable p)

    sets a Pageable (such as a Book) to be printed.


 java.awt.print.Book 1.2 

  • void append(Printable p, PageFormat format)

  • void append(Printable p, PageFormat format, int pageCount)

    append a section to this book. If the page count is not specified, the first page is added.

  • Printable getPrintable(int page)

    gets the printable for the specified page.

Print Services

So far, you have seen how to print 2D graphics. However, the printing API introduced in JDK 1.4 affords far greater flexibility. The API defines a number of data types and lets you find print services that are able to print them. Among the data types:

  • Images in GIF, JPEG, or PNG format

  • Documents in text, HTML, PostScript, or PDF format

  • Raw printer code data

  • Objects of a class that implements Printable, Pageable, or RenderableImage

The data themselves can be stored in a source of bytes or characters such as an input stream, a URL, or an array. A document flavor describes the combination of a data source and a data type. The DocFlavor class defines a number of inner classes for the various data sources. Each of the inner classes defines constants to specify the flavors. For example, the constant

 DocFlavor.INPUT_STREAM.GIF 

describes a GIF image that is read from an input stream. Table 7-3 lists the combinations.

Table 7-3. Document Flavors for Print Services

Data Source

Data Type

MIME Type

INPUT_STREAM

GIF

image/gif

URL

JPEG

image/jpeg

BYTE_ARRAY

PNG

image/png

 

POSTSCRIPT

application/postscript

 

PDF

application/pdf

 

TEXT_HTML_HOST

text/html (using host encoding)

 

TEXT_HTML_US_ASCII

text/html; charset=us-ascii

 

TEXT_HTML_UTF_8

text/html; charset=utf-8

 

TEXT_HTML_UTF_16

text/html; charset=utf-16

 

TEXT_HTML_UTF_16LE

text/html; charset=utf-16le (little-endian)

 

TEXT_HTML_UTF_16BE

text/html; charset=utf-16be (big-endian)

 

TEXT_PLAIN_HOST

text/plain (using host encoding)

 

TEXT_PLAIN_US_ASCII

text/plain; charset=us-ascii

 

TEXT_PLAIN_UTF_8

text/plain; charset=utf-8

 

TEXT_PLAIN_UTF_16

text/plain; charset=utf-16

 

TEXT_PLAIN_UTF_16LE

text/plain; charset=utf-16le (little-endian)

 

TEXT_PLAIN_UTF_16BE

text/plain; charset=utf-16be (big-endian)

 

PCL

application/vnd.hp-PCL (Hewlett Packard Printer Control Language)

 

AUTOSENSE

application/octet-stream (raw printer data)

READER

TEXT_HTML

text/html; charset=utf-16

STRING

TEXT_PLAIN

text/plain; charset=utf-16

CHAR_ARRAY

  

SERVICE_FORMATTED

PRINTABLE

N/A

 

PAGEABLE

N/A

 

RENDERABLE_IMAGE

N/A


Suppose you want to print a GIF image that is located in a file. First find out whether there is a print service that is capable of handling the task. The static lookupPrintServices method of the PrintServiceLookup class returns an array of PrintService objects that can handle the given document flavor.

 DocFlavor flavor = DocFlavor.INPUT_STREAM.GIF; PrintService[] services    = PrintServiceLookup.lookupPrintServices(flavor, null); 

The second parameter of the lookupPrintServices method is null to indicate that we don't want to constrain the search by specifying printer attributes. We cover attributes in the next section.

NOTE

JDK 1.4 supplies print services for basic document flavors such as images and 2D graphics, but if you try to print text or HTML documents, the lookup will return an empty array.


If the lookup yields an array with more than one element, you select from the listed print services. You can call the getName method of the PrintService class to get the printer names, and then let the user choose.

Next, get a document print job from the service:

 DocPrintJob job = services[i].createPrintJob(); 

For printing, you need an object that implements the Doc interface. The JDK supplies a class SimpleDoc for that purpose. The SimpleDoc constructor requires the data source object, the document flavor, and an optional attribute set. For example,

 InputStream in = new FileInputStream(fileName); Doc doc = new SimpleDoc(in, flavor, null); 

Finally, you are ready to print:

 job.print(doc, null); 

As before, the null parameter can be replaced by an attribute set.

Note that this printing process is quite different from that of the preceding section. There is no user interaction through print dialog boxes. For example, you can implement a server-side printing mechanism in which users submit print jobs through a web form.

The program in Example 7-14 demonstrates how to use a print service to print an image file.

Example 7-14. PrintServiceTest.java

[View full width]

   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.awt.image.*;   4. import java.io.*;   5. import java.util.*;   6. import javax.print.*;   7. import javax.swing.*;   8.   9. /**  10.    This program demonstrates the use of print services. The program lets you print a  GIF image  11.    to any of the print services that support the GIF document flavor.  12. */  13. public class PrintServiceTest  14. {  15.    public static void main(String[] args)  16.    {  17.       JFrame frame = new PrintServiceFrame();  18.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  19.       frame.setVisible(true);  20.    }  21. }  22.  23. /**  24.    This frame displays the image to be printed. It contains  25.    menus for opening an image file, printing, and selecting  26.    a print service.  27. */  28. class PrintServiceFrame extends JFrame  29. {  30.    public PrintServiceFrame()  31.    {  32.       setTitle("PrintServiceTest");  33.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  34.  35.       // set up menu bar  36.       JMenuBar menuBar = new JMenuBar();  37.       setJMenuBar(menuBar);  38.  39.       JMenu menu = new JMenu("File");  40.       menuBar.add(menu);  41.  42.       JMenuItem openItem = new JMenuItem("Open");  43.       menu.add(openItem);  44.       openItem.addActionListener(new  45.          ActionListener()  46.          {  47.             public void actionPerformed(ActionEvent event)  48.             {  49.                openFile();  50.             }  51.          });  52.  53.       JMenuItem printItem = new JMenuItem("Print");  54.       menu.add(printItem);  55.       printItem.addActionListener(new  56.          ActionListener()  57.          {  58.             public void actionPerformed(ActionEvent event)  59.             {  60.                printFile();  61.             }  62.          });  63.  64.       JMenuItem exitItem = new JMenuItem("Exit");  65.       menu.add(exitItem);  66.       exitItem.addActionListener(new  67.          ActionListener()  68.          {  69.             public void actionPerformed(ActionEvent event)  70.             {  71.                System.exit(0);  72.             }  73.          });  74.  75.       menu = new JMenu("Printer");  76.       menuBar.add(menu);  77.       DocFlavor flavor = DocFlavor.INPUT_STREAM.GIF;  78.       addPrintServices(menu, flavor);  79.  80.       // use a label to display the images  81.       label = new JLabel();  82.       add(label);  83.    }  84.  85.    /**  86.       Adds print services to a menu  87.       @param menu the menu to which to add the services  88.       @param flavor the flavor that the services need to support  89.    */  90.    public void addPrintServices(JMenu menu, DocFlavor flavor)  91.    {  92.       PrintService[] services = PrintServiceLookup.lookupPrintServices(flavor, null);  93.       ButtonGroup group = new ButtonGroup();  94.       for (int i = 0; i < services.length; i++)  95.       {  96.          final PrintService service = services[i];  97.          JRadioButtonMenuItem item = new JRadioButtonMenuItem(service.getName());  98.          menu.add(item);  99.          if (i == 0) 100.          { 101.             item.setSelected(true); 102.             currentService = service; 103.          } 104.          group.add(item); 105.          item.addActionListener(new 106.             ActionListener() 107.             { 108.                public void actionPerformed(ActionEvent event) 109.                { 110.                   currentService = service; 111.                } 112.             }); 113.       } 114.    } 115. 116.    /** 117.       Open a GIF file and display the image. 118.    */ 119.    public void openFile() 120.    { 121.       // set up file chooser 122.       JFileChooser chooser = new JFileChooser(); 123.       chooser.setCurrentDirectory(new File(".")); 124. 125.       // accept all files ending with .gif 126.       chooser.setFileFilter(new 127.          javax.swing.filechooser.FileFilter() 128.          { 129.             public boolean accept(File f) 130.             { 131.                return f.getName().toLowerCase().endsWith(".gif") || f.isDirectory(); 132.             } 133. 134.             public String getDescription() { return "GIF Images"; } 135.          }); 136. 137.       // show file chooser dialog 138.       int r = chooser.showOpenDialog(PrintServiceFrame.this); 139. 140.       // if image file accepted, set it as icon of the label 141.       if(r == JFileChooser.APPROVE_OPTION) 142.       { 143.          fileName = chooser.getSelectedFile().getPath(); 144.          label.setIcon(new ImageIcon(fileName)); 145.       } 146.    } 147. 148.    /** 149.       Print the current file using the current print service. 150.    */ 151.    public void printFile() 152.    { 153.       try 154.       { 155.          if (fileName == null) return; 156.          if (currentService == null) return; 157.          FileInputStream in = new FileInputStream(fileName); 158.          DocFlavor flavor = DocFlavor.INPUT_STREAM.GIF; 159.          Doc doc = new SimpleDoc(in, flavor, null); 160.          DocPrintJob job = currentService.createPrintJob(); 161.          job.print(doc, null); 162.       } 163.       catch (FileNotFoundException e) 164.       { 165.          JOptionPane.showMessageDialog(this, e); 166.       } 167.       catch (PrintException e) 168.       { 169.          JOptionPane.showMessageDialog(this, e); 170.       } 171.    } 172. 173.    private JLabel label; 174.    private String fileName; 175.    private PrintService currentService; 176.    private static final int DEFAULT_WIDTH = 300; 177.    private static final int DEFAULT_HEIGHT = 400; 178. } 


 javax.print.PrintServiceLookup 1.4 

  • PrintService[] lookupPrintServices(DocFlavor flavor, AttributeSet attributes)

    looks up the print services that can handle the given document flavor and attributes.

    Parameters:

    flavor

    The document flavor

     

    attributes

    The required printing attributes, or null if attributes should not be considered



 javax.print.PrintService 1.4 

  • DocPrintJob createPrintJob()

    creates a print job for printing an object of a class that implements the Doc interface, such as a SimpleDoc.


 javax.print.DocPrintJob 1.4 

  • void print(Doc doc, PrintRequestAttributeSet attributes)

    prints the given document with the given attributes.

    Parameters:

    doc

    The Doc to be printed

     

    attributes

    The required printing attributes, or null if no printing attributes are required



 javax.print.SimpleDoc 1.4 

  • SimpleDoc(Object data, DocFlavor flavor, DocAttributeSet attributes)

    constructs a SimpleDoc object that can be printed with a DocPrintJob.

    Parameters:

    data

    The object with the print data, such as an input stream or a Printable

     

    flavor

    The document flavor of the print data

     

    attributes

    Document attributes, or null if attributes are not required


Stream Print Services

A print service sends print data to a printer. A stream print service generates the same print data but instead sends them to a stream, perhaps for delayed printing or because the print data format can be interpreted by other programs. In particular, if the print data format is PostScript, then it is useful to save the print data to a file because many programs can process PostScript files.

JDK 1.4 includes a stream print service that can produce PostScript output from images and 2D graphics. You can use that service on all systems, even if there are no local printers.

Enumerating stream print services is a bit more tedious than locating regular print services. You need both the DocFlavor of the object to be printed and the MIME type of the stream output. You then get a StreamPrintServiceFactory array of factories.

 DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE; String mimeType = "application/postscript"; StreamPrintServiceFactory[] factories    = StreamPrintServiceFactory.lookupStreamPrintServiceFactories(flavor, mimeType); 

The StreamPrintServiceFactory class has no methods that would help us distinguish any one factory from another, so we just take factories[0]. We call the getPrintService method with an output stream parameter to get a StreamPrintService object.

 OutputStream out = new FileOutputStream(fileName); StreamPrintService service = factories[0].getPrintService(out); 

The StreamPrintService class is a subclass of PrintService. To produce a printout, simply follow the steps of the preceding section.

Example 7-15 prints the "Hello, World" graphic from the preceding examples to a PostScript file (see Figure 7-42).

Example 7-15. StreamPrintServiceTest.java

[View full width]

   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.awt.font.*;   4. import java.awt.geom.*;   5. import java.awt.print.*;   6. import java.io.*;   7. import java.util.*;   8. import javax.print.*;   9. import javax.print.attribute.*;  10. import javax.swing.*;  11.  12.  13. /**  14.    This program demonstrates the use of a stream print service.  15.    It prints a 2D graphic to a PostScript file.  16. */  17. public class StreamPrintServiceTest  18. {  19.    public static void main(String[] args)  20.    {  21.       JFrame frame = new StreamPrintServiceFrame();  22.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  23.       frame.setVisible(true);  24.    }  25. }  26.  27. /**  28.    This frame shows a panel with 2D graphics and buttons  29.    to print the graphics to a PostScript file and to set up  30.    the page format.  31. */  32. class StreamPrintServiceFrame extends JFrame  33. {  34.    public StreamPrintServiceFrame()  35.    {  36.       setTitle("StreamPrintServiceTest");  37.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  38.  39.       canvas = new PrintPanel();  40.       add(canvas, BorderLayout.CENTER);  41.  42.       attributes = new HashPrintRequestAttributeSet();  43.  44.       JPanel buttonPanel = new JPanel();  45.       JButton printButton = new JButton("Print");  46.       buttonPanel.add(printButton);  47.       printButton.addActionListener(new  48.          ActionListener()  49.          {  50.             public void actionPerformed(ActionEvent event)  51.             {  52.                String fileName = getFile();  53.                if (fileName != null)  54.                   printPostScript(fileName);  55.             }  56.          });  57.  58.       JButton pageSetupButton = new JButton("Page setup");  59.       buttonPanel.add(pageSetupButton);  60.       pageSetupButton.addActionListener(new  61.          ActionListener()  62.          {  63.             public void actionPerformed(ActionEvent event)  64.             {  65.                PrinterJob job = PrinterJob.getPrinterJob();  66.                job.pageDialog(attributes);  67.             }  68.          });  69.  70.       add(buttonPanel, BorderLayout.NORTH);  71.    }  72.  73.    /**  74.       Allows the user to select a PostScript file.  75.       @return the file name, or null if the user didn't  76.       select a file.  77.    */  78.    public String getFile()  79.    {  80.       // set up file chooser  81.       JFileChooser chooser = new JFileChooser();  82.       chooser.setCurrentDirectory(new File("."));  83.  84.       // accept all files ending with .ps  85.       chooser.setFileFilter(new  86.          javax.swing.filechooser.FileFilter()  87.          {  88.             public boolean accept(File f)  89.             {  90.                return f.getName().toLowerCase().endsWith(".ps") || f.isDirectory();  91.             }  92.  93.             public String getDescription() { return "PostScript Files"; }  94.          });  95.  96.       // show file chooser dialog  97.       int r = chooser.showSaveDialog(this);  98.  99.       // if file accepted, return path 100.       if(r == JFileChooser.APPROVE_OPTION) 101.          return chooser.getSelectedFile().getPath(); 102.       else 103.          return null; 104.    } 105. 106.    /** 107.       Prints the 2D graphic to a PostScript file. 108.       @param fileName the name of the PostScript file 109.    */ 110.    public void printPostScript(String fileName) 111.    { 112.       try 113.       { 114.          DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE; 115.          String mimeType = "application/postscript"; 116.          StreamPrintServiceFactory[] factories = 117.             StreamPrintServiceFactory.lookupStreamPrintServiceFactories(flavor,  mimeType); 118. 119.          FileOutputStream out = new FileOutputStream(fileName); 120.          if (factories.length == 0) return; 121.          StreamPrintService service = factories[0].getPrintService(out); 122. 123.          Doc doc = new SimpleDoc(canvas, flavor, null); 124.          DocPrintJob job = service.createPrintJob(); 125.          job.print(doc, attributes); 126.       } 127.       catch (FileNotFoundException e) 128.       { 129.          JOptionPane.showMessageDialog(this, e); 130.       } 131.       catch (PrintException e) 132.       { 133.          JOptionPane.showMessageDialog(this, e); 134.       } 135. 136.    } 137. 138.    private PrintPanel canvas; 139.    private PrintRequestAttributeSet attributes; 140. 141.    private static final int DEFAULT_WIDTH = 300; 142.    private static final int DEFAULT_HEIGHT = 300; 143. } 144. 145. /** 146.    This panel generates a 2D graphics image for screen display 147.    and printing. 148. */ 149. class PrintPanel extends JPanel implements Printable 150. { 151.    public void paintComponent(Graphics g) 152.    { 153.       super.paintComponent(g); 154.       Graphics2D g2 = (Graphics2D) g; 155.       drawPage(g2); 156.    } 157. 158.    public int print(Graphics g, PageFormat pf, int page) 159.       throws PrinterException 160.    { 161.       if (page >= 1) return Printable.NO_SUCH_PAGE; 162.       Graphics2D g2 = (Graphics2D) g; 163.       g2.translate(pf.getImageableX(), pf.getImageableY()); 164.       g2.draw(new Rectangle2D.Double(0, 0, pf.getImageableWidth(), pf .getImageableHeight())); 165. 166.       drawPage(g2); 167.       return Printable.PAGE_EXISTS; 168.    } 169. 170.    /** 171.       This method draws the page both on the screen and the 172.       printer graphics context. 173.       @param g2 the graphics context 174.    */ 175.    public void drawPage(Graphics2D g2) 176.    { 177.       FontRenderContext context = g2.getFontRenderContext(); 178.       Font f = new Font("Serif", Font.PLAIN, 72); 179.       GeneralPath clipShape = new GeneralPath(); 180. 181.       TextLayout layout = new TextLayout("Hello", f, context); 182.       AffineTransform transform = AffineTransform.getTranslateInstance(0, 72); 183.       Shape outline = layout.getOutline(transform); 184.       clipShape.append(outline, false); 185. 186.       layout = new TextLayout("World", f, context); 187.       transform = AffineTransform.getTranslateInstance(0, 144); 188.       outline = layout.getOutline(transform); 189.       clipShape.append(outline, false); 190. 191.       g2.draw(clipShape); 192.       g2.clip(clipShape); 193. 194.       final int NLINES =50; 195.       Point2D p = new Point2D.Double(0, 0); 196.       for (int i = 0; i < NLINES; i++) 197.       { 198.          double x = (2 * getWidth() * i) / NLINES; 199.          double y = (2 * getHeight() * (NLINES - 1 - i)) 200.             / NLINES; 201.          Point2D q = new Point2D.Double(x, y); 202.          g2.draw(new Line2D.Double(p, q)); 203.       } 204.    } 205. } 

Figure 7-42. Viewing a PostScript file



 javax.print.StreamPrintServiceFactory 1.4 

  • StreamPrintServiceFactory[] lookupStreamPrintServiceFactories(DocFlavor flavor, String mimeType)

    looks up the stream print service factories that can print the given document flavor and produce an output stream of the given MIME type.

  • StreamPrintService getPrintService(OutputStream out)

    gets a print service that sends the printing output to the given output stream.

Printing Attributes

The print service API contains a complex set of interfaces and classes to specify various kinds of attributes. There are four important groups of attributes. The first two specify requests to the printer.

  • Print request attributes request particular features for all doc objects in a print job, such as two-sided printing or the paper size.

  • Doc attributes are request attributes that apply only to a single doc object.

The other two attributes contain information about the printer and job status.

  • Print service attributes give information about the print service, such as the printer make and model or whether the printer is currently accepting jobs.

  • Print job attributes give information about the status of a particular print job, such as whether the job is already completed.

To describe the various attributes there is an interface Attribute with subinterfaces:

 PrintRequestAttribute DocAttribute PrintServiceAttribute PrintJobAttribute SupportedValuesAttribute 

Individual attribute classes implement one or more of these interfaces. For example, objects of the Copies class describe the number of copies of a printout. That class implements both the PrintRequestAttribute and the PrintJobAttribute interfaces. Clearly, a print request may contain a request for multiple copies. Conversely, an attribute of the print job may be how many of these copies were actually printed. That number might be lower, perhaps because of printer limitations or because the printer ran out of paper.

The SupportedValuesAttribute interface indicates that an attribute value does not reflect actual request or status data but rather the capability of a service. For example, the CopiesSupported class implements the SupportedValuesAttribute interface. An object of that class might describe that a printer supports 1 through 99 copies of a printout.

Figure 7-43 shows a class diagram of the attribute hierarchy.

Figure 7-43. The attribute hierarchy


In addition to the interfaces and classes for individual attributes, the print service API defines interfaces and classes for attribute sets. A superinterface, AttributeSet, has four subinterfaces:

 PrintRequestAttributeSet DocAttributeSet PrintServiceAttributeSet PrintJobAttributeSet 

Each of these interfaces has an implementing class, yielding the five classes:

 HashAttributeSet HashPrintRequestAttributeSet HashDocAttributeSet HashPrintServiceAttributeSet HashPrintJobAttributeSet 

Figure 7-44 shows a class diagram of the attribute set hierarchy.

Figure 7-44. The attribute set hierarchy


For example, you construct a print request attribute set like this:

 PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet(); 

After constructing the set, you are freed from worry about the Hash prefix.

Why have all these interfaces? They make it possible to check for correct attribute usage. For example, a DocAttributeSet accepts only objects that implement the DocAttribute interface. Any attempt to add another attribute results in a runtime error.

An attribute set is a specialized kind of map, where the keys are of type Class and the values belong to a class that implements the Attribute interface. For example, if you insert an object

 new Copies(10) 

into an attribute set, then its key is the Class object Copies.class. That key is called the category of the attribute. The Attribute interface declares a method

 Class getCategory() 

that returns the category of an attribute. The Copies class defines the method to return the object Copies.class, but it isn't a requirement that the category be the same as the class of the attribute.

When an attribute is added to an attribute set, the category is extracted automatically. You just add the attribute value:

 attributes.add(new Copies(10)); 

If you subsequently add another attribute with the same category, it overwrites the first one.

To retrieve an attribute, you need to use the category as the key, for example,

 AttributeSet attributes = job.getAttributes(); Copies copies = (Copies) attribute.get(Copies.class); 

Finally, attributes are organized by the values they can have. The Copies attribute can have any integer value. The Copies class extends the IntegerSyntax class that takes care of all integer-valued attributes. The getValue method returns the integer value of the attribute, for example,

 int n = copies.getValue(); 

The classes

 TextSyntax DateTimeSyntax URISyntax 

encapsulate a string, date and time, or URI (Uniform Resource Identifier).

Finally, many attributes can take a finite number of values. For example, the PrintQuality attribute has three settings: draft, normal, and high. They are represented by three constants:

 PrintQuality.DRAFT PrintQuality.NORMAL PrintQuality.HIGH 

Attribute classes with a finite number of values extend the EnumSyntax class, which provides a number of convenience methods to set up these enumerations in a typesafe manner. You need not worry about the mechanism when using such an attribute. Simply add the named values to attribute sets:

 attributes.add(PrintQuality.HIGH); 

Here is how you check the value of an attribute:

 if (attributes.get(PrintQuality.class) == PrintQuality.HIGH)    . . . 

Table 7-4 lists the printing attributes. The second column lists the superclass of the attribute class (for example, IntegerSyntax for the Copies attribute) or the set of enumeration values for the attributes with a finite set of values. The last four columns indicate whether the attribute class implements the DocAttribute (DA), PrintJobAttribute (PJA), PrintRequestAttribute (PRA), and PrintServiceAttribute (PSA) interfaces.

Table 7-4. Printing Attributes

Attribute

Superclass or Enumeration Constants

DA

PJA

PRA

PSA

Chromaticity

MONOCHROME, COLOR

 

ColorSupported

SUPPORTED, NOT_SUPPORTED

   

Compression

COMPRESS, DEFLATE, GZIP, NONE

   

Copies

IntegerSyntax

 

 

DateTimeAtCompleted

DateTimeSyntax

 

 

DateTimeAtCreation

DateTimeSyntax

 

  

DateTimeAtProcessing

DateTimeSyntax

 

  

Destination

URISyntax

 

 

DocumentName

TextSyntax

   

Fidelity

FIDELITY_TRUE, FIDELITY_FALSE

 

 

Finishings

NONE, STAPLE, EDGE_STITCH, BIND, SADDLE_STITCH, COVER, . . .

 

JobHoldUntil

DateTimeSyntax

 

 

JobImpressions

IntegerSyntax

 

 

JobImpressionsCompleted

IntegerSyntax

 

  

JobKOctets

IntegerSyntax

 

 

JobKOctetsProcessed

IntegerSyntax

 

  

JobMediaSheets

IntegerSyntax

 

 

JobMediaSheetsCompleted

IntegerSyntax

 

  

JobMessageFromOperator

TextSyntax

 

  

JobName

TextSyntax

 

 

JobOriginatingUserName

TextSyntax

 

  

JobPriority

IntegerSyntax

 

 

JobSheets

STANDARD, NONE

 

 

JobState

ABORTED, CANCELED, COMPLETED, PENDING, PENDING_HELD, PROCESSING, PROCESSING_STOPPED

 

  

JobStateReason

ABORTED_BY_SYSTEM, DOCUMENT_FORMAT_ERROR, many others

    

JobStateReasons

HashSet

 

  

MediaName

ISO_A4_WHITE, ISO_A4_TRANSPARENT, NA_LETTER_WHITE, NA_LETTER_TRANSPARENT

 

MediaSize

ISO.A0 - ISO.A10, ISO.B0 - ISO.B10, ISO.C0 - ISO.C10, NA.LETTER, NA.LEGAL, various other paper and envelope sizes

    

MediaSizeName

ISO_A0 - ISO_A10, ISO_B0 - ISO_B10, ISO_C0 - ISO_C10, NA_LETTER, NA_LEGAL, various other paper and envelope size names

MediaTray

TOP, MIDDLE, BOTTOM, SIDE, ENVELOPE, LARGE_CAPACITY, MAIN, MANUAL,

 

MultipleDocumentHandling

SINGLE_DOCUMENT, SINGLE_DOCUMENT_NEW_SHEET, SEPARATE_DOCUMENTS_COLLATED_COPIES, SEPARATE_DOCUMENTS_UNCOLLATED_COPIES

 

 

NumberOfDocuments

IntegerSyntax

 

  

NumberOfInterveningJobs

IntegerSyntax

 

  

NumberUp

IntegerSyntax

 

OrientationRequested

PORTRAIT, LANDSCAPE, REVERSE_PORTRAIT, REVERSE_LANDSCAPE

 

OutputDeviceAssigned

TextSyntax

 

  

PageRanges

SetOfInteger

 

PagesPerMinute

IntegerSyntax

   

PagesPerMinuteColor

IntegerSyntax

   

PDLOverrideSupported

ATTEMPTED, NOT_ATTEMPTED

   

PresentationDirection

TORIGHT_TOBOTTOM, TORIGHT_TOTOP, TOBOTTOM_TORIGHT, TOBOTTOM_TOLEFT, TOLEFT_TOBOTTOM, TOLEFT_TOTOP, TOTOP_TORIGHT, TOTOP_TOLEFT

 

 

PrinterInfo

TextSyntax

   

PrinterIsAcceptingJobs

ACCEPTING_JOBS, NOT_ACCEPTING_JOBS

   

PrinterLocation

TextSyntax

   

PrinterMakeAndModel

TextSyntax

   

PrinterMessageFromOperator

TextSyntax

   

PrinterMoreInfo

URISyntax

   

PrinterMoreInfoManufacturer

URISyntax

   

PrinterName

TextSyntax

   

PrinterResolution

ResolutionSyntax

 

PrinterState

PROCESSING, IDLE, STOPPED, UNKNOWN

   

PrinterStateReason

COVER_OPEN, FUSER_OVER_TEMP, MEDIA_JAM, and many others

    

PrinterStateReasons

HashMap

    

PrinterURI

URISyntax

   

PrintQuality

DRAFT, NORMAL, HIGH

 

QueuedJobCount

IntegerSyntax

   

ReferenceUriSchemesSupported

FILE, FTP, GOPHER, HTTP, HTTPS, NEWS, NNTP, WAIS

    

RequestingUserName

TextSyntax

  

 

Severity

ERROR, REPORT, WARNING

    

SheetCollate

COLLATED, UNCOLLATED

 

Sides

ONE_SIDED, DUPLEX (=TWO_SIDED_LONG_EDGE), TUMBLE (=TWO_SIDED_SHORT_EDGE)

 


NOTE

As you can see, there are lots of attributes, many of which are quite specialized. The source for most of the attributes is the Internet Printing Protocol 1.1 (RFC 2911).


NOTE

JDK 1.3 introduced the JobAttributes and PageAttributes classes, whose purpose is similar to the printing attributes covered in this section. Unless you need to specifically support JDK 1.3, you should instead use the more robust JDK 1.4 print service mechanism.



 javax.print.attribute.Attribute 1.4 

  • Class getCategory()

    gets the category of this attribute.

  • String getName()

    gets the name of this attribute.


 javax.print.attribute.AttributeSet 1.4 

  • boolean add(Attribute attr)

    adds an attribute to this set. If the set has another attribute with the same category, that attribute is replaced by the given attribute. Returns true if the set changed as a result of this operation.

  • Attribute get(Class category)

    retrieves the attribute with the given category key, or null if no such attribute exists.

  • boolean remove(Attribute attr)

  • boolean remove(Class category)

    remove the given attribute, or the attribute with the given category, from the set. Return TRue if the set changed as a result of this operation.

  • Attribute[] toArray()

    returns an array with all attributes in this set.


 javax.print.PrintService 1.4 

  • PrintServiceAttributeSet getAttributes()

    gets the attributes of this print service.


 javax.print.DocPrintJob 1.4 

  • PrintJobAttributeSet getAttributes()

    gets the attributes of this print job.

This concludes our discussion on printing. You now know how to print 2D graphics and other document types, how to enumerate printers and stream print services, and how to set and retrieve attributes. Next, we turn to two important user interface issues, the clipboard and support for the drag-and-drop mechanism.



    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net