[Métodos] Imprimir a través de una impresora en java


El objetivo es sencillo poder imprimir un String a través de una impresora.
Las dificultades que vamos a obviar son:
  • Que haya una impresora configurada para la maquina en curso.
  • Que este encendida.
  • Que tenga papel y tinta.

Suponiendo que lo anterior está resuelto procedemos a...
  • Crear un objeto de la clase java.awt.print.PrinterJob
    1
    PrinterJob job = PrinterJob.getPrinterJob();
    

  • Llamamos a su método setPrintable
    1
    job.setPrintable(new Imprimir());
    
    • public abstract void setPrintable(Printable painter)
      • Llama al impresor que renderiza las páginas. Las páginas en el documento que se imprimirá por este 'PrinterJob' se representan por el objeto de impresión, 'painter'. El 'PageFormat' para cada página es el formato de página predeterminado.
      • Recibe como paramétro un objeto de la interfaz Printable.

  • Nuestra pequña clase va a ser el objeto que le pasaremos por parámetro, por ende vamos a extender de la interfaz.
    1
    public class Imprimir implements Printable {
    

  • Esta interfaz va a necesitar que implementemos el método print
    • int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException
      • Imprime la página en el índice especificado en el contexto Graphics especificado en el formato especificado.
      • Parámetros (estos se toman con los valores por defecto)
        • graphics - el contexto en el que se dibuja la página
        • PageFormat - el tamaño y la orientación de la página que se está dibujando
        • PageIndex - el índice basado en cero de la página a dibujar
  • Lo siguiente es llamar al mètodo que muestra el tipico cuadro de pre-impresión del sistema operativo:

    1
    boolean doPrint = job.printDialog();
    

    • Este devuevle un boolean, el cual será de utilidad para determinar si se prosigue con la impresión ya que es el ultimo paso antes de cancelar.
    • Tambíen en este paso se setean el número de páginas a imprimir.

  • Ya sólo nos queda por saber cual ha sido el estado de la impresión, y llamar al método overradeado print
    1
    2
    3
    4
    5
    6
    7
    if (doPrint) {
        try {
            job.print();
        } catch (PrinterException e) {
            e.printStackTrace();
        }
    }
    

  • Dentro del método que sobreescribimos, devolvemos un int que puede ser 0 o 1 dependiendo de la existencia de la página. Y determinar el area imprimible y pasarle el string junto con la ubicacion cartesiana de donde comenzar:
    • Determinar existencia:
      1
      2
      3
      4
      5
      6
      if (page > 0) {
          return NO_SUCH_PAGE;
      }else{
      ...
          return  PAGE_EXISTS;
      }
      

      Ambas constantes son parte de java.awt.print.Printable, PAGE_EXISTS tiene un valor de '0' mientras que NO_SUCH_PAGE vale '1'

    • Determinar el area imprimible: Le pasamos la referencia del paramétro g casteada a un objeto Graphic2d y será este quien llame a su método translate que toma como parámetros los valores por defecto del eje X y eje Y de PageFormat pf, el otro parámetro de la firma print.
      1
      2
      Graphics2D g2d = (Graphics2D) g;
      g2d.translate(pf.getImageableX(), pf.getImageableY());
      

    • Y por ultimo, llamamos al método de Graphics g:dranstring
      1
      g.drawString("Papi ama a juli!", 100, 100);
      
      • El primer parámetro es el string a imprimir(aquí inclui el sentimiento por mi hija que me esta acompañando en esto)
      • Los siguientes corresponden a la posición (x,y) respectivamente.

El resultado será:

una hoja a4 con el string pasado por parametro, en la posicion (100,100) que imprimi y escanie para ilustrarlo. La clase completa es:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.dar10comyr.printer;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.*;

public class Imprimir implements Printable {

    public static void main(String[] args) {
        PrinterJob job = PrinterJob.getPrinterJob();
        job.setPrintable(new Imprimir());
        boolean doPrint = job.printDialog();
        System.out.println(doPrint);
        if (doPrint) {
            try {
                job.print();
            } catch (PrinterException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public int print(Graphics g, PageFormat pf, int page) throws PrinterException {
        if (page > 0) {
             return NO_SUCH_PAGE;
        }

        Graphics2D g2d = (Graphics2D) g;
        g2d.translate(pf.getImageableX(), pf.getImageableY());

        g.drawString("Papi ama a Juli!", 100, 100);

        return PAGE_EXISTS;
    }
}

[Métodos] Generar XML a partir de un BEAN y viceversa - Java XStream



XStream es una libreria para serializar objetos a XML y viceversa.

Es de las librerias mas faciles de usar, y cuando dice 'fácil' es realmente facil! (después de hacerse con la dependencia)
  • Crear un objeto de la clase XStream a través de un contructor vacío...
    1
    XStream xstream = new Xstream();
    
  • Serializar a XML

  • Simplemente llamando al método toXML(Object objecto), 'a xml' tiene sentido no?, y pasandóle como parámetro el objeto.
    1
    xstream.toXML(objeto);
    
  • Deserializar

  • el mismo objeto xstream llamará al método fromXML(String xml), pasandóle el xml como un objeto String
    1
    xstream.fromXML(stringXml);
    


Pequeño ejemplo ilustrado:


Contamos con una entidad llamada persona:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.dar10.entity;

public class Persona {
 // ATRIBUTOS
    private String nombre;
    private String apellido;
    private Integer años;
 // CONSTRUCTORES
    public Persona() {
    }
    public Persona(String nombre, String apellido, Integer años) {
        super();
        this.nombre = nombre;
        this.apellido = apellido;
        this.años = años;
    }
 // GETTERS Y SETTERS
    public String getNombre() {
        return nombre;
    }
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
    public String getApellido() {
        return apellido;
    }
    public void setApellido(String apellido) {
        this.apellido = apellido;
    }
    public Integer getAños() {
        return años;
    }
    public void setAños(Integer años) {
        this.años = años;
    }
}

Es necesario declarar un constructor vacío en caso de usar otro con los atributos, ya que xstream usará el por defecto para serializar el objeto.

Corriendo la app


1
2
3
4
5
6
    public static void main(String[] args) {
        XStream xstream = new XStream();
        Persona pepe = new Persona("José", "Hernandez", 28);
        String xml = xstream.toXML(pepe);
        System.out.println(xml);
    }

La salida será:


1
2
3
4
5
<com.dar10.entity.Persona>
  <nombre>JOSE</nombre>
  <apellido>Hernadez</apellido>
  <años>15</años>
</com.dar10.entity.Persona>

Si de repente parece feo el nombre de la clase a través de todos los paquetes a los que pertenece, puede ser cambiado configurando el objeto 'xstream' con su
metodo
alias(String alias, <nombre de la clase>)
1
xstream.alias("criatura", Persona.class);

Recordando como guardar cadenas de texto en algun viejo post podemos obtener algo asi:


Convirtiendo a Objeto


Si bien el método que hace la magia es fromXML


Existen diversos problemas como que:
  • El xml contenga otra jerarquia de etiquetas/atributos
  • El xml contenga distintos tipos de variables(un string en lugar de un número)
  • El xml esté vacío o finalice abrutamente
  • O simplemente el xml no es el correcto para deserializar la clase pretendida entre tantas otras...


Si el camino es feliz o tenemos la seguridad de convertir el xml que soñamos se hará correctamente, de lo contrario hay que cachear el XStreamException

1
2
3
4
5
try{
    xstream.fromXML(xmlString)
}catch(XStreamException e){
    e.printStackTrace();
}

[Métodos] Generar codigos de barras y codigos QR - Codigo de barras en un PDF con Barcode4j


Siguiendo el comportamiento de blog pasados, esta vez el desafio es hacer la generación de codigo en pdf, partiendo de las mismas clases pasadas, modificando un poco el main, pero tomando el objeto file de la imagen para pasarlo a un pdf.
  • Agregar dependencia de itext (para generar el pdf)
  • Crear una clase para generar un pdf con una imagen.
  • modificar el 'main' para que en lugar de la ventana llame al método creado.


inlcuir itext al pom

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>CodigoBarras</groupId>
    <artifactId>CodigoBarras</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>net.sf.barcode4j</groupId>
            <artifactId>barcode4j</artifactId>
            <version>2.1</version>
        </dependency>
        <dependency>
            <groupId>com.lowagie</groupId>
            <artifactId>itext</artifactId>
            <version>2.1.7</version>
        </dependency>
    </dependencies>
    <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Creando el pdfGenerador.java

esto guardará en el escritorio el resultado
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.dar10comyr.blogspot.main.barco;

import java.io.FileOutputStream;
import java.io.IOException;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Image;
import com.lowagie.text.pdf.PdfWriter;

public class pdfGenerador {

    public static void generarPDF(String pathImagen){
        Document documento = new Document();
        String escritorio = System.getProperty("user.home") + "/Desktop/miPDF.pdf";
        FileOutputStream fos;
        Image imagen;
        try {
            fos = new FileOutputStream(escritorio);

            PdfWriter pdfW = PdfWriter.getInstance(documento, fos);
            pdfW.setInitialLeading(20);

            documento.open();
            imagen = Image.getInstance(pathImagen);
            documento.add(imagen);

            documento.close();
        } catch (IOException | DocumentException e) {
            e.printStackTrace();
        }
    }
}

modificando el main...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.dar10comyr.blogspot.main.barco;

import java.io.File;

public class Main {
    public static void main(String[] args){
        MiBarco bar = new MiBarco();
        File imagen = bar.getBarcode("34198987");
        pdfGenerador.generarPDF(imagen.getAbsolutePath());
    }
}

y voila!

[Métodos] Generar codigos de barras y codigos QR - Codigo de barras en una JFrame con Barcode4j


Siguiendo la secuencia de pasos para generar un código de barras con barcode4j, esta vez el desafio es hacerlo en un simple JFrame:

Ingredientes

  • Una clase main, donde correr el pequeño jar, ésta llama a las siguientes.
  • Una clase encargada de la generación del código de barras, que contenga un método que devuelva un File con la imagen.
  • Otra clase encargada de la ventana, que reciba un File y lo ponga en una etiqueta dentro del marco.
La estructura del proyecto debería verse así: (en el pom.xml solo incluyo la dependencia de barcode4j)


La generación de código

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.dar10comyr.blogspot.main.barco;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.krysalis.barcode4j.impl.code39.Code39Bean;
import org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider;
import org.krysalis.barcode4j.tools.UnitConv;

public class MiBarco {

    public File getBarcode(String ean){
        Code39Bean codebean = new Code39Bean();
        final int dpi = 150;

        double ancho = UnitConv.in2mm(1.0f / dpi);
        codebean.setModuleWidth(ancho);
        codebean.setWideFactor(3);
        codebean.doQuietZone(false);

        String directorio = "resources/images/";
        File carpeta = new File(directorio);
        if(!carpeta.exists()){
            carpeta.mkdirs();
        }
        String nombreArchivo = "codigoBarras.png";
        File miFile = new File(directorio + nombreArchivo);
        OutputStream out = null;

        BitmapCanvasProvider canvas;
        try {
            out = new FileOutputStream(miFile);
            canvas = new BitmapCanvasProvider(out, "image/x-png", dpi, BufferedImage.TYPE_BYTE_BINARY, false, 0);
            codebean.generateBarcode(canvas, ean);
            canvas.finish();
        } catch(IOException ioe){
            System.err.println("Ha ocurrido un error: " + ioe.getMessage());
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                System.err.println("No se ha podido cerrar el outputstream " + e.getMessage());
            }
        }
        return miFile;
    }
}

La ventana

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.dar10comyr.blogspot.main.barco;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class Ventana {
    JFrame marco;
    JLabel etiqueta;
    ImageIcon imagen;

    public Ventana(File archivo){
        marco = new JFrame();
        marco.setSize(350, 300);
        marco.setLocationRelativeTo(null);
        marco.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        BufferedImage myPicture;
        try {
            myPicture = ImageIO.read(archivo);
            imagen = new ImageIcon(myPicture);
            etiqueta = new JLabel(imagen);
            marco.add(etiqueta);
        } catch (IOException e) {
            e.printStackTrace();
        }
        marco.setVisible(true);
    }
}

El main para correr todo =)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.dar10comyr.blogspot.main.barco;

import java.io.File;

public class Main {
    public static void main(String[] args){
        MiBarco bar = new MiBarco();
        File imagen = bar.getBarcode("34198987");
        new Ventana(imagen);
    }
}

El resultado final

[Métodos] Generar codigos de barras y codigos QR - Codigo de barras en una imagen con Barcode4j


La idea es simple, hacer una secuencia de carácteres un código de barras con una de las tantas librerías en java para ello, como barcode4j


Hacernos con la dependencia:

  • Desde su pagina de sourceforge podemos descargarnos el jar por versiones, link
  • Desde el repositorio de maven Maven repository, podemos descargar el jar como incluirlo en una dependencia en nuestro pom

Empezar a codear:

  • Creando un objeto de barcode4j que generará el codigo de barras:
    • Code39Bean codebean = new Code39Bean();
  • Al momento de crearlo, tomará por defecto estos valores:
    • De aquí nos importará setear: doQuietzone, moduleWidth, wideZone
  • Setear el codebean:
    • moduleWidth define el ancho del módulo
      • recibe un 'double', que podemos obtener a través de funcion de la clase de barcode4j UnitConv:
        • esta funcion es in2mm convierte pulgadas en milimetros (inches to milimetres) y le pasamos por cada pulgada la profundidad que definamos(dpi)
          • final int dpi = 150;
          • double ancho = UnitConv.in2mm(1.0f / dpi);
          • codebean.setModuleWidth(ancho);
          Este double ancho va a tomar un valor de 0.16933333743363618, reemplazando el valor por defecto de 0.1899999976158142
    • wideZone Define el factor por el cual las barras anchas son más anchas que las barras estrechas
      • codebean.setWideFactor(3)
      este valor por defecto era de 2.5
    • doQuietzone Establece si una zona tranquila debe ser incluida o no
      • codebean.doQuietZone(false);
      no se bien que es este factor pero por defecto era true
  • Y este objeto codebean generá con el método generateBarcode el código de barras como una imagen, recibiendo como parametros:
    • un objeto BitmapCanvasProvider a tráves de su constructor con muchos parámetros:
      1. un OutputStream que contenga la ruta donde se va a crear la imagen.
      2. el tipo MIME del formato de salida deseado, le pasamos el string "image/x-png"
      3. la resolucion, puediendo reutilizar el int que se determina al principio
      4. el tipo de imagen deseada, una constante de la clase BufferedImage.TYPE_BYTE_BINARY
      5. un boolean para determinar si habilitar el anti-aliasing
      6. un entero fijar la orientación( solo puede ser en grados 0, 90, 180, 270, -90, -180 o -270 )
      1
      2
      BitmapCanvasProvider canvas = new BitmapCanvasProvider(out, "image/x-png", dpi, 
                          BufferedImage.TYPE_BYTE_BINARY, false, 0);
      
    • la secuencia de caracteres a convertir en codigo de barras en forma de String

Todo el código completito para poner en main y salir andando:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    System.out.println("Creando su código de barras");
    // Creando el bean de barcode
    Code39Bean codebean = new Code39Bean();
    // Se declara los pixels por pulgada  = dpi (Dots Per Inch)
    final int dpi = 150;

    // Configurando el generador de barcode
    /** hace la distancia entre las barras, del ancho exacto a 1 pixel **/
    double ancho = UnitConv.in2mm(1.0f / dpi);
    /** Establece el ancho del módulo estrecho **/
    codebean.setModuleWidth(ancho);
    /** Define el factor por el cual las barras anchas son más anchas que las barras estrechas **/
    codebean.setWideFactor(3);
    /** Establece si una zona tranquila debe ser incluida o no **/
    codebean.doQuietZone(false);

    // Abriendo el archivo de salida
    String directorio = "resources/images/";
    File carpeta = new File(directorio);
    if(!carpeta.exists()){
        carpeta.mkdirs();
    }
    String nombreArchivo = "codigoBarras.png";
    File miFile = new File(directorio + nombreArchivo);
    OutputStream out = null;

    BitmapCanvasProvider canvas;
    try {
        out = new FileOutputStream(miFile);
        // Configurando el 'canvas provider' para un PNG monocromatico
        canvas = new BitmapCanvasProvider(out, //el outputstream para escribir
                                          "image/x-png", //el tipo MIME del formato de salida deseado
                                          dpi, //la resolución de imagen deseada
                                          BufferedImage.TYPE_BYTE_BINARY, //el tipo de imagen deseado
                                          false, //true si se debe habilitar el anti-aliasing
                                          0); // Orientation debe ser 0, 90, 180, 270, -90, -180 or -270

        // Generando el codigo de barras
        codebean.generateBarcode(canvas, "LegenDar10");

        // Cerrando el fin de la generacion
        canvas.finish();
    } catch(IOException ioe){
        System.err.println("Ha ocurrido un error: " + ioe.getMessage());
    } finally {
        try {
            out.close();
        } catch (IOException e) {
            System.err.println("No se ha podido cerrar el outputstream " + e.getMessage());
        }
    }
    System.out.println("Imagen creada exitosamente en " + miFile.getAbsoluteFile());
    System.out.println("fin del programa =)");
    System.exit(0);


El resultado:

[MySQL] Consultadas agrupadas: GROUP BY (Vs) DISTINCT


La clasula GROUP BY nos permite 'agrupar' los resultados de una columna que sean iguales usando las funciones de agregación (COUNT, MAX, MIN, SUM, AVG)
La clausula 'DISTINCT' nos permite 'agrupar' los resultados de una columna que sean iguales


Por ejemplo, tomando una tabla 'inventario' que contiene una columna llamada 'idEstadoInventario'
La cual en varios registros lleva valores como 1, 2 ó 3, étc:

  • GROUP BY necesariamente funciona como un complemento de sumar, contar, obtener máximo ó mínimo, promediar, étc
    • Puedo sumar los valores que agrupo por el valor inicial
    • Puedo contar la cantidad de registros 1, 2 y 3
    • Puedo promediar, obtener el maximo y minimo aunque carece de sentido porque el promedio y el maximo o minimo son los mismo valores de 1, 2 y 3
  • DISTINCT buscará entre todos los registros y seleccioná la primera ocurrencia pero obviará las que sean iguales.


Por otra parte el uso de GROUP BY en si mismo:
  • Al usar una funcion de agregacion, se hará por el total de los resultados
  • Al 'agrupar' se realiza la misma implementación aunque diferenciando entre los registros de diferentes valores