La API Java Printing
Traducción:
Rhaimanhs Labarca
Definición de
una página
Antes
de que nos involucremos en las tecnicidades del API de impresión,
vamos a comenzar definiendo una cierta terminología que se
utilizará a lo largo de este documento. Aunque esta
terminología puede parecer trivial, ayudará a despejar
ciertas confusiones sobre márgenes.
Quizás sabes (de
lo contrario lo sabrás ahora) que Gutenberg inventó la
primera prensa. En aquella época, él tuvo que crear una
terminología para describir la disposición de una
página. Aquí está la definición de que
dio de una página:
Página |
Página |
En las
imágenes anteriores, podemos ver que la página está
dividida en varias áreas. Los márgenes de la impresora
constituyen la periferia de la página. Ellos son dependientes
de la impresora y definen el área mínima a que la
impresora necesita alimentar la página. Los márgenes de
la impresora no son definibles por el usuario. Raramente usamos o
sabemos los tamaños de márgenes de la impresora, pero
algunos fabricantes de impresora publican esto en sus manuales. En
Java, no se necesita saber estas medidas; el API devuelve las
dimensiones del área que puede ser impresa.
Justo al
interior de los márgenes de la impresora son los márgenes
que definen el contorno de la página. Note que los márgenes
izquierdos y derechos extienden la longitud de la página,
menos los márgenes superiores e inferiores de impresión.
El margen al lado izquierdo (gutter) proporciona un margen adicional
que se utilice sobre todo para encuadernar las páginas en un
libro. En una página impresa en ambos lados, el margen puede
ser encontrado en el derecho de la página. El API de impresión
en sí mismo no soporta este margen. Tan extraño como
puede parecer, la API de impresión tampoco puede soportar
márgenes. La única manera de fijar márgenes es
fijar la localización y el tamaño del área
imprimible.
Finalmente,
el área en el medio de la página se llama el área
imprimible.
Unidades de medida
Al
trabajar con la clase Graphics2D, es importante entender la
diferencia entre el espacio de dispositivo y el espacio del usuario.
En el espacio de dispositivo, se trabaja en píxeles usando
la resolución del dispositivo. Un cuadrado de 100 x 100
píxeles dibujados en un dispositivo que tenga una resolución
de 1.024 x 768 píxeles no será del mismo tamaño
en comparación a la de un dispositivo que tenga una resolución
de 1.600 x 1400 píxeles. La razón es simple: porque el
segundo dispositivo ofrece más píxeles por pulgada, así
el cuadrado aparecerá más pequeño.
El espacio
del usuario, por otra parte, permite que pensemos en términos
de unidades de medida, sin importar la resolución del
dispositivo. Cuando se crea un componente Graphics2D para un
dispositivo dado (impresora o pantalla), un valor predeterminado
transforma el espacio de usuario al espacio del dispositivo.
En espacio
de usuario, el valor predeterminado se fija a 72 coordenadas por
pulgada. En vez de pensar en términos de píxeles, se
piensa en términos de unidades. Un cuadrado de una pulgada, es
en realidad un cuadrado de 72 x 72 unidades. Una página tamaño
carta (8,5 por 11 pulgadas) es en realidad un rectángulo de
612 x 792 puntos. Al usar el API de impresión, debemos fijar
todas nuestras medidas en unidades ya que todas las clases trabajan
en el espacio de usuario.
Sistema de impresión
de Java
El sistema
de impresión de Java se ha desarrollado considerablemente en
sus dos lanzamientos pasados. Comenzando con la versión 1,2,
el sistema de impresión permite que se utilice la API Java2D
para renderizar una página. Este API permite que cualquier
cosa que pueda ser dibujada en la pantalla pueda ser impresa.
Aunque se
ha avanzado bastante en el API de impresión, ésta solo
soporta la impresora seleccionada actualmente por el usuario en
cualquier momento. Java no soporta el Printer Discovery (obtención
de una lista de impresoras disponibles y de sus características
en una computadora dada). Las impresoras disponibles pueden ser
locales o conectadas en red. Al usar el API, no existe ninguna
manera para obtener una lista de la impresora a través de
codificación; solamente si se exhibe el diálogo de
impresión, es posible seleccionar una determinada impresora.
Esto es una característica que se echa de menos en el API.
El API de
impresión se basa en un modelo del retrollamada, en el que el
subsistema de impresión, no su programa, controla cuando se
renderiza una página.
Para
simplificar un poco esto, digamos que nuestro programa tiene un
contrato con el subsistema de impresión para proveer una
página dada en un momento dado. El subsistema de impresión
puede solicitar a mi programa que renderice una página más
de una vez, o renderice las páginas fuera de secuencia. Este
modelo proporciona varias ventajas. Primero, enviando las páginas
(pensemos que todas nuestra páginas en realidad es una larga
pagina, como una tira de papel continuo), en vez de la página
entera a la impresora, permite que el programa imprima documentos
complejos que requerirían más memoria de la que la
impresora dispone. El programa no tiene que saber como imprimir cada
tira; necesita solamente saber renderizar una página dada. El
API se encarga del resto. En este caso, el subsistema de impresión
puede solicitar que una página esté renderizada varias
veces dependiendo del número de las tiras requeridas para
imprimir totalmente la página. En segundo lugar, si se desea
imprimir en orden inversa, entonces mi programa debe proporcionar el
medio para imprimir el documento en forma inversa.
Representación
de modelos
Hay dos
modelos de impresión en Java: Printable jobs y Paginable jobs.
Veamos cada uno de ellos.
a)
Printable
El modelo
Printable job es el más simple de los dos modelos de
impresión. Este modelo utiliza solamente un PagePainter
(pintor de página) para el documento entero. Las páginas
se renderizan en secuencia, comenzando con la página cero.
Cuando la última página se imprime, pintor de página
(PagePinter) debe devolver el valor NO_SUCH_PAGE. El subsistema de
impresión solicitará siempre que el programa renderice
las páginas en forma secuencial. Como ejemplo, si al programa
se le pide imprimir las páginas cinco a siete, el subsistema
de impresión pedirá que todas las páginas sean
renderizadas (hasta la séptima página), pero se
imprimirá solamente las páginas cinco, seis, y siete.
Si la aplicación exhibe una cuadro de diálogo de
impresión, el número total de páginas que se
imprimirán no será mostrado puesto que es imposible
saber por adelantado el número de páginas en el
documento usando este modelo.
b) Pageable
El modelo
Pageable job ofrece más flexibilidad que el modelo anterior;
de hecho, este modelo permite que cada pagina se imprima en la
disposición que se desea, con un pintor propio. Por ejemplo
si queremos imprimir un documento de 4 páginas con las
siguientes disposiciones: Las primeras dos páginas en forma
vertical, la 3ra en forma horizontal y la ultima en forma vertical,
con este modelo podemos hacerlo. Hacer lo anterior, con el otro
modelo, será mas complejo de realizar. Pageable job se
utiliza lo más a menudo posible con Book, (libro) que se
interpreta como una colección de páginas. Por cierto,
una colección de páginas puede tener diversos formatos.
Una
Pageable job tiene las características siguientes:
-
Cada
página puede tener su propio pintor. Por ejemplo, podemos
hacer un pintor para imprimir la página de cubierta, otro
pintor para imprimir el contenido, y un tercero para imprimir el
documento entero.
-
Se
puede fijar un diverso formato de página para cada página
en el libro. En un Paginable job, podemos mezclar páginas en
orden horizontal como vertical.
-
El
subsistema de impresión puede pedir a nuestro programa
imprimir las páginas fuera de secuencia, y algunas páginas
se pueden saltar en caso de ser necesario. Una vez más no
tenemos de que preocuparnos, mientras proporcionemos cualquier
página que sea requerida.
-
Paginable
job no necesita saber cuántas páginas hay en el
documento a imprimir.
Books
(Libros)
Esta clase
permite que se creen documentos de multiples paginas. Cada página
puede tener su propio formato y su propio pintor, dándo la
flexibilidad de crear documentos sofisticados. Además la
clase Books implementa la interfaz Pageable.
Una Clase
Book representa una colección de páginas. Cuando
creamos un objeto Book, tal objeto es vacío. Para agregar
páginas, utilizamos simplemente uno de los dos métodos
append. Los parámetros de este método son un objeto
PageFormat, que define las características físicas de
la página, y un objeto PagePainter, el cual implementa la
interfaz Printable.
Si
desconocemos el número de páginas en nuestro documento,
simplemente pasamos el valor UNKNOWN_NUMBER_OF_PAGES al método
append(). El sistema de impresión encontrará
automáticamente el número de páginas llamando a
todos los pintores de la página en el libro hasta que recibe
el valor NO_SUCH_PAGE.
Definición
de la API
Todas las
clases requeridas para imprimir son localizadas en el paquete
java.awt.print,
que es compuesto de tres interfaces y cuatro clases. Las tablas
siguientes definen las clases y los interfaces del paquete print.
Nombre |
Tipo |
Descripción |
Paper |
Clase |
Esta |
PageFormat |
Clase |
PageFormat |
PrinterJob |
Clase |
Esta |
Book |
Clase |
El |
Pageable |
Interfaz |
Una |
Printable |
Interfaz |
Un
|
PrinterGraphics |
Interfaz |
El |
–
Interfaz Pageable
El
interfaz Paginable
incluye tres métodos:
Nombre |
Descripción |
int |
Devuelve |
PageFormat |
Devuelve
|
Printable |
Devuelve |
–
Interfaz Printable
El
interfaz Printable
destaca un método y dos
valores:
Nombre |
Tipo |
Descripción |
int |
Método |
Requiere |
NO_SUCH_PAGE |
Valor |
Esta
|
PAGE_EXISTS |
Valor |
El |
Cada
pintor de página debe implementar el interfaz Printable.
Ya que hay sólo un método a implementar, creando
pintores de página puede pensarse que está todo
resuelto. Sin embargo, debemos tener presente que el código
que escribamos debe ser capaz de dar cualquier página en una o
cualquiera determinada secuencia.
Hay tres
parámetros para imprimir,
incluyendo Graphics, que es la misma clase que se usa para dibujar en
la pantalla. Ya que la clase Graphics implementa el interfaz
PrinterGraphics,
podemos obtener el PrinterJob que instanció este trabajo de
impresión. Si la disposición de página es
compleja y se requiere de rasgos de dibujo avanzados, podemos castear
el parámetro Graphics a un objeto Graphics2D.
Antes que
se comience a usar el objeto Graphics, debemos notar que las
coordenadas no son trasladadas a la esquina superior izquierda del
área imprimible. Ve la siguiente Figura 3 para encontrar la
posición de origen que falta.
Coordenadas |
La
coordenada de origen ( 0, 0) aparece en la esquina superior izquierda
en los márgenes de impresora. Para imprimir un rectángulo
de 1 pulgada cuadrada desde la esquina superior izquierda de margen
(del área que es imprimible) deberíamos escribir el
siguiente código:
public
int print (Graphics g, PageFormat pF, int pagina){
Graphics2D g2d = (Graphics2D) g;
Rectangle2D.Double
rect = new Rectangle2D.Double ();
rect.setRect
(pF.getImageableX () + 72,
pF.getImageableY () + 72, 72,
72);
Graphics2D.draw (rect);
return (PAGE_EXISTS);
}
Del
ejemplo anterior, nosotros vemos que manualmente debemos trasladar el
origen del rectángulo de modo que esto imprima en lo alto del
área imprimible. Para simplificar el código, nosotros
podríamos trasladar las coordenadas una vez y emplear (0, 0)
como el origen del área imprimible. Modificando el ejemplo
anterior, tenemos:
public int
print (Graphics g, PageFormat pF, int pagina){
Graphics2D g2d = (Graphics2D) g;
g2d.translate(pF.getImageableX(),
pF.getImageableY());
Rectangle2D.Double rect = new
Rectangle2D.Double ();
rect.setRect (72, 72, 72,
72);
Graphics2D.draw (rect);
return (PAGE_EXISTS);
}
Usando el
método translate, podemos trasladar las
coordenadas y poner nuestro punto de origen (0, 0) en lo alto del
área imprimible. Desde este punto, nuestro código será
simplificado.
–
Interfaz PrinterGraphics
El
interfaz PrinterGraphics
consiste en un método:
Nombre |
Descripción |
PrinterJob |
Devuelve |
–
La Clase Paper
Ocho
métodos constituyen la clase Paper:
Nombre |
Descripción |
double |
Este |
double |
Este |
double |
Este |
double |
Este |
double |
Este |
double |
Este |
Void |
Este |
Void |
Este |
Antes que sigamos adelante, recordemos que la clase
Paper define las características físicas de la página.
La clase PageFormat
representa las características de toda la página,
como la orientación de página, el tamaño, y el
tipo de papel. Esta clase siempre es pasada como un parámetro
para la Interfaz Printable, mediante el método print(). Use
Paper para obtener la posición del área imprimible,
tamaño, y orientación de página con una matriz
de transformación.
–
La Clase PageFormat
El
PageFormat consiste en 12 métodos:
Nombre |
Descripción |
double |
Este |
double |
Este |
double |
Este |
double |
Este |
double |
Este |
double |
Este |
double |
Este |
double |
Este |
Int |
Este |
Void |
Este |
Paper |
Este |
Void |
Este |
Esto
concluye la descripción de las clases de página. A
continuación veamos la clase
PrinterJob.
–
La Clase PrinterJob
La clase
PrinterJob
controla el proceso de impresión. Esto puede tanto instanciar
como controlar un trabajo de impresión. Veamos los métodos
de esta clase:
Nombre |
Descripción |
abstract |
Este |
abstract |
Estas |
PageFormat |
Este |
abstract |
Este |
abstract |
Este |
abstract |
Este |
abstract |
Este |
static |
Este |
abstract |
Este |
abstract |
Este |
abstract |
Este |
abstract |
Este |
abstract |
Este |
abstract |
Este |
abstract |
Este
|
abstract |
Este |
abstract |
Este |
La
clase Book
Siete
métodos constituyen la clase Book:
Nombre |
Descripción |
void |
Este |
void |
Este |
int |
Este |
PageFormat |
Este |
Printable |
Este |
void |
Este |
Definiendo
los pasos para imprimir
Debemos
seguir los siguientes pasos si deseamos imprimir en Java:
a) Primero
se debe crear un objeto PrinterJob:
PrinterJob
printJob = PrinterJob.getPrinterJob ();
b)
Después, usando el
método setPrintable ()
del PrinterJob,
asignar el objeto Pintor al
PrinterJob. Note que un objeto Pintor es el que
implementa el interfaz Publicable.
printJob.setPrintable (Pintor);
O
podríamos poner el
PageFormat (formato de página) con
el pintor:
printJob.setPrintable (Pintor, FormatoDePagina);
c)
Finalmente, el objeto Painter
debe implementar el método imprimir():
public int print(Graphics g,
PageFormat pF, int pagina);
Aquí,
el primer parámetro es utilizado para renderizar la pagina, o
dicho de otra forma para crear el dibujo sobre la pagina que queremos
imprimir; el
pageFormat es el formato que será usado
para la página corriente; y el último parámetro
es el número de página a ser renderizado.
Esto es
todo lo que hay que hacer, fácil no???
Imprimiendo
nuestra primera pagina
Aunque
Printable puede imprimir documentos simples, este destaca varias
limitaciones, el principal es que todas las páginas deben
compartir el mismo formato. El modelo Pageable, ofrece mucho más
flexibilidad en este caso. Usadando Pageable en conjunto con la
clase Book podemos crear documentos de múltiples páginas,
con cada página formateada de manera diferente.
Examinenos
un poco los pasos requeridos para imprimir con el modelo Printable :
-
Cree
un objeto PrinterJob.
Este objeto controla el proceso de impresión mostrando la
página y diálogos de impresión, e iniciando la
acción de impresión.
-
Muestre
los diálogos apropiados, de impresión o de página.
-
Cree
una clase que implemente la interfaz Printable,
mediante el método print().
-
Valide
el número de página para ser renderizado (o dibujado).
-
Dibuje
su página usando el parámetro graphics.
-
Si la
página se dibuja, devuelva el valor de
PAGE_EXISTS; en caso contrario, devuelva el
valor de
NO_SUCH_PAGE.
Veamos el siguiente código de ejemplo:
import
java.awt.Color;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.geom.Rectangle2D;
import
java.awt.print.*;
public
class Ejemplo1 implements Printable{
public
Ejemplo1() {
super();
PrinterJob
printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(this);
if
(printJob.printDialog()) {
try {
printJob.print();
} catch (Exception PrinterException) {
PrinterException.printStackTrace();
}
}
}
public
int print(Graphics g, PageFormat pf, int pagina){
Graphics2D
g2d;
if
(pagina==0) {
g2d =
(Graphics2D)g;
g2d.setColor(Color.black);
g2d.translate(pf.getImageableX(),pf.getImageableY());
Rectangle2D.Double
rect = new Rectangle2D.Double(
0,0,pf.getImageableWidth(),pf.getImageableHeight());
g2d.draw(rect);
return
(PAGE_EXISTS);
} else {
return
(NO_SUCH_PAGE);
}
}
}
Use Pageables y Libros para imprimir
Como
usted aprendió del ejemplo anterior, imprimir con Printable es
un proceso directo, pero no ofrece mucha flexibilidad cuando se
requieren documentos más complejos a manejar. Para estos tipos
de tareas, las clases Pageable y Book entran en acción. La
clase Book permite escoger si la página de cada Book usará
un pintor de página diferente o el mismo pintor de página
que otras páginas.
Este
ejemplo imprimirá un documento de dos páginas que usa
un pintor para la portada y otro pintor para el documento en sí.
La portada imprimirá sobre una hoja en forma vertical,
mientras la segunda página será puesta en forma
horizontal. Note que en este código, cada pintor imprime una
página; no se valida el parámetro de página.
Para usar a un pintor para imprimir más que una página,
considere el siguiente ejemplo:
//Ejemplo2.java
import
java.awt.print.*;
public
class Ejemplo2 {
public
Ejemplo2() {
super();
PrinterJob
printJob = PrinterJob.getPrinterJob();
Book
libro = new Book();
libro.append(new
Portada(),printJob.defaultPage());
PageFormat
documentoPF = new PageFormat();
documentoPF.setOrientation(PageFormat.LANDSCAPE);
libro.append(new
Documento(),documentoPF);
printJob.setPageable(libro);
if
(printJob.printDialog()) {
try
{
printJob.print();
}
catch (Exception PrinterException) {
PrinterException.printStackTrace();
}
}
}
}
//Portada.java
import
java.awt.Color;
import
java.awt.Font;
import
java.awt.FontMetrics;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.geom.Rectangle2D;
import
java.awt.print.*;
public
class Portada implements Printable{
public
int print(Graphics g, PageFormat pf, int page){
final
double PULGADA = 72;
Graphics2D
g2d = (Graphics2D)g;
g2d.translate(pf.getImageableX(),pf.getImageableY());
g2d.setPaint(Color.black);
Rectangle2D.Double
borde = new Rectangle2D.Double(
0,0,pf.getImageableWidth(),pf.getImageableHeight());
g2d.draw(borde);
String
Titulo = «Imprimiendo con Java»;
Font
fuenteTitulo = new Font(«helvetica»,Font.BOLD,36);
g2d.setFont(fuenteTitulo);
FontMetrics
medidaFuente = g2d.getFontMetrics();
double
tituloX = (pf.getImageableWidth()/2) –
(medidaFuente.stringWidth(Titulo)/2);
double
tituloY = 3 * PULGADA;
g2d.drawString(Titulo,(int)tituloX,
(int)tituloY);
return
(PAGE_EXISTS);
}
}
//Documento.java
import
java.awt.BasicStroke;
import
java.awt.Color;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.geom.Rectangle2D;
import
java.awt.print.*;
public
class Documento implements Printable{
public
int print(Graphics g,PageFormat pf, int page){
final
int PULGADA = 72;
Graphics2D
g2d = (Graphics2D)g;
g2d.translate(pf.getImageableX(),pf.getImageableY());
g2d.setColor(Color.black);
g2d.setStroke(new
BasicStroke(12));
Rectangle2D.Double
borde = new Rectangle2D.Double(
0,0,pf.getImageableWidth(),
pf.getImageableHeight());
g2d.draw(borde);
g2d.drawString(«El
contenido»,PULGADA,PULGADA);
return
(PAGE_EXISTS);
}
}
En
este otro ejemplo, se añade una tercera página
utilizando el mismo pintor que se uso en la segunda. (Utilizamos la
misma clase Portada
descrita anteriormente)
//Ejemplo3.java
import
java.awt.print.Book;
import
java.awt.print.PageFormat;
import
java.awt.print.PrinterJob;
public
class Ejemplo3 {
public
Ejemplo3() {
super();
PrinterJob
printJob = PrinterJob.getPrinterJob();
Book
libro = new Book();
libro.append(new
Portada(),printJob.defaultPage());
PageFormat
documentoPF = new PageFormat();
documentoPF.setOrientation(PageFormat.LANDSCAPE);
libro.append(new
Documento2(),documentoPF);
libro.append(new
Documento2(),documentoPF);
printJob.setPageable(libro);
if
(printJob.printDialog()) {
try
{
printJob.print();
}
catch (Exception PrinterException) {
PrinterException.printStackTrace();
}
}
}
}
//Documento2.java
import
java.awt.BasicStroke;
import
java.awt.Color;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.geom.Rectangle2D;
import
java.awt.print.PageFormat;
import
java.awt.print.Printable;
public
class Documento2 implements Printable{
public
int print(Graphics g,PageFormat pf, int page){
final
int PULGADA = 72;
Graphics2D
g2d = (Graphics2D)g;
g2d.translate(pf.getImageableX(),pf.getImageableY());
g2d.setColor(Color.black);
g2d.setStroke(new
BasicStroke(12));
Rectangle2D.Double
borde = new Rectangle2D.Double(
0,0,pf.getImageableWidth(),
pf.getImageableHeight());
g2d.draw(borde);
if
(page == 1) {
g2d.drawString(«Contenido
de la pagina » + page,
PULGADA,PULGADA);
return
(PAGE_EXISTS);
}
else if (page == 2){
g2d.drawString(«Contenido
de la pagina » + page,
PULGADA,PULGADA);
return
(PAGE_EXISTS);
}
return
(NO_SUCH_PAGE);
}
}
Ahora que
sabemos renderizar (o dibujar) páginas que usan la API de
impresión, podría ser útil presentar un diálogo
con el cual el usuario puede escoger la impresora a utilizar. También
podríamos querer dar al usuario una oportunidad de cambiar el
formato de página y márgenes.
La API de
impresión ofrece dos ventanas de diálogo, el diálogo
de impresora y el diálogo de página.
Usando
diálogos de impresión
Utilización
diálogos de impresión damos una interfaz mas amigable
al usuario, para determinadas tareas las cuales el desea realizar al
imprimir. Veamos la siguiente figura:
Diálogo |
Con el
cuadro de diálogo de impresión, el usuario puede
seleccionar una impresora y modificar su configuración. El
usuario también puede poner el número de copias a
imprimir y seleccionar de una gama de página.
Usando
este diálogo, se transfiere el control del trabajo de
impresión al usuario. El cuadro de diálogo proporciona
el único lugar donde el usuario puede decidir seguir con el
trabajo de impresión o cancelarlo.
Veamos un
pequeño extracto de código que muestra el diálogo
de impresión:
if (printJob.printDialog())
{
try {
printJob.print();
}
catch (Exception
PrintException) {
PrintException.printStackTrace();
}
}
El método
printDialog(), que es parte de la clase PrinterJob, muestra el
diálogo de impresión al usuario. Este método
devuelve un valor Booleano. Si el usuario selecciona el botón
imprimir, printDialog
() devolverá verdadero; si el usuario
selecciona el botón cancelar, el método devuelve falso,
y el trabajo de impresión no procederá.
El
método printDialog() sólo actúa
con el programa devolviendo el valor Booleano (es decir si el usuario
aceptó o no imprimir). Ninguno de los parámetros del
cuadro de Diálogo (que modificque el usuario) afecta al
objecto PrinterJob.
El segundo
diálogo es el diálogo de página. Con este
diálogo, el usuario puede cambiar el sistema de página.
Vea la figura abajo:
Diálogo |
Usando
este diálogo, el usuario puede escoger todos los parámetros
del objeto PageFormat, incluyendo el tamaño de papel, la
fuente de papel, la orientación de la página, y los
márgenes.
El
siguiente extracto de código muestra como invocar al diálogo
de página:
PageFormat
documentPageFormat = new PageFormat ();
documentPageFormat
= printJob.pageDialog (documentPageFormat);
book.append
(new Document (), documentPageFormat);
Si el
usuario selecciona el botón OK, el método pageDialog()
devolverá una copia del objeto PageFormat que refleja la
elección del usuario. Si el usuario selecciona el botón
Cancelar, entonces el método devuelve una copia inalterada de
PageFormat.
Ahora se
viene una parte compleja del manejo de impresión. La
impresión de textos
Manejo
de Texto y Fuentes
La
renderización del texto probablemente representa el aspecto
más complejo de impresión. Mucho tiempo ha pasado de
los días de las impresoras de matriz de punto, cuando sólo
tuvimos que saber el número de caracteres por pulgada y la
anchura de la página en caracteres. La justificación
completa de un párrafo era simple; solamente añadimos
espacios entre cada palabra hasta que el texto quedara alineado con
el margen derecho. Para alinear el texto verticalmente, sólo
enviamos un código a la impresora y comenzamos a imprimir la
siguiente línea. Sin embargo, como ha cambiado esto para hoy
en día. Usando la tecnología de hoy para imprimir dos
líneas de texto, un encima del otro, exige mucho más
conocimiento, incluyendo como usar fuentes.
a)
Fuentes
Primero
debemos entender la estructura de una fuente. La figura (abajo)
ilustra como las fuentes son presentadas. Los caracteres alinean a lo
largo de una línea llamada línea
de fondo, que es el punto vertical de
referencia para colocar una fuente. El ascendiente es la distancia
entre la línea de fondo y la cima del carácter más
alto en un String. El espacio entre la línea de fondo y la
parte más baja de una String es el
descendiente. La distancia vertical entre
dos caracteres es el leading, y
la suma del Descendente con el leading es la altura de la fuente.
Finalmente,
llamamos el avance
a la longitud de una String. Todos estos parámetros
son dependiente de la fuente, significando esto que el texto que
utiliza una fuente Arial no ocupará el mismo espacio físico
que el mismo texto dado usando una fuente Lucida.
|
El proceso
de colocar fuentes se diferencia de colocar una figura(un número)
estándar geométrica. La parte superior izquierda de un
rectángulo es donde se pone el origen; la línea de
fondo sobre el Eje de ordenadas y el primer carácter sobre el
Eje de abscisas marca el origen de una fuente. Observa la siguiente
figura (el Número) 4 para ver la posición del origen de
fuente.
La |
Dos
estilos de fuentes están disponibles:
Serif
fuentes se destacan por ser líneas cortas que
contienen un ángulo en las partes superiores e inferiores de
una letra. Time new roman es un ejemplo de una fuente Serif.
Sans
serif fuentes no tienen ningún elemento
decorativo. La fuente Arial es una fuente sans serif.
Cada uno
de estos estilos de fuente puede ser monoespaciado
o proporcional.
Una fuente
monoespaciada es una fuente en la cual todos los carácteres
tienen la misma anchura. Viejas impresoras, como las de matrices,
usan fuentes monoespaciadas, como base de texto.
Cada
carácter en una fuente proporcional posee una ancho
diferente.
Las
fuentes proporcionales se dieron a conocer cuando las impresoras de
láser salieron al mercado. Los primeros ordenadores
comerciales para usar fuentes proporcionales tanto sobre la pantalla
como sobre la impresora eran Apple y Macintosh.
b)
Tener acceso a fuentes
Como
lo dice su lema: “diseñado para ser escrito una vez y
controlar en todas partes” , los creadores de java tuvieron que
producir una familia de fuente por defecto para las diferentes
plataformas. Java ofrece ocho fuentes por defecto: Serif, Sans
Serif, Dialog, Dialog Input, Lucida Sans, Lucida Sans Typewriter,
Lucida Bright, y Monospace.
c)
Clases de fuente relacionadas
El API
Graphics suministra muchas clases que alivian la tarea de manipular
fuentes. Las definiciones de algunas clases más útiles
se muestran en la siguiente tabla:
Nombre |
Tipo |
Descripción |
Font |
Clase |
La |
FontMetrics |
Clase |
Esta |
FontRenderContext |
Clase |
Esta |
No es
fácil renderizar los párrafos de textos. La escritura
de un algoritmo para justificar el texto que tiene una fuente
proporcional es una tarea laboriosa. La agregación del apoyo a
caracteres internacionales sólo añade mas complejidad.
Es por eso que usaremos el
TextLayout y las clases LineBreakMeasurer
para poder justificar los párrafos
d)
Usando TextLayout
TextLayout
ofrece mucha funcionalidad en la interpretación del texto de
alta calidad. Esta clase puede dar el texto bidireccional como el
texto japonés, donde las figuras alinean de derecho hhacia
abajoen vez del estilo norteamericano, que fluye de la izquierda a la
derecha.
Con
TextLayout podemos tener nuestros párrafos,
pero este no trabaja solo. Para arreglar el texto dentro de una
anchura especificada, se necesita la ayuda de la clase
LineBreakMeasurer.
LineBreakMeasurer
se adecua a un string para encajar una anchura predefinida. Ya que
esta es una clase multilingüe, esta conoce exactamente donde
romper una línea de texto según las reglas de cada
lengua. La clase LineBreakMeasurer
también no trabaja sola. Necesita la información de la
clase FontRenderContext,
que, como su función principal, devuelve el métrico de
fuente exacto. Para medir el texto con eficacia, FontRenderContext
debe saber las formas de renderizar el texto para el dispositivo dado
junto con el tipo de fuente usado.
Falta
introducir otra clase: AttributedString.
Esta clase es sumamente provechosa cuando se quiere encajar la
información de atributo dentro de un string.
Por
ejemplo, digamos que nosotros tengamos la oración siguiente:
Esta es una
Negrita letra.
Si se
imprimiera este string sin la clase AttributedString,
se escribiría algo como esto:
Font normalFont = new Font («serif»,
Font.PLAIN, 12);
Font boldFont = new Font («serif»,
Font.BOLD, 12);
g2.setFont (normalFont);
g2.drawString («Esta es una»);
g2.setFont
(boldFont);
g2.drawString («Negrita»);
g2.setFont (normalFont);
g2.drawString («letra»,
72, 72);
Usando
AttributedString:
AttributedString
attributedString = new AttributedString («Esta en una Negrita
letra»);
attributedString.addAttribute
(TextAttribute.WEIGHT,
TextAttribute.WEIGHT_BOLD, 11,
14);
g2.drawString (attributeString.getIterator (),
72, 72);
El siguiente
ejemplo, imprime un documento de varias lineas de texto, donde el
texto esta en forma justificada
//DocumentoConParrafos.java
import
java.awt.BasicStroke;
import
java.awt.Color;
import
java.awt.Font;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.font.FontRenderContext;
import
java.awt.font.LineBreakMeasurer;
import
java.awt.font.TextAttribute;
import
java.awt.font.TextLayout;
import
java.awt.geom.Point2D;
import
java.awt.geom.Rectangle2D;
import
java.awt.print.PageFormat;
import
java.awt.print.Printable;
import
java.text.AttributedString;
import
java.util.Vector;
import
javax.swing.text.html.HTML.Attribute;
public
class DocumentoConParrafos implements Printable{
public
int print(Graphics g,PageFormat pf, int page){
final
int PULGADA = 72;
Graphics2D
g2d = (Graphics2D)g;
g2d.translate(pf.getImageableX(),pf.getImageableY());
g2d.setColor(Color.black);
g2d.setStroke(new
BasicStroke(4));
Rectangle2D.Double
borde = new Rectangle2D.Double(
0,0,pf.getImageableWidth(),
pf.getImageableHeight());
g2d.draw(borde);
String
texto = new String();
texto
+= «A lo largo de este Tutorial, se ha aprendido «;
texto
+= «mediante las clases EjemploX a imprimir un «;
texto
+= «documento de diferentes formas. Lo mas dificil «;
texto
+= «es la impresion de texto, ya que se debe tener «;
texto
+= «mas conocimiento en lo referente a fuentes. «;
texto
+= «Se añora esos tiempos donde no debiamos, «;
texto
+= «preocuparnos de estas cosas. Sin embargo, «;
texto
+= «guardo esperanza que esta forma de imprimir «;
texto
+= «cambiara y sera mucho mas facil… «;
Point2D.Double
punto = new Point2D.Double(
0.25
* PULGADA, 0.25 * PULGADA);
double
ancho = 8 * PULGADA;
AttributedString
parrafo = new AttributedString(texto);
parrafo.addAttribute(TextAttribute.FONT,
new
Font(«Serif», Font.PLAIN, 12));
LineBreakMeasurer
lineaBreaker = new LineBreakMeasurer(
parrafo.getIterator(),
new
FontRenderContext(null,true,true));
TextLayout
Campo;
TextLayout
justCampo;
Vector
lineas = new Vector();
while
( (Campo = lineaBreaker.nextLayout((float)ancho))
!=
null) {
lineas.add(Campo);
}
for
(int i = 0; i < lineas.size(); i++) {
Campo
= (TextLayout)lineas.get(i);
if
(i != lineas.size()-1) {
justCampo
= Campo.getJustifiedLayout((float)ancho);
}
else {
justCampo
= Campo;
}
punto.y
+= justCampo.getAscent();
justCampo.draw(g2d,(float)punto.x,(float)punto.y);
punto.y
+= justCampo.getDescent() +
justCampo.getLeading();
}
return
(PAGE_EXISTS);
}
}
Impresión de imágenes
La
impresión de una imagen estal cual como se carga un objeto de
Imagen, invocando al método
Graphics2D.drawImage().
El proceso
de imprimir una imagen es dividido en tres pasos fáciles.
-
Cree
un URL que indicará la imagen que quieres imprimir.
-
Cargue
la imagen que usa una clase MediaTracker.
Con esta clase, usted puede cargar a GIF, JPEG, y archivos PNG. Si
se desea cargar otros tipos de imagen, use la API de Imágenes
avanzadas
-
Dibuje
la imagen usando
el método drawImage ()
de la clase Graphics2D.
drawImage
() necesita seis parámetros. El primero
es el objeto Imagen
a imprimir; el segundo y tercer parámetro son coordenadas
para el borde superior izquierdo de la imagen la cual le da la
posición a esta; el cuarto y quinto parámetros
especifican el ancho y alto de la imagen. El último parámetro
es para el
ImageObserver.
El ejemplo
siguiente ilustra como imprimir una imagen:
Para el
objetivo de este ejemplo, el
DocumentoConImagen.java amplía la clase
Componente.
Todas las clases Componentes
ponen en práctica el interfaz ImageObserver.
//DocumentoConImagen.java
import
java.awt.BasicStroke;
import
java.awt.Color;
import
java.awt.Component;
import
java.awt.Graphics;
import
java.awt.Graphics2D;
import
java.awt.Image;
import
java.awt.MediaTracker;
import
java.awt.Toolkit;
import
java.awt.geom.Rectangle2D;
import
java.awt.print.PageFormat;
import
java.awt.print.Printable;
import
java.net.MalformedURLException;
import
java.net.URL;
public class
DocumentoConImagen extends Component
implements
Printable{
public int
print(Graphics g,PageFormat pf, int page){
final int
PULGADA = 72;
Graphics2D
g2d = (Graphics2D)g;
g2d.translate(pf.getImageableX(),pf.getImageableY());
g2d.setColor(Color.black);
g2d.setStroke(new
BasicStroke(12));
Rectangle2D.Double
borde = new Rectangle2D.Double(
0,0,pf.getImageableWidth(),
pf.getImageableHeight());
g2d.draw(borde);
MediaTracker
mt = new MediaTracker(this);
URL
imagenURL = null;
try {
imagenURL
= new URL(«E:/logo_eclipse.gif»);
} catch (MalformedURLException e) {
e.printStackTrace();
}
Image imagen =
Toolkit.getDefaultToolkit().getImage(imagenURL);
mt.addImage(imagen,0);
try {
mt.waitForID(0);
}
catch (InterruptedException e) {
}
g2d.drawImage(imagen,
(int)(0.25 * PULGADA),
(int)(0.25 * PULGADA),(int)(8.5 * PULGADA),
(int)(6 * PULGADA),this);
return
(PAGE_EXISTS);
}
}
Referencias
http://www.javaworld.com/javaworld/jw-10-2000/jw-1020-print.html
http://www.javaworld.com/javaworld/jw-12-2000/jw-1201-print.html
Hola, este tutorial esta excelente!! pero tengo una pregunta. Estoy migrando un sistema de VB a Java y es para imprimir cheques, pero manejan sus coordenadas como (*p 1780x2345y) y supuse que en java las coordenadas serian x= 1780 y y= 2345 ej: g.drawString(\\\»texto\\\», 1780, 2345); pero cuando sale la hoja impresa se ven los datos muy separados e incluso incompletos, se salen del margen. Mi pregunta es como ordeno estas coordenadas?? Cual es la forma correcta de poner las coordenadas??
Ojala puedan responderme pronto. Gracias!
Hola que tal espero que me puedan ayudar.
Estoy tratando de imprimir una imagen con texto. Esto lo logre con label´s y prácticamente quiero imprimir una serie de label´s que los puse cada uno en hojas de un libro. tal y como muestra los ejemplos pero no puedo lograr que imprima los datos. De hecho imprime la hoja en blanco.
//Documento Sin portada.
public class Documento extends Component implements Printable {
JLabel label;
@SuppressWarnings(«unused»)
public int print(Graphics g,PageFormat pf, int page){
final int PULGADA = 72;
Graphics2D g2d = (Graphics2D)g;
g2d.translate(pf.getImageableX(),pf.getImageableY());
g2d.setColor(Color.black);
g2d.setStroke(new BasicStroke(12));
Rectangle2D.Double borde = new Rectangle2D.Double(0,0,pf.getImageableWidth(),pf.getImageableHeight());
g2d.draw(borde);
MediaTracker mt = new MediaTracker(this);
BufferedImage image = new BufferedImage(2*PULGADA, PULGADA, BufferedImage.TYPE_INT_ARGB);
Graphics imgGraphics = image.createGraphics();
label.paint(imgGraphics);
ImageIcon icon = new ImageIcon(image);
mt.addImage(icon.getImage(),0);
try {
mt.waitForID(0);
} catch (InterruptedException e) {
}
g2d.drawImage(icon.getImage(), (int)(0.25 * PULGADA),(int)(0.25 * PULGADA),(int)(8.5 * PULGADA),(int)(6 * PULGADA),this);
return (PAGE_EXISTS);
}
public void setLabel(JLabel local){
label = new JLabel();
label = local;
}
}
Cree un método que agrega paginas al libro con el label que tiene la información que deseo imprimir y un boton para llamar a la acción de imprimir.
public void setPage(ImageIcon imagen, String SKU, String descripcion) throws PrinterException {
Icon icono = new ImageIcon( imagen.getImage().getScaledInstance(lblSku.getWidth(), lblSku.getHeight(), Image.SCALE_AREA_AVERAGING));
lblSku.setIcon(icono);
lblDescripcion.setText(descripcion);
lblLblskudescripcion.setText(SKU);
lblSku.updateUI();
lblDescripcion.updateUI();
lblLblskudescripcion.updateUI();
PageFormat formatoPagina = new PageFormat();
Paper pagina = new Paper();
pagina.setSize(144, 72);
pagina.setImageableArea(0, 0, 17, 8);
formatoPagina.setPaper(pagina);
formatoPagina.setOrientation(PageFormat.LANDSCAPE);
Documento doc = new Documento();
doc.setLabel(labelTodo);
doc.print(labelTodo.getGraphics(), formatoPagina, libro.getNumberOfPages());
libro.append(doc, formatoPagina);
}
private void jButImprime_actionPerformed(ActionEvent e) {
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPageable(libro);
libro.getPrintable(1);
System.out.println(libro.toString());
if (printJob.printDialog()) {
try {
printJob.print();
} catch (Exception PrinterException) {
PrinterException.printStackTrace();
}
}
}
Espero que me puedan decir cual es mi error.
Y también quisiera saber si se con eso se puede imprimir en una hoja de papel física de 2 pulgadas por 1 pulgada