Convertir objetos Java a JSON y de regreso

JSON
JSON

El formato JSON se a convertido rápidamente en un estándar en como enviar y recibir información a y desde un webservice, si bien es un formato que se originó en JavaScript usarlo en Java es muy sencillo gracias a la librería Gson.

Gson

Gson es una librería en Java creada por Google que permite convertir un objeto Java a JSON y un JSON a un objeto Java, su uso es increíblemente sencillo, por eso se lo recomiendo.

Convirtiendo un Objeto Java a JSON.

Como primer ejemplo veamos como se convierte un objeto Java a JSON, para esto necesitara crear un objeto de clase Gson el cual se encargara de realizar la conversión y además deberá de asegurarse que la clase del objeto que desea convertir tenga todos los getter y setter necesarios, como se ve a continuación.

package mx.com.pydee.jsonexample;

import java.math.BigDecimal;

/**
 *
 * @author David
 */
public class DetallesVenta {
    private String producto;
    private BigDecimal importe;
    private BigDecimal precioUnitario;
    private Integer cantidad;
    
    public DetallesVenta(String prod, BigDecimal pu, BigDecimal imp, Integer cant) {
        producto = prod;
        precioUnitario = pu;
        importe = imp;
        cantidad = cant;
    }

    /**
     * @return the producto
     */
    public String getProducto() {
        return producto;
    }

    /**
     * @param producto the producto to set
     */
    public void setProducto(String producto) {
        this.producto = producto;
    }

    /**
     * @return the importe
     */
    public BigDecimal getImporte() {
        return importe;
    }

    /**
     * @param importe the importe to set
     */
    public void setImporte(BigDecimal importe) {
        this.importe = importe;
    }

    /**
     * @return the precioUnitario
     */
    public BigDecimal getPrecioUnitario() {
        return precioUnitario;
    }

    /**
     * @param precioUnitario the precioUnitario to set
     */
    public void setPrecioUnitario(BigDecimal precioUnitario) {
        this.precioUnitario = precioUnitario;
    }

    /**
     * @return the cantidad
     */
    public Integer getCantidad() {
        return cantidad;
    }

    /**
     * @param cantidad the cantidad to set
     */
    public void setCantidad(Integer cantidad) {
        this.cantidad = cantidad;
    }
}

Esto es necesario ya que de otro modo le objeto Gson mandara errores de acceso a las propiedades del objeto.

Ya que halla cumplido esos dos detalles la conversión es tan sencilla como llamar al método toJson del objeto Gson, como se ve a continuación.

String JSON = gson.toJson(detalle1);

Y el resultado se vera mas o menos asi

{"producto":"Aceite","importe":120.0,"precioUnitario":12.00,"cantidad":10}

Si el objeto que convirtió a JSON contenia un arreglo de objetos estos se agregaran con la notación adecuada, como se ve a continuación

{
	"fecha": "Jul 10, 2018 1:12:58 AM",
	"cliente": "CLIENTE DE PRUEBA",
	"detalle": [{
		"producto": "Aceite",
		"importe": 120.0,
		"precioUnitario": 12.00,
		"cantidad": 10
	}, {
		"producto": "Anticongelante",
		"importe": 90.0,
		"precioUnitario": 45.00,
		"cantidad": 2
	}, {
		"producto": "Reparacion Rin",
		"importe": 400.0,
		"precioUnitario": 400.00,
		"cantidad": 1
	}]
}

Convirtiendo de JSON a Objetos Java

Al usar un webservice no solo enviaremos información en forma de JSON sino que también la recibiremos en el mismo forma, asi que veamos como convertir de JSON a un objeto Java, la misma observación aplica aquí, el objeto receptor debe de tener todos los getter y setters necesarios o tendrá un error de conversión

El método usado para realizar la conversión es fromJson el cual toma dos argumentos, la cadena String con el JSON y la clase del objeto al que queremos convertir el Json.

Con la cadena de texto debe recordar un detalle importante y es que las comillas dobles son caracteres reservados en Java por lo que recomiendo revisar el Json que regreso el webservice y remplazar esas por comillas simples, para evitar problemas de conversión.

Con respecto a la clase del objeto esa se obtiene facilmente con la propiedad .class de la clase que deseamos usar

Para simplificar las cosas a continuación se presenta un ejemplo

String jsonComplejo = "{'fecha':'Jul 9, 2018 3:37:49 PM','cliente':'LIMPIEZA Y ASEO PROFESIONAL','detalle':[{'producto':'Cloro','importe':150.0,'precioUnitario':15.00,'cantidad':10},{'producto':'Escoba','importe':30,'precioUnitario':30.00,'cantidad':1},{'producto':'Aromatizante ambiental','importe':100.0,'precioUnitario':10.00,'cantidad':10}]}";

Venta venta2 = gson.fromJson(jsonComplejo, Venta.class);

Como notara la conversión puede encargarse sin problemas de un arreglo contenido

El ejemplo completo con lo puede encontrar aquí: https://gitlab.com/ticomWebcomic/JsonExample

Referencias.

Pagina oficial de Gson: https://github.com/google/gson


Espero que esta entrada fuera de utilidad y si lo fue y quiere cooperar con la causa tengo una pagina en Ko-fi para aceptar donativos

ko-fi

Anuncios

BigDecimal, no mas errores de redondeo

Suma de Doubles
Aquí veremos como evitar esto

Si ha usado Java para el desarrollo de aplicaciones donde los datos que maneje representen dinero seguramente se a encontrado con situaciones donde las operaciones con decimales le dan resultados incorrectos, específicamente en los decimales con el resultado quedando centésimas arriba o abajo del resultado correcto.

Esto se debe a la forma en que funciona la aritmética binaria a la hora de representar los decimales, si buen para la mayoría de los casos esos errores son demasiado pequeños las operaciones financieras usualmente implican la combinación ganadora de exigir precisión y operaciones sobre una cantidad significativa de datos de modo que esos pequeños errores de la aritmética binaria se acumulan y termina con una factura un centavo entero mas grande de lo esperado, y si cree que no le van a pelear ese centavo se equivoca, cuando es dinero hasta una décima de centavo basta para darse un encontrón con un cliente.

Por fortuna ya desde hace varias versiones Java incluye una clase que permite hacer esas operaciones sin preocuparnos por errores causados por la aritmética binaria, la clase BigDecimal.

BigDecimal.

La clase BigDecimal nos permite representar un numero decimal con las siguientes características

  • Operaciones decimales: Las operaciones sobre este tipo se realizan internamente en base 10, eso implica una perdida de rendimiento pero a cambio los errores de aritmética binaria se previenen por completo.
  • Precisión arbitraria: No se impone un limite en la cantidad de decimales, la cantidad de decimales a usar esta limitada únicamente por la memoria del sistema
  • Inmutable: Una vez creado un objeto BigDecimal el valor de este NO puede ser modificado, cada operación le generara un objeto nuevo con el valor resultado de la operación.

Si bien la perdida de rendimiento puede sonar preocupante esta esta en el area de mili/micro segundos, el usuario de su aplicación financiera no va a notar la diferencia e incluso si lo nota ese milisegundo es preferible a pelearse con un cliente por un centavo.

Operaciones con BigDecimal.

Las operaciones disponibles son las que espera, suma, resta, multiplicación, división, modulo, potencia, etcetera.

Sin embargo al momento de usarlas debe de tomar dos consideraciones, primero ya que en Java la sobrecarga de operadores no existe debera de usar los métodos .add, .multiply, .divide, .substract, .pow, etc

BigDecimal valor = new BigDecimal("10.0");
BigDecimal factor = new BigDecimal("5.5");

System.out.println("Suma: " + valor.add(factor));
System.out.println("Resta: " + valor.subtract(factor));
System.out.println("Multiplicación: " + valor.multiply(factor));
// MathContext indica cuandos bits deseamos usar para decimales, esto es debe especificar
// para manejar los casos donde la cantidad de decimales sea enorme (1/3 por ejemplo)
System.out.println("Division: " + valor.divide(factor, MathContext.DECIMAL128));
System.out.println("Potencia: " + valor.pow(3));

Hay que notar que las operaciones donde sea posible que ocurran numeros con decimales infinitos se debe especificar la cantidad de decimales a usar via la constante MathContext adecuada.

Otro detalle es que los objetos BigData son inmutables, por lo que las operaciones crean un objeto nuevo para el resultado por lo que debe asignar ese resultado a un objeto BigDecimal para poder usarlo

BigDecimal resultado = valor.add(factor);
System.out.println(“El resultado se guardo y accede desde la variable resultado: ” + resultado);

Ejemplo

Y para no dejar esta entrada sin ejemplo veamos como seria la sumatoria de la imagen en el inicio de la entrada usando BigDecimal en lugar de Double

package mx.hashCode.bigDecimal;

import java.math.BigDecimal;

public class App {    
    static public void main(String[] args){
        BigDecimal resultado = new BigDecimal("0.0");
        BigDecimal cent = new BigDecimal("0.01");

        resultado = resultado.add(cent);
        resultado = resultado.add(cent);
        resultado = resultado.add(cent);
        resultado = resultado.add(cent);
        resultado = resultado.add(cent);
        resultado = resultado.add(cent);

        System.out.println("Resultado de sumar 6 veces el valor 0.01 guardando en una variable BigDecimal");
        System.out.println(resultado);
    }
}

Y el resultado seria el siguiente:

Resultado de sumar 6 veces el valor 0.01 guardando en una variable BigDecimal
0.06

Referencias:

Por que usar BigDecimal (y no Double) para calculos aritmeticos financieros


Espero que esta entrada fuera de utilidad y si lo fue y quiere cooperar con la causa tengo una pagina en Ko-fi para aceptar donativos

ko-fi

Gracias y nos vemos en la próxima entrada :).

Cambiar la altura de las celdas de una Tabla en iText 7

Altura de celdas
Altura de celdas

Cuando pone una tabla en su documento iText 7 puede llegar a pensar que seria bueno aumentar o reducir la altura de las filas en una tabla ya sea para ahorrar espacio al imprimir o mejorar la legibilidad del documento, aquí esta como.

Método .setHeight(altura)

No hay una forma de cambiar la altura de todas las celdas de una tabla de golpe por lo que si queremos especificar la altura lo tenemos que hacer celda por celda, esto es crear un objeto Cell, agregar el contenido, indicar la altura que deseamos con .setHeight(altura) y agrega esa celda a la tabla, un ejemplo de como se ve a continuación.

// Creamos una celda
Cell celda1 = new Cell();

// Agregamos el contenido de la celda
celda1.add(new Paragraph("Celda 1"));

// indicamos la altura para la celda
celda1.setHeight(altura);

// agregamos esa celda a la tabla
tabla.addCell(celda1);

Hay una observación que hacer, aunque uno podria pensar que se podria crear una tabla donde cada celda tenga una altura diferente este no es el caso, en iText la celda mas alta de una fila es la que determinara la altura de toda esa fila por lo que debe tomar eso en consideración al diseñar su tabla.

Ejemplo

Ahora pasemos a un ejemplo, via un método crearemos una tabla y especificaremos la altura de las tablas, llamaremos ese método varias veces para generar varias tablas con celdas de diferente altura y las agregaremos al documento, el resultado sera como en la figura al inicio, una serie de tablas con celdas de altura incremental.

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mx.hash.fontsize.tablecellheight;

import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import java.io.FileNotFoundException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author david
 */
public class CellHeight {

    private static Logger LOGGER = Logger.getLogger("mx.hash.fontsize.tablecellheight.CellHeight");

    public Table crearTablaConAtura(float altura) {
        // Creamos la tabla
        float[] anchos = {150f, 150f, 150f};
        Table tabla = new Table(anchos);
        
        // Creamos las celdas
        Cell celda1 = new Cell();
        Cell celda2 = new Cell();
        Cell celda3 = new Cell();

        celda1.add(new Paragraph("Celda 1"));
        // Indicamos la altura para la celda 1
        celda1.setHeight(altura);

        celda2.add(new Paragraph("Celda 2"));
        // Indicamos la altura para la celda 2
        celda2.setHeight(altura);

        celda3.add(new Paragraph("Celda 3"));
        // Indicamos la altura para la celda 3
        celda3.setHeight(altura);
        
        // Agregamos las celdas a la tabla
        tabla.addCell(celda1);
        tabla.addCell(celda2);
        tabla.addCell(celda3);

        return tabla;
    }

    static public void main(String[] args) {
        PdfWriter pdfWriter;
        try {
            // Creamos un documento pdf con iText
            pdfWriter = new PdfWriter("./fontSize.pdf");
            PdfDocument pdfDoc = new PdfDocument(pdfWriter);
            Document doc = new Document(pdfDoc, PageSize.LETTER);
            
            CellHeight generadorTablas = new CellHeight();
            
            doc.add( new Paragraph("Altura 12f, demasiado chico para el tamaño de texto, por lo que no se vera, tenga cuidado con esto y redusca el tamaño de fuente acordemente") );
            doc.add( generadorTablas.crearTablaConAtura(12f) );
            
            Table tablaChica = generadorTablas.crearTablaConAtura(12f);
            // Cambiamos el tamaño de texto
            tablaChica.setFontSize(8f);
            
            doc.add( new Paragraph("Con un tamaño de texto acorde ya se ve mejor") );
            doc.add(tablaChica);
            
            doc.add( new Paragraph("Altura 22f") );
            doc.add( generadorTablas.crearTablaConAtura(22f) );
            
            doc.add( new Paragraph("Altura 32f") );
            doc.add( generadorTablas.crearTablaConAtura(32f) );
            
            doc.add( new Paragraph("Altura 42f") );
            doc.add( generadorTablas.crearTablaConAtura(42f) );
            
            doc.add( new Paragraph("Altura 52f") );
            doc.add( generadorTablas.crearTablaConAtura(52f) );
            
            doc.add( new Paragraph("El tamaño se respeta por *fila*, al cambiar de fila usa la altura de la celda mas alta de esa fila") );
            
            Table tablaExtendida =  generadorTablas.crearTablaConAtura(52f);
            tablaExtendida.addCell( new Paragraph("Esta celda deberia ser de tamaño normal!") );
            
            Cell celdaGrande = new Cell();
            
            celdaGrande.setHeight(100f);
            celdaGrande.add( new Paragraph("Esta celda cambia el tamaño de toda la fila"));
            tablaExtendida.addCell(celdaGrande);
            
            doc.add(tablaExtendida);

            doc.close();

        } catch (FileNotFoundException ex) {
            LOGGER.log(Level.SEVERE, null, ex);
        }
    }

}

Hay dos casos extra que se presentan en el ejemplo.

El primero es que pasa si indica un altura demasiado pequeña para que el texto aparesca completamente, en ese caso el texto simplemente no aparecera, esto no genera ningun mensaje de error por lo que debe de verificarlo con cuidado al reducir la altura de las celdas, una correción simple es reducir el tamaño del texto como se vio en una entrada anterior.

Lo segundo y mas importante es lo que mencionamos previamente, la altura de toda la fila es controlada por la celda mas alta de la fila, independientemente de lo que indiquen las demas tablas y ese tamaño se resetea cada cambio de fila, por lo que si se quiere poner creativo con eso debe tomar muy en cuenta cuantas celdas van en cada fila.

El código completo de este ejemplo puede ser encontrado en https://gitlab.com/ticomWebcomic/TableCellHeight

Espero que esta entrada les fuera de utilidad y desean cooperar, me pueden invitar una cerveza: https://www.paypal.me/hashRaygoza/20mxn Gracias y nos vemos en la próxima :).

 

Cambiar el tamaño de fuente en iText 7

Diferentes tamaños de fuente en iText7
Diferentes tamaños de fuente en iText7

Si esta utilizando iText 7 para generar reportes o documentos que vayan a ser impresos hay que ser considerados con el tamaño de texto, puede ser que se quiera ahorrar papel y que todo el reporte quepa en una sola pagina o que recuerde que en papel no hay función zoom así que debe ser legible, estas situaciones suelen implicar cambiar el tamaño del texto, ya sea reducirlo o aumentarlo y no se ustedes pero la documentación oficial de iText 7 es un poco críptica al respecto (o al menos Google tiene problemas en hallar donde estaba) asi que veamos como aumentar y reducir el tamaño del texto en iText 7 (Si se pregunta por que estoy repitiendo tanto el numero de versión es por que lo que lea de iText 5 NO funciona en iText 7).

El Método .setFontSize

Por fortuna esta vez iText 7 va a estar muy a nuestra favor ya que con la mayoría de los elementos que utilizan texto, como Paragraph y Table, basta con llamar el método

.setFontSize(tamaño)

donde tamaño es un valor float que indica el tamaño de la fuente que deseamos usar y con eso el tamaño cambia.

Ejemplo

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mx.hash.fontsize.fontsizeitext;

import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import java.io.FileNotFoundException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author david
 */
public class FontSizeExample {
    private static Logger LOGGER = Logger.getLogger("mx.hash.fontsize.fontsizeitext.FontSizeExample");
    
    static public void main(String[] args) {
        try {
            // Creamos un documento pdf con iText
            PdfWriter pdfWriter = new PdfWriter("./fontSize.pdf");
            PdfDocument pdfDoc = new PdfDocument(pdfWriter);
            Document doc = new Document(pdfDoc, PageSize.LETTER);
            
            // Creamos unos parrafos
            Paragraph parrafo1 = new Paragraph("Prueba con el tamaño por default Prueba con el tamaño por default Prueba con el tamaño por default Prueba con el tamaño por default Prueba con el tamaño por default Prueba con el tamaño por default Prueba con el tamaño por default Prueba con el tamaño por default Prueba con el tamaño por default Prueba con el tamaño por default Prueba con el tamaño por default Prueba con el tamaño por default Prueba con el tamaño por default");
            Paragraph parrafo2 = new Paragraph("Demostracion de letra mas pequeña Demostracion de letra mas pequeña Demostracion de letra mas pequeña Demostracion de letra mas pequeña Demostracion de letra mas pequeña Demostracion de letra mas pequeña Demostracion de letra mas pequeña Demostracion de letra mas pequeña Demostracion de letra mas pequeña Demostracion de letra mas pequeña Demostracion de letra mas pequeña Demostracion de letra mas pequeña Demostracion de letra mas pequeña");
            Paragraph parrafo3 = new Paragraph("Ejemplo con una letra mas grande Ejemplo con una letra mas grande Ejemplo con una letra mas grande Ejemplo con una letra mas grande Ejemplo con una letra mas grande Ejemplo con una letra mas grande Ejemplo con una letra mas grande Ejemplo con una letra mas grande Ejemplo con una letra mas grande Ejemplo con una letra mas grande Ejemplo con una letra mas grande Ejemplo con una letra mas grande Ejemplo con una letra mas grande ");
            
            // Creamos unas tablas
            float[] anchos = {150f, 150f, 150f};
            Table tabla1 = new Table(anchos);
            Table tabla2 = new Table(anchos);
            Table tabla3 = new Table(anchos);
            
            // Agregamos contenido a las tablas
            tabla1.addCell("Ejemplo con");
            tabla1.addCell("Con tamaño");
            tabla1.addCell("default");
            
            tabla2.addCell("Ejemplo con");
            tabla2.addCell("Con tamaño");
            tabla2.addCell("mas pequeño");
            
            tabla3.addCell("Ejemplo con");
            tabla3.addCell("Con tamaño");
            tabla3.addCell("mas grande");            
            
            // Cambiamos el tamaño de fuente del parrafo 2 lo hacemos mas pequeño
            parrafo2.setFontSize(8f);
            
            // Cambiamos el tamaño de fuente del parrafo 3 lo hacemos mas grande
            parrafo3.setFontSize(20f);
            
            // Cambiamos el tamaño de fuente de la tabla 1, lo hacemos mas pequeño
            tabla2.setFontSize(8f);
            
            // Cambiamos el tamaño de fiente de la tabla 2, lo hacemos mas grande
            tabla3.setFontSize(20f);            
            
            doc.add(parrafo1);
            doc.add(parrafo2);
            doc.add(parrafo3);
            doc.add(tabla1);
            doc.add(tabla2);
            doc.add(tabla3);
            
            doc.close();
        } catch (FileNotFoundException ex) {
            LOGGER.log(Level.SEVERE, null, ex);
        }
    }
    
}

Como puede ver cambiar el tamaño es tan sencillo como llamar un solo método, notara que al poner el tamaño puse una f después de los números esto es para que el compilador de Java sepa que es un numero de tipo float.

El código completo del proyecto puede localizarlo en https://gitlab.com/ticomWebcomic/FontSizeiText


Espero que esta entrada les fuera de utilidad y desean cooperar, me pueden invitar una cerveza: https://www.paypal.me/hashRaygoza/20mxn Gracias y nos vemos en la próxima :).

 

Imprimiendo en Java SIN mostrar el diálogo de impresión

Impreso automáticamente y sin pedir permiso
Impreso automáticamente y sin pedir permiso

Si se decida a programar sistemas hechos a la medida para diferentes negocios el generar reportes no debiera ser algo inusual, mucho menos que el cliente pida que se puedan mandar a la impresora pero ¿Que tal que el cliente pida que un reporte se imprima a una hora especifica?, esto suele tener un detalle inesperado, el que se imprima incluso si no hay nadie en la oficina para presionar el botón Imprimir, ¿Suena complicado?, ¿Algo que solo se puede hacer vía un cable serial y mandando los bits directamente?, nada de eso, aquí le digo como.

Para esto nos basaremos en lo visto en una entrada previa, por lo que le recomiendo le eche un vistazo si no lo ha hecho ya.

Mandando un documento iText directo a la impresora

Ya que halla refrescado su memoria veamos como mandar algo a la impresora sin mostrar el dialogo de impresión o pedir permiso al usuario (Obviamente solo usara este poder para el bien, ¿verdad?).

Teoría

De las entradas anteriores con la librería PdfBox seguro recordara el la clase PrinterJob es la que se encarga de mandar el documento a la impresora y para indicar la impresora necesitamos llamar al método printDialog, este nos deja seleccionar la impresora y nos pide la confirmación, pero esta no es la única forma de indicar la impresora.

Esto lo hacemos con la clase PrintService esta nos deja indicar directamente a PrinterJob la impresora saltandonos el dialogo de selección de impresora y la confirmación.

Ahora ¿Como creamos ese objeto PrintService? bueno para eso necesitamos dos cosas, primero saber el nombre exacto de la impresora que desea usar, hay dos formas de hacer esto, una es ver el dialogo de impresoras del sistema operativo, como se ve en la figura.

Así se ve en Fedora Linux, puede variar según su sistema operativo
Así se ve en Fedora Linux, puede variar según su sistema operativo

La otra es ejecutar la siguiente función la cual le listara todas las impresoras disponibles en su sistema.

/**
* Muestra en pantalla la lista de todas las impresoras disponibles en el
* sistema
*/
public void listarImpresoras() {
	PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);
	System.out.println("Lista de impresoras disponibles");

	for (PrintService printService: printServices) {
		System.out.println("\t" + printService.getName());
	}
}

Esta función le presentara en pantalla un listado completo de las impresoras del sistema, recuerde correrla en un proyecto que incluya PdfBox, la salida sera similar a la figura.

Como ve solo tengo una impresora conectada.
Como ve solo tengo una impresora conectada.

Como puede ver en el código de la función de búsqueda, la lista completa de impresoras nos la proporciono el método PrintServiceLookup.lookupPrintServices(null, null); el cual listo todas las impresoras, bueno usando esa misma idea podemos crear un método que nos regrese la impresora con el nombre indicado que seria como se ve a continuación

/**
* Nos regresa el PrintService que representa la impresora con el nombre que
* le indiquemos
* @param printerName nombre de la impresora que deseamos usar
* @return PrintService que representa la impresora que deseamos usar
*/
private PrintService findPrintService(String printerName) {
	PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);
	for (PrintService printService: printServices) {
		System.out.println(printService.getName());

		if (printService.getName().trim().equals(printerName)) {
			return printService;
		}
	}
	return null;
}

Basta con pasarle de parámetro el nombre de la impresora que deseamos usar y nos regresara el PrintService adecuado.

Lo ultimo que necesitaría hacer ya que tenga el PrintService es indicarle al objeto PrinterJob que deseamos usar ese PrintService, esto se logra con una sola linea de código que es:

printerJob.setPrintService(myPrintService);

Hecho esto basta con llamar el método print() y el documento se enviara directo a la impresora.

Pero para hacer mas claro todo veamos un ejemplo.

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mx.hash.impresioninmediata;

import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.swing.JOptionPane;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.printing.PDFPageable;

/**
 *
 * @author david
 */
public class ImpresionInmediata {

    private final static Logger LOGGER = Logger.getLogger("mx.hash.impresioninmediata.ImpresionInmediata");

    static public void main(String[] args) {
        ImpresionInmediata printer = new ImpresionInmediata();

        printer.listarImpresoras();

        try {
            ByteArrayOutputStream documentoBytes = printer.crearDocumentoiText();
            printer.imprimir(documentoBytes);
        } catch (IOException | PrinterException ex) {
            JOptionPane.showMessageDialog(null, "Error de impresion", "Error", JOptionPane.ERROR_MESSAGE);
            LOGGER.log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * Envia a imprimir el ByteArrayOutoutStream creado de un documento iText
     *
     * @param documentoBytes
     * @throws IOException
     * @throws PrinterException
     */
    public void imprimir(ByteArrayOutputStream documentoBytes) throws IOException, PrinterException {

        // Aqui convertimos la el arreglo de salida a uno de entrada que podemos
        // mandar a la impresora
        ByteArrayInputStream bais = new ByteArrayInputStream(documentoBytes.toByteArray());

        // Creamos un PDDocument con el arreglo de entrada que creamos        
        PDDocument document = PDDocument.load(bais);

        PrintService myPrintService = this.findPrintService("Deskjet-1510-series");
        PrinterJob printerJob = PrinterJob.getPrinterJob();

        printerJob.setPageable(new PDFPageable(document));
        printerJob.setPrintService(myPrintService);

        printerJob.print();

    }

    /**
     * Muestra en pantalla la lista de todas las impresoras disponibles en el
     * sistema
     */
    public void listarImpresoras() {
        PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);
        System.out.println("Lista de impresoras disponibles");

        for (PrintService printService : printServices) {
            System.out.println("\t" + printService.getName());
        }
    }

    /**
     * Nos regresa el PrintService que representa la impresora con el nombre que
     * le indiquemos
     * @param printerName nombre de la impresora que deseamos usar
     * @return PrintService que representa la impresora que deseamos usar
     */
    private PrintService findPrintService(String printerName) {
        PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);
        for (PrintService printService : printServices) {
            System.out.println(printService.getName());

            if (printService.getName().trim().equals(printerName)) {
                return printService;
            }
        }
        return null;
    }

    /**
     * Crea un documento via la libreria iText y lo almacena como un
     * ByteArrayOutputStream
     *
     * @return Documento iText en formato ByteArrayOutputStream
     */
    public ByteArrayOutputStream crearDocumentoiText() {
        // Es en este ByteArrayOutputStream donde se pone el documento una vez 
        // que se llama a documento.close()
        ByteArrayOutputStream documentoBytes = new ByteArrayOutputStream();

        PdfWriter pdfWriter = new PdfWriter(documentoBytes);
        PdfDocument pdfDoc = new PdfDocument(pdfWriter);

        Document documento = new Document(pdfDoc, PageSize.LETTER);
        documento.add(new Paragraph("Inicia el reporte"));
        documento.add(this.crearTabla());

        documento.close();

        return documentoBytes;
    }

    private Table crearTabla() {
        float[] anchos = {50F, 50F, 50F};
        Table tablaEncabezado = new Table(anchos);

        tablaEncabezado.setWidth(500F);

        tablaEncabezado.addCell("Hora Inicio");
        tablaEncabezado.addCell("Hora Fin");
        tablaEncabezado.addCell("");
        tablaEncabezado.addCell("Fecha Inicio");
        tablaEncabezado.addCell("Fecha Fin");
        tablaEncabezado.addCell("Fin de Turno");

        return tablaEncabezado;
    }    

}

Al correr este ejemplo muy posiblemente la indicara un error (a menos que por casualidad su impresora se llama exactamente igual que la mia), no se preocupe al correr el programa la lista completa de impresoras aparecerá en pantalla, solamente vea cual impresora desea usar y acomode la siguiente linea

PrintService myPrintService = this.findPrintService("Deskjet-1510-series");

Poniendo el nombre de la impresora como argumento a esa función, hecho esto el documento se imprimirá la siguiente vez que corra el programa.

El ejemplo completo lo puede encontrar aquí: https://github.com/HashRaygoza/ImpresionInmediata


Espero que esta entrada les fuera de utilidad y desean cooperar, me pueden invitar una cerveza: https://www.paypal.me/hashRaygoza/20mxn Gracias y nos vemos en la próxima :).

 

Mandando un documento iText directo a la impresora

¡Miren! Sin Pdf previo
¡Miren! Sin Pdf previo

En una entrada anterior detallamos como mandar un archivo Pdf a la impresora y si bien seguro puede usar eso para implementar la impresión de un documento iText, creandolo en disco duro e imprimiendolo desde ahí, también es posible hacerlo sin necesidad de guardar nada en disco duro, veamos como :).

Imprimiendo un Pdf desde Java

Teoria

Para esto usaremos una funcionalidad muy interesante de la clase PdfWriter de iText la habilidad de enviar el documento resultante no a un archivo en disco duro sino a un objeto ByteArrayOutputStream como se ve en las siguientes lineas de código.

ByteArrayOutputStream documentoBytes = new ByteArrayOutputStream();
PdfWriter pdfWriter = new PdfWriter(documentoBytes);

Ahora ¿De que nos sirve eso?, bueno podemos convertir el ByteArrayOutputStream en un ByteArrayInputStream.

ByteArrayInputStream bais = new ByteArrayInputStream(documentoBytes.toByteArray());

Y si ha leido la documentación de PdfBox sabrá que no solo puede cargar un archivo desde el disco duro, sino también un ByteArrayInputStream.

PDDocument document = PDDocument.load(bais);

Y de ese punto ya puede seguir el mismo procedimiento mostrado en otra entrada, llamando al proceso de impresión del sistema, pero para hacerlo mas claro veamos un ejemplo.

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mx.hash.printitext;

import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.printing.PDFPageable;

/**
 *
 * @author david
 */
public class PrintiText {

    private final static Logger LOGGER = Logger.getLogger("mx.hash.printitext.PrintiText");

    static public void main(String[] args) {
        try {
            PrintiText printer = new PrintiText();

            ByteArrayOutputStream documentoBytes = printer.crearDocumentoiText();
            printer.imprimir(documentoBytes);

        } catch (IOException | PrinterException ex) {
            JOptionPane.showMessageDialog(null, "Error de impresion", "Error", JOptionPane.ERROR_MESSAGE);
            LOGGER.log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Crea un documento via la libreria iText y lo almacena como un
     * ByteArrayOutputStream
     *
     * @return Documento iText en formato ByteArrayOutputStream
     */
    public ByteArrayOutputStream crearDocumentoiText() {
        LOGGER.log(Level.INFO, "Creando documento iText");

        // Es en este ByteArrayOutputStream donde se pone el documento una vez 
        // que se llama a documento.close()
        ByteArrayOutputStream documentoBytes = new ByteArrayOutputStream();

        PdfWriter pdfWriter = new PdfWriter(documentoBytes);
        PdfDocument pdfDoc = new PdfDocument(pdfWriter);

        Document documento = new Document(pdfDoc, PageSize.LETTER);
        LOGGER.log(Level.INFO, "Agregando los elementos al documento");

        documento.add(new Paragraph("Inicia el reporte"));
        documento.add(this.crearTabla());

        documento.close();

        return documentoBytes;
    }

    private Table crearTabla() {
        LOGGER.log(Level.INFO, "Creando tabla");

        float[] anchos = {50F, 50F, 50F};
        Table tablaEncabezado = new Table(anchos);

        tablaEncabezado.setWidth(500F);

        tablaEncabezado.addCell("Hora Inicio");
        tablaEncabezado.addCell("Hora Fin");
        tablaEncabezado.addCell("");
        tablaEncabezado.addCell("Fecha Inicio");
        tablaEncabezado.addCell("Fecha Fin");
        tablaEncabezado.addCell("Fin de Turno");

        return tablaEncabezado;
    }

    /**
     * Envia a imprimir el ByteArrayOutoutStream creado de un documento iText
     *
     * @param documentoBytes
     * @throws IOException
     * @throws PrinterException
     */
    public void imprimir(ByteArrayOutputStream documentoBytes) throws IOException, PrinterException {
        LOGGER.log(Level.INFO, "Enviando documento a imprimir");

        // Aqui convertimos la el arreglo de salida a uno de entrada que podemos
        // mandar a la impresora
        ByteArrayInputStream bais = new ByteArrayInputStream(documentoBytes.toByteArray());
        
        // Creamos un PDDocument con el arreglo de entrada que creamos        
        PDDocument document = PDDocument.load(bais);

        PrinterJob job = PrinterJob.getPrinterJob();

        LOGGER.log(Level.INFO, "Mostrando el dialogo de impresion");
        if (job.printDialog() == true) {
            job.setPageable(new PDFPageable(document));

            LOGGER.log(Level.INFO, "Imprimiendo documento");
            job.print();
        }
    }
}

Este programa usa iText para generar el documento que puede ver al principio de la entrada con un párrafo de texto y una tabla y después vía PdfBox mandarlo a la impresora.

El programa en ejecución.
El programa en ejecución.

Aquí puede aplicar sus conocimientos de iText y en el método crearDocumentoiText agregar mas elementos que deseé aparezcan en el documento y vea los resultados.

El ejemplo completo lo puede encontrar en GitHub en la siguiente dirección https://github.com/HashRaygoza/PrintiText


Espero que esta entrada les fuera de utilidad y desean cooperar, me pueden invitar una cerveza: https://www.paypal.me/hashRaygoza/20mxn Gracias y nos vemos en la próxima :).

Imprimiendo un Pdf desde Java

Imprimiendo un Pdf desde Java
Imprimiendo un Pdf desde Java

A la hora de investigar como mandar el Pdf que acaba de generar a la impresora es MUY posible que se encuentre con un mensaje perturbador, el hecho de que NO todas las impresoras entienden nativamente el formato Pdf por lo que si manda el archivo a la brava se arriesga a solo obtener basura a la salida de varias impresoras.

Por fortuna hay librerías capaces de convertir su archivo Pdf a algo que la impresora puede entender, por ejemplo Apache PdfBox ¿Como integrarlo a su proyecto? aquí le digo :).

Agregando PdfBox a su proyecto

Si su proyecto esta usando Maven agregar la librería PdfBox es tan sencillo como agregar esa dependencia, en el caso especifico de NetBeans seria como en la figura.

Agregando PdfBox en NetBeans
Agregando PdfBox en NetBeans

Si esta usando otro IDE o Maven directamente puede agregar la dependencia agregando esta dependencia a su archivo pom.xml

<dependency> 
<groupId>org.apache.pdfbox</groupId> 
<artifactId>pdfbox</artifactId>
<version>2.0.8</version> 
</dependency>

O si esta agregando las librerías manualmente puede descargar los .jar necesarios desde aquí: https://pdfbox.apache.org/download.cgi#20x

Imprimiendo el Pdf

Ahora la forma en que haremos la impresión es la siguiente:

  1. Cargaremos el archivo Pdf desde el disco duro vía un objeto PDDocument
  2. Obtendremos el proceso de impresión del sistema
  3. Seleccionaremos la impresora
  4. Usando un objeto PDFPageable enviaremos el documento a la impresora
  5. Imprimiremos

Notara que en este ejemplo cargaremos el Pdf desde el disco duro, esto es mas que nada para simplificar el ejemplo al no tener que incluir el código que genere el documento, pero sepa que es perfectamente posible mandar un Pdf que creo con iText a la impresora sin necesidad de guardarlo en disco, eso se mostrara en una entrada próxima.

A continuación se presenta el código de nuestro ejemplo:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mx.hash.impresionpdf;

import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.printing.PDFPageable;

/**
 *
 * @author david
 */
public class Impresor {

    private final static Logger LOGGER = Logger.getLogger("mx.hash.impresionpdf.Impresor");

    public static void main(String[] args) {
        Impresor impresor = new Impresor();

        try {
            impresor.imprimir();
        } catch (PrinterException | IOException ex) {
            JOptionPane.showMessageDialog(null, "Error de impresion", "Error", JOptionPane.ERROR_MESSAGE);
            LOGGER.log(Level.SEVERE, null, ex);
        }
    }

    /***
     * Manda un documento a imprimir a la impresora que se indique en el dialogo
     * @throws PrinterException
     * @throws IOException 
     */
    public void imprimir() throws PrinterException, IOException {
        // Indicamos el nombre del archivo Pdf que deseamos imprimir
        PDDocument document = PDDocument.load(new File("./documento.pdf"));

        PrinterJob job = PrinterJob.getPrinterJob();

        LOGGER.log(Level.INFO, "Mostrando el dialogo de impresion");
        if (job.printDialog() == true) {            
            job.setPageable(new PDFPageable(document));

            LOGGER.log(Level.INFO, "Imprimiendo documento");
            job.print();
        }
    }
}

Para que todo quede lo mas claro posible vamos a detallar que hacen las lineas mas relevantes del ejemplo.

Aquí cargamos el documento pdf desde la ruta indicada

PDDocument document = PDDocument.load(new File("./documento.pdf"));

Obtenemos el servicio de impresión del sistema

PrinterJob job = PrinterJob.getPrinterJob();

Mostramos el dialogo para seleccionar a que impresora deseamos enviar el documento, el método .printDialog() regresa true si selecciono una impresora, false en el caso contrario

if (job.printDialog() == true)

Via el método setPageable le pasamos al servicio de impresión nuestro documento que podría ser de una o mas paginas

job.setPageable(new PDFPageable(document));

Finalmente mandamos a imprimir

job.print();

Corriendo el ejemplo

Ahora vamos a correr el ejemplo, un preparativo extra que va a necesitar es poner un archivo pdf con nombre documento.pdf en la carpeta de su proyecto o modificar la ruta en esta linea.

PDDocument document = PDDocument.load(new File("./documento.pdf"));

Así es como la carpeta del proyecto se ve en mi equipo.

Ahí puse documento.pdf
Ahí puse documento.pdf

Hecho eso corra el programa y le aparecerá el siguiente dialogo (al menos en Linux, en Windows es posible le aparezca el dialogo de impresión nativo de Windows)

Dialogo de impresión
Dialogo de impresión

Seleccione la impresora que desea utilizar y al presionar imprimir enviara el documento a su impresora.

Como un extra vaya a el gestor de trabajos de impresión y vera que nuestra impresión aparece como Java Printing y puede cancelar la impresión desde ahí si lo desea.

Servicio de Impresión en GNOME
Servicio de Impresión en GNOME
Impresión en proceso
Impresión en proceso

Puede encontrar el ejemplo completo en GitHub en la siguiente dirección: https://github.com/HashRaygoza/ImpresionPdf


Espero que esta entrada les fuera de utilidad y desean cooperar, me pueden invitar una cerveza: https://www.paypal.me/hashRaygoza/20mxn Gracias y nos vemos en la próxima :).