import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.QRCodeWriter; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.util.EnumMap; import java.util.Map; /** * 判定"解不开的标签"到底是标签坏了,还是解码器的局限 * * 用法:java CompareQrRender <标签目录> <文件名1> [文件名2 ...] * * 做法:用与导出完全相同的编码参数(纠错级别 H、静区 4、边长 260)重新生成同一码值的二维码, * 再与标签图片中的二维码区域逐像素比对。 * - 完全一致 → 标签是该码值的忠实渲染,二维码数据有效,解不出是解码器的问题 * - 存在差异 → 标签渲染环节确实出了问题 */ public class CompareQrRender { /** 与 QrCodeImageUtils 保持一致 */ private static final int PADDING = 16; private static final int QR_SIZE = 260; /** 与 MesProQrLabelBuilder 一致:二维码内容为追溯网址 + code 参数 */ private static final String TRACE_BASE = "http://192.168.0.10:48080/trace.html"; public static void main(String[] args) throws Exception { File dir = new File(args[0]); for (int i = 1; i < args.length; i++) { File f = new File(dir, args[i]); String name = args[i]; String code = name.endsWith(".png") ? name.substring(0, name.length() - 4) : name; BufferedImage label = ImageIO.read(f); if (label == null) { System.out.println(name + " -> 无法读取"); continue; } BufferedImage expect = generate(code); BufferedImage actual = label.getSubimage(PADDING, PADDING, QR_SIZE, QR_SIZE); if (expect.getWidth() != actual.getWidth() || expect.getHeight() != actual.getHeight()) { System.out.println(name + " -> 尺寸不一致,无法比对"); continue; } long diff = 0; for (int y = 0; y < QR_SIZE; y++) { for (int x = 0; x < QR_SIZE; x++) { if (expect.getRGB(x, y) != actual.getRGB(x, y)) { diff++; } } } long total = (long) QR_SIZE * QR_SIZE; System.out.printf("%s%n 重新生成与标签二维码区域逐像素比对:不一致 %d / %d (%.4f%%)%n", name, diff, total, diff * 100.0 / total); System.out.println(" 判定:" + (diff == 0 ? "标签为该码值的忠实渲染 → 二维码数据有效,解不出属解码端问题" : "标签渲染与预期不符 → 渲染环节存在问题")); } } private static BufferedImage generate(String code) throws Exception { String content = TRACE_BASE + "?code=" + code; Map hints = new EnumMap<>(EncodeHintType.class); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); hints.put(EncodeHintType.MARGIN, 4); hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); BitMatrix matrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, QR_SIZE, QR_SIZE, hints); int w = matrix.getWidth(); int h = matrix.getHeight(); BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { img.setRGB(x, y, matrix.get(x, y) ? 0x000000 : 0xFFFFFF); } } return img; } }