Java 2D Graphics private PageFormat mPageFormat; private FilePageRenderer mPageRenderer; private String mTitle; public FilePrinter() { super(”FilePrinter v1.0″); createUI(); PrinterJob pj = PrinterJob.getPrinterJob(); mPageFormat = pj.defaultPage(); setVisible(true); } protected void createUI() { setSize(350, 300); center(); Container content = getContentPane(); content.setLayout(new BorderLayout()); // Add the menu bar. JMenuBar mb = new JMenuBar(); JMenu file = new JMenu(”File”, true); file.add(new FileOpenAction()).setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_O, Event.CTRL_MASK)); file.add(new FilePrintAction()).setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.CTRL_MASK)); file.add(new FilePageSetupAction()).setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.CTRL_MASK | Event.SHIFT_MASK)); file.addSeparator(); file.add(new FileQuitAction()).setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_Q, Event.CTRL_MASK)); mb.add(file); JMenu page = new JMenu(”Page”, true); page.add(new PageNextPageAction()).setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0)); page.add(new PagePreviousPageAction()).setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0)); mb.add(page); setJMenuBar(mb); // Add the contents of the window. getContentPane().setLayout(new BorderLayout()); // Exit the application when the window is closed. addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } protected void center() { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension frameSize = getSize(); int x = (screenSize.width - frameSize.width) / 2; int y = (screenSize.height - frameSize.height) / 2; setLocation(x, y); } public void showTitle() { int currentPage = mPageRenderer.getCurrentPage() + 1; int numPages = mPageRenderer.getNumPages(); setTitle(mTitle + ” - page ” + currentPage + ” of ” + numPages); } page 247
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services
Java 2D Graphics 13.3.2 Printing Multiple Pages In the real world, printing jobs usually span more than one page. The Printing API offers two ways to handle printing on multiple pages. The first solution is based on Printable. This section includes an example that demonstrates this technique. The other solution uses the Pageable interface, which serves as a container for Printables. I’ll talk about Pageable in the next section. A Printable’s print() method is called every time the Printing API wants to print a new page. The Printing API doesn’t know in advance how many pages will be printed. It just blindly calls the print() until the return value is NO_SUCH_PAGE. In the previous examples in this chapter, the print() methods return NO_SUCH_PAGE for every page except the very first. Thus, these examples only print out one page. Printing multiple pages can be a little tricky. Your application needs to be savvy about what gets rendered on each page. The following example demonstrates how to handle multiple pages with a single Printable instance. It lists the contents of a file. The example is divided into two pieces: FilePrinter This JFrame subclass runs the application. It has a menu that you can use to load a file, print the file, or change the page setup. You can also view different pages of a file, just as they will appear when they are printed. Figure 15.29 shows a snapshot of this class as it is running. FilePageRenderer This class renders a page of a file, either on the screen or on the printer. It reads the entire file into memory and figures out how the file should be paginated. The FilePrinter application is mostly concerned with the details of being a nice Swing application. It’s an expanded version of the SwingPrinter class that’s presented above. The FilePrintAction and FilePageSetupAction inner classes should look very familiar. FilePrinter keeps track of a FilePageRenderer in an instance variable called mPageRenderer. This object is responsible for displaying a page on the screen or on the printer. When you choose the Next page or Previous page menu items, FilePrinter notifies its FilePageRenderer to change the current page. FilePrinter shows the name of the file and the current page in the title bar of its frame window. Here’s the code for the FilePrinter class: import java.awt.*; import java.awt.event.*; import java.awt.print.*; import javax.swing.*; import javax.swing.event.*; public class FilePrinterextends JFrame { public static void main(String[] args) { new FilePrinter(); } page 246
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services
Java 2D Graphics It’s just as simple to print user interface components. In the last example, PatchworkComponent did all the rendering. What if, instead of Patchwork-Component, the frame window contained some user interface elements, like radio buttons and combo boxes? To see how this works, remove the following line in the createUI() method of SwingPrinter: getContentPane().add(new PatchworkComponent()); Replace it with the following: JPanel panel = new JPanel(); JButton printButton = new JButton(”Print”); panel.add(printButton); panel.add(new JList(new Object[] { “One”, “Two”, “Three” })); panel.add(new JButton(”Push me”)); panel.add(new JCheckBox(”Chess”, true)); panel.add(new JComboBox(new Object[] { “Eins”, “Zwei”, “Drei” })); setContentPane(panel); That’s all there is to it! This time, the SwingPrinter frame window looks like Figure 13.6. Printing is just as simple as before. Figure 13.6. Printing Swing components is just as easy This technique won’t work for AWT components. The reason has to do with lightweight and heavyweight components. The old AWT components were all heavyweight, which means that they had peers, or counterparts, in the native windowing system. The native windowing system was responsible for rendering these components on the screen. If you call the paint() method of heavyweight components, it may not do anything. By contrast, Swing components are all lightweight, which means they have no native counterpart and must render themselves. Whenever you call paint() on a lightweight component, you’re sure to get a representation of the component. page 245
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
Java 2D Graphics public PatchworkComponent() { float x = mOffset; float y = mOffset; float halfSide = mSide / 2; float x0 = x + halfSide; float y0 = y; float x1 = x + halfSide; float y1 = y + (mRows * mSide); mVerticalGradient = new GradientPaint( x0, y0, Color.darkGray, x1, y1, Color.lightGray, true); x0 = x; y0 = y + halfSide; x1 = x + (mColumns * mSide); y1 = y + halfSide; mHorizontalGradient = new GradientPaint( x0, y0, Color.darkGray, x1, y1, Color.lightGray, true); } public PatchworkComponent(String s) { this(); mString = s; } public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D)g; g2.rotate(Math.PI / 24, mOffset, mOffset); for (int row = 0; row < mRows; row++) { for (int column = 0; column < mColumns; column++) { float x = column * mSide + mOffset; float y = row * mSide + mOffset; if (((column + row) % 2) == 0) g2.setPaint(mVerticalGradient); else g2.setPaint(mHorizontalGradient); Rectangle2D r = new Rectangle2D.Float(x, y, mSide, mSide); g2.fill(r); } } FontRenderContext frc = g2.getFontRenderContext(); float width = (float)mFont.getStringBounds(mString, frc).getWidth(); LineMetrics lm = mFont.getLineMetrics(mString, frc); float x = ((mColumns * mSide) - width) / 2 + mOffset; float y = ((mRows * mSide) + lm.getAscent()) / 2 + mOffset; g2.setFont(mFont); g2.setPaint(Color.white); g2.drawString(mString, x, y); } public int print(Graphics g, PageFormat pageFormat, int pageIndex) { if (pageIndex != 0) return NO_SUCH_PAGE; paintComponent(g); return PAGE_EXISTS; } } Figure 13.5 shows the SwingPrinter example in action. Figure 13.5. A Swing application that prints page 244
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
Java 2D Graphics mPageFormat = pj.pageDialog(mPageFormat); } } public class FileQuitAction extends AbstractAction { public FileQuitAction() { super(”Quit”); } public void actionPerformed(ActionEvent ae) { System.exit(0); } } } Let’s take a closer look at the interesting parts of this example. SwingPrinter stores a PageFormatas a member variable called mPageFormat. This variable is initialized with a default page setup in SwingPrinter’s constructor: PrinterJob pj = PrinterJob.getPrinterJob(); mPageFormat = pj.defaultPage(); If the user chooses the Page setup menu item, mPageFormat will be modified by the page setup dialog. This is buried in the FilePageSetupAction inner class: PrinterJob pj = PrinterJob.getPrinterJob(); mPageFormat = pj.pageDialog(mPageFormat); Finally, SwingPrinter uses the stored page format when printing actually takes place, in the FilePrintAction inner class: PrinterJob pj = PrinterJob.getPrinterJob(); ComponentPrintable cp = new ComponentPrintable(getContentPane()); pj.setPrintable(cp, mPageFormat); Notice how the ComponentPrintable is used here. It is simply wrapped around the frame’s content pane and passed to the PrinterJob. To run this example, you’ll need the PatchworkComponent class, which renders some visually interesting stuff. You could use any component; this one renders some gradient-filled rectangles and text: import java.awt.*; import java.awt.font.*; import java.awt.geom.*; import java.awt.print.*; import javax.swing.*; public class PatchworkComponentextends JComponentimplements Printable { private float mSide = 36; private float mOffset = 36; private int mColumns = 8; private int mRows = 4; private String mString = “Captivated”; private Font mFont = new Font(”Serif”, Font.PLAIN, 64); private Paint mHorizontalGradient, mVerticalGradient; page 243
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
Java 2D Graphics createUI(); PrinterJob pj = PrinterJob.getPrinterJob(); mPageFormat = pj.defaultPage(); setVisible(true); } protected void createUI() { setSize(300, 300); center(); // Add the menu bar. JMenuBar mb = new JMenuBar(); JMenu file = new JMenu(”File”, true); file.add(new FilePrintAction()).setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.CTRL_MASK)); file.add(new FilePageSetupAction()).setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.CTRL_MASK | Event.SHIFT_MASK)); file.addSeparator(); file.add(new FileQuitAction()).setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_Q, Event.CTRL_MASK)); mb.add(file); setJMenuBar(mb); // Add the contents of the window. getContentPane().add(new PatchworkComponent()); // Exit the application when the window is closed. addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } protected void center() { Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); Dimension us = getSize(); int x = (screen.width - us.width) / 2; int y = (screen.height - us.height) / 2; setLocation(x, y); } public class FilePrintAction extends AbstractAction { public FilePrintAction() { super(”Print”); } public void actionPerformed(ActionEvent ae) { PrinterJob pj = PrinterJob.getPrinterJob(); ComponentPrintable cp = new ComponentPrintable(getContentPane()); pj.setPrintable(cp, mPageFormat); if (pj.printDialog()) { try { pj.print(); } catch (PrinterException e) { System.out.println(e); } } } } public class FilePageSetupAction extends AbstractAction { public FilePageSetupAction() { super(”Page setup…”); } public void actionPerformed(ActionEvent ae) { PrinterJob pj = PrinterJob.getPrinterJob(); page 242
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost JSP Web Hosting services
Java 2D Graphics jc.setDoubleBuffered(false); return wasBuffered; } private void restoreDoubleBuffering(Component c, boolean wasBuffered) { if (c instanceof JComponent) ((JComponent)c).setDoubleBuffered(wasBuffered); } } This class wraps an existing Component and implements the Printable interface. When asked to print, this class simply translates the Graphics2D’s origin to the imageable area of the page and renders the component: g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); // … mComponent.paint(g2); But there’s a catch. Swing containers automatically implement double buffering, a technique that is described in Chapter 9. A double buffered component draws its contents into an offscreen image; then the offscreen image is rendered on the screen. This is a nice technique for reducing flicker in animations or user interface components; however, it has a very undesirable effect on printing. The problem is that the offscreen image that is used for double buffering has the same resolution as the screen, typically 72 dots per inch (dpi). If you attempt to print a Swing component on a higher- resolution device (like a 300 dpi laser printer), the 72 dpi offscreen image will be rendered on the device. The end result is that your 300 dpi or 600 dpi printer will carefully render a 72 dpi image. The extra resolution of the printer will be wasted. Fortunately, there’s a way around this dilemma. It’s possible to disable double buffering in Swing components. You should do this before you attempt to print them. With double buffering disabled, the Swing component will draw directly to the printer, taking full advantage of the printer’s resolution. In the Component-Printable class, the disableDoubleBuffering() and restoreDouble-Buffering() methods take care of this. How would you use such a class? Let’s look at a very simple Swing application, called SwingPrinter.[5] This application consists of a JFrame, which contains a component that does some fancy drawing. The frame also contains a menu with Print and Page setup items. When the user chooses to print, a Component-Printable is wrapped around the frame’s contents and printed: [5] For more information on Swing programming, see Java Swing , by Bob Eckstein, Marc Loy, and Dave Wood (O’Reilly). import java.awt.*; import java.awt.event.*; import java.awt.print.*; import javax.swing.*; import javax.swing.event.*; public class SwingPrinterextends JFrame { public static void main(String[] args) { new SwingPrinter(); } private PageFormat mPageFormat; public SwingPrinter() { super(”SwingPrinter v1.0″); page 241
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services
Java 2D Graphics The printing proceeds as usual, except that the custom Paper is used. 13.3 Power Printing In this section, I’ll cover two important printing topics: Integrating printing into an existing application I’ll show how to add printing support to a simple Swing application. Along the way, I’ll develop a simple class that can print any screen component.[4] [4] Well, it prints any screen component that defines a paint()method. Heavyweight components cannot be printed this way. Printing more than one page If you know that all the pages you’ll print have the same PageFormat, you can use the Printable interface to print more than one page. If your needs are more sophisticated, the java.awt.print.Pageable interface keeps track of multiple pairs of PageFormats and Printables. It can be used to associate a different PageFormat with each page of a print job. The java.awt.print.Book class is a convenient implementation of the Pageable interface. 13.3.1 Printing User Interface Components The Printing API makes it easy to add printing capabilities to an existing application. After all, the print() method in the Printable interface can easily call the paint() method of a user interface component. It’s so simple that it can be done in 36 lines of code: import java.awt.*; import java.awt.print.*; import javax.swing.JComponent; public class ComponentPrintableimplements Printable { private Component mComponent; public ComponentPrintable(Component c) { mComponent = c; } public int print(Graphics g, PageFormat pageFormat, int pageIndex) { if (pageIndex > 0) return NO_SUCH_PAGE; Graphics2D g2 = (Graphics2D)g; g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); boolean wasBuffered = disableDoubleBuffering(mComponent); mComponent.paint(g2); restoreDoubleBuffering(mComponent, wasBuffered); return PAGE_EXISTS; } private boolean disableDoubleBuffering(Component c) { if (c instanceof JComponent == false) return false; JComponent jc = (JComponent)c; boolean wasBuffered = jc.isDoubleBuffered(); page 240
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services
Java 2D Graphics The following example pulls together a few of the things you’ve been learning recently. It demonstrates how to change the imageable area of your paper using the setPaper() method of PageFormat: import java.awt.*; import java.awt.geom.*; import java.awt.print.*; public class PageFormatMania { public static void main(String[] args) { PrinterJob pj = PrinterJob.getPrinterJob(); PageFormat pf = pj.defaultPage(); Paper paper = new Paper(); double margin = 36; // half inch paper.setImageableArea(margin, margin, paper.getWidth() - margin * 2, paper.getHeight() - margin * 2); pf.setPaper(paper); pj.setPrintable(new ManiaPrintable(), pf); if (pj.printDialog()) { try { pj.print(); } catch (PrinterException e) { System.out.println(e); } } } } class ManiaPrintable implements Printable { public int print(Graphics g, PageFormat pf, int pageIndex) { if (pageIndex != 0) return NO_SUCH_PAGE; Graphics2D g2 = (Graphics2D)g; g2.setFont(new Font(”Serif”, Font.PLAIN, 36)); g2.setPaint(Color.black); g2.drawString(”ManiaPrintable”, 100, 100); Rectangle2D outline = new Rectangle2D.Double( pf.getImageableX(), pf.getImageableY(), pf.getImageableWidth(), pf.getImageableHeight()); g2.draw(outline); return PAGE_EXISTS; } } This example uses a custom Paper with an imageable area that is 1/2-inch from each side of the page. It uses this Paper in a PageFormat by calling the setPaper() method: PrinterJob pj = PrinterJob.getPrinterJob(); PageFormat pf = pj.defaultPage(); Paper paper = new Paper(); double margin = 36; // half inch paper.setImageableArea(margin, margin, paper.getWidth() - margin * 2, paper.getHeight() - margin * 2); pf.setPaper(paper); When it’s time to print, the PageFormat is passed to PrinterJob’s setPrintable() method: pj.setPrintable(new ManiaPrintable(), pf); page 239
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services
Java 2D Graphics 13.2.2 Showing Dialogs (or Not!) To show the familiar print dialog, use the following method: public abstract boolean printDialog() This method displays the print dialog for this PrinterJob. Using this dialog, you can change the printer that will be used, the number of copies, the page range that will be printed, and other parameters. This method returns true if the user clicked on the OK button to leave the dialog and false otherwise. Note that after this method returns, you don’t have to do anything to retrieve the parameters the user selected. The PrinterJob is automatically updated with the user’s selections. You can also show a standard page setup dialog: public abstract PageFormat pageDialog(PageFormat page) This method shows a page setup dialog using the underlying operating system. It uses the supplied PageFormat to initialize the controls in the dialog. If the user clicks OK in the dialog, a new PageFormat containing the appropriate parameters is returned. Otherwise, the original PageFormat will be returned. Figure 13.4 shows a page setup dialog in Windows. Figure 13.4. The page setup dialog in Windows It is possible to print something without showing any dialogs whatsoever. This sneaky technique is called silent printing. Except for some specific applications, it’s not a good idea to print something without letting the user know about it. If you really want to print silently, however, you can just set up a PrinterJob and call print(). The job will be sent to the user’s default printer . page 238
Note: If you are looking for good and high quality web space to host and run your application check Lunarwebhost Java Web Hosting services