Printing to Plotters in Java

August 11th, 2008 | Carl

carljuggles-thumb.jpg
Carl juggles with his creation

One of the things our customers love to do is print our beautiful object graphs and tape them to the wall for discussion. What they hate to do is print 30 pages, line them up, and tape them to a poster one at a time. So we bought a plotter, and I started plotting.

I needed to print directly to a Java Graphics object. Unfortunately, the available information on large output printing from Java is thin at best. While there are lots of ways to successfully place ink on paper, I was only able to find one that reliably lets the application pick odd paper sizes that plotters use, like 24×19.7 inches. (The term “plotter” used to mean something with pens for printing blueprints and such. Now it just means a large format printer, commonly printers that can use roll paper as a source.)

One of the first things you’ll learn when you start working with printing in Java is that a language intended to be all things to all people (i.e., cross-platform) is utterly lousy at tasks highly specific to a given environment, such as printing. It will not surprise you to hear that native print services on Windows are pretty different from those available on a Mac, which themselves are pretty different from the CUPS system common to Unix systems.

So, by and large, you are reduced to the least common denominator of printing. Part and parcel of this least common denominator is agreeing on what constitutes a piece of paper and sticking to it. This is fine for people thinking, “My paper is 8.5 inches wide by 11 inches tall.” It poses a bit of a problem for people with plotters who are thinking, “My paper is 24 inches wide by as many damned inches tall as I need.” Even relatively powerful programs like PhotoShop or GIMP don’t seem to support plotters well. I believe Photoshop works by specifying the exact paper size you want to use, but any technique in which the easiest solution for the user is to pull out a calculator does not meet with my approval.

Advice in a nutshell

A tutorial by MartyHall provides some valuable insights into printing which I applied to my printing component. While it didn’t address my specific issues, props to a well-done starting point.

I’m starting with the assumption that you’ve got yourself a class that implements the Printable interface so that once the Java printing subsystem is coerced into accepting the appropriate paper size, Printable.print(Graphics, PageFormat, int) will be called correctly and your job will print properly. Since my usage was screen-visible as well as printable, I simplified and asserted that one screen pixel is equivalent to one point (1/72 inch), which is how Java prepares the scaling on the Graphics object passed to Printable.print(…), thus making printouts look about the size of onscreen displays. Then, knowing that the printout needed, for example, 1200 vertical pixels/points, I can automatically calculate that I want to use a paper size 18″ tall or so (1200 / 72 dpi = 16.67″ + margins).

Here’s a summary of the critical steps:

  1. Do the math yourself to get the paper size required in points (1/72 of an inch)
  2. Implement the Pageable interface to report page count (usually one) and the PageFormat using the computed paper size.
  3. Create a PrinterJob configured with the correct PrintService
  4. Set your Pageable on the PrinterJob instance
  5. Optionally display print setup dialog (PrinterJob.printDialog()) recognizing that changes to orientation or paper size will be ignored.
  6. call PrinterJob.print()

Generally, don’t use the standard dialogs

This might seem contrary to desired behavior, but I found that giving the user access to the usual native or cross-platform print dialogs can be a bit sticky because those are places where the user can alter the selected paper; in order to properly print to roll paper sources, we must override any paper settings the dialogs might provide. Ultimately this can be confusing for the user, but that’s an unfortunate side effect of supporting plotters.

Don’t take any advice too seriously

We still choose to display the dialog because complex printers like plotters have many options that the users may need to alter and would otherwise have no way to modify.

StickFigure: looking at plotting in Palantir

This work didn’t happen in a vacuum: we have a need to print large graphs for display. Being able to print them in a large format is one of the simplest and most beloved methods of collaboration.

To give an example of why plotter printing is interesting, we created a stick figure on the graph in Palantir. After creating this monstrosity, we went go print it and examined the layout. The first layout was with letter-size paper, and just by looking at the head, we could see that it was going to be a lot of pages:

letterzoom-thumb.jpgZoomed-in view of the head of the stick figure. (click for full image)

Zooming out to view the entire graph, it’s clear that it’s going to be 35 pages!

letter-thumb.jpgFull pagination of the stick figure. (click for full image)

Even switching to the largest standard paper size only gets us to two sheets:

arche.jpg
Paginated out to largest standard paper size, still two pages (click for full image)

When we switch over to using the plotter layout, we finally get to the one sheet we were looking for:

roll-thumb.jpg
Paginated onto a plotter roll. (click for full image)

And finally, Mr. StickFigure comes to life:

carljuggles-med.jpgCarl juggles with his creation

7 Responses to “Printing to Plotters in Java”

  1. Tomas Says:

    Interesting article, Carl. I’m currently working on a project where I sometimes need short wide pages (page width > page height), but when I call print, the PageFormat gets completely changed! Any advice about that?

  2. Carl Says:

    I remember fighting with Java about this early in my experimentation. There are a couple ways to configure & launch print jobs, and some of them let the printer change the PageFormat to a paper size it thinks it supports. Commonly this overrides with the closest paper size defined in MediaSize.java. Eventually, success came from:
    PrinterJob pj = PrinterJob.getPrinterJob();
    pj.setPrintServer(selectedPrintService);
    pj.setPageable(myPageable);

    where myPageable returns the correct PageFormat with a custom-sized Page object when Pageable.getPageFormat(int) is called. If this doesn’t solve your problem, I’d be happy to look at example code and compare it against mine.

  3. Tomas Says:

    Thanks a lot, Carl! That solution worked nicely.

  4. Tomas Says:

    Hello, Carl. I was able to get my page sizes to change, but it seems that the Pageable will only use the paper settings from the first page and ignores the rest. Actually, the PageFormat passed to each Printable looks good, so maybe it’s the Graphics clip that is going bad. I’m implementing the Pageable interface according to everything I’ve seen. Any insights on this problem?

  5. Carl Says:

    You’re suspecting the Graphics clip because the PageFormat objects look ok but you can’t see your output properly. The first thing to verify is that you can generate a single document (Microsoft Document Image Writer is a good way to quickly iterate tests) that has several pages with different sizes. Once you establish that, and verify that the ImageableWidth & ImageableHeight are correct, I have found some problems are traceable back to to the fact that default transforms for painting often differ from print transforms. I had to spend some time sorting out how to paint things in ways that worked when painting to screen and painting to print devices.

    At a minimum, write yourself a simple example that uses variable page size and paints within the Imageable region of the page to prove it works, then figure out what about your painting is causing attempts to paint out of the printable region.

  6. Bryan Says:

    Carl,

    While we have had a plotter interface working for some time under Java, and even though we want to only output simple lines (no rasters), on Windows we find that the Windows/Plotter Driver interface likes to build a large raster and then push that to the plotter (via a print server). With a large format drawing that can be a 400MB file, which takes forver to send and plot.

    In your research did you stumble upon any HPGL2 type drivers that plotters understand, that we could use to just convert JGeometries to HPGL2 language, thus only sending the small vector draw command file to the plotter?

  7. Carl Says:

    We were asked to implement a postscript-based solution, so I haven’t done any research on HPGL2, nor did I have any printer drivers I could test on. When I wrote a simple example that pretty much only did:
    g.drawOval(10, 10, 24*72-20, 12*72-20); // draw 24″x12″ oval
    I saw the postscript rendering had approximated it as a series of arcs, total file size 3k. So clearly PostScript can do it. I recommend writing a simple test that uses the most primitive drawing operation (like above), output to file instead of printing, and inspect the output. Hopefully HPGL2 works at least as well as PS. Then add elaboration until things start getting rasterized.

    For example, maybe the printer driver can handle vector drawing with simple lines but doesn’t work well with complicated curves (improbable, I know). If there’s a Swing component in the mix, be aware it’ll try to double-buffer, causing an automatic rasterizing. We have to take special steps to avoid that or it’ll send a 500 meg print job consisting of mostly white space!

    I would be interested in learning how you convert JGeometries to drawing commands. It looks like JGeometry stores representative points, so I don’t see an easy way to translate into drawing primitives.

Leave a Reply


Palantir