X

Geertjan's Blog

  • July 17, 2007

PrintCookie (Part 2)

Geertjan Wielenga
Product Manager
In PrintCookie (Part 1) we enabled the Print menu item. But, so what? That's like switching on the oven—nothing more than a means to an end. Let's now actually use the PrintCookie. But, first, how does it work? Wouldn't it be simpler to just create a "Print" button on our TopComponent, so that we could click it whenever we want to print the content of the TopComponent? Yes, it would, possibly. But then you wouldn't be providing the same user experience as provided by all the other windows in your application, which all make use of the Print menu item. And that's what's cool about the Print menu item. It provides the necessary infrastructure out of the box. Only once a PrintCookie is present, does the Print menu item become enabled. The underlying Print action, which is what's invoked when you press the Print menu item, is a context-sensitive CookieAction that listens for the presence of a PrintCookie. And because, in PrintCookie (Part 1), we created a node that provides a PrintCookie, and then added that node to the TopComponent's Lookup, the PrintCookie is present and hence the Print menu item is enabled.

When selected, our initial implementation of a Print menu item brings up a clear yet meaningless result:

class MyPrintCookie implements PrintCookie {
public void print() {
JOptionPane.showMessageDialog(null, "Printing...");
//Put all the standard Java printing code here!
}
}

Let's now put something in there so that we continue with the clarity but add some meaningfulness as well. We'll use Kirk Pepperdine's TopComponent, from a few days ago, as an example. This is what it looks like, displaying simulated output from some profiling tool that he's working on:

When we select the Print menu item, under the File menu, we will create a print preview, consisting of a screenshot:

The code used here comes from Chapter 22: Printing in the on-line version of Swing, by Matthew Robinson and Pavel Vorobiev. Possibly there's a new and better way of doing previews in the meantime. I found out from Toni Epple about printing JTables recently. But for preview functions, what follows is the only successful way I know how, without needing to make use of some additional library or something for which I need to pay. (I've seen some NbDocument methods that might be useful, but haven't looked at them in detail thus far.)

Ultimately, instead of showing a screenshot, it would be better to show the whole table, but I just haven't figured out how to do that yet, so the screenshot approach will be what I use here as an example, just to show how standard Java printing functionality integrates with the NetBeans API print infrastructure. Well, in fact, the NetBeans API side of the story is done, because the Print menu item is enabled. Everything else is pure JDK code. As Tom Wheeler wrote, in an e-mail to me last week: "NetBeans hasn't made printing substantially easier or harder, it is just a matter of putting normal Java printing code inside the PrintCookie.print() implementation."

Let's begin by creating a utility method for creating an image from a Swing component, which will be our JScrollPane, in this case:

private BufferedImage getImage(Container container) {
BufferedImage image = new BufferedImage(container.getWidth(),
container.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image.createGraphics();
container.paint(g2);
g2.dispose();
return image;
}

And here's our pretty unwieldy internal class, in the TopComponent, which follows the Printing API's requirements, with the addition of our NetBeans API PrintCookie:

class MyPrintCookie implements PrintCookie, Printable, ImageObserver {
/\* image to print \*/
protected RenderedImage image;
/\*\* Prepare the image to fit on the given page, within the given margins.
\* Returns null if it is unable to prepare the image for the given page.
\* Throws a IllegalArgumentException if the page were too small for the image.
\*\*/
protected RenderedImage prepareImage(PageFormat pf) throws IllegalArgumentException {
try {
AffineTransform af = new AffineTransform();
pf.setOrientation(PageFormat.LANDSCAPE);
image = (RenderedImage) getImage(jScrollPane1);
/\*\* notify if too big for page \*\*/
if (pf.getImageableWidth() - pf.getImageableX() < image.getWidth() ||
pf.getImageableHeight() - pf.getImageableY() < image.getHeight()) {
throw new IllegalArgumentException("Page too small for image"); //NOI18N
}
AffineTransformOp afo = new AffineTransformOp(
af, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
BufferedImage o = (BufferedImage) image;
BufferedImage i = new BufferedImage(o.getWidth() + (int) pf.getImageableX(),
o.getHeight() + (int) pf.getImageableY(), o.getType());
return afo.filter((BufferedImage) image, i);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}/\*\* Print the content of the object. \*/
public void print() {
Printable printable = new MyPrintCookie();
new PrintPreview(printable);
}

/\* Implements Printable \*/
public int print(Graphics graphics, PageFormat pageFormat, int page)
throws PrinterException {
if (page != 0) {
return Printable.NO_SUCH_PAGE;
}
Graphics2D g2 = (Graphics2D) graphics;
if (image == null) {
/\*\* prepareImage() failed,
\* most probably cause is image does not implement RenderedImage,
\* just draw the image then.
\*\*/
graphics.drawImage(getImage(jScrollPane1), (int) pageFormat.getImageableX(),
(int) pageFormat.getImageableY(), this);
} else {
g2.drawRenderedImage(image, new AffineTransform());
}
return Printable.PAGE_EXISTS;
}
public boolean imageUpdate(java.awt.Image image, int flags, int param2,
int param3, int param4, int param5) {
return false;
}
}

The method in bold above is the only bit that isn't standard JDK code. There, we send our Printable class to be handled by a JFrame called PrintPreview, which is again straight from the book. To see it, click here. The class creates our preview frame and adds the screenshot to it. And that's it. Plain JDK code hooked into the NetBeans API PrintCookie class. The cool thing is that now the Print menu item works for your TopComponent, just like any other TopComponent or editor in the IDE, so the user experience is the same throughout, which would not be the case if you created some kind of Print button on your TopComponent. The aim, ultimately, is to blend in with the existing environment, which means using the Print menu item somehow, the above being one way of doing so.

Join the discussion

Comments ( 1 )
  • Wadi Saturday, August 18, 2007

    Thanks Geertjan! This is very usefull. But it would be nice to print the hole table, even if it doesn't fit in the page. Do you know any way to do it without much code?Any other api?,

    Thanks in advnace,

    Regards,

    Wadi


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.