El API de Impresión

El paquete java.awt.print de la plataforma Java 2 nos permite imprimir cualquier cosa que pueda ser renderizada a un contexto Graphics o Graphics2D — incluyendo componentes AWT, componentes Swing y gráficos 2D. El API de impresión es fácil de usar. Nuestra aplicación le dice al sistema de impresión qué imprimir, y el sistema de impresión determina cuando se renderiza cada página. Este modelo de impresión por retrollamada permite soporte de impresión en un amplio rango de impresoras y sistemas. El modelo de retrollamada también permite al usuario imprimir a una impresora de mapa de bits desde un ordenador que no tiene suficiente memoria o espacio en disc para contener el bitmap de una página completa.

Un contexto gráfico permite a un programa dibujar en un dispositivo de renderización como una pantalla, una impresora o una imagen fuera de pantalla. Como los componentes Swing se renderizan a través de un objeto Graphics usando el soporte de gráficos AWT, es fácil imprimir componentes Swing con el nuevo API de impresión. Sin embargo, los componentes AWT no se renderizan a un dispositivo gráfico, debemos extender la clase del componente AWT e implementar el método de dibujo del componente AWT.


¿Qué hay en el Paquete?

El java.awt.print contiene los siguientes interfaces, clases y excepciones. Aquí podrás encontrar la Especificación del API.

Imprimir un Componente AWT

La aplicación printbutton.java muestra un panel con un MyButton sobre él. Cuando se pulsa el botón, la aplicación imprime el componente MyButton.

En el código, la clase Button se extiende para implementar Printable e incluye los método paint y print. Este último es necesario porque la clase implementa Printable, y el método paint es necesario porque describe como aparecen la forma del botón y la etiqueta de texto cuando se imprimen.

Para ver el botón, la contexto gráfico de impresión es trasladado a un área imaginable de la impresora, y para ver la etiqueta de texto, se selecciona una fuente en el contexto gráfico de impresión.

En este ejemplo, el botón se imprime a 164/72 pulgadas dentro del margen imaginable (hay 72 pixels por pulgada) y a 5/72 pulgadas del margen superior imaginado. Aquí es donde el botón es posicionado por el controlador de distribución y estos mismo número son devultos por las siguientes llamadas:

int X = (int)this.getLocation().getX();
int Y = (int)this.getLocation().getY();
Y aquí está el código de la clase MyButton:
class MyButton extends Button 
		implements Printable {

  public MyButton() {
    super("MyButton");
  }

  public void paint(Graphics g) {
  //To see the label text, you must specify a font for
  //the printer graphics context
    Font  f = new Font("Monospaced", Font.PLAIN,12);
    g2.setFont (f);

  //Using "g" render anything you want.
  //Get the button's location, width, and height
    int X = (int)this.getLocation().getX();
    int Y = (int)this.getLocation().getY();
    int W = (int)this.getSize().getWidth();
    int H = (int)this.getSize().getHeight();

  //Draw the button shape
    g.drawRect(X, Y, W, H);

  //Draw the button label
  //For simplicity code to center the label inside the
  //button shape is replaced by integer offset values  
    g.drawString(this.getLabel(), X+10, Y+15);

  }

  public int print(Graphics g, 
                     PageFormat pf, int pi) 
		       throws PrinterException {
    if (pi >= 1) {
      return Printable.NO_SUCH_PAGE;
    }

    Graphics2D g2 = (Graphics2D) g;

  //To see the button on the printed page, you
  //must translate the printer graphics context
  //into the imageable area
    g2.translate(pf.getImageableX(), pf.getImageableY());
    g2.setColor(Color.black);
    paint(g2);
    return Printable.PAGE_EXISTS;
   }
Nota: La impresión Graphics2D está basada en la clase BufferedImage y algunas plataformas no permiten un color de fondo negro por defecto. Si este es nuestro caso tenemos que añadir g2.setColor(Color.black) al método print antes de la invocación a paint.

Imprimir un Componente Swing

Imprimir un componente Swing es casi lo mismo que imprimir un componente AWT, excepto que la clase MyButton no necesita una implementación del método paint. Sin embargo, si teine un método print que llama al método paint del componente. La implementación del método paint no es necesaria porque los componentes Swing saben como dibujarse a sí mismos.

Aquí está el código fuente completo para la versión Swing de printbutton.java.

class MyButton extends JButton implements Printable {

  public MyButton() {
    super("MyButton");
  }

  public int print(Graphics g, 
                     PageFormat pf, int pi)
                       throws PrinterException {
    if (pi >= 1) {
      return Printable.NO_SUCH_PAGE;
    }

    Graphics2D g2 = (Graphics2D) g;
    g2.translate(pf.getImageableX(), 
                 pf.getImageableY());
    Font  f = new Font("Monospaced", Font.PLAIN,12);
    g2.setFont (f);
    paint(g2);
    return Printable.PAGE_EXISTS;
   }
Si extendemos un JPanel e implementamos Printable, podemos imprimir un componente panel y todos sus componentes.
public class printpanel extends JPanel 
			implements ActionListener, 
			Printable {
Aquí está el código de printpanel.java que imprime un objeto JPanel y el JButton que contiene, y el código de ComponentPrinterFrame.java que imprime un ojeto JFrame y los componentes JButton, JList, JCheckBox, y JComboBox que contiene.

Imprimir Gráficos en Swing

De la misma forma que el ejemplo AWT extiende un componente Button e implementa el método paint para dibujar un botón, podemos subclasificar componentes AWT y Swing e implementar el método paint para renderizar gráficos 2D en la pantalla o en la impresora. La aplicación Swing ShapesPrint.java muestra como se hace esto.

El método paintComponent llama al método drawShapes para renderizar gráficos 2D en la pantalla cuando arranca la aplicación. Cuando pulsamos sobre el botón, Print, se crea un contexto gráfico de impresión y es pasado al método drawShapes para el dibujado.

Diálogo de Impresión

Es fácil mostrar el Diálogo de Impresión para que el usuario final pueda intercambiar las propiedades del rabajo de impresión. El método actionPerformed del ejemplo Swing anterior modificado aquí hace justo esto:

public void actionPerformed(ActionEvent e) {
  PrinterJob printJob = PrinterJob.getPrinterJob();
  printJob.setPrintable((MyButton) e.getSource());
  if(printJob.printDialog()){
    try { printJob.print(); } 
    catch (Exception PrinterExeption) { }
  }
}
Nota: En Swing, la sentencia printJob.setPageable((MyButton) e.getSource()); puede escribirse como printJob.setPrintable((MyButton) e.getSource());. La diferencia es que setPrintable es para aplicaciones que no conocen el número de páginas que están imprimiendo. Si usamos setPrintable, necesitamos añadir if(pi >= 1){ return Printable.NO_SUCH_PAGE: } al principio del método print.

Diálogo de configuración de Página

Podemos añadir una línea de código que le dice al objeto PrinterJob que mueste el Diálogo de Configuración de Página para que el usuario final pueda modificar interactivamente el formato de la página para imprimir en vertical u horizontal, etc. El método actionPerformed ejemplo Swing acnterior está mostrado aquí para que muestre los diálogos de Impresión y Configuración de Página:

Nota: Algunas plataformas no soportan el diálogo de configuración de página. En estas plataformas, la llamada a pageDialog simplemente devuelven el objeto PageFormat que se les pasó y no muestran ningún diálogo.
public void actionPerformed(ActionEvent e) {
  PrinterJob printJob = PrinterJob.getPrinterJob();
  printJob.setPrintable((MyButton) e.getSource());
  PageFormat pf = printJob.pageDialog(
                             printJob.defaultPage());
  if(printJob.printDialog()){
    try { printJob.print(); } catch (Exception ex) { }
  }
}

Imprimir una Colección de Páginas

Podemos usar la clase Book para imprimir una colección de páginas que añadimos al libro. Esta páginas pueden estár en cualquier orden y tener diferentes formatos.

El ejemplo print2button.java pone los botones Print y Print 2 del tipo MyButton en un panel. Crea un libro que contiene las páginas para imprimir. Cuando pulsamos algun botón, el libro imprime una copia del botón Print en modo horizontal y dos copias del botón Print 2 en modo vertical, como se especifica en la implementación del método actionPerformed mostrada aquí:


Nota: Actualmente un Bug restringe a la plataforma Solaris a imprimir sólo en vertical.
public void actionPerformed(ActionEvent e) {
  PrinterJob printJob = PrinterJob.getPrinterJob();

/* Set up Book */
  PageFormat landscape = printJob.defaultPage();
  PageFormat portrait = printJob.defaultPage();
  landscape.setOrientation(PageFormat.LANDSCAPE);
  portrait.setOrientation(PageFormat.PORTRAIT);
  Book bk = new Book();
  bk.append((Printable)b, landscape);
  bk.append((Printable)b2, portrait, 2);
  printJob.setPageable(bk);

  try { printJob.print(); } catch (Exception ex) { }
}

Ozito