2023年12月11日发(作者:太叔雁芙)
java标签打印-斑马打印机
文章目录
java实现标签打印-斑马打印机
背景
之前项目中有一个功能-批量打印资产标签,用户在设备资产界面勾选资产,选择打印样式,然后打印到标签纸上。花了两个星期后终于把
这个功能上线了,头皮屑都掉了不知道多少
初步设计
这里就说一下初步设计的思路。因需求可以总结为两种情况,一种是打印资产信息加条形码(资产编码生成),另外一种是资产信息加二维
码(资产编码生成),一开始的设计简单可以分为三张表:模板、字段以及模板和字段的关联关系表。前端利用html按照业务提供的标签纸
参数大小,把两种情况的样子画好定为样式一、样式二,类似如图然后新建模板,选择样式保存,然后在模板里面添加字段,字段详情里面可以设置数据来源字段或者来源sql,最后可以点击图片中的名
称、编号等位置弹出弹框,在弹框中绑定字段。后端处理时利用图片中位置所绑定的字段设置的数据来源字段或者来源sql去得到实际的数
据替换html中对应位置的数据,二维码或者条形码是用实际数据(实际场景都是资产编码这种)的base64编码去替换html中的img的
src。
这样就得到实际的资产信息标签的html了
然后把转换后的html生成pdf导出
后面去了客户现场,才看到了标签打印机,之前一直都是云开发。
然后打印机通过usb连上笔记本,下载驱动,点开之前生成过的pdf,右击打印
…打印出来的完全对不上,整个标签出来了半个外边的框框,其他东西都没有了,而且还横跨了两张标签纸。
然后开始调试,调试代码中纸张长宽,打印区域长宽,打印方向,打印机首选项中大小边距方向等。调了一天也没调出来,调到最后打印队
列里面显示已打印,但是实际上啥也没有了。
这时我是猜测pdf是太大了,超过范围就打不出东西,于是我拿标签纸和模板里的样式对比了一下,真的大了好多,于是我把实际的长宽给
了前端,前端反馈按照这个长宽样式画不出来,内容塞不下。。。后面我在代码里面尝试设置缩放也没弄出来
(生成的pdf大小都有一百多K,后面用第二种方式生成的pdf最多只有十几K)
着急的我第二天晚上在网上看到了斑马打印机的指令语言zpl,于是去看了zpl指令文档和一些类似的用zpl打印标签的博客
后一天运行写好的demo,利用驱动打印稍微调一调,就出来了像点样子的标签了。
这时候,我的思路就是使用zpl指令,把两种样式都定义好(后面用户调成了四种),实际场景中直接用资产信息去替换指令中对应的位置
就好了。
这时候我又找到了另一个神器–ZebraDesigner 2
用这个直接画好后可以直接打印,也可以生成zpl指令文档,完美,接下来就是在服务中完成代码了。
部分需要的依赖
zpl指令驱动打印
打印类:
package ;
import .*;
import rName;
import .*;
import ortedEncodingException;
/**
* 斑马打印机打印类-ZPL语音
*/
public class ZplPrinter {
private PrintService printService = null;//打印机服务
private String content = "";
/*private String begin_1 = "u0010CT~~CD,~CC^~CT~n" +
"^XA~TA000~JSN^LT0^MNW^MTT^PON^PMN^LH0,0^JMA^PR3,3~SD28^JUS^LRN^CI0^XZn" +
"^XAn" +
"^MMTn" +
"^PW160n" +
"^LL0280n" +
"^LS0n"; // 开始(个)*/
private static final String begin = "u0010CT~~CD,~CC^~CT~" +
"^XA~TA000~JSN^LT0^MNW^MTT^PON^PMN^LH0,0^JMA^PR3,3~SD28^JUS^LRN^CI0^XZ" +
"^XA" +
"^MMT" +
"^PW695" +
"^LL0280" +
"^LS0"; //开始(排) "^LS0"; //开始(排)// private String logo_1 = "^FO64,0^GFA,01152,01152,00012,:Z64:n" +// "eJztUjtOAzEQHcdYKRKxKbBERSiRaCgRi2D3Bou07nOMVMiIi6zEJaKsxHKUHINqzfy8QEWZBsu2np6eZ97MGOB//bVMaCZs+/2EQwhVximlwyQPoc3yYRjeFXvkg+L1CEWKGqb9zlB2cjgMBvfCmxGv+afyAM5JArsDWLyII4fSUAtfoJcUnyXtU8AnkngR+7c9lMLXZNNzBWsg+wUb8obKcsw/YAER5sxj5Im/RzcHsB/KmwYM87eId2A75fEIv+VYhjtEFXl6InrK2gnfYnThtzCCjSB69GMmfd+jWPXUzhy/SCnraQDV5OdxiCb79z/4C7TK/BnZ0TjnAHdAmYXH7bhXp6y30jfews+oLLqp/4amfiJziQkntpR5YZ8bCkUrlvh/bgRX9H907hscQNwIXhkf6kowGrp+FUgpnVNMVpaKqTUrxXDJG9SpqzKexasMf/3nY68vBLVZLg==:36A9n"; private static final String logo_1 = "^FO192,170^GFA,01536,01536,00012,:Z64:" + "eJztUjFOw0AQnLMTCoQciliiQrzANQWKzwV9Cm+fF/ACinsKT/FTTlSI4pTSBbJZ7+5Z+QEUWfmi0WhvbjK7wLX+uLbkV3yLjwzdEX3GhXxaG76RLzzwORk+HvkYPn0DdybTUe83ikvMcyjtVUdETvENmpQQBO/ZD3U+y7dziILvgZq8PvAEHOQX8mQPc8pWXoDnzO/l42KFxgw5L83KD9JfKe+I+dr4abrgOZ+t2A/sf8TOdNi/8kVAkVKQfo7G84XcH9GGnfKcZ732jzB97l+8a/+w/K1Vf/EifoqIalj9SPrCu4jyDDzatBxl/0xOUbSUJ6/5jCh5AO/C9xwndcq/oflM4UswgTy9Kt9iDihHwXXH8lvNvxm4t9K51MvcbYN25+XSoPNlCWd8kfQIz1xNisFptj+GOU7KC3eYpikaZpvrgrJ9dqS17A9yNSmu+HKfr/XP6xchOF9e:F887" + "^FO512,170^GFA,02048,02048,00016,:Z64:" + "eJztk0FOAzEMRX8aIhCtKIuOxBFYskQqEsMNBmm85xgsI3XJJVhyhFFn06PMEbqsVDTBCTR2uuIAY7WVvl5tf8cJMMUUqKjU9zOvpatNo/VV+kjccgWt3wCjCpiXGqZWevMFPKjyjl5jjVMsbBixEL1yRO2F6Dv0fTcT3bB/ckX7MFoxwK2JlAEmh/T7Z4fdNZAJo/U9MCjOLZB5rNypAbizKThS/qNwRwxb4fZ4OON8/quTvoENYcCTcMPzC78G5v1W8isY/kOR7zFKfsX7Q5Fv42w6P3rPfJmOR/dP3rP/uccahf+0/cwvPZY74D3zGq7V8+/4AHzqkTnvR85vwDKMZp95w+sjJ/vp0H9s7WfWrSFDldzQbzvyFEPWFNtXddZ9zF174fF+qhfwzO4RRMfSTvF59/vNnBm1oi1vL+xF83aI9AM6cnilI1eSx+cHoCLef635/vtCn7/fKab4V/wA4xhbmA==:B3EC" + "^FO352,170^GFA,01152,01152,00012,:Z64:" + "eJztUjFOxDAQHAeOO0WUFBQcUJ5S8QAKHsL9gZICiEnJK1JG+wpT0FNQXqR7wpVXEIXxrh3dA6DDsqPRaLw7GS/wv/50leInfObuMixadBkf67a14I184YYnJNy2PAm/fwHnqUwjnV8YPnHjiJPUtRCRwvAp1v3GGV7RjzQ+l69HZw2ugUq8NaDHJ/1CW3ZITql8BB4yv9INxAr3yVDhVWy8V/2F8YWQr4x3w/cBz3zKiOeg/x0uUx36N37G3femZzSeF7I+oDZ96ZlnlfVuB0z66N30Pv7WVD96UT+zgKWf/Gj6yh8FzLfArfItCsn+t3BD0FrGi9d82HXOB9gr3zFOaSy3DdYfvftULBAvb8a/uJGudoqrhuVLy3/9Su3S3qWK754m6IpuUBsuWaJI/GxjR3lylRh2TLPeG45xSh6452EYQsK0OQ0o7Y8Jxt+SjDk/YcKH8/wL6wd4H3r/:A9FE" + "^FO0,170^GFA,01536,01536,00012,:Z64:" + "eJztUjFOw0AQnDMkjixKCgoClJErHkDBQ8gfKCkAHy55hUtrX2EKegrKWMoTUqbAMnO7d1Z+gJCyurNGo7nd8dwBx/oXVYif8Lm7TzBr0CZ8qstqwRPpwC13F3HTcEf88Q1cxDa1tH5heO7GEfM4NRORzPAZ1v3GGV7Rj9Q+ta9GZwNugFK8DaDHZ/1CR7aITql8Ah4Tv9IFhA4P0VDmVWy8V/2l8ZmQL413w88Bz3yKgHPQ/w5XsQ/9Gz/j6nvTMxrPA0nfoTJ94ZlnmfRuB0z64N30PvzW1D94UT+zDks/+dH0lT/pkG+BO+UbZJL8b+GGTnsZL17z4dScF7BXvmWcUltuG6w/e/elWCBe3o1/dSNd7RSXNdsXlv/6jdql3UsZ7j2+oGu6QWW4YIss8rONbeXJlWLYMc1qbzjEKenBvQzD0EVMm9MDpf0xwvBbkjDfTzfhw/d8rD+rX2f5ev8=:D9E6"; private static final String logo_2 = "^FO512,192^GFA,01152,01152,00012,:Z64:" + "eJztkUFKBDEQRX86E2agRUeYgOBGT6BLwU164T4DU3uP4BEis/Aa4zlmM0fppUcYbOg2oauq+waCWKvHS6iq/AB/rFZJ0dBOuYJVXsAEvQ7cCV9HrIXv+3I0VkO7hfB7911Je3hyzBZ22DO7PLiZxiak2VgefFH8aeTNzK+Kb0dem21AHPnGDAmv4onU43hMj+zzPtP9U63eBDP53Fx9ltrnCXiTfTYzXwMH8T4a9pdYfsmeHo4k0BrP3Z7f5fKeL+yXqa8+xDcRnr39POCW8ynP9ZwbzkAvTDD6kV2yZ/VbH4WvhqEVdkRB2OZ8hEtuWg/txC7gv36nfgDK2jZc:047B" + "^FO348,192^GFA,00768,00768,00008,:Z64:" + "eJzdkTFuwzAMRb8sCw6KwM4QrU2QOSg6dquHevcg7j1BzuCpvUZvkhzFYydn9ZT0k/IJOhXV8vAEipS+gL+zDhlBeuMKa+MGwbgHBmXXolV+zroFx/oNWVTX7xVZQtKWXKO5aYMtG5bWrkaxtHPkM92T/eJ7dQ5oQwfHAe/VDc5cknkxTbjQsXiFJ/OAMjuLzdt8HiPcRfv3ix9RDOqiJXjB7kvnJcSkDzziOjV63yASyUc/P+zIyGIh648BJ42n0zOAH+FnfX9C0MD8hGZUlzexfF7vd8srilh+Ne+f80056POQGX/xV/9l/QBSVjIV:871E" + "^FO160,192^GFA,01152,01152,00012,:Z64:" + "eJztkDFOw0AQRf/aCUQEYUfyNgjhlClTIkCKj7DFzl0oENqSY3CElCulSI7iY6RKGGBm7BNQIH719GzPjD/wl/M+oKfOeIm1cYfGOKFQdB5OuWh7fvSTCc/RQbN2/7EUrh2FILzG5qgLAnysbe0DZqO1E+ED+2tZO/aJ/Y14+AZT4dQecWGegvn5Lpfq3cjfYfANavP3gHmPYX6ZcKn38EXmscWVefr65Ds93pLeGUBBC92W+/ysPXiKUfil6levwpF7Vv94C5eF+Xed9lklVL3wlAvt1GdsknpaEDTn88mYKBo/7bIx92ZcDK/zRfjP7+UT49su6w==:8176" + "^FO0,192^GFA,00768,00768,00008,:Z64:" + "eJzVkbFuwjAQhn8npCCoSCrFC0IkIyNj1VZKHsGD7106IOSRx+ARGC0xwKPkMZhC7y7hCbq0Xj59lu/s+w38vXUcYKlV1tgpW5TKgERgLIwwqTreAiZ8Xgpm1fVUMwtDzjF3aO7SwMH6Qtu9Yza2mzBv7K/c7umBfckOWyJjhuqOF3Vy6otLTMXN6GsMXqJQ3wDqFkN9GjCV/nyDOs6Yq5McQYdDkPscyMmA5/Qav+S9lrxnfufdds/0PK/4xwomSjyl1AB5QN4xMx5Q5s8jGskjozfSvB6PXknklZ+XOObrlEk/5u5//3X/bv0AQ3wu6w==:13E8"; private static final String end = "^PQ1,0,1,Y^XZ"; private static final String BAR = "^BY2,3,37^FT${h},258^BCB,,Y,N" + "^FD>;${code}^FS"; private static final String QR = "^FT${h1},100^BQN,2,4" + "^FH^FDLA,${code}^FS" + "^FT${h2},102^A0B,17,16^FH^FD${code}^FS"; private static final String NUM = "${code}"; private static final String H = "${h}"; private static final String H1 = "${h1}"; private static final String H2 = "${h2}"; public static void main(String[] args) {// ZplPrinter p = new ZplPrinter("192.168.0.12ZDesigner 105SLPlus-300dpi ZPL"); ZplPrinter p = new ZplPrinter("ZDesigner ZT210-200dpi ZPL"); //1.打印单个条码
//1.打印单个条码
String bar0 = "7150000036";//条码内容
// String bar0Zpl = "^FO110,110^BY6,3.0,280^BCN,,Y,N,N^FD${data}^FS";//条码样式模板
// rB("name", 20, 60, 30, 30);
// t("型号", 40, 60, 30, 30, 5, 1, 1, 24);
in();
t2Img("中中中中中中中中中1", 20, 0, 22, );
t2Img("中中中中中中中中中1", 48, 0, 22, );
t2Img("中中中中中中中中中1", 76, 0, 22, );
t2Img("中中中中中中中中中2", 194, 0, 22, );
t2Img("中中中中中中中中中2", 222, 0, 22, );
t2Img("中中中中中中中中中2", 250, 0, 22, );
t2Img("中中中中中中中中中3", 368, 0, 22, );
t2Img("中中中中中中中中中3", 396, 0, 22, );
t2Img("中中中中中中中中中3", 424, 0, 22, );
t2Img("中中中中中中中中中4", 542, 0, 22, );
t2Img("中中中中中中中中中4", 570, 0, 22, );
t2Img("中中中中中中中中中4", 598, 0, 22, );
o(1);
();
// nese("型号",50,60,30,30);
// String bar0Zpl = "^FO102,0^BY2,3.0,40^BCB,,Y,N,N^FD${data}^FS";//条码样式模板
// code(bar0, bar0Zpl);
String zpl = ();
n(zpl);
boolean result1 = (zpl, 1);//打印
if (result1) {
return;
}
pl();//注意要清除上次的打印信息
}
/**
* add LOGO
*
* @param i 样式
*/
public void setLogo(int i) {
if (i == 1) {
content = content + logo_1;
} else if (i == 2){
content = content + logo_2;
}
}
/**
* setEnd
*/
public void setEnd() {
content = content + end;
}
/**
* setBegin
*/
public void setBegin() {
content = content + begin;
}
public void setText2Img(String str, int x, int y, int size, int style) {
content += tHex(bSubstring(str, 17), x, y, size, style);
}
/**
* 构造方法
* 构造方法
*
* @param printerURI 打印机路径
*/
public ZplPrinter(String printerURI) {
if (printerURI != null) {
//打印机完整路径
//初始化打印机
PrintService[] services = PrintServices(null, null);
if (services != null && > 0) {
for (PrintService service : services) {
if ((e())) {
printService = service;
break;
}
}
}
if (printService == null) {
//循环出所有的打印机
if (services != null && > 0) {
n("可用的打印机列表:");
for (PrintService service : services) {
n("[" + e() + "]");
}
}
throw new RuntimeException("没有找到打印机:[" + printerURI + "]");
} else {
n("找到打印机:[" + printerURI + "]");
n("打印机名称:[" + ribute().getValue() + "]");
}
}
}
/**
* 获取完整的ZPL
*
* @return
*/
public String getZpl() {
return content;
}
/**
* 重置ZPL指令,当需要打印多张纸的时候需要调用。
*/
public void resetZpl() {
content = "";
}
/**
* 打印
*
* @param zpl 完整的ZPL
* @param quantity
*/
public boolean print(String zpl, Integer quantity) {
if (printService == null) {
return false;
}
byte[] by = es();
// 直连打印
DocPrintJob job = PrintJob();
DocFlavor flavor = _NSE;
Doc doc = new SimpleDoc(by, flavor, null);
try {
(doc, null);
(doc, null);
n("已打印n" + zpl);
return true;
} catch (PrintException e) {
tackTrace();
return false;
}
// 复杂打印
/*PrinterJob job = nterJob();
PDDocument document = null;
try {
document = (by);
ntService(printService);
// 设置纸张属性
Paper paper = new Paper();
double paperHigh = 250.56;
double paperWidth = 100.8;
Double marginLeft = 0.0;
Double marginRight = 0.0;
Double marginTop = 0.0;
Double marginBottom = 0.0;
// 设置纸张宽高 以1/72英寸为单位 纸张宽250mm 则paperWidth为 25cm/2.54*72
e(paperWidth, paperHigh);
// 设置可成像区域
geableArea(marginLeft, marginTop, paperWidth - (marginLeft + marginRight), paperHigh - (marginTop + marginBottom));
// 设置纸张及缩放
PDFPrintable pdfPrintable = new PDFPrintable(document, _SIZE);
// 设置多页打印
Book book = new Book();
PageFormat pageFormat = new PageFormat();
// 设置打印方向 横向
entation(APE);
// 设置纸张
er(paper);
(pdfPrintable, pageFormat, berOfPages());
eable(book);
// 设置打印份数
ies(quantity);
// 添加打印属性
HashPrintRequestAttributeSet pars = new HashPrintRequestAttributeSet();
// 设置单双页
();
(pars);
return true;
} catch (Exception e) {
return false;
} finally {
if (document != null) {
try {
();
} catch (Exception e) {
tackTrace();
}
}
}*/
}
/**
* 条形码
*
* @param assetNum
* @param h
*/
public void setBar(String assetNum, int h) {
if (() == 13) {
if (() == 13) {
assetNum = e(f((12)), ">6" + (12));
}
content = content + e(NUM, assetNum).replace(H, f(h));
}
/**
* 按字节截取
*
* @param s
* @param length
* @return
* @throws Exception
*/
public static String bSubstring(String s, int length) {
if (s == null) {
return "";
}
try {
byte[] bytes = es("Unicode");
int n = 0; // 表示当前的字节数
int i = 2; // 要截取的字节数,从第3个字节开始
for (; i < && n < length; i++) {
// 奇数位置,如3、5、7等,为UCS2编码中两个字节的第二个字节
if (i % 2 == 1) {
n++; // 在UCS2第二个字节时n加1
} else {
// 当UCS2编码的第一个字节不等于0时,该UCS2字符为汉字,一个汉字算两个字节
if (bytes[i] != 0) {
n++;
}
}
}
// 如果i为奇数时,处理成偶数
if (i % 2 == 1) {
// 该UCS2字符是汉字时,去掉这个截一半的汉字
if (bytes[i - 1] != 0) {
i = i - 1;
}
// 该UCS2字符是字母或数字,则保留该字符
else {
i = i + 1;
}
}
String newStr = new String(bytes, 0, i, "Unicode");
StringBuilder newS = new StringBuilder(newStr);
if (getWordCount(newStr) < 17) {
for (int j = 0; j < 17 - getWordCount(newStr); j++) {
(" ");
}
}
return ng();
} catch (UnsupportedEncodingException e) {
tackTrace();
}
return "";
}
/**
* 二维码
*
* @param assetNum
* @param x5
* @param x6
*/
public void setQr(String assetNum, int x5, int x6) {
public void setQr(String assetNum, int x5, int x6) {
content = content + e(NUM, assetNum).replace(H1, f(x5)).replace(H2, f(x6)).replace(NUM, assetNum);
}
/**
* 获取字符串的字节长度
*
* @param s
* @return
*/
public static int getWordCount(String s) {
int length = 0;
for (int i = 0; i < (); i++) {
int ascii = intAt(s, i);
if (ascii >= 0 && ascii <= 255) {
length++;
} else {
length += 2;
}
}
return length;
}
}
汉字转zpl图片类:
package ;
import athResource;
import ce;
import .*;
import gle2D;
import edImage;
import fferInt;
import .*;
/**
* 汉字转zpl图片
*/
public class Font2ZplGF {
private static BufferedImage source = new BufferedImage(10, 10, _INT_ARGB);
private static Graphics2D gs = Graphics();
private static Font f;
static {
Resource resource = new ClassPathResource("font/");
try {
f = Font(PE_FONT, utStream());
} catch (FontFormatException | IOException e) {
throw new RuntimeException(e);
}
}
public static String getFontHex(String content, int x, int y, int size, int style) {
if (content == null || "".equals(content)) {
return "";
}
if (f == null) {
f = getSelfDefinedFont("font/", style, size);
} else {
f = Font(style, size);
}
// f = new Font("simhei", , size);
t(f);
FontMetrics fontMetrics = tMetrics();
Rectangle2D stringBounds = ingBounds(content, gs);
int width = (int) th();
width = (width + 7) / 8 * 8;
int height = width;
BufferedImage image = new BufferedImage(width, height, _INT_ARGB);
// image = modifyImageRatio(image,270);
Graphics2D g2 = Graphics();
t(f);
or();
// 图片旋转
(ans(270), width / 2, height / 2);
ring(content, 1, (int) (size * 0.88));
e();
StringBuilder zpl = new StringBuilder("^FO").append(x).append(",").append(y)
.append(getImage(image)).append("^FS");
return ng();
}
/**
* 自定义字体
* @param filepath 字体文件路径
* @param style 样式
* @param size 大小
* @return
*/
private static Font getSelfDefinedFont(String filepath, int style, float size){
Font font = null;
try{
Resource resource = new ClassPathResource(filepath);
font = Font(PE_FONT, utStream());
font = Font(style, size);
} catch (FontFormatException | IOException e){
throw new RuntimeException(e);
}
return font;
}
private static final char[] HEX = "ABCDEF".toCharArray();
private static String getImage(BufferedImage i) {
int w = th();
int h = ght();
boolean black[] = getBlackPixels(i, w, h);
int hex[] = getHexValues(black);
String data = ints2Hex(hex);
int bytes = () / 2;
int perRow = bytes / h;
return "^GFA," + bytes + "," + bytes + "," + perRow + "," + data;
}
/**
* Returns an array of ones or zeros. boolean is used instead of int for memory considerations.
*
* @param bi
* @param w
* @param h
* @return
*/
private static boolean[] getBlackPixels(BufferedImage bi, int w, int h) {
int[] rgbPixels = ((DataBufferInt) ter().getDataBuffer()).getData();
int i = 0;
boolean[] pixels = new boolean[];
for (int rgbpixel : rgbPixels) {
pixels[i++] = isBlack(rgbpixel);
}
return pixels;
}
private static boolean isBlack(int rgbPixel) {
int a = (rgbPixel & 0xFF000000) >>> 24;
if (a < 127) {
return false; // assume pixels that are less opaque than the luma threshold should be considered to be white
}
int r = (rgbPixel & 0xFF0000) >>> 16;
int g = (rgbPixel & 0xFF00) >>> 8;
int b = rgbPixel & 0xFF;
int luma = ((r * 299) + (g * 587) + (b * 114)) / 1000; //luma formula
return luma < 127;
}
private static int[] getHexValues(boolean[] black) {
int[] hex = new int[(int) ( / 8)];
// Convert every eight zero's to a full byte, in decimal
for (int i = 0; i < ; i++) {
for (int k = 0; k < 8; k++) {
hex[i] += (black[8 * i + k] ? 1 : 0) << 7 - k;
}
}
return hex;
}
private static String ints2Hex(int[] ints) {
char[] hexChars = new char[ * 2];
for (int i = 0; i < ; ++i) {
hexChars[i * 2] = HEX[(ints[i] & 0xF0) >> 4];
hexChars[i * 2 + 1] = HEX[ints[i] & 0x0F];
}
return new String(hexChars);
}
}
这中间主要的问题就是汉字问题
因为打印机中没有你设置的字体,所以打印出来的标签汉字不会显示,这里也看了网上蛮多方法都没有用,最后只好采取了汉字转图片打印
图片的方式
zpl指令转pdf打印
本地驱动打印很ok,但是服务器不支持这种方式,新的问题来了。由于这种斑马打印机不能联网,根据实际需求也不会给打印机装上很贵的
网卡。
所以只有一条路了,回到当初,导出pdf,客户自己本机打印pdf
zpl指令怎么转成pdf呢?
这里提供了接口供zpl指令转成pdf,只需通过http调用就好了,经测试ok
package ;
import ject;
import tputStream;
import ption;
import tream;
import LConnection;
import ;
import ist;
import ;
/**
* zpl指令转pdf
*/
public class Zpl2Pdf {
/*public static void main(String[] args) {
String zpl = "CT~~CD,~CC^~CT~^XA~TA000~JSN^LT0^MNW^MTT^PON^PMN^LH0,0^JMA^PR3,3~SD28^JUS^LRN^CI0^XZ^XA^MMT^PW695^LL0
280^LS0^FO352,160^GFA,01152,01152,00012,:Z64:eJztj7FtwzAQRT9pGRJYqUjgxgZdCq5TptAIatJnBBWC1RggRxFcETdFVsgmLl0IUOTk7qQNUiS/enw
k/5HAf34nxwW3b/VKLxs1MuUIK7g5lCbKwiUIZz7UUlRYGqSoNOHeav0piW/xUj6pr1Do2Ffsdax68/DZyvODTPQjcvGO1GPXN+pBSc+Xu1F9AfXwWPVAez7
W3kY4eY95XzwG9TeExrMfQIP41oRO/nW011QxX/xnc2GuXERiPj/Pd5hP310/8RH5jdnNYyNz3iHUzJZA4s00TZAQkXLfd8pWjz8u4K/kCwjBNFw=:F8AD^FO
544,160^GFA,00768,00768,00008,:Z64:eJztkbFtwzAQRT/p0AoEB1CTLkBUGtwhAEdw4y6BV3Dh0gY4CkvhptAIaoJUKryBAri1Zd6XMoHbHAh8PH7e8XgE/
mOOOMnr9g+5YYBK1QLPqqtDWNL3yapUp6tRrb1ETQhuPLdMl5TZtPjuAjmhZrkBe5YjO+Vq5nzBEocBb8qe/ISvPpBLifTdOJBrm+jvzeSv2WCBI6b8Errl8DK
zjcpFWFz0SCah35prq/3EUlJS3v38Drnf5KOy6T7huszNWnOA4waFvs/npfwRcdL3lykXzPF+KW6cl0jD+fV9x3mJJOpuDFTbTHM2m4d/7oG4AwAhQRY=:C6
CC^FO192,160^GFA,00768,00768,00008,:Z64:eJzlkTFqAzEQRf8oGyuocrFkUxjSBlcpU65v4Ca4tI/ggGFL6ygqlzmFr2AI5BoOBFyFzfwdN2kDqfwZ+DwN+o
xGwNUpXPx1eUE/qCAt/Q7I9LcjpmNfeQQszu81PauSBc0w8evKgAniKjpnBlS4yXJgnHON+Isf5MM56MhT6XawAZ6gigK8YDXIlpwT+VkOtyAHA6uZnGBl/X7k
GjvIhvnJ+3hcMj8h9My/Rzw7z9UmQCPDOhqHrCEZx8UWDecvhQmQzxZ7vrcfC/LFMs0ZaNof1yd6Ui302HWt70vHfcrw7XtOxb1q//Jb/6wfQRs9bQ==:060F^
FO0,192^GFA,01152,01152,00012,:Z64:eJztjzFOAzEQRb83axFppTXFbkMRpYxygihIsEdwYV8HuUQUHCFyiahSRpti9yiUOQJCaIMRM+McASSmen4z+jM
G/mhdZyxe44XOjSUK4Q6KUT0/YuSHdqlFMWaIS+JSe8tBcwyfG4lvHfsNbvq5+AalrF2glrXZx+SLC88HjfUOs0A+3cM+VMcX8cpbmccwiS/hZL7GKudAct5My
Pmqg+Z7VMweVvwJD8GQt2g79gfcH/hfVnnXsK8+pjviRtuG/bYK6Ilb/Z31U+YJeCfW6Rz2V3uYSKxcWkw8m8J5JIb3nhG3x1649U64Op+EkceBNf7rl9UXFGw
0lw==:0DFA^FT412,135^BQN,2,4^FH^FDLA,7150000036^FS^FT574,127^BQN,2,4^FH^FDLA,7150000036^FS^FT522,124^A0B,14,14^FH^FD12345
67891123^FS^FT234,136^BQN,2,4^FH^FDLA,7150000036^FS^FT685,117^A0B,14,14^FH^FD23^FS^FT54,133^BQN,2,4^FH^FDLA,71
50000036^FS^FT350,128^A0B,14,14^FH^FD23^FS^FT173,126^A0B,14,14^FH^FD23^FS^PQ1,0,1,Y^XZ";
String url = "/v1/printers/8dpmm/labels/3.42x1.37/0/";
for (int i = 0; i < 2; i++) {
getPdf(url,zpl);
url = "/v1/printers/8dpmm/labels/3.42x1.37/1/";
}
}*/
/**
* zpl指令转List
* @param url
* @param zpls
* @return
*/
public static List
return bitchSend(url,zpls);
}
private static List
List
h(zpl -> (send(url0,zpl)));
return inputStreamList;
}
/**
* zpl指令转InputStream
* @param url
* @param zpl
* @param zpl
* @return
*/
public static InputStream getPdf(String url, String zpl) {
return send(url,zpl);
/*try {
// 获取http客户端
CloseableHttpClient client = Default();
// get method
HttpPost httpPost = new HttpPost(url);
// URIBuilder uriBuilder = new URIBuilder(url);
// 第一种添加参数的形式
// ameter("name", "root");
// ameter("password", "123456");
// 第二种添加参数的形式
List
BasicNameValuePair param1 = new BasicNameValuePair("mode", "raw");
BasicNameValuePair param2 = new BasicNameValuePair("raw", zpl);
(param1);
(param2);
ity(new UrlEncodedFormEntity(list, "UTF-8"));
// ameters(list);
// 通过httpget方式来实现我们的get请求
// HttpPost httpPost = new HttpPost(());
// 传输的类型
der("Content-Type", "application/x-www-form-urlencoded");
// der("Content-Length", f(()));
der("Accept", "application/pdf");
// 通过client调用execute方法,得到我们的执行结果就是一个response,所有的数据都封装在response里面了
CloseableHttpResponse response = e(httpPost);
// HttpEntity
// 是一个中间的桥梁,在httpClient里面,是连接我们的请求与响应的一个中间桥梁,所有的请求参数都是通过HttpEntity携带过去的
// 所有的响应的数据,也全部都是封装在HttpEntity里面
if (tusLine().getStatusCode() == 200) {
HttpEntity entity = ity();
return tent();
} else {
throw new RuntimeException(tusLine().toString());
}
} catch (IOException ex) {
tackTrace();
throw new CommonException(ex);
}*/
}
private static InputStream send(String url0, String param) {
URL url;
HttpURLConnection connection;
try {
//创建连接
url = new URL(url0);
connection = (HttpURLConnection) nnection();
utput(true);
nput(true);
uestMethod("POST");
Caches(false);
tanceFollowRedirects(true);
uestProperty("Content-Type","application/x-www-form-urlencoded");
uestProperty("Connection", "Keep-Alive");
uestProperty("Accept","application/pdf");
nectTimeout(30000);
nectTimeout(30000);
dTimeout(30000);
//POST请求
DataOutputStream out = new DataOutputStream(putStream());
JSONObject body = new JSONObject();
("mode", "raw");
("raw", param);
(ng().getBytes("UTF-8"));
();
();
return utStream();
//读取响应
/*BufferedReader reader = new BufferedReader(new InputStreamReader(utStream()));
String lines;
StringBuffer sb = new StringBuffer("");
while ((lines = ne()) != null) {
lines = new String(es(), "utf-8");
(lines);
}
n(sb);
();*/
} catch (IOException e) {
throw new RuntimeException("网络超时,请重试:" + ng());
}
}
public static String convertStreamToString(InputStream is) {
StringBuilder sb1 = new StringBuilder();
byte[] bytes = new byte[4096];
int size = 0;
try {
while ((size = (bytes)) > 0) {
String str = new String(bytes, 0, size, "UTF-8");
(str);
}
} catch (IOException e) {
tackTrace();
} finally {
try {
();
} catch (IOException e) {
tackTrace();
}
}
return ng();
}
private static byte[] byteMergerAll(byte[]... values) {
int length_byte = 0;
for (byte[] value : values) {
length_byte += ;
}
byte[] all_byte = new byte[length_byte];
int countLength = 0;
for (byte[] b : values) {
opy(b, 0, all_byte, countLength, );
countLength += ;
}
return all_byte;
}
}本来想着提高效率通过list的并行流去http请求,但是没想到会报429,于是又改回了正常的循环。
最后,由于我这边是根据4个资产生成一个完成的zpl指令,然后得到一个pdf,实际场景中可能会选择许多资产,这样就会生成许多PDF,
存在一个pdf合并问题。
最后导出来的pdf非常完美,打印的效果也非常棒。
2023年12月11日发(作者:太叔雁芙)
java标签打印-斑马打印机
文章目录
java实现标签打印-斑马打印机
背景
之前项目中有一个功能-批量打印资产标签,用户在设备资产界面勾选资产,选择打印样式,然后打印到标签纸上。花了两个星期后终于把
这个功能上线了,头皮屑都掉了不知道多少
初步设计
这里就说一下初步设计的思路。因需求可以总结为两种情况,一种是打印资产信息加条形码(资产编码生成),另外一种是资产信息加二维
码(资产编码生成),一开始的设计简单可以分为三张表:模板、字段以及模板和字段的关联关系表。前端利用html按照业务提供的标签纸
参数大小,把两种情况的样子画好定为样式一、样式二,类似如图然后新建模板,选择样式保存,然后在模板里面添加字段,字段详情里面可以设置数据来源字段或者来源sql,最后可以点击图片中的名
称、编号等位置弹出弹框,在弹框中绑定字段。后端处理时利用图片中位置所绑定的字段设置的数据来源字段或者来源sql去得到实际的数
据替换html中对应位置的数据,二维码或者条形码是用实际数据(实际场景都是资产编码这种)的base64编码去替换html中的img的
src。
这样就得到实际的资产信息标签的html了
然后把转换后的html生成pdf导出
后面去了客户现场,才看到了标签打印机,之前一直都是云开发。
然后打印机通过usb连上笔记本,下载驱动,点开之前生成过的pdf,右击打印
…打印出来的完全对不上,整个标签出来了半个外边的框框,其他东西都没有了,而且还横跨了两张标签纸。
然后开始调试,调试代码中纸张长宽,打印区域长宽,打印方向,打印机首选项中大小边距方向等。调了一天也没调出来,调到最后打印队
列里面显示已打印,但是实际上啥也没有了。
这时我是猜测pdf是太大了,超过范围就打不出东西,于是我拿标签纸和模板里的样式对比了一下,真的大了好多,于是我把实际的长宽给
了前端,前端反馈按照这个长宽样式画不出来,内容塞不下。。。后面我在代码里面尝试设置缩放也没弄出来
(生成的pdf大小都有一百多K,后面用第二种方式生成的pdf最多只有十几K)
着急的我第二天晚上在网上看到了斑马打印机的指令语言zpl,于是去看了zpl指令文档和一些类似的用zpl打印标签的博客
后一天运行写好的demo,利用驱动打印稍微调一调,就出来了像点样子的标签了。
这时候,我的思路就是使用zpl指令,把两种样式都定义好(后面用户调成了四种),实际场景中直接用资产信息去替换指令中对应的位置
就好了。
这时候我又找到了另一个神器–ZebraDesigner 2
用这个直接画好后可以直接打印,也可以生成zpl指令文档,完美,接下来就是在服务中完成代码了。
部分需要的依赖
zpl指令驱动打印
打印类:
package ;
import .*;
import rName;
import .*;
import ortedEncodingException;
/**
* 斑马打印机打印类-ZPL语音
*/
public class ZplPrinter {
private PrintService printService = null;//打印机服务
private String content = "";
/*private String begin_1 = "u0010CT~~CD,~CC^~CT~n" +
"^XA~TA000~JSN^LT0^MNW^MTT^PON^PMN^LH0,0^JMA^PR3,3~SD28^JUS^LRN^CI0^XZn" +
"^XAn" +
"^MMTn" +
"^PW160n" +
"^LL0280n" +
"^LS0n"; // 开始(个)*/
private static final String begin = "u0010CT~~CD,~CC^~CT~" +
"^XA~TA000~JSN^LT0^MNW^MTT^PON^PMN^LH0,0^JMA^PR3,3~SD28^JUS^LRN^CI0^XZ" +
"^XA" +
"^MMT" +
"^PW695" +
"^LL0280" +
"^LS0"; //开始(排) "^LS0"; //开始(排)// private String logo_1 = "^FO64,0^GFA,01152,01152,00012,:Z64:n" +// "eJztUjtOAzEQHcdYKRKxKbBERSiRaCgRi2D3Bou07nOMVMiIi6zEJaKsxHKUHINqzfy8QEWZBsu2np6eZ97MGOB//bVMaCZs+/2EQwhVximlwyQPoc3yYRjeFXvkg+L1CEWKGqb9zlB2cjgMBvfCmxGv+afyAM5JArsDWLyII4fSUAtfoJcUnyXtU8AnkngR+7c9lMLXZNNzBWsg+wUb8obKcsw/YAER5sxj5Im/RzcHsB/KmwYM87eId2A75fEIv+VYhjtEFXl6InrK2gnfYnThtzCCjSB69GMmfd+jWPXUzhy/SCnraQDV5OdxiCb79z/4C7TK/BnZ0TjnAHdAmYXH7bhXp6y30jfews+oLLqp/4amfiJziQkntpR5YZ8bCkUrlvh/bgRX9H907hscQNwIXhkf6kowGrp+FUgpnVNMVpaKqTUrxXDJG9SpqzKexasMf/3nY68vBLVZLg==:36A9n"; private static final String logo_1 = "^FO192,170^GFA,01536,01536,00012,:Z64:" + "eJztUjFOw0AQnLMTCoQciliiQrzANQWKzwV9Cm+fF/ACinsKT/FTTlSI4pTSBbJZ7+5Z+QEUWfmi0WhvbjK7wLX+uLbkV3yLjwzdEX3GhXxaG76RLzzwORk+HvkYPn0DdybTUe83ikvMcyjtVUdETvENmpQQBO/ZD3U+y7dziILvgZq8PvAEHOQX8mQPc8pWXoDnzO/l42KFxgw5L83KD9JfKe+I+dr4abrgOZ+t2A/sf8TOdNi/8kVAkVKQfo7G84XcH9GGnfKcZ732jzB97l+8a/+w/K1Vf/EifoqIalj9SPrCu4jyDDzatBxl/0xOUbSUJ6/5jCh5AO/C9xwndcq/oflM4UswgTy9Kt9iDihHwXXH8lvNvxm4t9K51MvcbYN25+XSoPNlCWd8kfQIz1xNisFptj+GOU7KC3eYpikaZpvrgrJ9dqS17A9yNSmu+HKfr/XP6xchOF9e:F887" + "^FO512,170^GFA,02048,02048,00016,:Z64:" + "eJztk0FOAzEMRX8aIhCtKIuOxBFYskQqEsMNBmm85xgsI3XJJVhyhFFn06PMEbqsVDTBCTR2uuIAY7WVvl5tf8cJMMUUqKjU9zOvpatNo/VV+kjccgWt3wCjCpiXGqZWevMFPKjyjl5jjVMsbBixEL1yRO2F6Dv0fTcT3bB/ckX7MFoxwK2JlAEmh/T7Z4fdNZAJo/U9MCjOLZB5rNypAbizKThS/qNwRwxb4fZ4OON8/quTvoENYcCTcMPzC78G5v1W8isY/kOR7zFKfsX7Q5Fv42w6P3rPfJmOR/dP3rP/uccahf+0/cwvPZY74D3zGq7V8+/4AHzqkTnvR85vwDKMZp95w+sjJ/vp0H9s7WfWrSFDldzQbzvyFEPWFNtXddZ9zF174fF+qhfwzO4RRMfSTvF59/vNnBm1oi1vL+xF83aI9AM6cnilI1eSx+cHoCLef635/vtCn7/fKab4V/wA4xhbmA==:B3EC" + "^FO352,170^GFA,01152,01152,00012,:Z64:" + "eJztUjFOxDAQHAeOO0WUFBQcUJ5S8QAKHsL9gZICiEnJK1JG+wpT0FNQXqR7wpVXEIXxrh3dA6DDsqPRaLw7GS/wv/50leInfObuMixadBkf67a14I184YYnJNy2PAm/fwHnqUwjnV8YPnHjiJPUtRCRwvAp1v3GGV7RjzQ+l69HZw2ugUq8NaDHJ/1CW3ZITql8BB4yv9INxAr3yVDhVWy8V/2F8YWQr4x3w/cBz3zKiOeg/x0uUx36N37G3femZzSeF7I+oDZ96ZlnlfVuB0z66N30Pv7WVD96UT+zgKWf/Gj6yh8FzLfArfItCsn+t3BD0FrGi9d82HXOB9gr3zFOaSy3DdYfvftULBAvb8a/uJGudoqrhuVLy3/9Su3S3qWK754m6IpuUBsuWaJI/GxjR3lylRh2TLPeG45xSh6452EYQsK0OQ0o7Y8Jxt+SjDk/YcKH8/wL6wd4H3r/:A9FE" + "^FO0,170^GFA,01536,01536,00012,:Z64:" + "eJztUjFOw0AQnDMkjixKCgoClJErHkDBQ8gfKCkAHy55hUtrX2EKegrKWMoTUqbAMnO7d1Z+gJCyurNGo7nd8dwBx/oXVYif8Lm7TzBr0CZ8qstqwRPpwC13F3HTcEf88Q1cxDa1tH5heO7GEfM4NRORzPAZ1v3GGV7Rj9Q+ta9GZwNugFK8DaDHZ/1CR7aITql8Ah4Tv9IFhA4P0VDmVWy8V/2l8ZmQL413w88Bz3yKgHPQ/w5XsQ/9Gz/j6nvTMxrPA0nfoTJ94ZlnmfRuB0z64N30PvzW1D94UT+zDks/+dH0lT/pkG+BO+UbZJL8b+GGTnsZL17z4dScF7BXvmWcUltuG6w/e/elWCBe3o1/dSNd7RSXNdsXlv/6jdql3UsZ7j2+oGu6QWW4YIss8rONbeXJlWLYMc1qbzjEKenBvQzD0EVMm9MDpf0xwvBbkjDfTzfhw/d8rD+rX2f5ev8=:D9E6"; private static final String logo_2 = "^FO512,192^GFA,01152,01152,00012,:Z64:" + "eJztkUFKBDEQRX86E2agRUeYgOBGT6BLwU164T4DU3uP4BEis/Aa4zlmM0fppUcYbOg2oauq+waCWKvHS6iq/AB/rFZJ0dBOuYJVXsAEvQ7cCV9HrIXv+3I0VkO7hfB7911Je3hyzBZ22DO7PLiZxiak2VgefFH8aeTNzK+Kb0dem21AHPnGDAmv4onU43hMj+zzPtP9U63eBDP53Fx9ltrnCXiTfTYzXwMH8T4a9pdYfsmeHo4k0BrP3Z7f5fKeL+yXqa8+xDcRnr39POCW8ynP9ZwbzkAvTDD6kV2yZ/VbH4WvhqEVdkRB2OZ8hEtuWg/txC7gv36nfgDK2jZc:047B" + "^FO348,192^GFA,00768,00768,00008,:Z64:" + "eJzdkTFuwzAMRb8sCw6KwM4QrU2QOSg6dquHevcg7j1BzuCpvUZvkhzFYydn9ZT0k/IJOhXV8vAEipS+gL+zDhlBeuMKa+MGwbgHBmXXolV+zroFx/oNWVTX7xVZQtKWXKO5aYMtG5bWrkaxtHPkM92T/eJ7dQ5oQwfHAe/VDc5cknkxTbjQsXiFJ/OAMjuLzdt8HiPcRfv3ix9RDOqiJXjB7kvnJcSkDzziOjV63yASyUc/P+zIyGIh648BJ42n0zOAH+FnfX9C0MD8hGZUlzexfF7vd8srilh+Ne+f80056POQGX/xV/9l/QBSVjIV:871E" + "^FO160,192^GFA,01152,01152,00012,:Z64:" + "eJztkDFOw0AQRf/aCUQEYUfyNgjhlClTIkCKj7DFzl0oENqSY3CElCulSI7iY6RKGGBm7BNQIH719GzPjD/wl/M+oKfOeIm1cYfGOKFQdB5OuWh7fvSTCc/RQbN2/7EUrh2FILzG5qgLAnysbe0DZqO1E+ED+2tZO/aJ/Y14+AZT4dQecWGegvn5Lpfq3cjfYfANavP3gHmPYX6ZcKn38EXmscWVefr65Ds93pLeGUBBC92W+/ysPXiKUfil6levwpF7Vv94C5eF+Xed9lklVL3wlAvt1GdsknpaEDTn88mYKBo/7bIx92ZcDK/zRfjP7+UT49su6w==:8176" + "^FO0,192^GFA,00768,00768,00008,:Z64:" + "eJzVkbFuwjAQhn8npCCoSCrFC0IkIyNj1VZKHsGD7106IOSRx+ARGC0xwKPkMZhC7y7hCbq0Xj59lu/s+w38vXUcYKlV1tgpW5TKgERgLIwwqTreAiZ8Xgpm1fVUMwtDzjF3aO7SwMH6Qtu9Yza2mzBv7K/c7umBfckOWyJjhuqOF3Vy6otLTMXN6GsMXqJQ3wDqFkN9GjCV/nyDOs6Yq5McQYdDkPscyMmA5/Qav+S9lrxnfufdds/0PK/4xwomSjyl1AB5QN4xMx5Q5s8jGskjozfSvB6PXknklZ+XOObrlEk/5u5//3X/bv0AQ3wu6w==:13E8"; private static final String end = "^PQ1,0,1,Y^XZ"; private static final String BAR = "^BY2,3,37^FT${h},258^BCB,,Y,N" + "^FD>;${code}^FS"; private static final String QR = "^FT${h1},100^BQN,2,4" + "^FH^FDLA,${code}^FS" + "^FT${h2},102^A0B,17,16^FH^FD${code}^FS"; private static final String NUM = "${code}"; private static final String H = "${h}"; private static final String H1 = "${h1}"; private static final String H2 = "${h2}"; public static void main(String[] args) {// ZplPrinter p = new ZplPrinter("192.168.0.12ZDesigner 105SLPlus-300dpi ZPL"); ZplPrinter p = new ZplPrinter("ZDesigner ZT210-200dpi ZPL"); //1.打印单个条码
//1.打印单个条码
String bar0 = "7150000036";//条码内容
// String bar0Zpl = "^FO110,110^BY6,3.0,280^BCN,,Y,N,N^FD${data}^FS";//条码样式模板
// rB("name", 20, 60, 30, 30);
// t("型号", 40, 60, 30, 30, 5, 1, 1, 24);
in();
t2Img("中中中中中中中中中1", 20, 0, 22, );
t2Img("中中中中中中中中中1", 48, 0, 22, );
t2Img("中中中中中中中中中1", 76, 0, 22, );
t2Img("中中中中中中中中中2", 194, 0, 22, );
t2Img("中中中中中中中中中2", 222, 0, 22, );
t2Img("中中中中中中中中中2", 250, 0, 22, );
t2Img("中中中中中中中中中3", 368, 0, 22, );
t2Img("中中中中中中中中中3", 396, 0, 22, );
t2Img("中中中中中中中中中3", 424, 0, 22, );
t2Img("中中中中中中中中中4", 542, 0, 22, );
t2Img("中中中中中中中中中4", 570, 0, 22, );
t2Img("中中中中中中中中中4", 598, 0, 22, );
o(1);
();
// nese("型号",50,60,30,30);
// String bar0Zpl = "^FO102,0^BY2,3.0,40^BCB,,Y,N,N^FD${data}^FS";//条码样式模板
// code(bar0, bar0Zpl);
String zpl = ();
n(zpl);
boolean result1 = (zpl, 1);//打印
if (result1) {
return;
}
pl();//注意要清除上次的打印信息
}
/**
* add LOGO
*
* @param i 样式
*/
public void setLogo(int i) {
if (i == 1) {
content = content + logo_1;
} else if (i == 2){
content = content + logo_2;
}
}
/**
* setEnd
*/
public void setEnd() {
content = content + end;
}
/**
* setBegin
*/
public void setBegin() {
content = content + begin;
}
public void setText2Img(String str, int x, int y, int size, int style) {
content += tHex(bSubstring(str, 17), x, y, size, style);
}
/**
* 构造方法
* 构造方法
*
* @param printerURI 打印机路径
*/
public ZplPrinter(String printerURI) {
if (printerURI != null) {
//打印机完整路径
//初始化打印机
PrintService[] services = PrintServices(null, null);
if (services != null && > 0) {
for (PrintService service : services) {
if ((e())) {
printService = service;
break;
}
}
}
if (printService == null) {
//循环出所有的打印机
if (services != null && > 0) {
n("可用的打印机列表:");
for (PrintService service : services) {
n("[" + e() + "]");
}
}
throw new RuntimeException("没有找到打印机:[" + printerURI + "]");
} else {
n("找到打印机:[" + printerURI + "]");
n("打印机名称:[" + ribute().getValue() + "]");
}
}
}
/**
* 获取完整的ZPL
*
* @return
*/
public String getZpl() {
return content;
}
/**
* 重置ZPL指令,当需要打印多张纸的时候需要调用。
*/
public void resetZpl() {
content = "";
}
/**
* 打印
*
* @param zpl 完整的ZPL
* @param quantity
*/
public boolean print(String zpl, Integer quantity) {
if (printService == null) {
return false;
}
byte[] by = es();
// 直连打印
DocPrintJob job = PrintJob();
DocFlavor flavor = _NSE;
Doc doc = new SimpleDoc(by, flavor, null);
try {
(doc, null);
(doc, null);
n("已打印n" + zpl);
return true;
} catch (PrintException e) {
tackTrace();
return false;
}
// 复杂打印
/*PrinterJob job = nterJob();
PDDocument document = null;
try {
document = (by);
ntService(printService);
// 设置纸张属性
Paper paper = new Paper();
double paperHigh = 250.56;
double paperWidth = 100.8;
Double marginLeft = 0.0;
Double marginRight = 0.0;
Double marginTop = 0.0;
Double marginBottom = 0.0;
// 设置纸张宽高 以1/72英寸为单位 纸张宽250mm 则paperWidth为 25cm/2.54*72
e(paperWidth, paperHigh);
// 设置可成像区域
geableArea(marginLeft, marginTop, paperWidth - (marginLeft + marginRight), paperHigh - (marginTop + marginBottom));
// 设置纸张及缩放
PDFPrintable pdfPrintable = new PDFPrintable(document, _SIZE);
// 设置多页打印
Book book = new Book();
PageFormat pageFormat = new PageFormat();
// 设置打印方向 横向
entation(APE);
// 设置纸张
er(paper);
(pdfPrintable, pageFormat, berOfPages());
eable(book);
// 设置打印份数
ies(quantity);
// 添加打印属性
HashPrintRequestAttributeSet pars = new HashPrintRequestAttributeSet();
// 设置单双页
();
(pars);
return true;
} catch (Exception e) {
return false;
} finally {
if (document != null) {
try {
();
} catch (Exception e) {
tackTrace();
}
}
}*/
}
/**
* 条形码
*
* @param assetNum
* @param h
*/
public void setBar(String assetNum, int h) {
if (() == 13) {
if (() == 13) {
assetNum = e(f((12)), ">6" + (12));
}
content = content + e(NUM, assetNum).replace(H, f(h));
}
/**
* 按字节截取
*
* @param s
* @param length
* @return
* @throws Exception
*/
public static String bSubstring(String s, int length) {
if (s == null) {
return "";
}
try {
byte[] bytes = es("Unicode");
int n = 0; // 表示当前的字节数
int i = 2; // 要截取的字节数,从第3个字节开始
for (; i < && n < length; i++) {
// 奇数位置,如3、5、7等,为UCS2编码中两个字节的第二个字节
if (i % 2 == 1) {
n++; // 在UCS2第二个字节时n加1
} else {
// 当UCS2编码的第一个字节不等于0时,该UCS2字符为汉字,一个汉字算两个字节
if (bytes[i] != 0) {
n++;
}
}
}
// 如果i为奇数时,处理成偶数
if (i % 2 == 1) {
// 该UCS2字符是汉字时,去掉这个截一半的汉字
if (bytes[i - 1] != 0) {
i = i - 1;
}
// 该UCS2字符是字母或数字,则保留该字符
else {
i = i + 1;
}
}
String newStr = new String(bytes, 0, i, "Unicode");
StringBuilder newS = new StringBuilder(newStr);
if (getWordCount(newStr) < 17) {
for (int j = 0; j < 17 - getWordCount(newStr); j++) {
(" ");
}
}
return ng();
} catch (UnsupportedEncodingException e) {
tackTrace();
}
return "";
}
/**
* 二维码
*
* @param assetNum
* @param x5
* @param x6
*/
public void setQr(String assetNum, int x5, int x6) {
public void setQr(String assetNum, int x5, int x6) {
content = content + e(NUM, assetNum).replace(H1, f(x5)).replace(H2, f(x6)).replace(NUM, assetNum);
}
/**
* 获取字符串的字节长度
*
* @param s
* @return
*/
public static int getWordCount(String s) {
int length = 0;
for (int i = 0; i < (); i++) {
int ascii = intAt(s, i);
if (ascii >= 0 && ascii <= 255) {
length++;
} else {
length += 2;
}
}
return length;
}
}
汉字转zpl图片类:
package ;
import athResource;
import ce;
import .*;
import gle2D;
import edImage;
import fferInt;
import .*;
/**
* 汉字转zpl图片
*/
public class Font2ZplGF {
private static BufferedImage source = new BufferedImage(10, 10, _INT_ARGB);
private static Graphics2D gs = Graphics();
private static Font f;
static {
Resource resource = new ClassPathResource("font/");
try {
f = Font(PE_FONT, utStream());
} catch (FontFormatException | IOException e) {
throw new RuntimeException(e);
}
}
public static String getFontHex(String content, int x, int y, int size, int style) {
if (content == null || "".equals(content)) {
return "";
}
if (f == null) {
f = getSelfDefinedFont("font/", style, size);
} else {
f = Font(style, size);
}
// f = new Font("simhei", , size);
t(f);
FontMetrics fontMetrics = tMetrics();
Rectangle2D stringBounds = ingBounds(content, gs);
int width = (int) th();
width = (width + 7) / 8 * 8;
int height = width;
BufferedImage image = new BufferedImage(width, height, _INT_ARGB);
// image = modifyImageRatio(image,270);
Graphics2D g2 = Graphics();
t(f);
or();
// 图片旋转
(ans(270), width / 2, height / 2);
ring(content, 1, (int) (size * 0.88));
e();
StringBuilder zpl = new StringBuilder("^FO").append(x).append(",").append(y)
.append(getImage(image)).append("^FS");
return ng();
}
/**
* 自定义字体
* @param filepath 字体文件路径
* @param style 样式
* @param size 大小
* @return
*/
private static Font getSelfDefinedFont(String filepath, int style, float size){
Font font = null;
try{
Resource resource = new ClassPathResource(filepath);
font = Font(PE_FONT, utStream());
font = Font(style, size);
} catch (FontFormatException | IOException e){
throw new RuntimeException(e);
}
return font;
}
private static final char[] HEX = "ABCDEF".toCharArray();
private static String getImage(BufferedImage i) {
int w = th();
int h = ght();
boolean black[] = getBlackPixels(i, w, h);
int hex[] = getHexValues(black);
String data = ints2Hex(hex);
int bytes = () / 2;
int perRow = bytes / h;
return "^GFA," + bytes + "," + bytes + "," + perRow + "," + data;
}
/**
* Returns an array of ones or zeros. boolean is used instead of int for memory considerations.
*
* @param bi
* @param w
* @param h
* @return
*/
private static boolean[] getBlackPixels(BufferedImage bi, int w, int h) {
int[] rgbPixels = ((DataBufferInt) ter().getDataBuffer()).getData();
int i = 0;
boolean[] pixels = new boolean[];
for (int rgbpixel : rgbPixels) {
pixels[i++] = isBlack(rgbpixel);
}
return pixels;
}
private static boolean isBlack(int rgbPixel) {
int a = (rgbPixel & 0xFF000000) >>> 24;
if (a < 127) {
return false; // assume pixels that are less opaque than the luma threshold should be considered to be white
}
int r = (rgbPixel & 0xFF0000) >>> 16;
int g = (rgbPixel & 0xFF00) >>> 8;
int b = rgbPixel & 0xFF;
int luma = ((r * 299) + (g * 587) + (b * 114)) / 1000; //luma formula
return luma < 127;
}
private static int[] getHexValues(boolean[] black) {
int[] hex = new int[(int) ( / 8)];
// Convert every eight zero's to a full byte, in decimal
for (int i = 0; i < ; i++) {
for (int k = 0; k < 8; k++) {
hex[i] += (black[8 * i + k] ? 1 : 0) << 7 - k;
}
}
return hex;
}
private static String ints2Hex(int[] ints) {
char[] hexChars = new char[ * 2];
for (int i = 0; i < ; ++i) {
hexChars[i * 2] = HEX[(ints[i] & 0xF0) >> 4];
hexChars[i * 2 + 1] = HEX[ints[i] & 0x0F];
}
return new String(hexChars);
}
}
这中间主要的问题就是汉字问题
因为打印机中没有你设置的字体,所以打印出来的标签汉字不会显示,这里也看了网上蛮多方法都没有用,最后只好采取了汉字转图片打印
图片的方式
zpl指令转pdf打印
本地驱动打印很ok,但是服务器不支持这种方式,新的问题来了。由于这种斑马打印机不能联网,根据实际需求也不会给打印机装上很贵的
网卡。
所以只有一条路了,回到当初,导出pdf,客户自己本机打印pdf
zpl指令怎么转成pdf呢?
这里提供了接口供zpl指令转成pdf,只需通过http调用就好了,经测试ok
package ;
import ject;
import tputStream;
import ption;
import tream;
import LConnection;
import ;
import ist;
import ;
/**
* zpl指令转pdf
*/
public class Zpl2Pdf {
/*public static void main(String[] args) {
String zpl = "CT~~CD,~CC^~CT~^XA~TA000~JSN^LT0^MNW^MTT^PON^PMN^LH0,0^JMA^PR3,3~SD28^JUS^LRN^CI0^XZ^XA^MMT^PW695^LL0
280^LS0^FO352,160^GFA,01152,01152,00012,:Z64:eJztj7FtwzAQRT9pGRJYqUjgxgZdCq5TptAIatJnBBWC1RggRxFcETdFVsgmLl0IUOTk7qQNUiS/enw
k/5HAf34nxwW3b/VKLxs1MuUIK7g5lCbKwiUIZz7UUlRYGqSoNOHeav0piW/xUj6pr1Do2Ffsdax68/DZyvODTPQjcvGO1GPXN+pBSc+Xu1F9AfXwWPVAez7
W3kY4eY95XzwG9TeExrMfQIP41oRO/nW011QxX/xnc2GuXERiPj/Pd5hP310/8RH5jdnNYyNz3iHUzJZA4s00TZAQkXLfd8pWjz8u4K/kCwjBNFw=:F8AD^FO
544,160^GFA,00768,00768,00008,:Z64:eJztkbFtwzAQRT/p0AoEB1CTLkBUGtwhAEdw4y6BV3Dh0gY4CkvhptAIaoJUKryBAri1Zd6XMoHbHAh8PH7e8XgE/
mOOOMnr9g+5YYBK1QLPqqtDWNL3yapUp6tRrb1ETQhuPLdMl5TZtPjuAjmhZrkBe5YjO+Vq5nzBEocBb8qe/ISvPpBLifTdOJBrm+jvzeSv2WCBI6b8Errl8DK
zjcpFWFz0SCah35prq/3EUlJS3v38Drnf5KOy6T7huszNWnOA4waFvs/npfwRcdL3lykXzPF+KW6cl0jD+fV9x3mJJOpuDFTbTHM2m4d/7oG4AwAhQRY=:C6
CC^FO192,160^GFA,00768,00768,00008,:Z64:eJzlkTFqAzEQRf8oGyuocrFkUxjSBlcpU65v4Ca4tI/ggGFL6ygqlzmFr2AI5BoOBFyFzfwdN2kDqfwZ+DwN+o
xGwNUpXPx1eUE/qCAt/Q7I9LcjpmNfeQQszu81PauSBc0w8evKgAniKjpnBlS4yXJgnHON+Isf5MM56MhT6XawAZ6gigK8YDXIlpwT+VkOtyAHA6uZnGBl/X7k
GjvIhvnJ+3hcMj8h9My/Rzw7z9UmQCPDOhqHrCEZx8UWDecvhQmQzxZ7vrcfC/LFMs0ZaNof1yd6Ui302HWt70vHfcrw7XtOxb1q//Jb/6wfQRs9bQ==:060F^
FO0,192^GFA,01152,01152,00012,:Z64:eJztjzFOAzEQRb83axFppTXFbkMRpYxygihIsEdwYV8HuUQUHCFyiahSRpti9yiUOQJCaIMRM+McASSmen4z+jM
G/mhdZyxe44XOjSUK4Q6KUT0/YuSHdqlFMWaIS+JSe8tBcwyfG4lvHfsNbvq5+AalrF2glrXZx+SLC88HjfUOs0A+3cM+VMcX8cpbmccwiS/hZL7GKudAct5My
Pmqg+Z7VMweVvwJD8GQt2g79gfcH/hfVnnXsK8+pjviRtuG/bYK6Ilb/Z31U+YJeCfW6Rz2V3uYSKxcWkw8m8J5JIb3nhG3x1649U64Op+EkceBNf7rl9UXFGw
0lw==:0DFA^FT412,135^BQN,2,4^FH^FDLA,7150000036^FS^FT574,127^BQN,2,4^FH^FDLA,7150000036^FS^FT522,124^A0B,14,14^FH^FD12345
67891123^FS^FT234,136^BQN,2,4^FH^FDLA,7150000036^FS^FT685,117^A0B,14,14^FH^FD23^FS^FT54,133^BQN,2,4^FH^FDLA,71
50000036^FS^FT350,128^A0B,14,14^FH^FD23^FS^FT173,126^A0B,14,14^FH^FD23^FS^PQ1,0,1,Y^XZ";
String url = "/v1/printers/8dpmm/labels/3.42x1.37/0/";
for (int i = 0; i < 2; i++) {
getPdf(url,zpl);
url = "/v1/printers/8dpmm/labels/3.42x1.37/1/";
}
}*/
/**
* zpl指令转List
* @param url
* @param zpls
* @return
*/
public static List
return bitchSend(url,zpls);
}
private static List
List
h(zpl -> (send(url0,zpl)));
return inputStreamList;
}
/**
* zpl指令转InputStream
* @param url
* @param zpl
* @param zpl
* @return
*/
public static InputStream getPdf(String url, String zpl) {
return send(url,zpl);
/*try {
// 获取http客户端
CloseableHttpClient client = Default();
// get method
HttpPost httpPost = new HttpPost(url);
// URIBuilder uriBuilder = new URIBuilder(url);
// 第一种添加参数的形式
// ameter("name", "root");
// ameter("password", "123456");
// 第二种添加参数的形式
List
BasicNameValuePair param1 = new BasicNameValuePair("mode", "raw");
BasicNameValuePair param2 = new BasicNameValuePair("raw", zpl);
(param1);
(param2);
ity(new UrlEncodedFormEntity(list, "UTF-8"));
// ameters(list);
// 通过httpget方式来实现我们的get请求
// HttpPost httpPost = new HttpPost(());
// 传输的类型
der("Content-Type", "application/x-www-form-urlencoded");
// der("Content-Length", f(()));
der("Accept", "application/pdf");
// 通过client调用execute方法,得到我们的执行结果就是一个response,所有的数据都封装在response里面了
CloseableHttpResponse response = e(httpPost);
// HttpEntity
// 是一个中间的桥梁,在httpClient里面,是连接我们的请求与响应的一个中间桥梁,所有的请求参数都是通过HttpEntity携带过去的
// 所有的响应的数据,也全部都是封装在HttpEntity里面
if (tusLine().getStatusCode() == 200) {
HttpEntity entity = ity();
return tent();
} else {
throw new RuntimeException(tusLine().toString());
}
} catch (IOException ex) {
tackTrace();
throw new CommonException(ex);
}*/
}
private static InputStream send(String url0, String param) {
URL url;
HttpURLConnection connection;
try {
//创建连接
url = new URL(url0);
connection = (HttpURLConnection) nnection();
utput(true);
nput(true);
uestMethod("POST");
Caches(false);
tanceFollowRedirects(true);
uestProperty("Content-Type","application/x-www-form-urlencoded");
uestProperty("Connection", "Keep-Alive");
uestProperty("Accept","application/pdf");
nectTimeout(30000);
nectTimeout(30000);
dTimeout(30000);
//POST请求
DataOutputStream out = new DataOutputStream(putStream());
JSONObject body = new JSONObject();
("mode", "raw");
("raw", param);
(ng().getBytes("UTF-8"));
();
();
return utStream();
//读取响应
/*BufferedReader reader = new BufferedReader(new InputStreamReader(utStream()));
String lines;
StringBuffer sb = new StringBuffer("");
while ((lines = ne()) != null) {
lines = new String(es(), "utf-8");
(lines);
}
n(sb);
();*/
} catch (IOException e) {
throw new RuntimeException("网络超时,请重试:" + ng());
}
}
public static String convertStreamToString(InputStream is) {
StringBuilder sb1 = new StringBuilder();
byte[] bytes = new byte[4096];
int size = 0;
try {
while ((size = (bytes)) > 0) {
String str = new String(bytes, 0, size, "UTF-8");
(str);
}
} catch (IOException e) {
tackTrace();
} finally {
try {
();
} catch (IOException e) {
tackTrace();
}
}
return ng();
}
private static byte[] byteMergerAll(byte[]... values) {
int length_byte = 0;
for (byte[] value : values) {
length_byte += ;
}
byte[] all_byte = new byte[length_byte];
int countLength = 0;
for (byte[] b : values) {
opy(b, 0, all_byte, countLength, );
countLength += ;
}
return all_byte;
}
}本来想着提高效率通过list的并行流去http请求,但是没想到会报429,于是又改回了正常的循环。
最后,由于我这边是根据4个资产生成一个完成的zpl指令,然后得到一个pdf,实际场景中可能会选择许多资产,这样就会生成许多PDF,
存在一个pdf合并问题。
最后导出来的pdf非常完美,打印的效果也非常棒。