热敏打印机图片打印ESC/POS指令

28 Apr 2021

这几天研究热敏打印机的打印指令,
通过蓝牙或接口,发送给打印机进行打印。
使用的是位图打印命令。
本问主要说明怎样将图片转换成指令。
适用于Android系统平台的代码。

用360*360大小的Bitmap转成byte[]指令

指令说明

ESC * 设定位图命令
格式:
ASCII: ESC * m n1 n2 d1..dk
DEC: 27 42 m n1 n2 d1..dk
HEX: 1B 2A m n1 n2 d1..dk
注释:
设定位图方式(用 m)、点数(用 n1 n2)以及位图内容(d1..dk)
m=0,1,32,33,n1=0~ 255。n2=0~3。d=0~255。
k=n1+256×n2 (m=0,1)
k=(n1+256×n2)×3 (m=32,33)
水平方向点数为 n1+256×n2
m 用于选择位图方式。

m Mode 横向最多点数
0 8点单密度 n1+n2×256[不超过 288 点]
1 8点双密度 n1+n2×256[不超过 576 点]
32 24点单密度 (n1+n2×256)×3[不超过 288点]
33 24点双密度 (n1+n2×256)×3[不超过 576点]

注:
1、 如果 m 值超出上面指定的范围,
n1以后的数据会被当作普通打印字符来打印。
2、 横向点数为 n1+256×n2。
3、 如果点数超过一行,超过其最大点数
(与选择的位图方式有关,详见上表)的部分被忽略。
4、 d 代表图形数据。其中每个字节的某位为 1 的
时候打印出一点,为 0 的时候,为空白。
5、 打印数据超过 GS L 和 GS W 规定的打印区域的
时候,超出数据会被忽略。
6、 打印完图形模式后,打印机会回到字符打印模式。
但图形下打印速度较慢,所以一般要求
在打印字符前加 ESC @指令恢复速度设置。
7、 字符修饰指令,如强调、下划线、反转以及尺寸变化
等指令在图形模式下无效。对齐指令有效。

转换代码

1.将图片地址转Bitmap对象

将图片缩放到360*360尺寸
/**
 * 通过图片地址获取图片对象
 * @param path 图片地址
 * @return Bitmap
 */
public static Bitmap getBitmap(String path) {
    Bitmap bitmapTemp = BitmapFactory.decodeFile(path);
    Bitmap bitmap = Bitmap.createScaledBitmap(
            bitmapTemp, 360, 360, false);
    // 如果没有缩放,那么不回收
    if (bitmapTemp != bitmap) {
        // 释放Bitmap的native像素数组
        bitmapTemp.recycle(); 
    }
    return bitmap;
}

2.获取指令数组

包含三个方法:
2.1 图片生成指令组
/**
 * 用于打印360*360大小的图片
 * 360 = 24 * 15
 * 换行 10
 * 0x68 = 104
 * 360 = 0x68 + 0x01 * 256
 * 16290 = ( 5 + 3 * 360 + 1 ) * 15
 *
 * @param bit 图片
 * @return 打印指令
 */
public static byte[] draw2PxPoint360(Bitmap bit) {
    byte[] data = new byte[16290];
    int k = 0;
    for (int j = 0; j < 15; j++) {
        data[k++] = 0x1B;
        data[k++] = 0x2A;
        data[k++] = 33; //24点双密度打印,分辨率200DPI。
        data[k++] = 0x68;
        data[k++] = 0x01;
        for (int i = 0; i < 360; i++) {
            for (int m = 0; m < 3; m++) {
                for (int n = 0; n < 8; n++) {
                    //获取图片的灰度值
                    byte b = px2Byte(i, 
                             j * 24 + m * 8 + n, 
                             bit);
                    data[k] += data[k] + b;
                }
                k++;
            }
        }
        //换行
        data[k++] = 10;
    }
    return data;
}
2.2 像素转指令
/**
 * 图片二值化,黑色是1,白色是0
 *
 * @param x   横坐标
 * @param y   纵坐标
 * @param bit 位图
 * @return 像素对应点数据
 */
public static byte px2Byte(int x, int y, Bitmap bit) {
    byte b = 0;
    int pixel = bit.getPixel(x, y);
    int red = (pixel & 0x00ff0000) >> 16; // 取高两位
    int green = (pixel & 0x0000ff00) >> 8; // 取中两位
    int blue = pixel & 0x000000ff; // 取低两位
    int gray = RGB2Gray(red, green, blue);
    if (gray < 128) {
        b = 1;
    } else {
        b = 0;
    }
    return b;
}
2.3 图片灰度的转化
/**
 * 图片灰度的转化
 *
 * @param r 红
 * @param g 绿
 * @param b 蓝
 * @return 灰度值
 */
private static int RGB2Gray(int r, int g, int b) {
    return (int) (0.29900 * r + 0.58700 * g + 0.11400 * b);
}

3.完整打印指令

有些冗余指令,可根据效果调整。
3.1 复位打印机
  new byte[]{0x1B, 0x40};
3.2 开始打印图片
1B,40 初始化打印机
1B,33 设定 n/180 英寸行间距
  new byte[]{0x00, 0x1B, 0x40, 0x1B, 0x33, 0x00};
3.3 设置居中
  new byte[]{0x1b, 0x61, 0x31};
3.4 打印图片指令
  byte[] command = draw2PxPoint360(bitmap);
3.5 结束打印图片
设置左边距
  new byte[]{0x1d, 0x4c, 0x1f, 0x00};
3.6 换行
  new byte[]{10};

4.并排打印图片

因为小票纸宽的问题
把图片缩放到192*192
80mm宽纸最多可以打印3张
58mm宽纸可以打印2张
public static byte[] draw2PxPoint(
                     Bitmap bit1, Bitmap bit2) {
    byte[] data = new byte[9264];
    int k = 0;
    for (int j = 0; j < 8; j++) {
        data[k++] = 0x1B;
        data[k++] = 0x2A;
        data[k++] = 33; //24点双密度打印
        data[k++] = (byte) (128 & 0xFF);
        data[k++] = 0x01;
        for (int i = 0; i < 192; i++) {
            for (int m = 0; m < 3; m++) {
                for (int n = 0; n < 8; n++) {
                    byte b = px2Byte(i,
                            j * 24 + m * 8 + n, bit1);
                    data[k] += data[k] + b;
                }
                k++;
            }
        }
        for (int i = 0; i < 192; i++) {
            for (int m = 0; m < 3; m++) {
                for (int n = 0; n < 8; n++) {
                    byte b = px2Byte(i,
                            j * 24 + m * 8 + n, bit2);
                    data[k] += data[k] + b;
                }
                k++;
            }
        }
        data[k++] = 10;
    }
    return data;
}

5.图片转换说明

图片的颜色标准为RGB,例如:#00ff00
即红绿蓝,根据算法转换成一个灰度值
判断灰度值大于128,
就是打印机上的一个黑点,用1标识
否则就是空白,用0标识

用24点双密度的模式去打印图片
一个byte有8位,3个8位就是24点
打印机横向滑行一次,一列上可以打印24个点


宽360px,高360px的图片

图片上有360行像素点
第一列的8行像素点可以放一个byte
第一列的9到16行可以放第二个byte
第一列的17到24行可以放第三个byte
3个byte在打印机上可以打印一次
然后在像右移动一列,打印3个byte
就是第二列的前24行

所以将图片分成360列15行组,
一行组有24个像素点数据(3个byte)
对于byte数组,就有360*3列15行

例如:
360*360图片
行是15,列是360
像素点 360 * 360 =( 3 * 8 * 15 )* 360
byte 360 * 360 / 8 = 15 * 360 * 3

192*192图片
行是8,列是192
像素点 192 * 192 =( 3 * 8 * 8 )* 192
byte 192 * 192 / 8 = 8 * 360 * 3

6.常用打印指令

6.1 复位
  new byte[]{0x1B, 0x40};
6.2 居中
  new byte[]{0x1b, 0x61, 0x01};
6.3 居左
  new byte[]{0x1b, 0x61, 0x00};
6.4 普通大小
  new byte[]{0x1d, 0x21, 0x00};
6.5 加高
  new byte[]{0x1d, 0x21, 0x01};
6.6 字体加粗
  new byte[]{0x1b, 0x45, 0x01};
6.7 取消字体加粗
  new byte[]{0x1b, 0x45, 0x00};
6.8 换行
  new byte[]{10};
参考文献:
MCP-58系列用户手册.pdf
打印机编程手册ESC指令.pdf
https://blog.csdn.net/bzdwdmzjsmff/article/details/51325718
https://blog.csdn.net/qq_34942689/article/details/72820226?locationNum=3&fps=1