Get Java Printer GraphicsDevice

1.5k views Asked by At

The JavaDocs indicate that the GraphicsEnvironment supports "screen and printer devices".

I can see how to obtain information about screen devices, but I can't seem to be able to find out information about how to obtain informaiton about a Printer Device.

Basically, when printing, I want to be able to create a compatible buffered image by using the devices GraphicsConfiguration.

The main reasons for wanting to do this is:

  1. We want to buffer a page request on the first request of the page and simply paint back the buffer on subsequent requests of the page (as the page contains a number of images and some complex rendering - rather then wasting time painting each page request, we want to use a buffer)
  2. Maintain the high resolution output of the printer. We have found that if we paint directly to the printers Graphics context, we obtain a substantually higher quanity output then we do if we try and use a buffered image of the same size.

I've tried searching the JavaDocs and Google without luck.

Any suggestions??

Cheers

Updated based on additional ideas

Based on additional ideas, it has been suggested to try using something like...

GraphicsConfiguration conf = ((Graphics2D) graphics).getDeviceConfiguration();
assert conf.getDevice().getType() == GraphicsDevice.TYPE_PRINTER;
System.out.println("Device: " + conf.getDevice().getIDstring());

final AffineTransform trans = conf.getDefaultTransform();
double dpi = trans.transform(new Point2D.Float(72, 0), null).getX();
System.out.println(dpi + " DPI");

Rectangle bounds = conf.getBounds();
System.out.println("page size: " + bounds.width + "x" + bounds.height);
// now you could do
buffer = conf.createCompatibleImage(bounds.width, bounds.height);
// verify values, you wouldn’t do this in production code:

Which does actually generate a BufferedImage which would be translated to the printers DPI

To make testing easier, I wrote a simple print method...

public void print(Graphics2D g2d, double width, double height) {
    Font font = g2d.getFont();
    font = font.deriveFont(64f);
    g2d.setFont(font);
    FontMetrics fm = g2d.getFontMetrics();
    String text = "Hello World!";
    double x = (width - fm.stringWidth(text)) / 2;
    double y = (height - fm.getHeight()) / 2;
    g2d.drawString(text, (float) x, (float) y);
}

Which I could user either the printer Graphics context or other Graphics context.

Now obvious, when printing to an A4 page @ 72dpi, the resulting image size is 595x841, at 600dpi (which was been reported by the example above), this results in an image of 4970x7029. Okay, this is fine, I'll only need to scale the image down when drawing to the target printer Graphics context using something like...

g2d.drawImage(buffer, 0, 0, (int) pageFormat.getImageableWidth(), (int) pageFormat.getImageableWidth(), null);

(This is test, so don't jump on me about quality related issues just yet)...

Printing

(Normal on the left, BufferedImage on the right)...okay, that won't do

So, I then thought, I could apply an AffineTransform scale to the buffer Graphics context, using something like...

double scale = dpi / 72d;
AffineTransform scaleAT = AffineTransform.getScaleInstance(scale, scale);
g2d.setTransform(scaleAT);
print(g2d, pageFormat.getImageableWidth(), pageFormat.getImageableWidth());

This would mean I wouldn't need to apply any "print mode" type translations within the underlying existing paint routines we have...

This resulted in...

Not quite

But wait an minute, what's gone wrong here?

So I went back and had a look at all the measurements...

GraphicsConfiguration conf = ((Graphics2D) graphics).getDeviceConfiguration();
//...
Rectangle bounds = conf.getBounds();
buffer = conf.createCompatibleImage(bounds.width, bounds.height);

Was reporting an image size of 4960x7015, but it should be 4970x7029...but wait, what about the imaginable area...at 600dpi it should be, 3768x5827...so that can't be relied upon.

Even after correcting for this, the result still doesn't have the image lining up with expectations in either position nor quality...

Still misaligned...

1

There are 1 answers

6
Holger On

Graphics2D provides a method to get a GraphicsConfiguration which is linked to GraphicsDevice. What is missing is the information, when you can safely assume that a Graphics is a Graphics2D in the context of printing.

E.g. the following program works in my setup:

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.awt.print.*;
import javax.print.*;

public class PrintDev implements Printable {
  public static void main(String[] args) throws PrintException {
    final DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
    PrintService ps=PrintServiceLookup.lookupDefaultPrintService();
    System.out.println(ps.getName());
    ps.createPrintJob().print(new SimpleDoc(new PrintDev(), flavor, null), null);
  }
  @Override
  public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
      throws PrinterException {
    GraphicsConfiguration conf = ((Graphics2D)graphics).getDeviceConfiguration();
    assert conf.getDevice().getType()==GraphicsDevice.TYPE_PRINTER;
    System.out.println("Device: "+conf.getDevice().getIDstring());
    final AffineTransform trans = conf.getDefaultTransform();
    System.out.println(trans.transform(new Point2D.Float(72,0),null).getX()+" DPI");
    Rectangle bounds = conf.getBounds();
    System.out.println("page size: "+bounds.width+"x"+bounds.height);
    // now you could do
    BufferedImage bi=conf.createCompatibleImage(bounds.width, bounds.height);
    // verify values, you wouldn’t do this in production code:
    try { trans.invert(); }
    catch(NoninvertibleTransformException ex){ return NO_SUCH_PAGE; }
    Point2D p=trans.transform(new Point2D.Float(bounds.width, bounds.height),null);
    System.out.printf("page in inches: %.2fx%.2f%n", p.getX()/72, p.getY()/72);
    return NO_SUCH_PAGE;
  }
}