Xamarin POS系统集成蓝牙打印机打印图片失败问题求助
Xamarin POS系统蓝牙打印机图片打印异常问题
我在Xamarin开发的POS系统中,纯文本打印功能正常,但图片打印出现异常。将图片转为字节数组发送给蓝牙打印机后无法正常输出,打印出的logo效果如下:

使用的蓝牙打印机设备如下:

以下是当前使用的打印服务代码:
public class PrintServiceRenderer { private static byte[] SELECT_BIT_IMAGE_MODE = { 0x1B, 0x2A, 33, 255, 0 }; public IList<string> GetDeviceList() { using (BluetoothAdapter bluetoothAdapter = BluetoothAdapter.DefaultAdapter) { var btdevice = bluetoothAdapter?.BondedDevices.Select(i => i.Name).ToList(); return btdevice; } } public async Task Print(string deviceName, byte[] imageData) { using (BluetoothAdapter bluetoothAdapter = BluetoothAdapter.DefaultAdapter) { MemoryStream stream = new MemoryStream(); BluetoothDevice device = (from bd in bluetoothAdapter?.BondedDevices where bd?.Name == deviceName select bd).FirstOrDefault(); try { using (BluetoothSocket bluetoothSocket = device?. CreateRfcommSocketToServiceRecord( UUID.FromString("00001101-0000-1000-8000-00805f9b34fb"))) { bluetoothSocket?.Connect(); stream.Write(imageData, 0, imageData.Length); stream.Write(SELECT_BIT_IMAGE_MODE, 0, SELECT_BIT_IMAGE_MODE.Length); var bytes = stream.ToArray(); bluetoothSocket?.OutputStream.Write(bytes, 0, bytes.Length); bluetoothSocket.Close(); } } catch (Exception exp) { throw exp; } } } }
问题根源分析
- 指令顺序错误:当前代码先发送图片数据,再发送位图模式指令,打印机未进入位图解析模式就收到图片数据,无法正确解析。
- 位图指令参数不匹配:
SELECT_BIT_IMAGE_MODE中的宽度参数(255, 0)对应255像素宽度,若你的打印机是80mm宽(标准384像素),该参数会导致图片显示异常。 - 图片数据格式不符合要求:打印机需要的是单色位图数据(1bit/像素,仅黑/白),而非原始JPG/PNG的字节数组,需先做格式转换。
修正后的代码实现
1. 添加图片转单色位图数据的工具方法
private byte[] ConvertImageToPrinterBitmap(Image image, int printerWidth = 384) { // 调整图片尺寸适配打印机宽度 var scaledImage = ScaleImage(image, printerWidth); // 转为灰度图再转单色位图 using (var bitmap = new Bitmap(scaledImage)) { var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat); var bytesPerRow = (bitmap.Width + 7) / 8; // 每行字节数(1bit/像素) var totalBytes = bytesPerRow * bitmap.Height; var result = new byte[totalBytes]; unsafe { byte* ptr = (byte*)bitmapData.Scan0; int stride = bitmapData.Stride; for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { // 取灰度值,低于阈值设为黑色(1),否则白色(0) byte gray = ptr[y * stride + x * 3]; int bitPos = x % 8; int bytePos = y * bytesPerRow + x / 8; if (gray < 128) { result[bytePos] |= (byte)(0x80 >> bitPos); } } } } bitmap.UnlockBits(bitmapData); return result; } } private Image ScaleImage(Image image, int targetWidth) { var ratio = (double)targetWidth / image.Width; var targetHeight = (int)(image.Height * ratio); var scaled = new Bitmap(targetWidth, targetHeight); using (var g = Graphics.FromImage(scaled)) { g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.DrawImage(image, 0, 0, targetWidth, targetHeight); } return scaled; }
2. 修正Print方法的指令顺序与参数
public async Task Print(string deviceName, Image image) { using (BluetoothAdapter bluetoothAdapter = BluetoothAdapter.DefaultAdapter) { if (bluetoothAdapter == null) throw new InvalidOperationException("蓝牙未启用"); BluetoothDevice device = bluetoothAdapter.BondedDevices.FirstOrDefault(bd => bd?.Name == deviceName); if (device == null) throw new ArgumentException("未找到指定蓝牙设备"); try { using (BluetoothSocket bluetoothSocket = device.CreateRfcommSocketToServiceRecord( UUID.FromString("00001101-0000-1000-8000-00805f9b34fb"))) { await bluetoothSocket.ConnectAsync(); // 转换图片为打印机兼容格式 var bitmapData = ConvertImageToPrinterBitmap(image); int imageWidth = 384; // 80mm打印机标准宽度,根据你的设备调整 // 构造正确的位图模式指令:ESC * m nL nH 数据 // m=33 表示24点密度的位图模式 byte nL = (byte)(imageWidth % 256); byte nH = (byte)(imageWidth / 256); byte[] selectBitImageMode = { 0x1B, 0x2A, 33, nL, nH }; // 先发送模式指令,再发送图片数据 await bluetoothSocket.OutputStream.WriteAsync(selectBitImageMode, 0, selectBitImageMode.Length); await bluetoothSocket.OutputStream.WriteAsync(bitmapData, 0, bitmapData.Length); // 发送换行指令,确保图片打印完成 await bluetoothSocket.OutputStream.WriteAsync(new byte[] { 0x0A }, 0, 1); bluetoothSocket.Close(); } } catch (Exception exp) { throw exp; } } }
额外注意事项
- 确认打印机的最大打印宽度,调整
printerWidth参数(58mm打印机一般是203像素,80mm是384像素)。 - 若使用Xamarin.Forms,需将共享项目中的Image转为平台特定的Bitmap(Android用
Android.Graphics.Bitmap,iOS用UIKit.UIImage),可通过依赖服务实现跨平台处理。 - 图片转换时的灰度阈值(128)可根据实际打印效果调整,优化黑白对比度。
内容的提问来源于stack exchange,提问作者Đức Khánh Hoàng
相关产品推荐
相关产品推荐

