您的当前位置:首页正文

Android打印二维码对热敏打印机的适配

2024-11-08 来源:个人技术集锦

经过一段时间的研究,目前得出了Android连接蓝牙打印机打印二维码的方式有2种:

第一种:采用ESC/POS二维码指令打印的方式打印,代码如下

    /**
     * 设置二维码大小
     */
    public static final byte[] setCodeSize = new byte[8];
    /**
     * 设置纠错正等级
     */
    public static final byte[] setCodeLevel = new byte[] {0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x45, 0x30};
     /**
     * 加载二维码
     */
    public static byte[] setCode = new byte[8];
    /**
     * 打印二维码
     */
    public static final byte[] printCode = new byte[]{0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x51, 0x30};
    /**
     * 设置加载二维码指令
     * @param code
     */
    public static void doSetCode(String code) {
        setCode[0] = 0x1D;
        setCode[1] = 0x28;
        setCode[2] = 0x6B;
        setCode[3] = (byte) (code.length()+3);
        setCode[4] = 0x00;
        setCode[5] = 0x31;
        setCode[6] = 0x50;
        setCode[7] = 0x30;
    }


    /**
     * 设置二维码大小
     * @param size
     */
    public static void doSetQrSize(int size) {
        setCodeSize[0] = 0x1D;
        setCodeSize[1] = 0x28;
        setCodeSize[2] = 0x6B;
        setCodeSize[3] = 0x03;
        setCodeSize[4] = 0x00;
        setCodeSize[5] = 0x31;
        setCodeSize[6] = 0x43;
        setCodeSize[7] = (byte)size;
    }

    /**
     * byte转Byte
     *
     * @param srcArray
     * @param cpyArray
     */
    public static void CopyArray(byte[] srcArray, byte[] cpyArray) {
        for (int index = 0; index < cpyArray.length; index++) {
            cpyArray[index] = srcArray[index];
        }
    }

打印二维码的方法

    /**
     * 打印二维码
     * @param qrCode 二维码url
     * @param size 二维码大小
     * @throws IOException
     */
    public ArrayList<byte[]> printQr(String qrCode,int size) throws IOException{
        ArrayList<byte[]> arr = new ArrayList<>();
        PrinterUtils.doSetQrSize(size);
        arr.add(PrinterUtils.setCodeSize);
        arr.add(PrinterUtils.setCodeLevel);
        PrinterUtils.doSetCode(qrCode);
        arr.add(PrinterUtils.setCode);
        byte[] data = (qrCode +"\r\n\r\n").getBytes(CHARSET);
        byte[] tempList = new byte[data.length];
        PrinterUtils.CopyArray(data, tempList);
        arr.add(tempList);
        arr.add(PrinterUtils.printCode);

        return arr;
    }

这种指令方式打印二维码非常快,几乎和打印纯文字的速度差不多,但是在内部测试的时候发现只能适用于佳博GPrinter的打印机,对于其他的机器,打印出来的只是一段URL文字,似乎对该二维码指令不支持,但是经过联系厂家拿到机器指令后,发现指令都是一模一样的,厂家的人也说是支持ESC/POS指令的,但是我实在不清楚问题出在哪里,至今也没有解决,希望知道的大神能够指教一下。

第二种:采用位图的方式打印二维码,代码如下

先将二维码连接生成图片

    /**
     * 生成二维码Bitmap
     *
     */
    public static Bitmap createQRImage(String url,int width,int height){
        try{
            if (url == null || "".equals(url) || url.length() < 1){//判断URL合法性
                return null;
            }
            Hashtable<EncodeHintType, String> hints = new Hashtable<>();
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            //图像数据转换,使用了矩阵转换
            BitMatrix bitMatrix = new QRCodeWriter().encode(url, BarcodeFormat.QR_CODE, width, height, hints);
            int[] pixels = new int[width * height];
            //下面这里按照二维码的算法,逐个生成二维码的图片,
            //两个for循环是图片横列扫描的结果
            for (int y = 0; y < height; y++){
                for (int x = 0; x < width; x++){
                    if (bitMatrix.get(x, y)){
                        pixels[y * width + x] = 0xff000000;
                    }
                    else{
                        pixels[y * width + x] = 0xffffffff;
                    }
                }
            }
            //生成二维码图片的格式,使用ARGB_8888
            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
            return bitmap;
        }catch (WriterException e){
            e.printStackTrace();
            return  null;
        }
    }

再将生成的图片转换为字节数组

public  byte[] draw2PxPoint(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; // m=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;
    }
public int changePointPx1(byte[] arry){
        int v = 0;
        for (int j = 0; j <arry.length; j++) {
            if( arry[j] == 1) {
                v = v | 1 << j;
            }
        }
        return v;
    }

    public byte px2Byte(int x, int y, Bitmap bit) {
        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;
    }

生成字节数组后,将这些字节传给打印机就可以了。上面对应的二维码图片时360*360大小的,如果想改变大小得自己去算,比较麻烦,也可以采用下面的方式

    /**
     * 获取图片数据流
     *
     * @param image 图片
     * @return 数据流
     */
    public ArrayList<byte[]> getImageByte(Bitmap image) {
        //TYPE_58 纸宽58mm,width = 380;
        //TYPE_80 = 80 纸宽80mm,width = 500;
        int maxWidth = 380;
        Bitmap scalingImage = scalingBitmap(image, maxWidth);
        if (scalingImage == null)
            return null;
        ArrayList<byte[]> data = PrinterUtils.decodeBitmapToDataList(image, heightParting);
        image.recycle();
        return data;
    }
    /**
     * 解码图片
     *
     * @param image   图片
     * @param parting 高度分割值
     * @return 数据流
     */
    @SuppressWarnings("unused")
    public static ArrayList<byte[]> decodeBitmapToDataList(Bitmap image, int parting) {
        if (parting <= 0 || parting > 255)
            parting = 255;
        if (image == null)
            return null;
        final int width = image.getWidth();
        final int height = image.getHeight();
        if (width <= 0 || height <= 0)
            return null;
        if (width > 2040) {
            // 8位9针,宽度限制2040像素(但一般纸张都没法打印那么宽,但并不影响打印)
            final float scale = 2040 / (float) width;
            Matrix matrix = new Matrix();
            matrix.postScale(scale, scale);
            Bitmap resizeImage;
            try {
                resizeImage = Bitmap.createBitmap(image, 0, 0, width, height, matrix, true);
            } catch (OutOfMemoryError e) {
                return null;
            }
            ArrayList<byte[]> data = decodeBitmapToDataList(resizeImage, parting);
            resizeImage.recycle();
            return data;
        }

        // 宽命令
        String widthHexString = Integer.toHexString(width % 8 == 0 ? width / 8 : (width / 8 + 1));
        if (widthHexString.length() > 2) {
            // 超过2040像素才会到达这里
            return null;
        } else if (widthHexString.length() == 1) {
            widthHexString = "0" + widthHexString;
        }
        widthHexString += "00";

        // 每行字节数(除以8,不足补0)
        String zeroStr = "";
        int zeroCount = width % 8;
        if (zeroCount > 0) {
            for (int i = 0; i < (8 - zeroCount); i++) {
                zeroStr += "0";
            }
        }
        ArrayList<String> commandList = new ArrayList<>();
        // 高度每parting像素进行一次分割
        int time = height % parting == 0 ? height / parting : (height / parting + 1);// 循环打印次数
        for (int t = 0; t < time; t++) {
            int partHeight = t == time - 1 ? height % parting : parting;// 分段高度

            // 高命令
            String heightHexString = Integer.toHexString(partHeight);
            if (heightHexString.length() > 2) {
                // 超过255像素才会到达这里
                return null;
            } else if (heightHexString.length() == 1) {
                heightHexString = "0" + heightHexString;
            }
            heightHexString += "00";

            // 宽高指令
            String commandHexString = "1D763000";
            commandList.add(commandHexString + widthHexString + heightHexString);

            ArrayList<String> list = new ArrayList<>(); //binaryString list
            StringBuilder sb = new StringBuilder();
            // 像素二值化,非黑即白
            for (int i = 0; i < partHeight; i++) {
                sb.delete(0, sb.length());
                for (int j = 0; j < width; j++) {
                    // 实际在图片中的高度
                    int startHeight = t * parting + i;
                    //得到当前像素的值
                    int color = image.getPixel(j, startHeight);
                    int red, green, blue;
                    if (image.hasAlpha()) {
                        //得到alpha通道的值
                        int alpha = Color.alpha(color);
                        //得到图像的像素RGB的值
                        red = Color.red(color);
                        green = Color.green(color);
                        blue = Color.blue(color);
                        final float offset = alpha / 255.0f;
                        // 根据透明度将白色与原色叠加
                        red = 0xFF + (int) Math.ceil((red - 0xFF) * offset);
                        green = 0xFF + (int) Math.ceil((green - 0xFF) * offset);
                        blue = 0xFF + (int) Math.ceil((blue - 0xFF) * offset);
                    } else {
                        //得到图像的像素RGB的值
                        red = Color.red(color);
                        green = Color.green(color);
                        blue = Color.blue(color);
                    }
                    // 接近白色改为白色。其余黑色
                    if (red > 160 && green > 160 && blue > 160)
                        sb.append("0");
                    else
                        sb.append("1");
                }
                // 每一行结束时,补充剩余的0
                if (zeroCount > 0) {
                    sb.append(zeroStr);
                }
                list.add(sb.toString());
            }
            // binaryStr每8位调用一次转换方法,再拼合
            ArrayList<String> bmpHexList = new ArrayList<>();
            for (String binaryStr : list) {
                sb.delete(0, sb.length());
                for (int i = 0; i < binaryStr.length(); i += 8) {
                    String str = binaryStr.substring(i, i + 8);
                    // 2进制转成16进制
                    String hexString = binaryStrToHexString(str);
                    sb.append(hexString);
                }
                bmpHexList.add(sb.toString());
            }

            // 数据指令
            commandList.addAll(bmpHexList);
        }
        ArrayList<byte[]> data = new ArrayList<>();
        for (String hexStr : commandList) {
            data.add(hexStringToBytes(hexStr));
        }
        return data;
    }

    /**
     * 2进制转成16进制
     *
     * @param binaryStr 2进制串
     * @return 16进制串
     */
    @SuppressWarnings("unused")
    public static String binaryStrToHexString(String binaryStr) {
        String hex = "";
        String f4 = binaryStr.substring(0, 4);
        String b4 = binaryStr.substring(4, 8);
        for (int i = 0; i < binaryArray.length; i++) {
            if (f4.equals(binaryArray[i]))
                hex += hexStr.substring(i, i + 1);
        }
        for (int i = 0; i < binaryArray.length; i++) {
            if (b4.equals(binaryArray[i]))
                hex += hexStr.substring(i, i + 1);
        }
        return hex;
    }

    /**
     * 16进制指令list转换为byte[]指令
     *
     * @param list 指令集
     * @return byte[]指令
     */
    @SuppressWarnings("unused")
    public static byte[] hexListToByte(List<String> list) {
        ArrayList<byte[]> commandList = new ArrayList<>();
        for (String hexStr : list) {
            commandList.add(hexStringToBytes(hexStr));
        }
        int len = 0;
        for (byte[] srcArray : commandList) {
            len += srcArray.length;
        }
        byte[] destArray = new byte[len];
        int destLen = 0;
        for (byte[] srcArray : commandList) {
            System.arraycopy(srcArray, 0, destArray, destLen, srcArray.length);
            destLen += srcArray.length;
        }
        return destArray;
    }

    /**
     * 16进制串转byte数组
     *
     * @param hexString 16进制串
     * @return byte数组
     */
    @SuppressWarnings("unused")
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    /**
     * 16进制char 转 byte
     *
     * @param c char
     * @return byte
     */
    private static byte charToByte(char c) {
        return (byte) hexStr.indexOf(c);
    }

采用该位图方式基本上都能在热敏打印机上打印出二维码,但缺点就是慢,要是能接受,可以采用这种方式。

就先写到这,如果以后有新的进展再来补充。






显示全文