JAVA对接斑马打印机打印RFID标签和普通标签
1、打印RFID标签
在打印RFID标签时,如果机器在没有校准的情况下进行打印标签,此时如果还需要获取到RFID的epc值,那么打印机返回的EPC值,有可能不是当前标签的epc值。考虑到此种情形,我们采用zpl命令进行控制打印机打印:命令包含写入EPC,同时包含返回EPC值。用写入的epc是否等于返回的epc,加以校验,从而避免这种情况的发生。以下用zt411型号验证过的代码
所需第三方jar:
ZSDK_API.jar 斑马打印机sdk
ts24.lib 字体库
在程序启动时,首先加载字体库:
private byte[] dotFont;
......
/**
* 构造方法 加载中文字体支持
*/
public ZplPrinter() {
String currentp = System.getProperty("user.dir");
String tspath = currentp + File.separator + "lib" + File.separator + "ts24.lib";
File file = new File(tspath);
log.info("the path of ts24.lib is == " + tspath);
FileInputStream fis;
try {
fis = new FileInputStream(file);
dotFont = new byte[fis.available()];
fis.read(dotFont);
fis.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
拼接ZPL命令:
private String getRFIDZPL(MaterialInfo m) {
StringBuilder sb = new StringBuilder("^XA^CW1,E:HEITI.TTF^FS^CI28");// 字体
String fontsize = "^A1N,30,30";// 字体大小
int x = 150;
int y = 70;
int step = 40;
// 商品名称
String str = "名称:" + m.getProductName();
sb.append(fontsize);
sb.append("^FO" + x + "," + (y + step) + "^FD" + str + "^FS");
// 商品编码
str = "编码:" + m.getProductCode();
sb.append(fontsize);
sb.append("^FO" + x + "," + (y + 2 * step) + "^FD" + str + "^FS");
// 商品规格
str = "规格:" + m.getSpecifications();
sb.append(fontsize);
sb.append("^FO" + x + "," + (y + 3 * step) + "^FD" + str + "^FS");
// 商品批次
str = "批次:" + m.getBatchNumber();
sb.append(fontsize);
sb.append("^FO" + x + "," + (y + 4 * step) + "^FD" + str + "^FS");
// 商品效期
str = "效期:" + m.getExpiredDate();
sb.append(fontsize);
sb.append("^FO" + x + "," + (y + 5 * step) + "^FD" + str + "^FS");
// 商品生产厂家
str = "供应商:" + m.getSupplier();
sb.append(fontsize);
sb.append("^FO" + x + "," + (y + 6 * step) + "^FD" + str + "^FS");
String writec = "^RS8^RFW,H,1,2,1^FD3400^FS^RFW,H,2,12,1^FD" + m.getRfidCode() + "^FS";
sb.append(writec);
// 条形码
sb.append("^BY3,3,60^FT150,70^BCN,50,Y,N^FD>:").append(m.getBarCode()).append("^FS");
/// 二维码
sb.append("^FT520,270^BQN,2,7^FH\\^FDLA,").append(m.getBarCode()).append("^FS");
sb.append("^RS8,,,1 ^RFR,H,0,8,E^FN1^FS^HV1,,^FS");
// 打印数量
sb.append("^PQ1,0,1,Y");
// 结束标识
sb.append("^XZ");
return sb.toString();
连接打印机,发送打印请求:
public void printRFID(String printerip, MaterialInfo m) throws Exception {
Connection connection = ConnectionBuilder.build("TCP_MULTI:" + printerip + ":9100:9200");
Integer timeout = 8000;
Date date = new Date();
try {
connection.open();
if (StringUtils.isBlank(m.getBarCode())) {
String barcode = customerService.createSPDUniqueCode();// 自动生成spd唯一码
m.setBarCode(barcode);
}
if (StringUtils.isBlank(m.getRfidCode())) {
m.setRfidCode(com.yinghui.common.utils.uuid.UUID.get24UUID().toUpperCase());
}
String sendStr = getRFIDZPL(m);// getPrintData(m);
if (log.isDebugEnabled()) {
log.debug("耗材信息:[{}],rfid标签值:[{}],[{}]", JSON.toJSONString(m), m.getRfidCode(), sendStr);
}
StringBuilder epcBuilder = new StringBuilder();
// ifid标签值
byte[] sendAndWaitForResponse = connection.sendAndWaitForResponse(sendStr.getBytes(), timeout, timeout, "");
epcBuilder = new StringBuilder();
for (byte b : sendAndWaitForResponse) {
char c = (char) b;
String str = String.valueOf(c);
epcBuilder.append(str);
}
String epc = epcBuilder.toString();
log.info("the value of epc from printer is : " + epc + ", " + m.getRfidCode());
if (!m.getRfidCode().equals(epc)) {
throw new Exception("请重新校准打印机!");
}
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
throw ex;
} finally {
try {
connection.close();
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
}
}
2、打印普通标签
在打印普通标签时,排版比较复杂,所以我们可以采用先生成图片、然后打印图片的方式进行打印。以下是用zt410验证过的代码
同时,标签上面可能会用到二维码和条形码,需要引入第三方包:
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.0</version>
</dependency>
生成图片:
public static BufferedImage createImage(MaterialInfo m) throws Exception {
Font font1 = new Font("黑体", Font.BOLD, 36);// 标题字体
Font font2 = new Font("宋体", Font.BOLD, 30);// 编号字体
String productName = StringUtils.nvl(m.getProductName(),"");
String specifications = StringUtils.nvl(m.getSpecifications(),"");
String manufactor = StringUtils.nvl(m.getManufacturer(),"");
String batchNumber = StringUtils.nvl(m.getBatchNumber(),"");
String edate = StringUtils.nvl(m.getExpiredDate(),"");
String deptname = StringUtils.nvl(m.getDeptName(),"");
String barcode = StringUtils.nvl(m.getBarCode(),"");
String unit = StringUtils.nvl(m.getUnit(),"");
String beihuo = StringUtils.nvl(m.getOther(),"");
// 创建图片
// int width = 480;
// int height = 300;
int width = 650;
int height = 500;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);// 创建图片画布
Graphics2D g = (Graphics2D) image.getGraphics();
// g.setColor(Color.BLACK); // 先用白色填充整张图片,也就是背景
g.fillRect(0, 0, width, height);// 画出矩形区域,以便于在矩形区域内写入文字
g.setColor(Color.black);
g.setFont(font1);
// g.setPaintMode();
int x = 20;
int y = 35;
int gap = 35;
//顶部1/3
g.drawString("▲" + productName, 5, y);// 商品名称
g.setFont(font2);
java.awt.FontMetrics fm = g.getFontMetrics(font2);
g.drawString("规格:" + specifications, x, y * 1 + fm.getHeight()+10);// 规格
//中间2/3
g.drawString("厂家:" + manufactor, x, y * 2 + fm.getHeight()+gap*2);// 厂家
g.drawString("批号:" + batchNumber, x, y * 3 + fm.getHeight()+gap*2);// 批号
g.drawString("有效期:" + edate,x, y * 4 + fm.getHeight()+gap*2);// 有效期
g.drawString("高值备货 "+beihuo, x, y * 5 + fm.getHeight()+gap*2);// 高值备货
g.drawString(barcode, x, y * 6 + fm.getHeight()+gap*2);// spd唯一码
// 二维码
BufferedImage qrImage = getQrCodeImage(barcode,200,200);//MatrixToImageWriter.toBufferedImage(bitMatrix);
g.drawImage(qrImage, 480, 80, qrImage.getWidth(), qrImage.getHeight(), null);
//底部3/3
g.setFont(font1);
g.drawString("★高值寄售", x, y * 7 + fm.getHeight()+gap*3+10);// 高值寄售
g.setFont(font2);
g.drawString(deptname, 400, y * 7 + fm.getHeight()+gap*3+10);// 科室
//条形码
BufferedImage barcodeImage = getBarCodeImage(barcode, 380, 60);
g.drawImage(barcodeImage, 350, 400, barcodeImage.getWidth(), barcodeImage.getHeight(), null);
g.drawString(beihuo, x, y * 8 + fm.getHeight()+gap*3+10);
g.drawString(barcode, x, y * 9 + fm.getHeight()+gap*3+10);
String other = unit+"/"+productName+"/"+specifications;
g.drawString(other, x, y * 10 + fm.getHeight()+gap*3+10);
// 底下
g.setColor(Color.black);// 再换成黑色,以便于写入文字
// g.setFont(font1);// 设置画笔字体
// g.drawString(title, 2, 50);
g.setFont(font2);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setPaintMode();
//int w = fm.stringWidth(title);
// g.drawString(title, (width - w) / 2, (codeImg.getHeight() + heightDiff));
g.dispose();
return image;
}
//生成条形码
private static BufferedImage getBarCodeImage(String vaNumber, int width, int height) {
try {
Code128Writer writer = new Code128Writer();
// 编码内容, 编码类型, 宽度, 高度, 设置参数
BitMatrix bitMatrix = writer.encode(vaNumber, BarcodeFormat.CODE_128, width, height, new HashMap());
return MatrixToImageWriter.toBufferedImage(bitMatrix);
} catch (WriterException e) {
log.error(e.getMessage(), e);
}
return null;
}
//生成二维码
private static BufferedImage getQrCodeImage(String barcode, int width, int height) {
// 二维码
BitMatrix bitMatrix = null;
Map hints = new HashMap();
try {
bitMatrix = new QRCodeWriter().encode(barcode, BarcodeFormat.QR_CODE, width, height, hints);
} catch (WriterException e) {
log.error(e.getMessage(),e);
}
// 将生成码位图转换成BufferedImage
BufferedImage qrImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
return qrImage;
}
形如:
将生成的图片信息,转换成zpl命令:
package com.yinghui.tag.utils;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
/**
* 实现思路: 1、获取图片的二值化字节数组 这一步是关键 2、将字节数组转为十六进制 3、压缩十六进制字符串
* 结尾为1、0或者与上一行相同的;相同的连续字符压缩 4、拼凑ZPL编码,宽度需要扩大,因为需要时8个点(1字节)的整数倍
*/
public class Image2Zpl {
public static int imgLength = 0;
static Pattern ZEROS = Pattern.compile("0+$"), ONES = Pattern.compile("1+$"),
MULTI_W = Pattern.compile("([0-9A-Z])\\1{2,}");
public static void main(String[] args) throws IOException {
System.out.println(image2Zpl(ImageIO.read(new File("d://label1.png"))));
}
/**
* 第一种:只把二维码图片转换成相对应的zpl指令
*
* @param image
* @return
*/
public static String image2Zpl(BufferedImage image) {
// 获取图片的字节数组
DataBufferByte data = (DataBufferByte) getBinaryGrayImage(image).getRaster().getDataBuffer();
byte[] imgData = data.getData();
System.out.println("image.getWidth(): " + image.getWidth());
int newW = (image.getWidth() + 7) / 8;// 实际每行字节大小,8个点,每个点1位,共8位
String[] strs = byte2HexStr(imgData, newW);
int bytes = imgData.length;
imgLength = bytes;
// return String.format("^XA~DG%d,%d,%d,%s^FO50,50^XG%d,1,1^FS^XZ", bytes,
// bytes, newW, compress(strs),bytes);
return String.format("~DG%d,%d,%d,%s", bytes, bytes, newW, compress(strs));
}
/**
* 把整个标签图片转换成完成的zpl指令
*
* @param image
* @return
*/
public static String image2Zpl2(BufferedImage image) {
// 获取图片的字节数组
DataBufferByte data = (DataBufferByte) getBinaryGrayImage(image).getRaster().getDataBuffer();
byte[] imgData = data.getData();
System.out.println("image.getWidth(): " + image.getWidth());
int newW = (image.getWidth() + 7) / 8;// 实际每行字节大小,8个点,每个点1位,共8位
String[] strs = byte2HexStr(imgData, newW);
int bytes = imgData.length;
imgLength = bytes;
return String.format("^XA~DG%d,%d,%d,%s^FO50,50^XG%d,1,1^FS^XZ", bytes, bytes, newW, compress(strs), bytes);
// return String.format("~DG%d,%d,%d,%s", bytes, bytes, newW, compress(strs));
}
/**
* 获取二值化图,并取反
*
* @param srcImage
* @return
*/
private static BufferedImage getBinaryGrayImage(BufferedImage srcImage) {
BufferedImage dstImage = new BufferedImage(srcImage.getWidth(), srcImage.getHeight(),
BufferedImage.TYPE_BYTE_BINARY);
dstImage.getGraphics().drawImage(srcImage, 0, 0, null);
for (int y = 0; y < dstImage.getHeight(); y++) {
for (int x = 0; x < dstImage.getWidth(); x++) {
Color color = new Color(dstImage.getRGB(x, y));
// 获取该点的像素的RGB的颜色
Color newColor = new Color(255 - color.getRed(), 255 - color.getGreen(), 255 - color.getBlue());
dstImage.setRGB(x, y, newColor.getRGB());
}
}
return dstImage;
}
/**
* 压缩图片数据
*
* @param data
* @return
*/
private static String compress(String[] data) {
StringBuffer sb = new StringBuffer();
String pre = null;
for (String d : data) {
String a = d;
Matcher m = ZEROS.matcher(a);
if (m.find()) {
a = m.replaceFirst(",");
}
m = ONES.matcher(a);
if (m.find()) {
a = m.replaceFirst("!");
}
a = minimizeSameWord(a);
if (pre != null && a.equals(pre)) {
a = ":";
} else {
pre = a;
}
sb.append(a);
sb.append("\n");
}
return sb.toString();
}
/**
* 十六进制串中相同字母压缩
*
* @param str
* @return
*/
private static String minimizeSameWord(String str) {
Matcher matcher = MULTI_W.matcher(str);
while (matcher.find()) {
String group = matcher.group();
int len = group.length();
String c = "";
if (len > 20) {
c = Character.toString((char) ('f' + len / 20));
}
if (len % 20 > 0) {
c = c + Character.toString((char) ('F' + len % 20));
}
str = str.replaceFirst(group, c + group.charAt(0));
}
return str;
}
/**
* 字节数组转为十六进制
*
* @param b
* @param rowSize
* @return
*/
private static String[] byte2HexStr(byte[] b, int rowSize) {
int len = b.length / rowSize;
String[] arr = new String[len];
for (int n = 0; n < len; n++) {
StringBuffer hs = new StringBuffer();
for (int j = 0; j < rowSize; j++) {
String stmp = Integer.toHexString(b[n * rowSize + j] & 0XFF);
if (stmp.length() == 1)
hs.append("0");
hs.append(stmp);
}
arr[n] = hs.toString().toUpperCase();
}
return arr;
}
}
调用打印机打印:
public void printCommon(String printerip, MaterialInfo m) throws Exception {
Date date = new Date();
Socket socket = null;
OutputStream out = null;
try {
socket = new Socket(printerip, 9100);
BufferedImage image = null;
try {
image = ImageProducerUtil.createImage(m);
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
throw new Exception(ex.getMessage());
}
String codeBegin = Image2Zpl.image2Zpl2(image);
String content = codeBegin + "^FO50,70^XG" + Image2Zpl.imgLength + ",1,1^FS\n";
out = socket.getOutputStream();
// 发送ZPL指令到打印机
out.write(content.getBytes("UTF-8")); // 使用UTF-8编码
out.flush();
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
throw ex;
} finally {
try {
out.close();
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
try {
socket.close();
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
}
}