Making reports prettier

Paperclips is a Java library which aims to simplify printing support in SWT by providing an object model for constructing documents. These ‘building blocks’ consist of tables, headings and images.

In RapiTime 1.4, I implemented printing support from the new Java GUI based on Eclipse, and chose Paperclips as a quick solution for printing report pages. In version 2.0, the GUI embeds charts directly into the report pages (previously only tables were displayed), so the printed reports needed to reflect this.

The charts are drawn unsing the excellent JFreeChart library, which provides renderers for a great many different types of chart. Due to the nature of the RapiTime dataset, we’re mostly using bar and pie charts for the time being.

Embedding JFreeChart in Eclipse Forms

The first step was to get JFreeChart embedded in the report pages on screen. The reports are built using the JFace Forms framework, which is built on top of SWT, but JFreeChart is natively a Swing component. SWT has some support for hosting AWT (the layer underneath Swing) controls, so that was my first attempt.

It wasn’t great though - the JFreeChart control kept all its AWT/Swing menus and properties dialogs, which look slightly out of place amongst the slick OS-native interface provided by Eclipse. I probably could have improved matters by applying the correct “look and feel” to Swing, but decided to try out the experimental SWT container for the chart instead.

This basically reimplements most of what the Swing Chart control does using SWT. When it gets to providing a drawing surface for the chart plot, the swing graphics context is emulated by a wrapper around a SWT graphics context called SWTDraw2D. The result is surprisingly effective, and allows seamless embedding of the chart in any SWT window.

Printing Reports

The reports are built up using the Paperclips GridPrint class. It’s pretty flexible, and all I need to do is pull the values out of the tables in the on-screen report, and place them in the printed equivalent.

For charts, we don’t have an alternative data structure, we just want to use the same JFreeChart renderer as we use on the screen. Luckily paperclips provides the ability to create new PrintPiece objects which render directly to a graphics context, so that’s what I attempted to do.

public void paint(GC gc, int x, int y) {
Transform t = new Transform(gc.getDevice());
gc.getTransform(t);

float[] elements = new float[6];
t.getElements(elements);
Transform t2 = new Transform(gc.getDevice(), elements);

// Apply transformation for printer resolution
Point printerDPI = gc.getDevice().getDPI();
Point screenDPI = Display.getCurrent().getDPI();
float scaleX = printerDPI.x / screenDPI.x * 1.5f;
float scaleY = printerDPI.y / screenDPI.y * 1.5f;
t2.scale(scaleX, scaleY);
gc.setTransform(t2);

SWTGraphics2D graphics2D = new SWTGraphics2D(gc);

// Decrease rendering size
chart.draw(graphics2D, new Rectangle((int) (x/scaleX), (int) (y/scaleY), (int) (width/scaleX), (int) (height/scaleY)));
graphics2D.dispose();

// Restore original scaling
gc.setTransform(t);
}

The code above scales the coordinates used for rendering to match the printer’s resolution. It then creates a new SWTGraphics2D instance which is used to render the chart control.

Unfortunately there is an incompatibility between the JFreeChart’s SWT renderer and Paperclips.

In SWTUtils.java, there is a method which checks that font sizes match each other between SWT and AWT. This fails however when the device behind the SWT graphics context is a printer:

GC tmpGC = new GC(device);
Font tmpFont = new Font(device, fontData);
tmpGC.setFont(tmpFont);

My workaround is to omit this check when the device is an instance of Printer. This doesn’t seem to cause great problems, but due to my scaling of the chart coordinates above, I’m not sure exactly what it does to the font sizes. The patch can be downloaded here.

Roll the presses!

Putting it all together, you end up with something like this pdf.