The Source for Java Technology Collaboration
User: Password:



Sergey Groznyh

Sergey Groznyh's Blog

Generating Print Preview of Swing Text Components

Posted by g_s_m on September 05, 2007 at 04:42 AM | Comments (6)

This question, “how to generate print preview of Java Swing components,” is asked often on various discussion forums, because currently (as of version 6) the Java SE doesn't have standard facilities for building print previews. While working on the Swing Tutorial, I tried to address this issue under the topic “Using JTextComponent.getPrintable method.” This will hopefully appear in the updated version of the Swing Tutorial for JDK6. In the meantime, I'll put the relevant information here.

There exist several resources on the Internet related to generating print previews in Java, but in most cases they are either commercial, or use an outdated API, or attempt to build some all-in-one custom dialog solution without paying much attention to details. In this issue I'll try to focus on how to create a print preview—what necessary parts are involved and how they do communicate.

What to Print

The JTextComponent class defines the getPrintable method which returns an instance of the Printable interface containing a single method, print. Assuming the textComponent is a JTextComponent object we are going to generate print preview for, the following code shows how to obtain a Printable instance out of it.

Printable p = textComponent.getPrintable();

Page Format

Next thing we need to know is the parameters of the output media, most important ones being the physical page dimensions. In real-life situation the available page dimensions should be obtained by querying some PrintService known to your Java environment. For the purposes of this example we'll just use the default page format.

PageFormat f = new PageFormat();
int pageWidth = f.getWidth();
int pageHeight = f.getHeight();

Where to Print

Then we need a print destination that will receive the actual drawing commands. The destination should be an instance of the Graphics class. A simple way of obtaining this is using the BufferedImage class as physical backing store. This class defines the getGraphics method which returns a Graphics instance.

Image page = new BufferedImage(
        pageWidth, pageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics g = page.getGraphics();

How to Print

Now we are ready to generate page images. We need to repeatedly invoke the print method on the Printable instance, specifying the page number to print (0 is the first page) and the Graphic and PageFormat objects. The print method returns some status code. The value of PAGE_EXISTS indicates the success of the operation.

for (int n = 0; p.print(g, f, n) == Printable.PAGE_EXISTS; n++) {
    // N-th page printed successfully
}

How to Scale

The generated page image is usually too large for on-screen display, so we need to shrink the image for viewing. The following code creates an image with size reduced by two times on both dimensions.

double scaleFactor = 0.5;
int previewWidth = (int) (pageWidth * scaleFactor);
int previewHeight = (int) (pageHeight * scaleFactor);
Image preview = page.getScaledInstance(
        previewWidth, previewHeight, SCALE_SMOOTH);

Back to Swing

Until now we dealt mostly with 2D and AWT parts of the Client Java API. Now we need to place the generated preview image into some Swing component. For this we'll create a subclass of the JPanel, implementing the custom paintComponent method and overriding the getPreferredSize, getMinimumSize and getMaximumSize methods so our preview component is laid out correctly.

JPanel previewPane = new JPanel() {
    public void paintComponent(Graphics g) {
        g.drawImage(preview, 0, 0, previewWidth, previewHeight, null);
    }

    public Dimension getPreferredSize() {
        return new Dimension(previewWidth, previewHeight);
    }

    public Dimension getMinimumSize() {
        return getPreferredSize();
    }

    public Dimension getMaximumSize() {
        return getPreferredSize();
    }
}

The resulting print preview pane can then be added to Swing container, just like any other Swing component.

Below are links to the example containing the complete print preview implementation as described in this issue. Using this implementation, the print preview functionality could be added to any text component in a couple lines of code.

Please note that in order to run this demo, you'll need Java runtime version 1.6 (aka 6.0) or higher.

The demo displays a chapter from “Alice in Wonderland” in the JEditorPane component. Print preview window could be activated from menu or by using a keyboard shortcut. Standard Java Print Dialog could be invoked from inside the print preview.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Excellent demo.. short and easy. But, in practical use we need something that aggregates the content of an application in a report.. like Crystal Reports...

    I use Jasper with success in this demo and I guess if Swing provides something similar to Jasper then we can start to think that as a commercial solution..

    Posted by: felipegaucho on September 05, 2007 at 05:34 AM

  • Oh well, one more time: http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html

    Posted by: trembovetski on September 05, 2007 at 03:17 PM

  • Hi Dmitri,

    You wrote: "Do not use Image.getScaledInstance()."

    Could you please provide an one-liner (for this being a demo) that is better suited here, from your point of view? Thanks!

    -S.

    Posted by: g_s_m on September 05, 2007 at 09:32 PM

  • "provide an one-liner"

    That is almost exactly the argument I used when I rewrote the Icon demo . Chris Campbell recommended I implement something else simply for the fact that getScaledInstance() should not be encouraged. I ended up writing this:


    /**
    * Resizes an image using a Graphics2D object backed by a BufferedImage.
    * @param srcImg - source image to scale
    * @param w - desired width
    * @param h - desired height
    * @return - the new resized image
    */
    private Image getScaledImage(Image srcImg, int w, int h){
    BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = resizedImg.createGraphics();
    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g2.drawImage(srcImg, 0, 0, w, h, null);
    g2.dispose();
    return resizedImg;
    }

    I think there needs to be a simple one line way to do something like this.

    Posted by: aberrant on September 06, 2007 at 07:33 AM

  • Actually in your case you might not need to make an intermediary image.

    public void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    g.drawImage(preview, 0, 0, getWidth(), getHeight, null);
    }

    Posted by: aberrant on September 06, 2007 at 07:39 AM

  • Thanks for this. I found that I didn't need to tweak very much for your sample to work for JTables. Can you say something about the arguments to getPrintable()? In your sample, you pass null and null, so it isn't clear what they are for.

    Posted by: geertjan on September 09, 2007 at 05:04 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds