最近美团出餐宝要求服务商支持在小票上显示订单一维码信息,时间很赶,抓紧行动。由于我们有多个项目与打印相关。用到的打印机有驱动打印机,网口打印机,蓝牙打印机。不用惊讶,驱动打印机是用于delphi开发的桌面项目。开发的时候,找资料花了很长时间,完全把一维码的生成规则弄明白后,才解决了驱动机打印小票的问题。用delphi的人并不多,我还是先写安卓连接网口打印机和蓝牙打印机打印二维码吧。
此段代码为条形码转换方式
public class OneDimensionUtil {private static String chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";private static int scale = 62;/*** 数字转62进制*/private static String encode(long num) {Long newNum = num;StringBuilder sb = new StringBuilder();int remainder;while (newNum > scale - 1) {//对 scale 进行求余,然后将余数追加至 sb 中,由于是从末位开始追加的,因此最后需要反转字符串remainder = (int) (newNum % scale);sb.append(chars.charAt(remainder));//除以进制数,获取下一个末尾数newNum /= scale;}sb.append(chars.charAt(Integer.parseInt(String.valueOf(newNum))));return sb.toString();}/*** 添加平台标志M前缀*/public static String encodeM(long num) {return "M" + encode(num);}
}
打印相关命令如下:
package com.yx.printerexec.bluetoothsdk;/*** author : dawn* e-mail : 272398235@qq.com* date : 2021/5/8 15:07* desc : 打印命令*/
public class CommandConstants {/*** 复位打印机*/public static byte[] RESET = new byte[]{0x1b, 0x40};/*** 左对齐*/public static byte[] ALIGN_LEFT = new byte[]{0x1b, 0x61, 0x00};/*** 中间对齐*/public static byte[] ALIGN_CENTER = new byte[]{0x1b, 0x61, 0x01};/*** 右对齐*/public static byte[] ALIGN_RIGHT = new byte[]{0x1b, 0x61, 0x02};/*** 选择加粗模式*/public static byte[] BOLD = new byte[]{0x1b, 0x45, 0x01};/*** 取消加粗模式*/public static byte[] BOLD_CANCEL = new byte[]{0x1b, 0x45, 0x00};/*** 宽高加倍*/public static byte[] DOUBLE_HEIGHT_WIDTH = new byte[]{0x1d, 0x21, 0x11};/*** 宽加倍*/public static byte[] DOUBLE_WIDTH = new byte[]{0x1d, 0x21, 0x10};/*** 高加倍*/public static byte[] DOUBLE_HEIGHT = new byte[]{0x1d, 0x21, 0x01};/*** 字体不放大*/public static byte[] NORMAL = new byte[]{0x1d, 0x21, 0x00};/*** 设置默认行间距*/public static byte[] LINE_SPACING_DEFAULT = new byte[]{0x1b, 0x32};/*** 设置条形码高*/public static byte[] BAR_CODE_H = new byte[]{0x1d, 0x68,0x6e};/*** 设置条形码宽*/public static byte[] BAR_CODE_W = new byte[]{0x1d, 0x77, 0x3};/*** 换行*/public static byte[] WRAP = new byte[]{0x0A};/*** 走纸(防止内容已打印,包含内容的纸张打印机未全部吐出来)*/public static byte[] PAPER_FEED = new byte[]{0x1b, 0x4a, (byte) 0x96};/*** 切纸*/public static byte[] PAPER_CUT = new byte[]{0x1d, 0x56, 0x00, 0x30};
}
1)、android连接网口打印机
如果是连接网口打印机的话,打印完一定要记得断开连接,否则会导致一直占用其他的程序没有办法正常连接。
2)、android连接蓝牙打印机
这个方法里有一个outputStream 有一个outputStream,如果能用蓝牙打印机连接打印,应该知道怎么来的吧。我就直接略过了。只要蓝牙设备连接成功,就会有一个 BluetoothSocket 。
这两种打印机只是连接方式不一样,发送编码的方式完全相同
java发送指令部分 CODE B
/*** 设置打印格式** @param command 格式指令*/public void selectCommand(byte[] command) {try {outputStream.write(command);outputStream.flush();} catch (IOException e) {e.printStackTrace();}}
private void printBarCode(String content) {selectCommand(CommandConstants.BAR_CODE_W);//设置条码的宽selectCommand(CommandConstants.BAR_CODE_H);//设置条码的高度byte[] headerBytes = new byte[]{29, 107, 73, 00, 123, 66};byte[] contentByteArray = createByteCode(content);headerBytes[3] = (byte) (contentByteArray.length + headerBytes.length - 4);byte[] bytes = new byte[headerBytes.length + contentByteArray.length];System.arraycopy(headerBytes, 0, bytes, 0, headerBytes.length);System.arraycopy(contentByteArray, 0, bytes, headerBytes.length, contentByteArray.length);selectCommand(bytes);}
private byte[] createByteCode(String content) {byte[] arr = new byte[content.length()];for (int i = 0; i < content.length(); i++) {arr[i] = (byte) content.charAt(i);}return arr;}
Kotlin发送指令部分:CODE C
/*** 打印条形码*设置其他值参考ESC/POS指令手册** @param content 条码内容* @param w 指定条码宽 默认:3* @param h 指定条码高 默认:162*/fun printBarCode(content: String, w: Int = 3, h: Int = 162) {try {val width = byteArrayOf(0X1D, 0X77, w.toByte())val height = byteArrayOf(0X1D, 0X68, h.toByte())selectCommand(width)//设置条码的宽selectCommand(height)//设置条码的高度val formats =byteArrayOf(29, 107, 73, 0, 123, 66, 78, 111, 46, 123, 67)val contentByteArray = splitText(content)formats[3] = (contentByteArray.size + 7).toByte()val bytes = ByteArray(formats.size + contentByteArray.size)System.arraycopy(formats, 0, bytes, 0, formats.size)System.arraycopy(contentByteArray, 0, bytes, formats.size, contentByteArray.size)selectCommand(bytes)} catch (e: IOException) {e.printStackTrace()}}
/*** 按照两位分割字符串*/private fun splitText(content: String): ByteArray {val sb = StringBuffer(content)var count = 0for (i in content.indices) {val index = i + 1//如果下标是偶数数则加入特殊字符if (index % 2 == 0 && index != content.length) {sb.insert(index + count, "&")count++}}val split = sb.toString().split("&")val byteArray = ByteArray(split.size)for (i in split.indices) {byteArray[i] = split[i].toByte()}return byteArray}
=========================================================================
byte[] headerBytes = new byte[]{29, 107, 73, 00, 123, 66};
应该有人对这句代码有疑惑,待我解释一下。 这还要从我从官网下载下来的编程手册说起。写打印功能的同行,应该对《XP-80X中文编程手册》不陌生。里面关于打印条形码是这么说的。条码类型很多,我只选择了我所熟悉的CODE128
Code128 A码可表示:大写英文字母、数字、控制字符组成的字符串,比如:ABC、ABC123。
Code128 B码可表示:大小写英文字母、数字、字符组成的字符串,比如:Abc123、A-123(B)。
Code128 C码可表示:仅可表示100个“两位”数字编码(00-99),比如:123456、00225869。
一般来说,如果条码内容是大写英文字母,用Code128 A码或B码都可以,如果是包含大小写字母就需要用Code128 B码,如果是纯数字的一般用C码。在这里有一点需要注意的是,如果是条码内容位数是“奇数”的纯数字,那么就需要把条码内容给拆分,前面偶数用Code128 C码,后面一位奇数用B码或者A码。比如条码:1234567
拆分后,12、34、56是Code128 C码:,而最后一位7就是B码或者A码。在实际应用中Code 128 A码、Code128 B码和Code128 C码是可以相互组合的。
数据位可以只使用CODE B 编码,也可以只使用CODE C编码,也可以混合编码。需要注意的是,转换编码前需要提前说一下,也就是在真实数据前,注明编码。
混合编码方式:如M15202145
CODE B 编码方式:
CODE C 编码方式:
注明一下:以上三张图只是声明传值格式,数据位的内容是随机写的。大家对照着上面的例子看,希望能有所帮助。
至于要验证从字符转换为编码后是否正确,自行参考https://blog.csdn.net/weixin_30654419/article/details/95916713
参考:
https://jingyan.baidu.com/article/b907e6270f790906e6891c14.html
Code128一维条码中A码、B码、C码的区别