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

使用Xamarin.Forms在通用蓝牙打印机打印二维码失败求助

蓝牙打印机二维码打印问题解决方案

问题核心

你直接向打印机写入二维码的原始图片字节(Base64解码后的PNG/JPG数据),但通用蓝牙打印机需要遵循ESC/POS指令集处理图片打印,无法直接识别图片文件字节。打印机演示能正常打印是因为演示程序已封装了正确的指令逻辑。


解决方案步骤

1. 转换二维码为ESC/POS兼容的单色位图数据

需要先将Base64格式的二维码图片解码为Bitmap,再转换为打印机可识别的单色位图,最后用ESC/POS指令包裹数据。以下是Android平台的实现代码:

// 将Base64二维码转为Bitmap
private Bitmap GetQrCodeBitmap(string base64Data)
{
    byte[] imageBytes = Convert.FromBase64String(base64Data);
    using (var stream = new MemoryStream(imageBytes))
    {
        return BitmapFactory.DecodeStream(stream);
    }
}

// 把Bitmap转换为ESC/POS打印指令字节数组
private byte[] ConvertBitmapToEscPos(Bitmap bitmap)
{
    // 适配80mm打印机宽度(约384点,58mm打印机改为203)
    int targetWidth = 384;
    int targetHeight = (int)((float)bitmap.Height * targetWidth / bitmap.Width);
    Bitmap resizedBitmap = Bitmap.CreateScaledBitmap(bitmap, targetWidth, targetHeight, true);

    // 转为单色位图
    Bitmap monochromeBitmap = Bitmap.CreateBitmap(resizedBitmap.Width, resizedBitmap.Height, Bitmap.Config.Rgb565);
    Canvas canvas = new Canvas(monochromeBitmap);
    Paint paint = new Paint();
    ColorMatrix colorMatrix = new ColorMatrix();
    colorMatrix.SetSaturation(0);
    ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
    paint.SetColorFilter(filter);
    canvas.DrawBitmap(resizedBitmap, 0, 0, paint);

    // 构造ESC/POS指令
    List<byte> escPosData = new List<byte>();
    // 初始化打印机
    escPosData.AddRange(new byte[] { 0x1B, 0x40 });
    // 设置图片打印模式(位图模式)
    escPosData.AddRange(new byte[] { 0x1B, 0x2A, 0x01 });
    // 写入图片宽度(字节数,低字节在前)
    int widthBytes = (resizedBitmap.Width + 7) / 8;
    escPosData.Add((byte)(widthBytes & 0xFF));
    escPosData.Add((byte)((widthBytes >> 8) & 0xFF));
    // 写入图片高度(低字节在前)
    escPosData.Add((byte)(resizedBitmap.Height & 0xFF));
    escPosData.Add((byte)((resizedBitmap.Height >> 8) & 0xFF));

    // 写入单色位图数据
    for (int y = 0; y < resizedBitmap.Height; y++)
    {
        for (int xByte = 0; xByte < widthBytes; xByte++)
        {
            byte b = 0;
            for (int bit = 0; bit < 8; bit++)
            {
                int x = xByte * 8 + bit;
                if (x < resizedBitmap.Width)
                {
                    Color color = monochromeBitmap.GetPixel(x, y);
                    // 黑色像素设为1,白色设为0(根据打印机灰度阈值调整)
                    if (color.R < 128)
                    {
                        b |= (byte)(1 << (7 - bit));
                    }
                }
            }
            escPosData.Add(b);
        }
    }

    // 换行+切纸指令
    escPosData.AddRange(new byte[] { 0x0A, 0x0A });
    escPosData.AddRange(new byte[] { 0x1D, 0x56, 0x41, 0x00 });

    // 回收Bitmap释放资源
    resizedBitmap.Recycle();
    monochromeBitmap.Recycle();
    return escPosData.ToArray();
}

2. 修改打印逻辑,发送正确的ESC/POS指令

更新你的Print方法,替换直接写入原始二维码字节的逻辑,改为发送转换后的ESC/POS指令:

public async Task Print(string deviceName, string text)
{
    BluetoothDevice device = (from bd in bluetoothAdapter?.BondedDevices
                              where bd?.Name == deviceName
                              select bd).FirstOrDefault(); 

    string qrCodeData = Base64CodeQR();
    Bitmap qrBitmap = GetQrCodeBitmap(qrCodeData);
    byte[] escPosQrBytes = ConvertBitmapToEscPos(qrBitmap);
    qrBitmap.Recycle();

    try
    {                 
        BluetoothSocket bluetoothSocket = device?.CreateRfcommSocketToServiceRecord(
            UUID.FromString("00001101-0000-1000-8000-00805f9b34fb"));

        bluetoothSocket?.Connect();
      
        // 打印文本(可选,根据需求保留)
        if (!string.IsNullOrEmpty(text))
        {
            // 用GB2312编码适配中文打印,若打印机支持UTF-8可替换
            byte[] textBytes = Encoding.GetEncoding("GB2312").GetBytes(text + "\n");
            bluetoothSocket?.OutputStream.Write(textBytes, 0, textBytes.Length);
        }

        // 发送二维码打印指令
        bluetoothSocket?.OutputStream.Write(escPosQrBytes, 0, escPosQrBytes.Length);                 

        await Task.Delay(1000);

        bluetoothSocket.Close();
    }
    catch(Exception exp)
    {                
        throw exp;
    }
}

3. 额外注意事项

  • 字符编码:打印中文时需确认打印机支持的编码(如GB2312或UTF-8),对应调整Encoding.GetEncoding的参数。
  • 打印机宽度:不同规格打印机的打印宽度不同,80mm对应384点,58mm对应203点,需根据你的设备修改targetWidth值。
  • 权限配置:Android平台需在AndroidManifest.xml中添加蓝牙相关权限:
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:07:23