You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android端Chrome打印服务热敏打印图片出现乱码求助

解决热敏打印乱码问题:Bitmap转打印字节数组的替代方案

你的问题核心是自定义位图转码逻辑与部分热敏打印机的ESC/POS指令兼容性不匹配,导致乱码。以下是几种更稳定的替代方案:

方案1:使用标准GS v0 图形打印指令(通用兼容性)

GS v0是绝大多数热敏打印机支持的标准位图打印指令,比你当前使用的ESC *兼容性更好,直接生成该指令对应的字节数组,避免指令转换带来的异常。

代码实现

import android.graphics.Bitmap;
import android.graphics.Color;

public class PrintUtils {
    // 将Bitmap转为GS v0格式的打印字节数组
    public static byte[] bitmapToGSv0Bytes(Bitmap bitmap) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int bytesPerLine = (width + 7) / 8; // 每行字节数(8点1字节)
        int totalLength = 8 + bytesPerLine * height; // 指令头+位图数据长度

        byte[] result = new byte[totalLength];
        // GS v0指令头:GS v 0 0 xL xH yL yH
        result[0] = 0x1D; // GS
        result[1] = 0x76; // v
        result[2] = 0x30; // 0
        result[3] = 0x00; // 密度设置(0为通用8点/毫米)
        
        // 写入宽度(低字节在前)
        result[4] = (byte) (bytesPerLine & 0xFF);
        result[5] = (byte) ((bytesPerLine >> 8) & 0xFF);
        // 写入高度(低字节在前)
        result[6] = (byte) (height & 0xFF);
        result[7] = (byte) ((height >> 8) & 0xFF);

        // 填充黑白二值化位图数据
        int dataIndex = 8;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x += 8) {
                byte b = 0;
                for (int bit = 0; bit < 8; bit++) {
                    int posX = x + bit;
                    if (posX >= width) break;
                    int color = bitmap.getPixel(posX, y);
                    // 二值化阈值:RGB总和小于480判定为黑色(可根据打印效果调整)
                    boolean isBlack = (Color.red(color) + Color.green(color) + Color.blue(color)) < 480;
                    if (isBlack) {
                        b |= (1 << (7 - bit));
                    }
                }
                result[dataIndex++] = b;
            }
        }
        return result;
    }

    // 打印调用示例
    public void printBitmap(Bitmap bitmap) throws Exception {
        // 发送初始化指令,清除打印机残留状态
        mOutputStream.write(new byte[]{0x1B, 0x40});
        // 设置行间距为24点(适配图形打印)
        mOutputStream.write(new byte[]{0x1B, 0x33, 0x18});
        // 写入图形数据
        mOutputStream.write(bitmapToGSv0Bytes(bitmap));
        // 换行3次
        mOutputStream.write(new byte[]{0x0A, 0x0A, 0x0A});
        mOutputStream.flush();
    }
}

方案2:使用成熟的ESC/POS打印库

避免自己实现底层转码逻辑,直接使用经过兼容性测试的开源库,比如Java平台的escpos-coffee或Android专属的ThermalPrinterLibrary,这些库已处理不同打印机的指令差异。

示例(escpos-coffee库)

import com.github.anastaciocintra.escpos.EscPos;
import com.github.anastaciocintra.escpos.image.Bitlow;
import com.github.anastaciocintra.escpos.image.CoffeeImage;
import com.github.anastaciocintra.output.PrinterOutputStream;

import java.awt.image.BufferedImage;
import android.graphics.Bitmap;

public void printWithLibrary(Bitmap bitmap) throws Exception {
    // 将Android Bitmap转为库兼容的BufferedImage
    BufferedImage bufferedImage = convertBitmapToBufferedImage(bitmap);
    CoffeeImage coffeeImage = new CoffeeImage(bufferedImage);
    
    PrinterOutputStream printerStream = new PrinterOutputStream(mOutputStream);
    EscPos escpos = new EscPos(printerStream);
    
    // 打印图像+换行
    escpos.write(new Bitlow().write(coffeeImage));
    escpos.feed(3);
    escpos.close();
}

// 辅助转换方法:Android Bitmap转BufferedImage
private BufferedImage convertBitmapToBufferedImage(Bitmap bitmap) {
    // 实现像素逐行拷贝逻辑,可参考开源工具类实现
    // 此处省略具体转换代码
}

方案3:简化二值化逻辑

你当前的bitmapToBytes包含复杂的梯度灰度处理,可能在部分设备上导致解析异常。简化为基础黑白二值化,减少兼容性风险:

public static byte[] simpleBitmapToBytes(Bitmap bitmap) {
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int bytesPerLine = (width + 7) / 8;
    byte[] imageBytes = new byte[bytesPerLine * height];
    
    int index = 0;
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x += 8) {
            byte b = 0;
            for (int k = 0; k < 8; k++) {
                int posX = x + k;
                if (posX >= width) break;
                int color = bitmap.getPixel(posX, y);
                int gray = (Color.red(color) + Color.green(color) + Color.blue(color)) / 3;
                // 灰度小于128标记为黑色
                if (gray < 128) {
                    b |= 1 << (7 - k);
                }
            }
            imageBytes[index++] = b;
        }
    }
    return imageBytes;
}

额外建议

  • 打印前必发初始化指令0x1B, 0x40,避免之前的指令残留影响
  • 针对乱码设备,查阅其官方文档确认支持的图形打印指令集,优先使用厂商推荐的格式
  • 测试不同的行间距设置,确保图像完整输出

内容的提问来源于stack exchange,提问作者MrX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 15:58:26