最近在公司里面做了个业务,主要是对接工业打印机打印条形码,然后在CSDN和稀土掘金看了一下其他的方案很久嘛,然后自己又总结了一些,加入了进来,方法可能有点low,大家有更好的方案推荐,可以评论区call下。
在这里主要是对接的一台比较老的机型,Zebra S4M机型 203Pi。
用到的亚银标签纸格式是60mm*38mm , 碳带也是60mm的,不过其他型号应该也是倒差不差的,差不了太多。
本文参考了本文Java调用Zebra800条码打印机_ts24.lib_fmwind的博客-CSDN博客
话不多说,直接上代码!
package com.south.framework.web.testPrint;
import javax.print.*;
import javax.print.attribute.standard.PrinterName;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
//import sun.awt.AppContext;
/**
* 采用点阵字库ts24.lib
* @author Labrador
*
*/
public class ZplPrinter {
private String printerURI = null; //打印机完整路径
private PrintService printService = null; //打印机服务
private byte[] dotFont;
private String darkness = "~SD22"; //Set Darkness设置色带颜色的深度 0-30
private String width = "^PW1200"; //Print Width打印宽度0-1500
private String length = "^LL800"; //Label Length标签长度0-x(暂无作用)
private String begin = "^XA" + darkness + width; //标签格式以^XA开始
private String end = "^XZ"; //标签格式以^XZ结束
private String content = ""; //打印内容
private String message = ""; //打印的结果信息
/**
* 构造方法
* @param printerURI 打印机路径
*/
public ZplPrinter(String printerURI){
this.printerURI = printerURI;
//加载字体 点阵图文件
File file = new File("D:\\ts24.lib");
if(file.exists()){
FileInputStream fis;
try {
fis = new FileInputStream(file);
dotFont = new byte[fis.available()];
fis.read(dotFont);
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}else{
System.out.println("D://ts24.lib文件不存在");
}
//初始化打印机
PrintService[] services = PrintServiceLookup.lookupPrintServices(null,null);
if (services != null && services.length > 0) {
for (PrintService service : services) {
if (printerURI.equals(service.getName())) {
printService = service;
break;
}
}
}
if (printService == null) {
System.out.println("没有找到打印机:["+printerURI+"]");
//循环出所有的打印机
if (services != null && services.length > 0) {
System.out.println("可用的打印机列表:");
for (PrintService service : services) {
System.out.println("["+service.getName()+"]");
}
}
}else{
System.out.println("找到打印机:["+printerURI+"]");
System.out.println("打印机名称:["+printService.getAttribute(PrinterName.class).getValue()+"]");
}
}
/**
* 设置条形码
* @param barcode 条码字符
* @param zpl 条码样式模板
*/
public void setBarcode(String barcode,String zpl) {
content += zpl.replace("${data}", barcode);
}
/**
* 中文字符、英文字符(包含数字)混合
* @param str 中文、英文
* @param x x坐标
* @param y y坐标
* @param eh 英文字体高度height
* @param ew 英文字体宽度width
* @param es 英文字体间距spacing
* @param mx 中文x轴字体图形放大倍率。范围1-10,默认1
* @param my 中文y轴字体图形放大倍率。范围1-10,默认1
* @param ms 中文字体间距。24是个比较合适的值。
*/
public void setText(String str, int x, int y, int eh, int ew, int es, int mx, int my, int ms) {
byte[] ch = str2bytes(str);
for (int off = 0; off < ch.length;) {
if (((int) ch[off] & 0x00ff) >= 0xA0) {//中文字符
try {
int qcode = ch[off] & 0xff;
int wcode = ch[off + 1] & 0xff;
content += String.format("^FO%d,%d^XG0000%01X%01X,%d,%d^FS\n", x, y, qcode, wcode, mx, my);
begin += String.format("~DG0000%02X%02X,00072,003,\n", qcode, wcode);
qcode = (qcode + 128 - 32) & 0x00ff;
wcode = (wcode + 128 - 32) & 0x00ff;
int offset = ((int) qcode - 16) * 94 * 72 + ((int) wcode - 1) * 72;
for (int j = 0; j < 72; j += 3) {
qcode = (int) dotFont[j + offset] & 0x00ff;
wcode = (int) dotFont[j + offset + 1] & 0x00ff;
int qcode1 = (int) dotFont[j + offset + 2] & 0x00ff;
begin += String.format("%02X%02X%02X\n", qcode, wcode, qcode1);
}
x = x + ms * mx;
off = off + 2;
} catch (Exception e) {
e.printStackTrace();
//替换成X号
setChar("X", x, y, eh, ew);
x = x + es;//注意间距更改为英文字符间距
off = off + 2;
}
} else if (((int) ch[off] & 0x00FF) < 0xA0) {//英文字符
setChar(String.format("%c", ch[off]), x, y, eh, ew);
x = x + es;
off++;
}
}
}
/**
* 英文字符串(包含数字)
* @param str 英文字符串
* @param x x坐标
* @param y y坐标
* @param h 高度
* @param w 宽度
*/
public void setChar(String str, int x, int y, int h, int w) {
content += "^FO" + x + "," + y + "^A0," + h + "," + w + "^FD" + str + "^FS";
}
/**
* 英文字符(包含数字)顺时针旋转90度
* @param str 英文字符串
* @param x x坐标
* @param y y坐标
* @param h 高度
* @param w 宽度
*/
public void setCharR(String str, int x, int y, int h, int w) {
content += "^FO" + x + "," + y + "^A0R," + h + "," + w + "^FD" + str + "^FS";
}
/**
* ZPL条形码边框指令设置
* @param x 边框的最右侧X轴坐标
* @param y 边框的最右侧y轴坐标
* @param width 边框的宽度
* @param height 边框的高度
* @param thickness 边框的粗细程度
* */
public void setBox(int x, int y, int width, int height, int thickness) {
content += "^FO" + x + "," + y + "^GB" + width + "," + height + "," + thickness + "B^FS";
}
/**
* ZPL条形码边框指令设置
* @param x 边框的最右侧X轴坐标
* @param y 边框的最右侧y轴坐标
* @param h 条形码的高度设置
* @param w 条形码的宽度设置
* @param hw 条形码的宽高比 指宽度是高的倍数,不过好像有点瓦
* @param size 设置条形码条码格式,可以不填,不填就是打印机将自动根据条码数据的内容选择最适合的条码格式。
* Y,N,N 分别表示 第一个参数 Y 表示显示校验码,第二个参数 N 表示不显示文字标签,第二个参数 N 表示不显示文字标签。
* 他还有一种写法 FO30,230^BY2,2,50^BCN,32,Y,N,N^FD>;EDEzj200303170006^FS 这里的>;起始符是为了告诉打印机这是Code 128条形码的起始位置
* */
public String setZPLGrammer(int x,int y,double h,int w,int hw,int size){
return "^FO"+x+","+y+"^BY"+h+","+w+","+hw+"^BCN,"+size+",Y,N,N^FD${data}^FS";
}
/**
* 获取完整的ZPL
* @return
*/
public String getZpl() {
return begin + content + end;
}
/**
* 重置ZPL指令,当需要打印多张纸的时候需要调用。
*/
public void resetZpl() {
begin = "^XA" + darkness + width;
end = "^XZ";
content = "";
}
/**
* 打印
* @param zpl 完整的ZPL
*/
public boolean print(String zpl){
if(printService==null){
message = "打印出错:没有找到打印机["+printerURI+"]";
System.out.println("打印出错:没有找到打印机["+printerURI+"]");
return false;
}
DocPrintJob job = printService.createPrintJob();
byte[] by = zpl.getBytes();
DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
Doc doc = new SimpleDoc(by, flavor, null);
try {
job.print(doc, null);
message = "已打印";
System.out.println("已打印");
return true;
} catch (PrintException e) {
message = "打印出错:"+e.getMessage();
System.out.println("打印出错:"+e.getMessage());
e.printStackTrace();
return false;
}
}
public String getMessage(){
return message;
}
/**
* 字符串转byte[]
* @param s
* @return
*/
private byte[] str2bytes(String s) {
if (null == s || "".equals(s)) {
return null;
}
byte[] abytes = null;
try {
abytes = s.getBytes("gb2312");
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}
return abytes;
}
/**
*
* @param unit 单位名称
* @param orderId 工单号
* @param type 类别
*/
public static boolean printPicking(String unit,String orderId,String type){
ZplPrinter p = new ZplPrinter("ZDesigner S4M-203dpi ZPL");
p.setText(unit, 253, 26, 40, 40, 20, 1, 1, 24);
p.setChar(orderId, 253, 76, 20, 20);
p.setText(type, 253, 120, 53, 53, 20, 1, 1, 24);
String zpl = p.getZpl();
//System.out.println(zpl);
boolean flag=p.print(zpl);
return flag;
}
}
这个是调用打印机的工具类代码,之看到CSDN别人的,然后可能加了些其他功能上去。
再来看测试类,封装了一个对象传进去在main方法里面进行测试。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
public class ZplPrinterTest {
public static void main(String[] args) throws ParseException {
// 选择自己的打印机
ZplPrinter zplPrinter = new ZplPrinter("ZDesigner S4M-203dpi ZPL");
PrintGoods printGoods = new PrintGoods(
"测试打印电脑标签纸",
"戴尔主机",
"1234567890",
"222222A22M2222B222JH",
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),
"DEzj200303170006");
// 打印抬头
zplPrinter.setText(printGoods.getTitileName(), 125, 10,40, 40, 20, 1, 1, 24);
// 打印边框
// 设置左边框
zplPrinter.setBox(15,40,550,40,3);
zplPrinter.setBox(15,80,550,40,3);
zplPrinter.setBox(15,120,550,40,3);
zplPrinter.setBox(15,160,550,40,3);
zplPrinter.setBox(15,200,550,80,3);
// 设置右边框
zplPrinter.setBox(460,40,550,40,3);
zplPrinter.setBox(460,80,550,40,3);
zplPrinter.setBox(460,120,550,40,3);
zplPrinter.setBox(460,160,550,40,3);
zplPrinter.setBox(460,160,550,40,3);
zplPrinter.setBox(460,200,550,80,3);
// 打印字段
zplPrinter.setText("设备编码", 50, 50, 40, 40, 20, 1, 1, 24);
zplPrinter.setChar(printGoods.getCoding(), 170, 55, 20, 20);
zplPrinter.setText("资产名称", 50, 90, 40, 40, 20, 1, 1, 24);
zplPrinter.setText(printGoods.getGoodsName(), 170, 90, 40, 40, 20, 1, 1, 24);
zplPrinter.setText("设备", 50, 130, 40, 40, 20, 1, 1, 24);
zplPrinter.setChar("S/N", 100, 135, 20, 20);
zplPrinter.setText("码", 130, 130, 40, 40, 20, 1, 1, 24);
zplPrinter.setChar(printGoods.getSn(), 170, 135, 20, 20);
zplPrinter.setText("购买时间", 50, 170, 53, 53, 20, 1, 1, 24);
zplPrinter.setChar(printGoods.getShopTime(), 170, 175, 20, 20);
// ZPL解析语句
String bar2Pap = "^FO30,215^BY1.5,30,100^BCN,32,Y,N,N^FD${data}^FS";//条码样式模板
zplPrinter.setZPLGrammer(30,215,1.5,30,100,32);
// 将${data}替换成扫描二维码后展示的数据
zplPrinter.setBarcode(printGoods.getBarCode(),bar2Pap);
// 执行打印方法
zplPrinter.print(zplPrinter.getZpl());
}
static class PrintGoods {
private String titileName;
private String goodsName;
private String coding;
private String sn;
private String shopTime;
private String barCode;
public PrintGoods() {}
public PrintGoods(String titileName, String goodsName, String shopTime, String coding, String sn,String barCode) {
this.titileName = titileName;
this.goodsName = goodsName;
this.coding = coding;
this.sn = sn;
this.shopTime = shopTime;
this.barCode = barCode;
}
public String getTitileName() {
return titileName;
}
public void setTitileName(String titileName) {
this.titileName = titileName;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
public String getShopTime() {
return shopTime;
}
public void setShopTime(String shopTime) {
this.shopTime = shopTime;
}
public String getCoding() {
return coding;
}
public void setCoding(String coding) {
this.coding = coding;
}
public String getSn() {
return sn;
}
public void setSn(String sn) {
this.sn = sn;
}
public String getBarCode() {
return barCode;
}
public void setBarCode(String barCode) {
this.barCode = barCode;
}
@Override
public String toString() {
return "PrintGoods{" +
"titileName='" + titileName + '\'' +
", goodsName='" + goodsName + '\'' +
", shopTime='" + shopTime + '\'' +
", coding='" + coding + '\'' +
", sn='" + sn + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PrintGoods)) return false;
PrintGoods that = (PrintGoods) o;
return Objects.equals(titileName, that.titileName) &&
Objects.equals(goodsName, that.goodsName) &&
Objects.equals(shopTime, that.shopTime) &&
Objects.equals(coding, that.coding) &&
Objects.equals(sn, that.sn);
}
@Override
public int hashCode() {
return Objects.hash(titileName, goodsName, shopTime, coding, sn);
}
}
}
详细的讲解也写在里面的注释了,反正就是对于中文和英文数字是有格式要求的,中文要用setText指令来执行,英文和数字要用setChar指令执行
在这里可能需要用到一个点阵图ts24.lib,这个文件的链接我就放在下方了,不出意外就是长期有效了
链接:https://pan.baidu.com/s/1mUpF6xEzbiZRtADzQNTP3w?pwd=yyds
提取码:yyds