Generación y lectura de códigos QR con logo mediante ZXing en Java

ZXing (Zebra Crossing) es una biblioteca open source para procesar códigos de baras 1D y 2D, incluyendo QR, PDF 417, EAN, UPC, Aztec y Data Matrix. A continuación se muestra cómo integrarla en un proyecto Java para generar códigos QR con logo, decodificarlos y exponerlos desde un servicio web.

Dependencias Maven

Agrega los siguientes artefactos al pom.xml:

<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>3.3.3</version>
</dependency>
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>javase</artifactId>
    <version>3.3.3</version>
</dependency>

Generación, superopsición de logo y lectura

El siguiente ejemplo crea un QR, le coloca un logo en el centro y después lo decodifica.

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class ZxingQRUtil {

    public static void main(String[] args) {
        try {
            generarCodigoQR();
            leerCodigoQR(new File("D:\\zxing_logo.gif"));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void generarCodigoQR() throws WriterException, IOException {
        String texto = "https://www.ejemplo.com/articulo/zxing";
        int ancho = 250;
        int alto = 250;
        String formato = "gif";

        Map<EncodeHintType, Object> opciones = new HashMap<>();
        opciones.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        opciones.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        opciones.put(EncodeHintType.MARGIN, 1);

        BitMatrix matriz = new MultiFormatWriter().encode(texto, BarcodeFormat.QR_CODE, ancho, alto, opciones);

        MatrixToImageConfig estilo = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
        BufferedImage qrBase = MatrixToImageWriter.toBufferedImage(matriz, estilo);

        BufferedImage qrFinal = superponerLogo(qrBase, new File("D:\\logo.png"));

        ImageIO.write(qrFinal, formato, new File("D:\\zxing_logo.gif"));
        System.out.println("QR generado correctamente.");
    }

    public static void leerCodigoQR(File archivo) throws IOException, NotFoundException {
        MultiFormatReader lector = new MultiFormatReader();
        BufferedImage imagen = ImageIO.read(archivo);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(imagen)));

        Map<DecodeHintType, Object> opciones = new HashMap<>();
        opciones.put(DecodeHintType.CHARACTER_SET, "utf-8");

        Result resultado = lector.decode(bitmap, opciones);

        System.out.println("Texto decodificado: " + resultado.getText());
        System.out.println("Formato detectado: " + resultado.getBarcodeFormat());
    }

    public static BufferedImage superponerLogo(BufferedImage qrImagen, File archivoLogo) throws IOException {
        Graphics2D grafico = qrImagen.createGraphics();

        int anchoQR = qrImagen.getWidth();
        int altoQR = qrImagen.getHeight();

        BufferedImage logo = ImageIO.read(archivoLogo);

        int anchoLogo = anchoQR / 5;
        int altoLogo = altoQR / 5;
        int posX = (anchoQR - anchoLogo) / 2;
        int posY = (altoQR - altoLogo) / 2;

        grafico.drawImage(logo, posX, posY, anchoLogo, altoLogo, null);

        grafico.setStroke(new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        grafico.setColor(Color.WHITE);
        grafico.draw(new RoundRectangle2D.Float(posX, posY, anchoLogo, altoLogo, 20, 20));

        grafico.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        grafico.setColor(new Color(128, 128, 128));
        grafico.draw(new RoundRectangle2D.Float(posX + 2, posY + 2, anchoLogo - 4, altoLogo - 4, 20, 20));

        grafico.dispose();
        qrImagen.flush();
        return qrImagen;
    }
}

Exposición del QR desde un servicio web

Para devolver la imagen directamente al navegader, se puede crear un método que genere un BufferedImage y un controlador que lo escriba en la respuesta.

public static BufferedImage generarImagenQR(String texto) {
    int ancho = 250;
    int alto = 250;

    Map<EncodeHintType, Object> opciones = new HashMap<>();
    opciones.put(EncodeHintType.CHARACTER_SET, "UTF-8");
    opciones.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
    opciones.put(EncodeHintType.MARGIN, 1);

    BufferedImage resultado = null;
    try {
        BitMatrix matriz = new MultiFormatWriter().encode(texto, BarcodeFormat.QR_CODE, ancho, alto, opciones);
        MatrixToImageConfig estilo = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
        BufferedImage qrBase = MatrixToImageWriter.toBufferedImage(matriz, estilo);
        resultado = superponerLogo(qrBase, new File(System.getProperty("user.dir")
                + "\\src\\main\\resources\\static\\img\\logo.png"));
    } catch (WriterException | IOException e) {
        e.printStackTrace();
    }
    return resultado;
}
@GetMapping("obtenerQR/{identificador}")
public void obtenerQR(@PathVariable("identificador") String identificador, HttpServletResponse respuesta) throws IOException {
    respuesta.setHeader("Pragma", "no-cache");
    respuesta.setHeader("Cache-Control", "no-cache");
    respuesta.setDateHeader("Expires", 0);
    respuesta.setContentType("image/jpeg");

    String datos = "{\"id\":\"" + identificador + "\"}";
    BufferedImage imagen = generarImagenQR(datos);

    ImageIO.write(imagen, "JPEG", respuesta.getOutputStream());
}

Vista con Thymeleaf

En la plantilla HTML se puede mostrar el QR y recargarlo desde JavaScript.

<img id="qrImagen" th:src="@{'/obtenerQR/' + ${id}}"/>
<br/><br/>
<button th:onclick="'document.getElementById(\'qrImagen\').src = \'' + @{/obtenerQR/{id}(id=${id})} + '?t=' + new Date().getTime()' ">Recargar</button>

Etiquetas: ZXing java QR Code maven Thymeleaf

Publicado el 7-22 06:56