小票打印就是向打印設備發送控制打印格式的指令集,而這些打印格式須要去查詢相應打印機的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("\n");
}
/** * 在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);
