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);
			}
		}
	}
                

















