在项目中使用斑马打印机遇到了以下问题:java
在实际打印信息前,须要修改打印机驱动设置,主要是设置打印颜色深度,默认值通常会打印的比较浅。一般状况下,在设置的颜色深度后,打印机可以很好的工做。可是若是我将程序注册为windows系统服务,以自启动的方式运行程序后,对于打印机驱动的设置将不会生效。而直接使用控制台启动程序,倒是正常的。通过分析以后,怀疑多是因为,系统服务的启动方式和直接控制台启动的方式是有区别的,两种方式程序的登陆用户是不一样的,系统服务的登陆用户是SYSTEM(非administration)用户,控制台启动方式就是普通的USER用户。而打印机的驱动设置,貌似每一个用户之间是相互独立的,因此修改了USER用户的设置,SYSTEM用户的设置仍然是默认值,因此打印机驱动就不会生效。git
这个问题的一个解决方案,就是使用ZPL指令,直接控制打印机。ZPL是打印机专用的一种编程语言,具体能够参考《ZPL语言中文手册》。经过使用ZPL指令,能够打印问题,条码,图片,也能够修改各类打印参数,包括修改打印浓度参数。因此经过ZPL的方式能够忽略系统打印机的驱动设置,也就可以随意修改颜色深度设置了。github
可是如何调用ZPL呢?编程
一种方式能够经过cmd命令行的方式向打印机发送ZPL指令文件,具体可参考链接:http://www.chongshang.com.cn/news/view.asp?id=334。json
另外一种是经过JAVA调用打印机,在发送ZPL指令。这里我参考了github上的代码,详见:https://github.com/w3blogfr/zebra-zpl。从这个代码中就知道如何经过JAVA向打印机发送ZPL指令了。windows
github上的这个demo程序,用于打印通常的文字,条码是挺不错的,可是个人项目中须要打印一张位图图片,这个demo中并无相关的示例。因此我查了下ZPL语言的手册,发现其中的~DG命令可以知足个人需求,通过研究以后,终于可以使用ZPL打印图片了。如下是打印部分的参考代码:数组
import java.awt.image.BufferedImage; import java.awt.print.PrinterException; import java.io.File; import javax.imageio.ImageIO; import javax.print.Doc; import javax.print.DocFlavor; import javax.print.DocPrintJob; import javax.print.PrintService; import javax.print.SimpleDoc; import net.sf.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @detail GK888t斑马打印机ZPL指令打印 * 相比以前的打印方式,这种方式可以经过程序设置打印机参数 * 而且解决了使用服务启动时,不能调整打印机参数的问题 * */ public class PrinterZPL { private Logger logger = LoggerFactory.getLogger(getClass()); /** * @param parcelDetailBo * @return 0成功,-1打印机不存在 * @throws PrinterException */ public JSONObject startPrint() { try { // 根据打印机名获取打印服务 PrintService service = PrinterUtil.getPrintService(printerName); // 判断打印机是否找到 if (service == null) { printJson.put("fullName", "未找到打印机,多是驱动未安装"); return printJson; } else { printJson.put("fullName", service.toString()); } // 判断打印机是否链接 if (Devcon.DeviceCheck(printerName).isEmpty()) { printJson.put("connect", "打印机未链接,或电源未打开"); return printJson; } else { printJson.put("connect", "打印数据已发送至打印机"); } // 颜色浓度值 String darkness = SystemProperties.readString("gk888t_darkness", "20"); // 从文件读取图像数据,并完成转换 ZPLVo zplVo = getImagePixel(imgPath); // 组装ZPL打印指令,指令详情请参考ZPL的相关手册 String zplCode = "~DGR:SAMPLE.GRF," + zplVo.getData().length + "," + // 图形中的总字节数 zplVo.getWidth() + "," // 每行的字节数 + bytesToHexString(zplVo.getData()) // 该数据字符串用于定义图像,也表示图像的 ASCII 十六进制。每一个字符表示横向的 8 个点。 + "~SD" + darkness + "^XA" + "^FO50,20^XGR:SAMPLE.GRF,2,2^FS" // FO后面的为起始坐标,^SF前的两个参数分别为横向、纵向扩展比例 + "^XZ";// 指令结束 // 调用打印机,发送ZPL指令 Doc doc = new SimpleDoc(zplCode.getBytes(), DocFlavor.BYTE_ARRAY.AUTOSENSE, null); service.createPrintJob().print(doc, null); } catch (Exception e) { logger.error("打印异常", e); printJson.put("error", e.getMessage()); } return printJson; } /** * 根据坐标获取图像的颜色值 * @param bi * @param x * @param y * @return */ public int getRgb(BufferedImage bi, int x, int y) { try { return bi.getRGB(x + 0, y) & 0xffffff; } catch (Exception e) { return 1; } } /** * 读取一张图片的RGB值 * * @throws Exception */ public ZPLVo getImagePixel(String image) throws Exception { try { ZPLVo zplVo = new ZPLVo(); // 读取图像 BufferedImage bi = ImageIO.read(new File(image)); // 设置宽度,不能整除时向上取整 zplVo.setWidth((int)Math.ceil(bi.getWidth() / 8.0)); zplVo.setData(new byte[zplVo.getWidth() * bi.getHeight()]); int zplIndex = 0; // 循环读取图像像素 for (int j = 0; j < bi.getHeight(); j++) { for (int i = 0; i < bi.getWidth(); i += 8) { // 当前像素值 int rgb0 = getRgb(bi, i + 0, j); int rgb1 = getRgb(bi, i + 1, j); int rgb2 = getRgb(bi, i + 2, j); int rgb3 = getRgb(bi, i + 3, j); int rgb4 = getRgb(bi, i + 4, j); int rgb5 = getRgb(bi, i + 5, j); int rgb6 = getRgb(bi, i + 6, j); int rgb7 = getRgb(bi, i + 7, j); byte value = 0; // 实际每一个像素点在ZPL的图像数据中只占1位 value |= (rgb0 == 0 ? 0x00 : 0x80); value |= (rgb1 == 0 ? 0x00 : 0x40); value |= (rgb2 == 0 ? 0x00 : 0x20); value |= (rgb3 == 0 ? 0x00 : 0x10); value |= (rgb4 == 0 ? 0x00 : 0x08); value |= (rgb5 == 0 ? 0x00 : 0x04); value |= (rgb6 == 0 ? 0x00 : 0x02); value |= (rgb7 == 0 ? 0x00 : 0x01); // ZPL显示的图像颜色须要取反 value = (byte) ~value; // 保存 zplVo.getData()[zplIndex++] = value; } } return zplVo; } catch (Exception e) { logger.error("", e); return null; } } /** * 把字节数组转换成16进制字符串 * * @param bArray * @return */ public static final String bytesToHexString(byte[] bArray) { StringBuffer sb = new StringBuffer(bArray.length); String sTemp; for (int i = 0; i < bArray.length; i++) { sTemp = Integer.toHexString(0xFF & bArray[i]); if (sTemp.length() < 2) sb.append(0); sb.append(sTemp.toUpperCase()); } return sb.toString(); } }