From 67473f1f586a3c709301fcac345963ec9efc4aa2 Mon Sep 17 00:00:00 2001 From: "han\\hanst" Date: Wed, 20 Aug 2025 10:11:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=8C=E7=BB=B4=E7=A0=81=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E6=94=B9=E6=88=90=E7=94=9F=E6=88=90=E4=BA=8C?= =?UTF-8?q?=E7=BB=B4=E7=A0=81=E5=9B=BE=E7=89=87=EF=BC=8C=E5=86=8D=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +- .../Impl/ReportLabelListServiceImpl.java | 12 +- .../modules/base/utils/ZplGenerator.java | 242 +++++++++++------- 3 files changed, 161 insertions(+), 98 deletions(-) diff --git a/build.gradle b/build.gradle index aad341f..07f7c9d 100644 --- a/build.gradle +++ b/build.gradle @@ -109,6 +109,9 @@ dependencies { implementation 'com.itextpdf:itext7-core:7.2.5' implementation 'com.itextpdf:html2pdf:4.0.5' + implementation 'com.google.zxing:core:3.5.3' + implementation 'com.google.zxing:javase:3.5.3' + } test { @@ -126,4 +129,4 @@ sourceSets { srcDir 'src/main/resources' } } -} \ No newline at end of file +} diff --git a/src/main/java/com/gaotao/modules/base/service/Impl/ReportLabelListServiceImpl.java b/src/main/java/com/gaotao/modules/base/service/Impl/ReportLabelListServiceImpl.java index 3f729c7..35fde1d 100644 --- a/src/main/java/com/gaotao/modules/base/service/Impl/ReportLabelListServiceImpl.java +++ b/src/main/java/com/gaotao/modules/base/service/Impl/ReportLabelListServiceImpl.java @@ -169,17 +169,15 @@ public class ReportLabelListServiceImpl extends ServiceImpl {}", fontFamily, fontFile); } - + // 如果FontService没有找到,使用静态映射 if (fontFile == null) { fontFile = mapFontFamilyToFile(fontFamily); log.debug("通过静态映射获取字体文件: {} -> {}", fontFamily, fontFile); } - + if (fontFile != null) { fontCmd.append(" ^CWJ,E:").append(fontFile); } else { @@ -224,13 +235,13 @@ public class ZplGenerator { // 默认使用微软雅黑 fontCmd.append(" ^CWJ,E:MSYH.TTF"); } - + // 设置字体大小 fontCmd.append(" ^CFJ,").append(fontSize); - + return fontCmd.toString(); } - + /** * 映射字体族到字体文件 * 注意:这是一个静态方法,在实际使用中应该通过FontService来获取 @@ -285,7 +296,7 @@ public class ZplGenerator { return null; // 使用默认字体 } } - + /** * 获取文本对齐参数 */ @@ -293,7 +304,7 @@ public class ZplGenerator { if (textAlign == null) { return "L"; // 默认左对齐 } - + switch (textAlign) { case "center": return "C"; @@ -304,7 +315,7 @@ public class ZplGenerator { return "L"; } } - + /** * 估算文本宽度(用于下划线) */ @@ -312,11 +323,11 @@ public class ZplGenerator { if (text == null || text.isEmpty()) { return 0; } - + // 简单估算:中文字符按字体大小计算,英文字符按字体大小的0.6倍计算 int chineseCount = 0; int englishCount = 0; - + for (char c : text.toCharArray()) { if (c >= 0x4e00 && c <= 0x9fff) { chineseCount++; @@ -324,10 +335,10 @@ public class ZplGenerator { englishCount++; } } - + return (int) (chineseCount * fontSize + englishCount * fontSize * 0.6); } - + /** * 生成一维码ZPL代码 */ @@ -335,14 +346,14 @@ public class ZplGenerator { String orientation = config.getBarcodeOrientation(); String barcodeType = element.getBarcodeType() != null ? element.getBarcodeType() : "CODE128"; boolean showContent = element.getShowContent() != null ? element.getShowContent() : true; - + // 将毫米转换为ZPL单位 double widthMM = element.getWidth() != null ? element.getWidth().doubleValue() : 2.0; int width = Math.max(1, Math.min(10, (int) Math.round(widthMM * 1.5))); - + // 高度:毫米转换为点数 int height = Math.max(1, Math.round((element.getHeight() != null ? element.getHeight() : 15) * this.dpi / 25.4f)); - + // 根据条码类型选择ZPL指令 String zplCommand; switch (barcodeType) { @@ -368,48 +379,99 @@ public class ZplGenerator { zplCommand = "^BC" + orientation; // CODE128 break; } - + // 构建完整的ZPL指令 String contentParam = showContent ? "Y" : "N"; String additionalParams = "B".equals(orientation) ? "," + contentParam + ",N,N" : "," + contentParam; String data = element.getData() != null ? element.getData() : ""; - - return String.format("^FO%d,%d^BY%d%s,%d%s^FD%s^FS", + + return String.format("^FO%d,%d^BY%d%s,%d%s^FD%s^FS", x, y, width, zplCommand, height, additionalParams, data); } - + /** * 生成二维码ZPL代码 */ private String generateQRCodeZPL(ReportLabelList element, int x, int y) { - String orientation = config.getQrcodeOrientation(); String data = element.getData() != null ? element.getData() : ""; - // 将毫米转换为ZPL尺寸单位 int sizeInDots = Math.max(1, Math.round((element.getHeight() != null ? element.getHeight() : 10) * this.dpi / 25.4f)); - int size = Math.max(1, Math.min(10, Math.round(sizeInDots / 25f))); - - // 根据数据长度自动调整最小尺寸 - if (data.length() > 300) { - size = Math.max(size, 8); - } else if (data.length() > 200) { - size = Math.max(size, 6); - } else if (data.length() > 100) { - size = Math.max(size, 4); - } else { - size = Math.max(size, 2); + try { + return generateZplQRCode(data, sizeInDots, x, y); + } catch (Exception e) { + throw new RuntimeException(e); } - - // 根据数据类型选择模式 - if (data.matches("^\\d+$")) { - // 纯数字内容,使用数字模式 - return String.format("^FO%d,%d^BQ%s,2,%d^FDMN,%s^FS", x, y, orientation, size, data); - } else { - // 混合内容,使用自动模式 - return String.format("^FO%d,%d^BQ%s,2,%d^FDMA,%s^FS", x, y, orientation, size, data); + } + + /** + * 生成二维码并转为 ZPL + * + * @param content 二维码内容(支持中文) + * @param size 二维码宽高(像素 == dots) + * @param x 打印起点 X 坐标(单位:dots) + * @param y 打印起点 Y 坐标(单位:dots) + * @return ZPL 字符串 + */ + public String generateZplQRCode(String content, int size, int x, int y) throws Exception { + byte[] qrBytes = generateQRCode(content, size, size); + return convertToZpl(qrBytes, x, y); + } + + // 1. 生成二维码(支持中文) + private static byte[] generateQRCode(String content, int width, int height) throws Exception { + Map hints = new HashMap<>(); + hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); + hints.put(EncodeHintType.MARGIN, 1); + BitMatrix bitMatrix = new MultiFormatWriter().encode( + content, + BarcodeFormat.QR_CODE, + width, + height, + hints + ); + BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(image, "png", baos); + return baos.toByteArray(); + } + + // 2. 转换 PNG 图片为 ZPL ^GF 指令 + private static String convertToZpl(byte[] imageBytes, int x, int y) throws Exception { + BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes)); + int widthBytes = (image.getWidth() + 7) / 8; // 每行字节数 + int totalBytes = widthBytes * image.getHeight(); + StringBuilder zpl = new StringBuilder(); + zpl.append("^FO").append(x).append(",").append(y).append("\n"); + zpl.append("^GFA,").append(totalBytes).append(",").append(totalBytes).append(",").append(widthBytes).append(","); + StringBuilder imageData = new StringBuilder(); + for (int yPos = 0; yPos < image.getHeight(); yPos++) { + int bit = 0; + int currentByte = 0; + for (int xPos = 0; xPos < image.getWidth(); xPos++) { + int rgb = image.getRGB(xPos, yPos); + int gray = (rgb >> 16 & 0xff) * 299 + (rgb >> 8 & 0xff) * 587 + (rgb & 0xff) * 114; + gray = gray / 1000; + + currentByte <<= 1; + if (gray < 128) { // 黑色点 + currentByte |= 1; + } + bit++; + if (bit == 8) { + imageData.append(String.format("%02X", currentByte)); + bit = 0; + currentByte = 0; + } + } + if (bit > 0) { + currentByte <<= (8 - bit); + imageData.append(String.format("%02X", currentByte)); + } } + zpl.append(imageData); + return zpl.toString(); } - + /** * 生成图片ZPL代码 */ @@ -418,10 +480,10 @@ public class ZplGenerator { if (data == null || data.isEmpty()) { return ""; } - + return String.format("^FO%d,%d^GFA,%s", x, y, data); } - + /** * 生成横线ZPL代码 */ @@ -431,52 +493,52 @@ public class ZplGenerator { BigDecimal width = element.getWidth() != null ? element.getWidth() : BigDecimal.valueOf(100); int elementX = element.getX() != null ? element.getX() : 0; int adjustedY = (int) Math.round(1200 - elementX) - width.intValue(); - return String.format("^FO%d,%d^FWR^GB%d,%s,3,B^FS", - x, adjustedY, - element.getHeight() != null ? element.getHeight() : 3, + return String.format("^FO%d,%d^FWR^GB%d,%s,3,B^FS", + x, adjustedY, + element.getHeight() != null ? element.getHeight() : 3, width.toString()); } else { - return String.format("^FO%d,%d^GB%s,%d,3,B^FS", - x, y, - element.getWidth() != null ? element.getWidth().toString() : "100", + return String.format("^FO%d,%d^GB%s,%d,3,B^FS", + x, y, + element.getWidth() != null ? element.getWidth().toString() : "100", element.getHeight() != null ? element.getHeight() : 3); } } - + /** * 生成竖线ZPL代码 */ private String generateVerticalLineZPL(ReportLabelList element, int x, int y) { if ("landscape".equals(this.orientation)) { - return String.format("^FO%d,%d^FWR^GB%d,%s,3,B^FS", - x, y, - element.getHeight() != null ? element.getHeight() : 100, + return String.format("^FO%d,%d^FWR^GB%d,%s,3,B^FS", + x, y, + element.getHeight() != null ? element.getHeight() : 100, element.getWidth() != null ? element.getWidth().toString() : "3"); } else { - return String.format("^FO%d,%d^GB1,%d,%s,B^FS", - x, y, - element.getHeight() != null ? element.getHeight() : 100, + return String.format("^FO%d,%d^GB1,%d,%s,B^FS", + x, y, + element.getHeight() != null ? element.getHeight() : 100, element.getWidth() != null ? element.getWidth().toString() : "3"); } } - + /** * 生成流水号ZPL代码 */ private String generateSerialNumberZPL(ReportLabelList element, int x, int y) { StringBuilder zpl = new StringBuilder(); - + // 设置中文字体 Integer fontSize = element.getFontSize() != null ? element.getFontSize() : 30; zpl.append("^CI28 ^CWJ,E:MSYH.TTF ^CFJ,").append(fontSize).append("\n"); - + // 基础文本 String data = element.getData() != null ? element.getData() : "流水号"; zpl.append("^FO").append(x).append(",").append(y).append("^FD").append(data).append("^FS"); - + return zpl.toString(); } - + /** * ZPL配置 */ @@ -486,4 +548,4 @@ public class ZplGenerator { private String barcodeOrientation; private String qrcodeOrientation; } -} \ No newline at end of file +}