关键词:
小票打印就是向打印设备发送控制打印格式的指令集,而这些打印格式需要去查询对应打印机的API文档,这里我把常用的api给封装了一下
- 文字对齐方式
- 打印字体大小
- 字体是否加粗
- 打印二维码
- 打印条形码
- 切纸
- 打开钱箱
- 字符串转字节数组
- 字符拼接
PrintFormatUtils.java
/**
* 打印格式
* Created by john on 17-3-23.
*/
public class PrintFormatUtils {
// 对齐方式
public static final int ALIGN_LEFT = 0; // 靠左
public static final int ALIGN_CENTER = 1; // 居中
public static final int ALIGN_RIGHT = 2; // 靠右
//字体大小
public static final int FONT_NORMAL = 0; // 正常
public static final int FONT_MIDDLE = 1; // 中等
public static final int FONT_BIG = 2; // 大
//加粗模式
public static final int FONT_BOLD = 0; // 字体加粗
public static final int FONT_BOLD_CANCEL = 1; // 取消加粗
/**
* 打印二维码
* @param qrCode
* @return
*/
public static String getQrCodeCmd(String qrCode) {
byte[] data;
int store_len = qrCode.length() + 3;
byte store_pL = (byte) (store_len % 256);
byte store_pH = (byte) (store_len / 256);
// QR Code: Select the model
// Hex 1D 28 6B 04 00 31 41 n1(x32) n2(x00) - size of model
// set n1 [49 x31, model 1] [50 x32, model 2] [51 x33, micro qr code]
// https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=140
byte[] modelQR = {(byte)0x1d, (byte)0x28, (byte)0x6b, (byte)0x04, (byte)0x00, (byte)0x31, (byte)0x41, (byte)0x32, (byte)0x00};
// QR Code: Set the size of module
// Hex 1D 28 6B 03 00 31 43 n
// n depends on the printer
// https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=141
byte[] sizeQR = {(byte)0x1d, (byte)0x28, (byte)0x6b, (byte)0x03, (byte)0x00, (byte)0x31, (byte)0x43, (byte)0x08};
// Hex 1D 28 6B 03 00 31 45 n
// Set n for error correction [48 x30 -> 7%] [49 x31-> 15%] [50 x32 -> 25%] [51 x33 -> 30%]
// https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=142
byte[] errorQR = {(byte)0x1d, (byte)0x28, (byte)0x6b, (byte)0x03, (byte)0x00, (byte)0x31, (byte)0x45, (byte)0x31};
// QR Code: Store the data in the symbol storage area
// Hex 1D 28 6B pL pH 31 50 30 d1...dk
// https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=143
// 1D 28 6B pL pH cn(49->x31) fn(80->x50) m(48->x30) d1…dk
byte[] storeQR = {(byte)0x1d, (byte)0x28, (byte)0x6b, store_pL, store_pH, (byte)0x31, (byte)0x50, (byte)0x30};
// QR Code: Print the symbol data in the symbol storage area
// Hex 1D 28 6B 03 00 31 51 m
// https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=144
byte[] printQR = {(byte)0x1d, (byte)0x28, (byte)0x6b, (byte)0x03, (byte)0x00, (byte)0x31, (byte)0x51, (byte)0x30};
data = byteMerger(modelQR, sizeQR);
data = byteMerger(data, errorQR);
data = byteMerger(data, storeQR);
data = byteMerger(data, qrCode.getBytes());
data = byteMerger(data, printQR);
return new String(data);
}
/**
* 打印条码
* @param barcode
* @return
*/
public static String getBarcodeCmd(String barcode) {
// 打印 Code-128 条码时需要使用字符集前缀
// "{A" 表示大写字母
// "{B" 表示所有字母,数字,符号
// "{C" 表示数字,可以表示 00 - 99 的范围
byte[] data;
String btEncode;
if (barcode.length() < 18) {
// 字符长度小于15的时候直接输出字符串
btEncode = "{B" + barcode;
} else {
// 否则做一点优化
int startPos = 0;
btEncode = "{B";
for (int i = 0; i < barcode.length(); i++) {
char curChar = barcode.charAt(i);
if (curChar < 48 || curChar > 57 || i == (barcode.length() - 1)) {
// 如果是非数字或者是最后一个字符
if (i - startPos >= 10) {
if (startPos == 0) {
btEncode = "";
}
btEncode += "{C";
boolean isFirst = true;
int numCode = 0;
for (int j = startPos; j < i; j++) {
if (isFirst) { // 处理第一位
numCode = (barcode.charAt(j) - 48) * 10;
isFirst = false;
} else { // 处理第二位
numCode += (barcode.charAt(j) - 48);
btEncode += (char) numCode;
isFirst = true;
}
}
btEncode += "{B";
if (!isFirst) {
startPos = i - 1;
} else {
startPos = i;
}
}
for (int k = startPos; k <= i; k++) {
btEncode += barcode.charAt(k);
}
startPos = i + 1;
}
}
}
// 设置 HRI 的位置,02 表示下方
byte[] hriPosition = {(byte) 0x1d, (byte) 0x48, (byte) 0x02};
// 最后一个参数表示宽度 取值范围 1-6 如果条码超长则无法打印
byte[] width = {(byte) 0x1d, (byte) 0x77, (byte) 0x02};
byte[] height = {(byte) 0x1d, (byte) 0x68, (byte) 0xfe};
// 最后两个参数 73 : CODE 128 || 编码的长度
byte[] barcodeType = {(byte) 0x1d, (byte) 0x6b, (byte) 73, (byte) btEncode.length()};
byte[] print = {(byte) 10, (byte) 0};
data = PrintFormatUtils.byteMerger(hriPosition, width);
data = PrintFormatUtils.byteMerger(data, height);
data = PrintFormatUtils.byteMerger(data, barcodeType);
data = PrintFormatUtils.byteMerger(data, btEncode.getBytes());
data = PrintFormatUtils.byteMerger(data, print);
return new String(data);
}
/**
* 切纸
* @return
*/
public static String getCutPaperCmd() {
// 走纸并切纸,最后一个参数控制走纸的长度
byte[] data = {(byte) 0x1d, (byte) 0x56, (byte) 0x42, (byte) 0x15};
return new String(data);
}
/**
* 对齐方式
* @param alignMode
* @return
*/
public static String getAlignCmd(int alignMode) {
byte[] data = {(byte) 0x1b, (byte) 0x61, (byte) 0x0};
if (alignMode == ALIGN_LEFT) {
data[2] = (byte) 0x00;
} else if (alignMode == ALIGN_CENTER) {
data[2] = (byte) 0x01;
} else if (alignMode == ALIGN_RIGHT) {
data[2] = (byte) 0x02;
}
return new String(data);
}
/**
* 字体大小
* @param fontSize
* @return
*/
public static String getFontSizeCmd(int fontSize) {
byte[] data = {(byte) 0x1d, (byte) 0x21, (byte) 0x0};
if (fontSize == FONT_NORMAL) {
data[2] = (byte) 0x00;
} else if (fontSize == FONT_MIDDLE) {
data[2] = (byte) 0x01;
} else if (fontSize == FONT_BIG) {
data[2] = (byte) 0x11;
}
return new String(data);
}
/**
* 加粗模式
* @param fontBold
* @return
*/
public static String getFontBoldCmd(int fontBold) {
byte[] data = {(byte) 0x1b, (byte) 0x45, (byte) 0x0};
if (fontBold == FONT_BOLD) {
data[2] = (byte) 0x01;
} else if (fontBold == FONT_BOLD_CANCEL) {
data[2] = (byte) 0x00;
}
return new String(data);
}
/**
* 打开钱箱
* @return
*/
public static String getOpenDrawerCmd() {
byte[] data = new byte[4];
data[0] = 0x10;
data[1] = 0x14;
data[2] = 0x00;
data[3] = 0x00;
return new String(data);
}
/**
* 字符串转字节数组
* @param str
* @return
*/
public static byte[] stringToBytes(String str) {
byte[] data = null;
try {
byte[] strBytes = str.getBytes("utf-8");
data = (new String(strBytes, "utf-8")).getBytes("gbk");
} catch (UnsupportedEncodingException exception) {
exception.printStackTrace();
}
return data;
}
/**
* 字节数组合并
* @param bytesA
* @param bytesB
* @return
*/
public static byte[] byteMerger(byte[] bytesA, byte[] bytesB) {
byte[] bytes = new byte[bytesA.length + bytesB.length];
System.arraycopy(bytesA, 0, bytes, 0, bytesA.length);
System.arraycopy(bytesB, 0, bytes, bytesA.length, bytesB.length);
return bytes;
}
}
有了打印格式,还要对具体的打印小票设置打印模板,主要就是利用上面的打印格式工具类,进行字符或字符串拼接,设置文字间空格的长度,以及使用换行符换行等。
有些小票打印的内容有可能是通用的,比如底部结束语–可能是公司宣传语或广告语,这些内容是否展示需要根据具体需求加以控制,还有二维码、条形码打印,是否切纸等需要根据实际场景取舍,所以最好封装一个打印配置类,以控制打印内容显示。
/**
* 打印模板
*/
public class PrintContract {
/**
* 打印内容
*/
public static StringBuilder createXxTxt(String ...) {
StringBuilder builder = new StringBuilder();
//设置大号字体以及加粗
builder.append(PrintFormatUtils.getFontSizeCmd(PrintFormatUtils.FONT_BIG));
builder.append(PrintFormatUtils.getFontBoldCmd(PrintFormatUtils.FONT_BOLD));
// 标题
builder.append("Title");
//换行,调用次数根据换行数来控制
addLineSeparator(builder);
//设置普通字体大小、不加粗
builder.append(PrintFormatUtils.getFontSizeCmd(PrintFormatUtils.FONT_NORMAL));
builder.append(PrintFormatUtils.getFontBoldCmd(PrintFormatUtils.FONT_BOLD_CANCEL));
//内容
......
//设置某两列文字间空格数, x需要计算出来
addIdenticalStrToStringBuilder(builder, x, " ");
//切纸
builder.append(PrintFormatUtils.getCutPaperCmd());
return builder;
}
/**
* 向StringBuilder中添加指定数量的相同字符
*
* @param printCount 添加的字符数量
* @param identicalStr 添加的字符
*/
private static void addIdenticalStrToStringBuilder(StringBuilder builder, int printCount, String identicalStr) {
for (int i = 0; i < printCount; i++) {
builder.append(identicalStr);
}
}
/**
* 根据字符串截取前指定字节数,按照GBK编码进行截取
*
* @param str 原字符串
* @param len 截取的字节数
* @return 截取后的字符串
*/
private static String subStringByGBK(String str, int len) {
String result = null;
if (str != null) {
try {
byte[] a = str.getBytes("GBK");
if (a.length <= len) {
result = str;
} else if (len > 0) {
result = new String(a, 0, len, "GBK");
int length = result.length();
if (str.charAt(length - 1) != result.charAt(length - 1)) {
if (length < 2) {
result = null;
} else {
result = result.substring(0, length - 1);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
/**
* 添加换行符
*/
private static void addLineSeparator(StringBuilder builder) {
builder.append("
");
}
/**
* 在GBK编码下,获取其字符串占据的字符个数
*/
private static int getCharCountByGBKEncoding(String text) {
try {
return text.getBytes("GBK").length;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 打印相关配置
*/
public static class PrintConfig {
public int maxLength = 30;
public boolean printBarcode = false; // 打印条码
public boolean printQrCode = false; // 打印二维码
public boolean printEndText = true; // 打印结束语
public boolean needCutPaper = false; // 是否切纸
}
}
有了打印模板,接下来就是调用打印设备打印方法发送打印指令
//调用打印机打印方法,传入上面某个小票打印模板返回的字符串
String str = PrintContract.createXxTxt(...);
printer.print(str, null);
//打开钱箱方法
printer.print(PrintFormatUtils.getOpenDrawerCmd(), null);
浏览器打印58小票方法及限制
浏览器打印58小票浏览器端的打印是极其普遍的一个应用场景,如票据、OA管理、应用文档笔记等的打印。随着基于云端管理的web应用越来越广泛,对于浏览器的打印需求会越来越多。小票打印就是近年来的一个新的浏览器打印... 查看详情
crt100中心定位好但是电脑小票数值低
参考技术A要根据您的机型和软件配套设置。一般在管理软件里面---系统设置(或者前台设置)---外接设备设置(或打印机设置)---选择打印机---选择打印格式---保存设置。如果还是不行,可以给我留言 查看详情
关于一体机打印新加菜按钮更改为下单小票打印设置faq(适用正餐6.0.1.0+,轻餐4.0.6.2+)
适用版本:正餐6.0.1.0+,轻餐4.0.6.2+实际场景:更新后小票设置中的打印新加菜按钮更换为下单小票打印设置,更换后,设置中,有3个选项:1.仅打印新加菜 (选中后,订单加菜后前台小票只打印新点的菜品,之前订单... 查看详情
优库蓝牙小票打印机怎么弄
...术A先安装好纸并开机,然后在手机设置上开启蓝牙。1.将打印机装好打印纸,确保出纸方向是从上方出来,然后接通电源开机。2、查看打印机顶部亮灯是否一个在online,一个在receipt(票据)上。3、打开APP,找到基础---设置--打... 查看详情
小票打印机乱码问题
...-----------------------------editor:YeungChidate:2016/3/29describe:小票打印机乱码问题文章内容:一、参数解释二、串口连接打印机设置案例--------------------------------------串口是一种非常通用的设备通信的协议;一、参数解释波特率这是一个... 查看详情
小票打印机页面打印无法自适应长度导致超长空白打印
1、问题描述:58或80打印机,打印内容只有两三行内容,结果会按照设置的纸张大小(58的有三种2102973276),必定打出该纸张大小,头部出现页码,底部出现网站url地址2、原因:IE浏览器搞的鬼,默认有选择页眉页脚导致上... 查看详情
delphi10seattle小票打印控件tq_printer
...持Android、iOS以及WIN32/64的开发,可控制兼容ESC/POS的小票打印机。目前控件已在多个Android、iOS项目中配合蓝牙小 查看详情
java票据打印,类似于超市的小票那样的,怎么控制打印的纸张大小啊?
类似于超市的小票那样的,怎么控制打印的纸张大小啊?若答案满意,我送出我所有的分!谢谢大家了!最好给个C/S版的源码看看啊!楼上的人一看就是在混分。其实这种问题应该到更专业的地方问,比如CSDN或者JAVAEYE。还是把... 查看详情
web使用热敏打印小票(ie环境)
概述在html页下使用EpsonP60II热敏纸下打印小票,使用的打印方案为调用window.print()。代码实现1、定义窗体,设置宽度和高度<bodyonload="window.external.Print(0,0);"style="margin-top:0px;"><formid= 查看详情
模拟打印小票(支持暂停功能)
publicclassMainFrameextendsJFrameprivateJButtonprintButton=newJButton("打印");privateJButtonsuspendButton=newJButton("暂停");privateJLabelprintText=newJLabel("模拟小票打印");privateJTextAreaprintTextArea=newJT 查看详情
uniapp实现蓝牙小票打印功能
...的一个项目增加了小票蓝牙打印的功能,由于之前对蓝牙打印机了解不多,所以遇到的坑比较多,花了点时间把蓝牙连接、打印模块封装成通用组件,并写了个打印的例子,这里做个记录,以防忘记。组件:组件例子项目需要实... 查看详情
如何用lodop能过usb热敏打印机打开钱箱
热敏打印机驱动里设置打印时开钱箱,打印小票的时候会自动开启钱箱。参考技术A usb端口只能借用打印驱动,选择打印机之后, 用SEND_PRINT_RAWDATA语句发送相关指令。 参考技术B热敏打印机驱动里设置打印时开钱箱,打印... 查看详情
小票打印机维修方法
...先捏住弹簧一起放置上5.零部件可以任意拆卸组装,建议打印机做标记,把好的零部件安置到好的打印机上面6.打印机的螺丝全部都可以拆卸,背板底部的灰尘可以直接的清理7.发动机不要随便的拆卸工具:小型起子,通用起子 查看详情
完成打印购物小票并计算积分
importjava.util.Scanner;/***@author蓝色以太*完成打印购物小票并计算积分*/publicclassShoppingReceipt{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);finalintNUM1=245,NUM2=570,NUM3=320;System.out.print 查看详情
单据打印模板默认启用打印机本身设置尺寸设置方法
usePaperKind设置为true就会启用打印机本身配置参数。合川公司由原针式打印机更换为爱普生热敏打印机,更换后,单据打印不全,同一打印模板针式打印机可以打印全,爱普生标签打印机打印不全,经多次测试分析后,... 查看详情
更好的小票打印体验,huanent.printer2.0发布
huanent.printer2.0是一个专注消费小票打印的类库。拥有许多先进的特性例如居中打印、自动换行等特性,可以通过简洁的代码来打印出复杂的消费小票。huanent.printer通过MIT方式授权,可随意用于商业用途,只要注明作者即可。你可... 查看详情
dynamics365onlinepdf导出及打印
...引入了Word和Excel模板,对于项目中的一些格式化导出打印需求有了一定的帮助,不再需要进行很重的自定义开发。在介绍本篇要分享的这个新功能前,如果需要实现PDF的导出和打印一般有三种方式:第一也是最简... 查看详情
关于一体机外卖单不打印外卖单号faq(适用正餐6.0.09,轻餐4.0.6.1,轻餐4.0.6.2)
适用情景:升级版本后打印机打印出的外卖小票不出现外卖单号。解决方案:设置-功能设置-小票设置-小票自定义-前台小票-外卖订单-------选择编辑,选中右侧中外卖单号或者外卖订单编号,点击保存即可。图示如下:1.2.3.4. 查看详情