Printing in Java, Part 5

Discover the print framework's support classes

1 2 Page 2
Page 2 of 2

Drawing primitives

The print framework offers three drawing primitives

  1. The PFFrame to draw rectangles
  2. The PFLine to render lines
  3. The PFCircle to render circles

Those three primitives enable you to enhance your printed output and can form the base for drawing more complicated figures.

Rectangles

In the print framework, the PFFrame class represents a rectangle drawing. Don't confuse it with the PFRectangle, which the measurement system uses. You can render rectangles with several attributes: line thickness, fill color, and line color. Since all the attribute methods are basically setters/getters, I will focus on the print() method located between lines 130 and 170 in Listing 2.

Listing 2: PFFrame

First, you compute the size and position of the PFFrame object by calling the computePositionAndSize() method (line 145). Next, a Rectangles2D.Double object is created (line 148). Use that rectangle object as your rendering object. Notice the use of the getDrawingOrigin() and getDrawingSize() methods, which set the rectangle's position and size. To set the line thickness, use a BasicStroke object (lines 160-161). Finally, the rendering process takes place at line 163. But just before you print the child objects, restore the Graphics2d object to its previous color.

As the PFCircle and PFLine classes work in a similar way as PFFrame, I will not go into their details.

The next rendering class that you will explore is the PFImage class, which will enable you to render GIF and JPEG images.

Images

At first glance, rendering images might look like a tedious process, but thanks to the Java Print API, rendering images has never been easier. The PFImage class implementation is also extremely simple. To render an image, you must set the image URL using the setURL() method. The method will save the URL and load the image in memory. The print() method, located between lines 76 and 98 in Listing 3, uses the same structure as the previously explained print methods. The computePositionAndSize() is called first, and then the Image object is rendered. Finally, the child objects are rendered.

Listing 3: PFImage

Of course, you could extend this method to support more image formats. You could use the JAI (Java Advanced Imaging) library from Sun to extend this class or use JavaWorld's Java Tip 43 (Jeff West, with John D. Mitchell) to add support for 8- and 24-bit bitmap images.

Print Preview window

I started to present the structure of the print-preview window in Part 3. Recall that the print-preview window is divided into two major classes. The first one, the PFPrintPreview, renders the page on screen; the second class, the PFPrintPreviewToolbar, presents the toolbar and calls the appropriate method when the user selects a button. The PFPrintPreviewToolbar is an inner class of the PFPrintPreview. Let's take a look at the PFPrintPreviewToolbar class first, located between lines 369 and 488 of Listing 4.

Listing 4: PFPrintPreview

Aside from creating the toolbar, the only other thing worth mentioning about PFPrintPreviewToolbar is that you must pass a PFPrintPreview object to the constructor (line 403). The toolbar sends user requests to the PFPrintPreview. The ActionPerformed() method completes the routing (lines 462-486).

The PagePanel class performs a page's rendering process (lines 270-366). I will focus my explanations on the paint() method (lines 338-364). The PagePanel class implements a JPanel object. You override the paint() method to render the page. Next, create a scaleFactor variable (line 348) that keeps the page's proportion relative to a printed page. At this time, the print preview supports only letter-sized pages. For an 8.5-by-11-inch page, the scale factor is 0.77. You obtain this value by dividing the page's width by its height.

At this point, a BufferedImage object is created (line 350). That object will be used to render the page, then the image is rendered later on the screen. Next, paint the background white (line 352). Subsequently, you scale the drawing area in accordance with the paper size by using the previously calculated scale factor and applying it to the g2d object.

Finally, render the page (lines 358-359). First, make sure that you don't render a nonexistent page, then call the page.print() method with the Graphics2D object. After those two lines of code, the page will be rendered to your doubleBuffer image object. The last step is to render the image on the screen (line 361-362).

That concludes the discussion on the PFPrintPreview class. You have learned how the classes involved in the print preview system interact with one another to enable users to preview documents on screen. Our discussion will now focus on the PFPageSetupDialog.

Page-setup dialog

Using the simple dialog PFPageSetupDialog, you can select the paper size, margins, and -- in the future, when JDK 1.4 is ready -- the paper source. You use this class in conjunction with the PFPageFormat class. Recall from Part 4 that PFPageFormat acts as a container for page metrics.

To use the PFPageSetupDialog, you need to pass a PFPageFormat object to the constructor. The dialog will then show the PFPageFormat values to the user. The user is free to accept or change the page-format values. Figure 2 displays the page-setup dialog:

Figure 2. Page-setup dialog.

Let's take a look at the implementation:

Listing 5: PFPageSetupDialog

To ease the layout of components in the dialog, use a FormPanel object that implements a FormLayout layout manager (see Resources below for more information on FormLayout). With FormLayout, you can treat labels and input fields as one entity. You then position the label/field pairs by using a row/column value. Believe me, FormLayout is a lot easier to use than the GridBagLayout.

Initialize the dialog box (lines 88-91). The setFields() method is then called (line 94), which assigns the parameters stored in the PFPageFormat to their related fields. Then, the layout of the fields is achieved (lines 95-100). See how easy it is to lay out fields using the FormPanel class! Next, the two buttons on the dialog -- Accept and Cancel -- are initialized.

When the page-setup dialog is displayed, the user can either accept the changes he or she made by selecting the Accept button, or ignore the changes by choosing the Cancel button. Take a look at the ActionPerformed() method at line 182. When the user selects Accept, the getFields() method is called to save the fields' values back to the PFPageFormat object. Then, the dialog box is discarded. If the user chooses Cancel, the dialog is disposed of without saving the values to the PFPageFormat object.

The page-setup dialog provides a platform-independent dialog and fully integrates itself with the print framework.

I will now move on to a subject that has generated many emails, printing visual components. To add this function to the print framework, I created the PFVisualComponent.

Printing AWT/Swing components

Because visual components rely on layout managers, printing those components can be tricky. To circumvent problems, I created a component image and rendered the image instead of the component itself. You can more easily position and resize an image on a canvas.

Take a look at the print method located between lines 58 and 101 in Listing 6; you will see that I created BufferedImage to hold the component's image.

Listing 6: PFVisualComponent

The component is asked to render itself on the double-buffer image. And finally, the image of the component renders on the page.

You have seen all the features currently implemented in the print framework, but it is still young.

Missing features

The print framework is missing a few features, which, if added, would enhance its power. First, it lacks a flow object, which would work on top of the print framework to flow text, images, and other elements on a single page or on multiple pages. The flow object would ease the creation of columns.

Also, the framework does not export to PDF or HTML formats. I've also omitted support for printing RTF and HTML files.

In addition to adding PDF, RTF, and HTML support, you could also implement an XML-based text system. You could embed commands to format the text using XML tags.

Conclusion

You have covered a lot of material in this series -- from learning how to use the Java Print API to building a print framework that adds functionality to the Java printing system. You can use the framework as it is, or as the base for future development.

I tried to present as much information as I could in such a small amount of time and space. When Sun releases JDK 1.4, I will return to explain the new Java Print API. Until then, if you have any comments or questions regarding this series, don't hesitate to email me. Happy printing!

Jean-Pierre Dubé is an independent Java consultant. He founded Infocom in 1988. Since then, Infocom has developed several custom applications for manufacturing, document management, and large scale electrical power line management. Jean-Pierre has extensive programming experience in C, Visual Basic, and Java, which is now the primary language for all new projects. He dedicates this series to his mother, who passed away while he was writing this article.

Learn more about this topic

1 2 Page 2
Page 2 of 2