Android 蓝牙打印指令

一、概述

目前打印打印机支持的无线打印方式一般为wifi和蓝牙。

蓝牙打印关于蓝牙连接部分请查看上篇文章 -> Android 蓝牙连接 ,本篇文章讨论Android中蓝牙打印的指令实现。

蓝牙打印机的种类繁多,支持的打印格式也不尽相同。按照指令集划分,主要可分为:ESC指令集、CPCL指令集。

大部分热敏打印机使用的ESC指令集。部分支持CPCL指令集。

二、ESC指令

指令一览表:

Android 蓝牙打印指令_第1张图片

常用打印指令:

1、初始化

Android 蓝牙打印指令_第2张图片

2、设置文本对齐方式

Android 蓝牙打印指令_第3张图片

3、设置行间距

1)默认行间距

2)指定行间距

4、设定字符打印模式

Android 蓝牙打印指令_第4张图片

5、打印图片

Android 蓝牙打印指令_第5张图片

参数说明:

  • m:取值十进制 0、1、32、33。设置打印精度,0、1对应每行8个点,32、33对应每行24个点,对应最高的打印精度(其实这里也没太搞清楚取值0、1或者取值32、33的区别,只要记住取值33,对应每行24个点,后面还有用)
  • n1, n2 : 表示图片的宽度,为什么有两个?其实只是分成了高位和低位两部分,因为每部分只有8bit,最大表示256。所以 n1 = 图片宽度 % 256,n2 = 图片宽度 / 256。假设图片宽300,那么n1=1,n2=44
  • d1 d2 ... dk 这部分就是转换成字节流的图像数据了

更多ESC指令详情跳转 ->  ESC/POS 打印控制命令

需要注意的是:在实际打印中,图片的分辨率需要调整,宽度(每行像素点数)不能超出打印机可支持范围。所以图片需要做一下压缩处理,同时,由于打印图片是黑白的,所以需要灰度转化对图片进行黑白化处理。

 

此处贴出一个ESC打印工具类,未使用到的指令可以依照指令表实现。

public class PrinterManagerForESC extends PrinterManager{

    private OutputStreamWriter writer = null;
    private OutputStream outputStream = null;

    public PrinterManagerForESC(OutputStream outputStream, String encoding) throws IOException {
        writer = new OutputStreamWriter(outputStream, encoding);
        this.outputStream = outputStream;
        initPrinter();
    }

    //初始化
    protected void initPrinter() throws IOException {
        writer.write(0x1B);
        writer.write(0x40);
        completeFlush();
    }

    public void completeFlush() throws IOException {
        writer.flush();
    }

    public void printString(String str){
        try {
            writer.write(str);
            completeFlush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    void printStrWithAlign(String value, int alignment) {
        try {
            writer.write(0x1b);
            writer.write(0x61);
            writer.write(alignment);

            writer.write(value);
            writer.write("\n");
            completeFlush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 打印分割线
     */
    public void printSeparateLine(int paperpoints){
        String res = "";
        int count = Math.round(paperpoints/12);
        for (int i=0; i< count; i++){
            res = res + "-";
            if (i== (count-1)){
                res = res + "\n";
            }
        }
        printString(res);
    }


    private void printBytes(byte[] bytes){
        try {
            outputStream.write(bytes);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void printBitmap(Bitmap bitmap, int paperwidth) throws IOException {
        Bitmap resizeBitmap = compressPic(bitmap,paperwidth);
        byte[] bmpByteArray = draw2PxPoint(resizeBitmap);
        printBytes(bmpByteArray);
    }

    /**
     * 打印换行
     *
     * @return length 需要打印的换行数
     * @throws IOException
     */
    public void printNextLine(int lineNum) {
        try {
            for (int i = 0; i < lineNum; i++) {
                writer.write("\n");
            }
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*************************************************************************
     * 假设一个360*360的图片,分辨率设为24, 共分15行打印 每一行,是一个 360 * 24 的点阵,y轴有24个点,存储在3个byte里面。
     * 即每个byte存储8个像素点信息。因为只有黑白两色,所以对应为1的位是黑色,对应为0的位是白色
     **************************************************************************/
    private byte[] draw2PxPoint(Bitmap bmp) {
        //计算所需字节空间大小
        int bitmapsize = (int) (Math.ceil(bmp.getHeight() / 24f)) * bmp.getWidth() * 3;
        int cmdsize = (int) (Math.ceil(bmp.getHeight() / 24f)) * 6 + 5;
        int size = bitmapsize + cmdsize;
        byte[] tmp = new byte[size];
        int k = 0;
        // 设置行距为0
        tmp[k++] = 0x1B;
        tmp[k++] = 0x33;
        tmp[k++] = 0x00;
        for (int j = 0; j < bmp.getHeight() / 24f; j++) {
            tmp[k++] = 0x1B;
            tmp[k++] = 0x2A;// 0x1B 2A 表示图片打印指令
            tmp[k++] = 33; // m=33时,选择24点密度打印
            tmp[k++] = (byte) (bmp.getWidth() % 256); // nL
            tmp[k++] = (byte) (bmp.getWidth() / 256); // nH
            for (int i = 0; i < bmp.getWidth(); i++) {
                for (int m = 0; m < 3; m++) {
                    for (int n = 0; n < 8; n++) {
                        byte b = px2Byte(i, j * 24 + m * 8 + n, bmp);
                        tmp[k] += tmp[k] + b;
                    }
                    k++;
                }
            }
            tmp[k++] = 10;// 换行
        }
        // 恢复默认行距
        tmp[k++] = 0x1B;
        tmp[k++] = 0x32;

        byte[] result = new byte[k];
        System.arraycopy(tmp, 0, result, 0, k);
        return result;
    }

    /**
     * 图片二值化,黑色是1,白色是0
     *
     * @param x   横坐标
     * @param y   纵坐标
     * @param bit 位图
     * @return
     */
    private byte px2Byte(int x, int y, Bitmap bit) {
        if (x < bit.getWidth() && y < bit.getHeight()) {
            byte b;
            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;
        }
        return 0;
    }

    /**
     * 图片灰度的转化
     */
    private int RGB2Gray(int r, int g, int b) {
        int gray = (int) (0.29900 * r + 0.58700 * g + 0.11400 * b); // 灰度转化公式
        return gray;
    }

    public byte[] getGbkStr(String stText) {
        byte[] returnText = null;
        try {
            returnText = stText.getBytes("GBK"); 
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return returnText;
    }

    private int getStringPixLength(String str) {
        int pixLength = 0;
        char c;
        for (int i = 0; i < str.length(); i++) {
            c = str.charAt(i);
            if (Pinyin.isChinese(c)) {
                pixLength += 24;
            } else {
                pixLength += 12;
            }
        }
        return pixLength;
    }

    /**
     * 图片压缩
     */
    public static Bitmap compressPic(Bitmap bitmap, int paperwidth) {
        // 获取这个图片的宽和高
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        
        int newWidth = paperwidth;
        int newHeight = Math.round ( height * newWidth / width);
        Bitmap targetBmp = Bitmap.createBitmap(newWidth, newHeight,
                Bitmap.Config.ARGB_8888);
        Canvas targetCanvas = new Canvas(targetBmp);
        targetCanvas.drawColor(0xffffffff);
        targetCanvas.drawBitmap(bitmap, new Rect(0, 0, width, height),
                new Rect(0, 0, newWidth, newHeight), null);

        return targetBmp;
    }
}

三、CPCL指令

常用指令:

1、打印机起始命令

Android 蓝牙打印指令_第6张图片

2、打印命令

Android 蓝牙打印指令_第7张图片

3、结束命令

Android 蓝牙打印指令_第8张图片

4、文本命令

Android 蓝牙打印指令_第9张图片

5、图片命令

Android 蓝牙打印指令_第10张图片

更多CPCL指令文档跳转 -> CPCL指令集 

 

此处贴出一个CPCL指令工具类,未实现的指令可按照文档实现。

public class PrinterManagerForCPCL extends PrinterManager{

    private OutputStreamWriter writer = null;
    private OutputStream outputStream = null;
    private String dpi;
    private StringBuffer buffer;

    public PrinterManagerForCPCL(OutputStream outputStream, String encoding, String dpi) throws IOException {
        writer = new OutputStreamWriter(outputStream,encoding);
        this.outputStream = outputStream;
        this.dpi = dpi;
        this.buffer = new StringBuffer("");
    }

    //开始指令
    public void beginInstruction(int height) throws IOException {
        // {offset}   {hegiht} {qty}
        buffer = new StringBuffer("");
        String begincmd = "! 0 " + dpi + " " + dpi + " " + height + " " + 1 + "\r\n";
        buffer.append(begincmd);
    }

    //结束指令
    public void endInstructionFlush() throws IOException {
        buffer.append("FORM\r\n");
        buffer.append("PRINT\r\n");
        byte[] res = buffer.toString().getBytes();
        outputStream.write(res);
        outputStream.flush();
    }

    public void endPrintForStr() throws IOException {
        buffer.append("PRINT\r\n");
        byte[] res = buffer.toString().getBytes("GBK");
        outputStream.write(res);
        outputStream.flush();
    }

    public void printString(String str){
        try {
            writer.write(str);
            completeFlush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void completeFlush() throws IOException {
        writer.flush();
    }

    /**
     * 打印CPCL字符串
     */
    public void printCPCLString(String data) throws IOException {
        //TEXT(或T) {font} {size} {x} {y} {data}
        String strCmd = "TEXT " + 1 + " " + 0 + " " + 0 + " " + 0 + " " + data + "\r\n";
        buffer.append(strCmd);
    }

    /**
     * 打印图片
     *
     * @param bitmap
     * @throws IOException
     */
    public void printImage(Bitmap bitmap){

        int loopWidth = 8 - (bitmap.getWidth() % 8);
        if (loopWidth == 8) {
            loopWidth = bitmap.getWidth();
        } else {
            loopWidth += bitmap.getWidth();
        }

        buffer.append("EG "+ (loopWidth / 8) + " "+ bitmap.getHeight() + " 0 0 "+"");

        for (int y = 0; y < bitmap.getHeight(); y++) {
            int bit = 128;
            int currentValue = 0;
            for (int x = 0; x < loopWidth; x++) {
                int intensity;

                if (x < bitmap.getWidth()) {
                    int color = bitmap.getPixel(x, y);
                    intensity = 255 - ((Color.red(color) + Color.green(color) + Color.blue(color)) / 3);
                } else {
                    intensity = 0;
                }

                if (intensity >= 128) currentValue |= bit;
                bit = bit >> 1;
                if (bit == 0) {
                    String hex = IntegerToHexString(currentValue);
                    buffer.append(hex.toUpperCase() + " ");
                    bit = 128;
                    currentValue = 0;
                }
            }
        }
        buffer.append("\r\n");
    }

    private String IntegerToHexString(int data) {
        String s = Integer.toHexString(data);
        return s.length() < 2 ? s = "0" + s : s;
    }

    public void printSeparateLine(int paperpoints){
        String res = "";
        int count = Math.round(paperpoints / 12);
        for (int i = 0; i < count; i++) {
            res = res + "-";
            if (i == (count - 1)) {
                res = res + "\n";
            }
        }
        printString(res);
    }

    @Override
    void printStrWithAlign(String value, int alignment) {
        try {
            //居中特殊处理
            if (1 == alignment){
                beginInstruction(24);
                buffer.append("CENTER\r\n");
                printCPCLString(value);
                endPrintForStr();
            }else {
                writer.write(value);
                writer.write("\n");
                completeFlush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void printBytes(byte[] bytes) {
        try {
            outputStream.write(bytes);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 打印换行
     *
     * @return length 需要打印的换行数
     * @throws IOException
     */
    public void printNextLine(int lineNum){
        try {
            for (int i = 0; i < lineNum; i++) {
                writer.write("\r\n");
            }
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public byte[] getGbkStr(String stText) {
        byte[] returnText = null;
        try {
            returnText = stText.getBytes("GBK"); 
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return returnText;
    }

    private int getStringPixLength(String str) {
        int pixLength = 0;
        char c;
        for (int i = 0; i < str.length(); i++) {
            c = str.charAt(i);
            if (Pinyin.isChinese(c)) {
                pixLength += 24;
            } else {
                pixLength += 12;
            }
        }
        return pixLength;
    }


    /**
     * 图片压缩
     * @param bitmap
     */
    public static Bitmap compressPic(Bitmap bitmap, int paperwidth) {
        // 获取这个图片的宽和高
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
      
        int newWidth = paperwidth;
        int newHeight = Math.round ( height * newWidth / width);
        Bitmap targetBmp = Bitmap.createBitmap(newWidth, newHeight,
                Bitmap.Config.ARGB_8888);
        Canvas targetCanvas = new Canvas(targetBmp);
        targetCanvas.drawColor(0xffffffff);
        targetCanvas.drawBitmap(bitmap, new Rect(0, 0, width, height),
                new Rect(0, 0, newWidth, newHeight), null);

        return targetBmp;
    }
}

 

你可能感兴趣的:(Android)