|
@@ -14,21 +14,34 @@ import java.nio.charset.StandardCharsets;
|
|
|
import java.util.Arrays;
|
|
import java.util.Arrays;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * TSC TTP-244 Pro 标签打印。
|
|
|
|
|
|
|
+ * 得力 Deli DL-730C 标签打印。
|
|
|
* <p>
|
|
* <p>
|
|
|
- * 使用 TSPL 指令直打,配合打印机 Tear(撕纸)模式:打完送出标签,
|
|
|
|
|
- * 下一张前回抽,并由 SIZE/GAP 保证回抽后仍对准缝隙。
|
|
|
|
|
- * <p>
|
|
|
|
|
- * 打印机 Post-Print Action 请保持为 Tear。
|
|
|
|
|
|
|
+ * 使用 TSPL 指令直打,配合打印机 Tear(撕纸)模式。
|
|
|
|
|
+ * 标签纸规格:100mm × 80mm。
|
|
|
*/
|
|
*/
|
|
|
public class PrintUtil {
|
|
public class PrintUtil {
|
|
|
public static final Logger log = LoggerFactory.getLogger(PrintUtil.class);
|
|
public static final Logger log = LoggerFactory.getLogger(PrintUtil.class);
|
|
|
|
|
|
|
|
- private static final float LABEL_WIDTH_MM = 35f;
|
|
|
|
|
- private static final float LABEL_HEIGHT_MM = 25f;
|
|
|
|
|
- /** 标签间隙 */
|
|
|
|
|
|
|
+ private static final String PRINTER_NAME = "Deli DL-730C(NEW)";
|
|
|
|
|
+ /** 标签宽 100mm、高 80mm */
|
|
|
|
|
+ private static final float LABEL_WIDTH_MM = 100f;
|
|
|
|
|
+ private static final float LABEL_HEIGHT_MM = 80f;
|
|
|
|
|
+ /** 标签间隙,常见 2~3mm,不对时再调 */
|
|
|
private static final float LABEL_GAP_MM = 2f;
|
|
private static final float LABEL_GAP_MM = 2f;
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * DL-730C 为 203DPI:1mm ≈ 8 dots;100×80mm ≈ 800×640 dots。
|
|
|
|
|
+ */
|
|
|
|
|
+ private static final int DOTS_PER_MM = 8;
|
|
|
|
|
+ private static final int LABEL_WIDTH_DOTS = Math.round(LABEL_WIDTH_MM * DOTS_PER_MM);
|
|
|
|
|
+ private static final int LABEL_HEIGHT_DOTS = Math.round(LABEL_HEIGHT_MM * DOTS_PER_MM);
|
|
|
|
|
+ /** 实测微调:整体略往右、往下挪(dots,约 1mm=8) */
|
|
|
|
|
+ private static final int SHIFT_X_DOTS = 48;
|
|
|
|
|
+ private static final int SHIFT_Y_DOTS = 40;
|
|
|
|
|
+ /** 文字相对整块再往右;二维码相对整块再往左 */
|
|
|
|
|
+ private static final int TEXT_SHIFT_X_DOTS = 40;
|
|
|
|
|
+ private static final int QR_SHIFT_X_DOTS = -40;
|
|
|
|
|
+
|
|
|
public static void printLabel(String barCode) {
|
|
public static void printLabel(String barCode) {
|
|
|
log.info("sn:" + barCode);
|
|
log.info("sn:" + barCode);
|
|
|
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
|
|
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
|
|
@@ -37,15 +50,16 @@ public class PrintUtil {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- String printerName = "TTP-244 Pro";
|
|
|
|
|
PrintService selectedService = Arrays.stream(services)
|
|
PrintService selectedService = Arrays.stream(services)
|
|
|
- .filter(s -> s.getName().equals(printerName))
|
|
|
|
|
|
|
+ .filter(s -> s.getName().equals(PRINTER_NAME) || s.getName().contains("DL-730C"))
|
|
|
.findFirst()
|
|
.findFirst()
|
|
|
.orElse(null);
|
|
.orElse(null);
|
|
|
if (selectedService == null) {
|
|
if (selectedService == null) {
|
|
|
- log.info(printerName + ":Printer not found.");
|
|
|
|
|
|
|
+ log.info(PRINTER_NAME + ":Printer not found. available={}",
|
|
|
|
|
+ Arrays.toString(Arrays.stream(services).map(PrintService::getName).toArray()));
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+ log.info("use printer: {}", selectedService.getName());
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
byte[] tspl = buildTsplCommands(barCode).getBytes(Charset.forName("GB18030"));
|
|
byte[] tspl = buildTsplCommands(barCode).getBytes(Charset.forName("GB18030"));
|
|
@@ -65,12 +79,35 @@ public class PrintUtil {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * TTP-244 Pro 为 203DPI:1mm ≈ 8 dots。
|
|
|
|
|
- * 文字在上、二维码在下(两行)。
|
|
|
|
|
- * 标签约 280x200 dots(35x25mm)。
|
|
|
|
|
|
|
+ * 文字 + 二维码整块水平、垂直居中。
|
|
|
*/
|
|
*/
|
|
|
private static String buildTsplCommands(String barCode) {
|
|
private static String buildTsplCommands(String barCode) {
|
|
|
String data = sanitizeTsplText(barCode);
|
|
String data = sanitizeTsplText(barCode);
|
|
|
|
|
+ int len = Math.max(data.length(), 1);
|
|
|
|
|
+
|
|
|
|
|
+ // 内置字体 "3"=16×24;SN 较长时略缩小
|
|
|
|
|
+ int textMul = len > 18 ? 2 : 3;
|
|
|
|
|
+ int charWidth = 16 * textMul;
|
|
|
|
|
+ int textHeight = 24 * textMul;
|
|
|
|
|
+ int textWidth = len * charWidth;
|
|
|
|
|
+
|
|
|
|
|
+ // 203DPI 下 cell=10,二维码约 25~33mm,在 100×80 上大小合适
|
|
|
|
|
+ int qrCell = 10;
|
|
|
|
|
+ int qrModules = estimateQrModules(len);
|
|
|
|
|
+ int qrSize = qrModules * qrCell;
|
|
|
|
|
+
|
|
|
|
|
+ int gapBetween = 48;
|
|
|
|
|
+ int blockWidth = Math.max(textWidth, qrSize);
|
|
|
|
|
+ int blockHeight = textHeight + gapBetween + qrSize;
|
|
|
|
|
+
|
|
|
|
|
+ int blockX = Math.max(0, (LABEL_WIDTH_DOTS - blockWidth) / 2) + SHIFT_X_DOTS;
|
|
|
|
|
+ int blockY = Math.max(0, (LABEL_HEIGHT_DOTS - blockHeight) / 2) + SHIFT_Y_DOTS;
|
|
|
|
|
+
|
|
|
|
|
+ int textX = blockX + Math.max(0, (blockWidth - textWidth) / 2) + TEXT_SHIFT_X_DOTS;
|
|
|
|
|
+ int textY = blockY;
|
|
|
|
|
+ int qrX = Math.max(0, blockX + Math.max(0, (blockWidth - qrSize) / 2) + QR_SHIFT_X_DOTS);
|
|
|
|
|
+ int qrY = blockY + textHeight + gapBetween;
|
|
|
|
|
+
|
|
|
StringBuilder sb = new StringBuilder();
|
|
StringBuilder sb = new StringBuilder();
|
|
|
sb.append("SIZE ").append(fmt(LABEL_WIDTH_MM)).append(" mm,").append(fmt(LABEL_HEIGHT_MM)).append(" mm\r\n");
|
|
sb.append("SIZE ").append(fmt(LABEL_WIDTH_MM)).append(" mm,").append(fmt(LABEL_HEIGHT_MM)).append(" mm\r\n");
|
|
|
sb.append("GAP ").append(fmt(LABEL_GAP_MM)).append(" mm,0\r\n");
|
|
sb.append("GAP ").append(fmt(LABEL_GAP_MM)).append(" mm,0\r\n");
|
|
@@ -79,16 +116,36 @@ public class PrintUtil {
|
|
|
sb.append("OFFSET 0 mm\r\n");
|
|
sb.append("OFFSET 0 mm\r\n");
|
|
|
sb.append("SET TEAR ON\r\n");
|
|
sb.append("SET TEAR ON\r\n");
|
|
|
sb.append("CLS\r\n");
|
|
sb.append("CLS\r\n");
|
|
|
- // 第一行:条码文字
|
|
|
|
|
- sb.append("TEXT 36,36,\"2\",0,1,1,\"").append(data).append("\"\r\n");
|
|
|
|
|
- // 第二行:二维码
|
|
|
|
|
- sb.append("QRCODE 100,68,L,4,A,0,\"").append(data).append("\"\r\n");
|
|
|
|
|
|
|
+ sb.append("TEXT ").append(textX).append(",").append(textY)
|
|
|
|
|
+ .append(",\"3\",0,").append(textMul).append(",").append(textMul)
|
|
|
|
|
+ .append(",\"").append(data).append("\"\r\n");
|
|
|
|
|
+ sb.append("QRCODE ").append(qrX).append(",").append(qrY)
|
|
|
|
|
+ .append(",L,").append(qrCell).append(",A,0,\"")
|
|
|
|
|
+ .append(data).append("\"\r\n");
|
|
|
sb.append("PRINT 1,1\r\n");
|
|
sb.append("PRINT 1,1\r\n");
|
|
|
String cmd = sb.toString();
|
|
String cmd = sb.toString();
|
|
|
- log.info("TSPL:\n{}", cmd);
|
|
|
|
|
|
|
+ log.info("TSPL dpiApprox={}, dots={}x{}, text=({},{}), qr=({},{}), qrSize={}\n{}",
|
|
|
|
|
+ DOTS_PER_MM * 25, LABEL_WIDTH_DOTS, LABEL_HEIGHT_DOTS, textX, textY, qrX, qrY, qrSize, cmd);
|
|
|
return cmd;
|
|
return cmd;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /** 按字母数字长度粗估 QR 版本模组边长 */
|
|
|
|
|
+ private static int estimateQrModules(int alphanumericLen) {
|
|
|
|
|
+ if (alphanumericLen <= 14) {
|
|
|
|
|
+ return 21;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (alphanumericLen <= 25) {
|
|
|
|
|
+ return 25;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (alphanumericLen <= 40) {
|
|
|
|
|
+ return 29;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (alphanumericLen <= 60) {
|
|
|
|
|
+ return 33;
|
|
|
|
|
+ }
|
|
|
|
|
+ return 37;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private static String sanitizeTsplText(String text) {
|
|
private static String sanitizeTsplText(String text) {
|
|
|
if (text == null) {
|
|
if (text == null) {
|
|
|
return "";
|
|
return "";
|