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>