Browse Source

二维码生成方式改成生成二维码图片,再打印图片

master
han\hanst 5 months ago
parent
commit
67473f1f58
  1. 5
      build.gradle
  2. 12
      src/main/java/com/gaotao/modules/base/service/Impl/ReportLabelListServiceImpl.java
  3. 242
      src/main/java/com/gaotao/modules/base/utils/ZplGenerator.java

5
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'
}
}
}
}

12
src/main/java/com/gaotao/modules/base/service/Impl/ReportLabelListServiceImpl.java

@ -169,17 +169,15 @@ public class ReportLabelListServiceImpl extends ServiceImpl<ReportLabelListMappe
rfidFlag = labelSettingDataList.getFirst().getRfidFlag();
}
try {
// 1. 生成带真实数据的ZPL代码如果没有提供ZPL代码
String zplCode = printRequest.getZplCode();
if (zplCode == null || zplCode.trim().isEmpty()) {
log.info("未提供ZPL代码,使用真实数据生成ZPL: reportId={}", printRequest.getReportId());
zplCode = previewLabelWithRealData(printRequest.getReportId());
printRequest.setZplCode(zplCode);
}
// 1. 生成带真实数据的ZPL代码
String zplCode = previewLabelWithRealData(printRequest.getReportId());
printRequest.setZplCode(zplCode);
// 2. 验证ZPL代码
if (zplCode == null || zplCode.trim().isEmpty()) {
throw new RuntimeException("ZPL代码不能为空");
} else {
log.info("生成的ZPL代码: {}", zplCode);
}
// 3. 获取用户的打印机配置

242
src/main/java/com/gaotao/modules/base/utils/ZplGenerator.java

@ -8,38 +8,49 @@ import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.util.List;
import com.google.zxing.*;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
/**
* ZPL代码生成器
* 统一处理不同打印方向的ZPL代码生成
*/
@Slf4j
public class ZplGenerator {
private String orientation;
private Integer dpi;
private CoordinateTransformer transformer;
private ZplConfig config;
private com.gaotao.modules.base.service.FontService fontService;
public ZplGenerator(String orientation, Integer dpi, CoordinateTransformer.CanvasSize canvasSize) {
this.orientation = orientation != null ? orientation : "portrait";
this.dpi = dpi != null ? dpi : 203;
this.transformer = new CoordinateTransformer(this.orientation, canvasSize);
this.config = getConfig();
}
public ZplGenerator(String orientation, Integer dpi, CoordinateTransformer.CanvasSize canvasSize,
public ZplGenerator(String orientation, Integer dpi, CoordinateTransformer.CanvasSize canvasSize,
com.gaotao.modules.base.service.FontService fontService) {
this(orientation, dpi, canvasSize);
this.fontService = fontService;
}
/**
* 获取ZPL配置参数
*/
private ZplConfig getConfig() {
ZplConfig config = new ZplConfig();
if ("landscape".equals(this.orientation)) {
config.setFieldOrientation("^FWB"); // 旋转90度
config.setBarcodeOrientation("B"); // 旋转90度条码
@ -49,10 +60,10 @@ public class ZplGenerator {
config.setBarcodeOrientation("N"); // 正常条码
config.setQrcodeOrientation("N"); // 正常二维码
}
return config;
}
/**
* 生成完整的ZPL代码
*/
@ -60,11 +71,11 @@ public class ZplGenerator {
if (elements == null || elements.isEmpty()) {
return "";
}
StringBuilder zpl = new StringBuilder();
zpl.append("^XA\n"); // ZPL开始标记
zpl.append(config.getFieldOrientation()).append("\n"); // 设置打印方向
// 处理每个元素
for (ReportLabelList element : elements) {
String elementZpl = generateElementZPL(element);
@ -72,12 +83,12 @@ public class ZplGenerator {
zpl.append(elementZpl).append("\n");
}
}
zpl.append("^XZ\n"); // ZPL结束标记
return zpl.toString();
}
/**
* 生成单个元素的ZPL代码
*/
@ -85,12 +96,12 @@ public class ZplGenerator {
if (element == null) {
return "";
}
CoordinateTransformer.ZplCoordinate coord = transformer.toZPL(
element.getX() != null ? element.getX() : 0,
element.getY() != null ? element.getY() : 0
);
switch (element.getType()) {
case "text":
return generateTextZPL(element, coord.getX(), coord.getY());
@ -110,30 +121,30 @@ public class ZplGenerator {
return "";
}
}
/**
* 生成文本ZPL代码
*/
private String generateTextZPL(ReportLabelList element, int x, int y) {
StringBuilder zpl = new StringBuilder();
// 设置字体
Integer fontSize = element.getFontSize() != null ? element.getFontSize() : 30;
String fontCommand = generateFontCommand(element, fontSize);
zpl.append(fontCommand).append("\n");
// 如果有复选框
if (Boolean.TRUE.equals(element.getIsChecked())) {
zpl.append("^FO").append(x - 60).append(",").append(y)
.append("^GB45,45,2,B,0^FS ^FO").append(x - 50).append(",").append(y + 10)
.append("^AJN,35,35^FD√^FS\n");
}
String data = element.getData() != null ? element.getData() : "";
// 处理文本对齐
String alignmentParam = getTextAlignmentParam(element.getTextAlign());
// 基础文本
if (Boolean.TRUE.equals(element.getNewline())) {
// 多行文本
@ -148,7 +159,7 @@ public class ZplGenerator {
zpl.append("^FO").append(x).append(",").append(y)
.append("^FD").append(data).append("^FS");
}
// 加粗效果通过偏移重复打印实现
if (Boolean.TRUE.equals(element.getBold())) {
if (Boolean.TRUE.equals(element.getNewline())) {
@ -175,7 +186,7 @@ public class ZplGenerator {
.append("^FD").append(data).append("^FS");
}
}
// 下划线效果通过线条实现
if (Boolean.TRUE.equals(element.getFontUnderline())) {
int textWidth = estimateTextWidth(data, fontSize);
@ -183,36 +194,36 @@ public class ZplGenerator {
zpl.append("\n^FO").append(x).append(",").append(underlineY)
.append("^GB").append(textWidth).append(",2,2,B^FS");
}
return zpl.toString();
}
/**
* 生成字体命令
*/
private String generateFontCommand(ReportLabelList element, Integer fontSize) {
StringBuilder fontCmd = new StringBuilder();
// 设置字符编码
fontCmd.append("^CI28");
// 根据字体族选择字体
String fontFamily = element.getFontFamily();
String fontFile = null;
if (fontFamily != null && !"default".equals(fontFamily)) {
// 优先使用FontService获取字体文件
if (fontService != null) {
fontFile = fontService.getFontFilePath(fontFamily);
log.debug("通过FontService获取字体文件: {} -> {}", 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<EncodeHintType, Object> 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;
}
}
}
Loading…
Cancel
Save