One of the most useful capabilities that can be added to a Java program is printing information that is created with the program. Printing support was added in version 1.1 of the JDK but was both primitive and inconsistent. JDK 1.2 adds new printing support that corrects the deficiencies of JDK 1.1.
This chapter shows you how to print from your window programs. It identifies the AWT classes and methods used for printing, covers several programming examples that explain how to print text and graphics, and shows how to work with print jobs. When you finish this chapter, you'll be able to include printing capabilities in your applications and applets.
The printing support provided with JDK 1.1 uses the PrintJob class of java.awt. This is an abstract class used to encapsulate print requests. An object of the PrintJob class is returned by the getPrintJob() method of the Toolkit class. This method initiates a platform-dependent print request using platform-specific dialog boxes. It has three parameters: a Frame object that identifies the application window from which the print request is generated, a String object that provides a title for the print job, and a Properties object that allows job-specific print properties to be specified and retrieved.
NOTE: In the Java vernacular, you draw on a Graphics object to display information to the screen or printer.
The PrintJob class provides the following six methods:
The getGraphics() method is key to printing--it provides a Graphics object that is used for drawing text and graphics. This Graphics object implements the PrintGraphics interface, which can be used to distinguish between a Graphics object that is used for printing and one that is used for screen display. The PrintGraphics interface consists of a single method, getPrintJob(), which returns the PrintJob associated with the Graphics object.
After drawing has been completed for a Graphics object, the object's dispose() method is invoked, which causes it to be printed. The end() method is invoked to end a print job.
The getPageResolution()and getPageDimension()methods are used to determine how a page is to be laid out. The getPageResolution() method returns the number of pixels per inch supported by a printer. The page dimensions returned by getPageDimension() are not the actual pixel dimensions of the page to be printed. Rather, the dimensions are a mapping (often unsuccessful) of screen coordinates to the graphics context used for printing. Because the dimensions returned by this method are inaccurate (as you'll see in the next section), it should not be used for page layout.
The PrintJob class is easy to use. To create a PrintJob object, invoke the getPrintJob() method of the Toolkit class. You can then invoke the methods of the PrintJob class to retrieve printer information, obtain a PrintGraphics object, or complete a print job. The PrintTestApp program, shown in Listing 18.1, shows how to work with PrintJob objects to obtain printer-related information. When you run this program, the opening window, shown in Figure 18.1, is displayed. Select Print from the File menu and a Print dialog box, similar to the one shown in Figure 18.2, is displayed. Click the OK button and the Print dialog box closes. The program window is updated to display the following information, as shown in Figure 18.3:
Depending on your printer and printer driver, the program may cause a blank page to be ejected.
import java.awt.*; import java.awt.event.*; import java.util.Properties; import ju.ch09.*; public class PrintTestApp extends Frame { Object menuItems[][] = {{"File","Print","-","Exit"}}; MenuItemHandler mih = new MenuItemHandler(); MyMenuBar menuBar = new MyMenuBar(menuItems,mih,mih); TextArea textArea = new TextArea(); Toolkit toolkit; int screenWidth = 300; int screenHeight = 300; public static void main(String args[]){ PrintTestApp app = new PrintTestApp(); } public PrintTestApp() { super("PrintTestApp"); setup(); setSize(screenWidth,screenHeight); addWindowListener(new WindowEventHandler()); show(); } void setup() { setMenuBar(menuBar); toolkit=getToolkit(); add("Center",textArea); } class MenuItemHandler implements ActionListener, ItemListener { public void actionPerformed(ActionEvent ev){ String s=ev.getActionCommand(); String name="Test print job"; Properties properties=new Properties(); if(s=="Exit"){ System.exit(0); }else if(s=="Print"){ PrintJob pj=toolkit.getPrintJob(PrintTestApp.this, name,properties); if(pj==null) textArea.setText("A null PrintJob was returned."); else{ String output="Name: "+name+"\nProperties: "+properties.toString(); Dimension pageDim=pj.getPageDimension(); int resolution=pj.getPageResolution(); boolean lastPageFirst=pj.lastPageFirst();
output+="\n height: "+String.valueOf(pageDim.height); output+="\n width: "+String.valueOf(pageDim.width); output+="\nResolution (pixels/inch): "+String.valueOf(resolution); output+="\nLast Page First: "+String.valueOf(lastPageFirst); textArea.setText(output); Graphics g = pj.getGraphics(); g.dispose(); pj.end(); } } } public void itemStateChanged(ItemEvent e){ } } class WindowEventHandler extends WindowAdapter { public void windowClosing(WindowEvent e){ System.exit(0); } }
}
FIGURE 18.1. The opening window of the PrintTestApp program.
FIGURE 18.2. A Windows 95 Print dialog box.
Figure 18.3 shows the parameters that are displayed for my Canon BJC-70 printer.
FIGURE 18.3. The printer information for a Canon BJC-70 printer.
The setup() method of the PrintTestApp class creates a Toolkit object and assigns it to the toolkit variable. It also creates a TextArea object to display results to the user. The MenuItemHandler class handles the selection of the Print menu item by invoking the Toolkit class's getPrintJob() method to return a PrintJob object for printing. This object is assigned to the pj variable. The pj variable is checked to make sure that the print job is not null. In this case, the getPageDimension(), getPageResolution(), and lastPageFirst() methods are invoked to retrieve information about the printer. This information is displayed to the screen, along with the Properties object returned by the getPrintJob() method. The getGraphics() method of the PrintJob class is invoked to retrieve a Graphics object for printing. This object is immediately disposed by invoking its dispose() method. The end() method of the PrintJob class is then invoked to complete the processing of the print job.
The values of the page dimensions reported for the Canon BJC-70 printer were 792 pixels by 612 pixels. The values returned for other printers will differ. These dimensions are not the physical dimensions of a printed page, as you'll see in the next section. The printer resolutions in pixels per inch returned by getPageResolution() is 72. My printer is configured for 360 pixels per inch. However, 792 by 612 at 72 pixels per inch represents an 11 by 8.5 inch page.
Java programs are required to perform their own page layout and pagination. The getPageDimension() and getPageResolution() methods provide the mechanism to do this. Unfortunately, these methods do not always return correct results. The methods return logical page dimensions that are used to provide compatibility with the screen display.
The PrintDimApp program, shown in Listing 18.2, illustrates the use of the getPageDimension() method in laying out pages for printing. The PrintDimApp opening screen is shown in Figure 18.4. When you select Print from the File menu, it prints a rectangle around the border of a page. On my Canon BJC-70 printer, it prints the rectangle with 10 percent margins around the page. You can use this program to determine how well the page dimensions returned by getPageDimension() work with your printer.
FIGURE 18.4. The opening window of the PrintDimApp program.
import java.awt.*; import java.awt.event.*; import java.util.Properties; import ju.ch09.*; public class PrintDimApp extends Frame { Object menuItems[][] = {{"File","Print","-","Exit"}}; MenuItemHandler mih = new MenuItemHandler(); MyMenuBar menuBar = new MyMenuBar(menuItems,mih,mih); Toolkit toolkit; int screenWidth = 200; int screenHeight = 200; public static void main(String args[]){ PrintDimApp app = new PrintDimApp(); } public PrintDimApp() { super("PrintDimApp"); setup(); setSize(screenWidth,screenHeight); addWindowListener(new WindowEventHandler()); show(); } void setup() { setMenuBar(menuBar); toolkit=getToolkit(); }
int width=size.width; int height=size.height; int x1=(int) (width*0.1); int x2=(int) (width*0.9); int y1=(int) (height*0.1); int y2=(int) (height*0.9); g.drawRect(x1,y1,x2-x1,y2-y1); g.dispose(); } class MenuItemHandler implements ActionListener, ItemListener { public void actionPerformed(ActionEvent ev){ String s=ev.getActionCommand(); String name="Test print job"; Properties properties=new Properties(); if(s=="Exit"){ System.exit(0); }else if(s=="Print"){ PrintJob pj=toolkit.getPrintJob(PrintDimApp.this, name,properties); if(pj!=null){ printDimensions(pj.getGraphics(),pj.getPageDimension()); pj.end(); } } } public void itemStateChanged(ItemEvent e){ } } class WindowEventHandler extends WindowAdapter { public void windowClosing(WindowEvent e){ System.exit(0); } }
}
The overall structure of PrintDimApp is similar to PrintTestApp. The major difference between the two programs is the way that the Print menu item is handled.
The PrintDimApp program creates a PrintJob object using the getPrintJob() method of the Toolkit class. It invokes the printDimensions() method, passing it a Graphics object and Dimension object retrieved via the getGraphics() and getPageDimension() methods of PrintJob.
The printDimensions() method uses the Dimension object to draw a rectangle on the Graphics object. This rectangle is drawn with margins that are 10% of the page width and page height. The dispose() method is invoked to initiate printing of the Graphics object.
The PrintJob class provides an easy-to-use interface for printing. Text and graphics are drawn to the printer in the same way they are drawn to the screen--you just display them to a different Graphics object. In fact, you can copy everything that is displayed to the screen to the printer by using a common drawing method for both. The PrintSampleApp program, shown in Listing 18.3, demonstrates how this can be accomplished.
When you run PrintSampleApp, it displays the opening window, shown in Figure 18.5. If you select Print from the File menu, the contents of the window can be sent to your printer. The PrintSampleApp program shows how graphics (rectangles, circles, and lines) and text are printed.
FIGURE 18.5. The opening window of the PrintSampleApp program.
import java.awt.*; import java.awt.event.*; import java.util.Properties; import ju.ch09.*; public class PrintSampleApp extends Frame { Object menuItems[][] = {{"File","Print","-","Exit"}}; MenuItemHandler mih = new MenuItemHandler(); MyMenuBar menuBar = new MyMenuBar(menuItems,mih,mih); MyCanvas canvas = new MyCanvas(); Toolkit toolkit; int screenWidth = 500; int screenHeight = 500; public static void main(String args[]){ PrintSampleApp app = new PrintSampleApp(); } public PrintSampleApp() { super("PrintSampleApp"); setup(); setSize(screenWidth,screenHeight); addWindowListener(new WindowEventHandler()); show(); } void setup() {
toolkit=getToolkit(); add("Center",canvas); } class MenuItemHandler implements ActionListener, ItemListener { public void actionPerformed(ActionEvent ev){ String s=ev.getActionCommand(); String name="Test print job"; Properties properties=new Properties(); if(s=="Exit"){ System.exit(0); }else if(s=="Print"){ PrintJob pj=toolkit.getPrintJob(PrintSampleApp.this, name,properties); if(pj!=null){ canvas.printAll(pj.getGraphics()); pj.end(); } } } public void itemStateChanged(ItemEvent e){ } } class WindowEventHandler extends WindowAdapter { public void windowClosing(WindowEvent e){ System.exit(0); } } } class MyCanvas extends Canvas { public void paint(Graphics g){ Dimension size=getSize(); int width=size.width; int height=size.height; int x1=(int) (width*0.1); int x2=(int) (width*0.9); int y1=(int) (height*0.1); int y2=(int) (height*0.9); g.drawRect(x1,y1,x2-x1,y2-y1); g.drawOval(x1,y1,x2-x1,y2-y1); g.drawLine(x1,y1,x2,y2); g.drawLine(x2,y1,x1,y2); String text = "Print Me! "; text+=text; text+=text; g.drawString(text,x1,(int)((y1+y2)/2)); g.dispose(); }
}
The PrintSampleApp program creates an object of the MyCanvas class and adds it to the center of the application window. The paint() method of the MyCanvas class draws a rectangle, oval, two lines, and the text Print Me! Print Me! Print Me! on a graphics object. When the screen is initially painted and then repainted, the Graphics object is displayed to the application window. When the Print menu item is selected from the File menu, the printAll() method of the Component class is invoked for the MyCanvas object displayed in the application window. The printAll() method is passed the Graphics object of a PrintJob as an argument. The printAll() method prints a Component object (and all subcomponents) to a Graphics object. This causes the contents of the screen to be copied to the printer.
The java.awt.print package is a new AWT package that was added with JDK 1.2, replacing the print capabilities of JDK 1.1. The package consists of the following three interfaces and four classes:
These classes provide the capability to create and process multiple page printouts using the current system printer. The GraphicsEnvironment class of java.awt provides the getPrinterJob() method, which can be used to create a PrinterJob object. The getLocalGraphicsEnvironment() method is used to produce an instance of the GraphicsEnvironment class.
Once you create a PrinterJob object, you invoke its print() method, passing it an object that implements the Pageable interface. The Book class implements this interface and is the type of object that you will most likely be printing.
Objects of the Book class are created using the Book() constructor and pages are added to the book via the append() method. This method takes an argument of the PageFormat class and an argument that implements the Printable interface. The PageFormat argument merely specifies the page format (letter, landscape, and so on). In most cases, you will be using a default page format. The object that implements the Printable interface is the heart of the printing application. You must create objects of a class that implements Printable to determine how text and graphics are drawn to each page.
The Printable interface specifies one method--the print() method. This method is invoked with an object of the Graphics class. You implement this method by drawing to the Graphics object. The PrintBookApp program, described in the next section, shows how this is accomplished.
The PrintBookApp program, shown in Listing 18.4, shows how to use the classes and interfaces of the java.awt.print package. When you run the program, it displays the opening window, shown in Figure 18.6. Select Print from the file menu and the program prints three pages on your printer. The first page displays the text The JDK 1.2 Printing API really works! in the center of the page and a horizontal line at the bottom of the page. The second page displays an oval that is surrounded by a rectangle. The third page displays the text Last Page! in landscape mode. These three pages illustrate the printing versatility provided by the java.awt.print package.
FIGURE 18.6. The opening window of the PrintBookApp program.
The actionPerformed() method of the MenuItemHandler class handles the selection of the Print menu item. It creates a PrinterJob object and assigns it to the pj variable. This variable is then passed to the createBook() method, which returns the Book object to be printed. The setPageable() method of the PrinterJob class is used to set the Book object to be printed. The Book object is printed using the print() method.
The createBook() method creates the three-page book that is printed. This method begins by creating a new Book object, followed by PageFormat objects representing the default page format (letter) and the landscape page format. A PagePrinter (described below) array is created to specify the Printable object for each page of the book.
The size of the printable area of each page is calculated in points. (A point is 1/72 of an inch.) The getImageableWidth() and getImageableHeight methods return the page's dimensions. These dimensions are assigned to pageWidth and pageHeight. The default font is then set to 18-point bold Helvetica.
The first page of the book is created as a PagePrinter object. The addPrintElement() method is used to specify what is to be printed to the page. The argument to addPrintElement() is an object of the PrintElement class (described later). The first print element consists of the text The JDK 1.2 Printing API really works!, which is printed using the value of font at x-position 100 (picas) and y-position pageHeight/2 picas. The second print element consists of a line that is drawn from (0,pageHeight) to (pageWidth,pageHeight).
The second page draws a rectangle from (100,100) to (pageWidth-100,pageHeight-100) and an oval from (120,120) to (pageWidth-120,pageHeight-120).
The third page draws the text Last Page! at (pageHeight/2,pageWidth/2). However, these coordinates are switched when the page is printed in landscape mode.
The three pages are appended to the book, with the first two pages using the default format and the last page using a landscape format.
The PagePrinter class implements the Printable interface and supports the printing of each page of the book. It manages a Vector of PrintElement elements. This Vector is assigned to the pageContents variable. The print() method prints a page's contents to a Graphics object. This method is invoked by the underlying printing system, and the Graphics object is mapped to the printable portion of a page.
The print() method creates an Enumeration of the keys of the pageContents vector. It then loops through these keys, extracts each key's value, and invokes the print() method of PrintElement object.
The PrintElement class encapsulates the objects that are printed to a page. It supports text, lines, rectangles, and ovals. The type variable is set to text or graphics. The text variable is set to any text that is to be printed. The font variable specifies the text's font. The shape variable is used to identify the type of geometric shape to be displayed. The x, y, width, and height variables are used to specify the position and size of an object that is printed.
Two constructors are provided--one for creating text elements and the other for creating graphics elements. The print() method uses methods of the Graphics class to print text, lines, rectangles, and ovals.
import java.awt.*; import java.awt.event.*; import java.awt.print.*; import java.awt.geom.*; import java.utll.*; import ju.cho9.*; public class PrintBookApp extends Frame { Object menuItems [] [] = {{"File","Print","-","Exit"}}; MenuItemHandler mih = new MenuItemHandler (); MyMenuBar menuBar = new MyMenuBar (menuItems,mih,mih); int screenWidth = 400; int screenHeight = 400; public static void main(String args [] ) { PrintBookApp app = new PrintBookApp (); } public PrintBookApp() { super("PrintBookApp")' setMenuBar(menuBar)' setSize(screenWidth,screenHeight); addWindowListener (new WindowEventHandler()); show(); } Book createBook(PrinterJob pj) { Book book = neew Book(); //Default page format PageFormat defaultFormat = new PageFormat ()' defaultFormat = pj.defaultPage (defaultFormat); //Landscape page format PageFormat landscapeFormat = new PageFormat (); landscapeFormat.setOrientation(PageFormat.LANDSCAPE); //Contents of each page PagePrinter [] page = new PagePrinter [3]; //Determine page size in points (1/72 of an inch) int pageWidth = (int) defaultFormat.getImageableWidth (); int pageHeight = (int) defaultFormat.getImageableHeight (); Font font = new Font ("Helvetica", Font.BOLD,18); //Page 0 page [0] = new PagePrinter(); page[0].addPrintElement( new PrintElement("The JDK 1.2 Printing API really works!",font, 100,pageHeight/2)); page[0].addPrintElement (new PrintElement("line", 0,pageHeight,pageWidth,pageHeight)); //Page 1 page[1] = new PagePrinter(); page[1].addPrintElement (new PrintElement("rectangle",100,100,pageWidth-200,pageHeight-200)); page[1].addPrintElement(new PrintElement("oval",120,120,pageWidth-240,pageHeight-240)); //Page 2 page[2] = new PagePrinter(); page[2].addPrintElement (new PrintElement("Last Page!",font,pageheight/2,pageWidth/2)); //Add pages to book book.append(page[0],defaultFormat); book append(page[1],defaultFormat); book append(page[2],landscapeFormat); return book; } class MenuItemHandler implements ActionListener, ItemListener { public void actionPerformed(ActionEvent ev) { String s=ev.getActionCommand(); if(s=="Exit"){ System.exit(0); }else if(s=="Print"{ PrinterJob pj=PrinterJob.getPrinterJob(); Book book = createBook(pj); try{ pj.setPageable(book); pj.print(); }catch (Exception ex) { System.out.println(ex.toString()); } } } public void itemStateChanged(ItemEvent e){ } } class WindowEventHandler extends WindowAdapter { public voidd windowClosing(WindowEvent e){ System.exit(0);
}
} } class PagePrinter implements Printable { Vector pageContents; public PagePrinter() { pageContents = new Vector(); } public int print(Graphics g,PageFormat pageFormat, int pageIndex) { Enumeration printElements = pageContents.elements(); while(printElements.hasMoreElements()) { PrintElement pe = (PrintElement) printElements.nextElement(); pe.print(g); } return Printable.PAGE_EXISTS; } public void addPrintElement(PrintElement pe) { pageContents.addElement(pe); } } class PrntElement { static final int TEXT = 1; static final int GRAPHICS = 2; int type; String text; Font font; String shape; int x,y,width,height; public PrintElement(String text,Font font,int x, int y) { type = TEXT; this.text = text; this.font = font; this.x =x; this.y = y; } public PrintElement(String shape,int x,int y,int width,int height) { type = GRAPHICS; this.shape = shape.toUpperCase(); this.x = x; this.y = y; this.width = width; this height = height; } public void print(Graphics g) { Font oldFont = g.getFont(); if(type == TEXT) { g.setFont(font); g.drawString(text,x,y); }else if(type == GRAPHICS) { if(shape.equals("LINE")) { g.drawLine(x,y,width,height); }else if(shape.equals("OVAL")) { g.drawOval(x,y,width,height)' }else if(shape.equals(RECTANGLE")) { g.drawRect(x,y,width,height); } } g.setFont(oldFont); }
}
NOTE: The classes of the Java 2D API are covered in Chapter 20, "Working with 2D and 3D Graphics."
This chapter showed you how to include printing capabilities in your window programs, and covered the classes and methods used to support printing. You created examples that showed you how to print text and graphics and work with print jobs. Chapter 19, "Internationalization," shows how to develop Java programs that provide multilanguage capabilities.
© Copyright 1998, Macmillan Publishing. All rights reserved.