打印图像时热敏打印机停止
thermal printer stalls when printing image
我有两台蓝牙热敏打印机和一台集成设备。
其中一台打印机不支持通过 GS ( k .. 49, so I'm printing by loading a file.bmp into a Bitmap kotlin class and then sending as image via GS v 0 的二维码。
我面临的问题是,当我打印 QR 图像时,另一台打印机停止运行 mid-image。
我必须重启打印机才能正常工作,否则会打印乱码。
源文件具有以下特点:
- 82x82 像素
- 2.9x2.9 cm 打印尺寸(需为 3 cm)
- 每像素 24 位
- 2 种颜色
它像这样加载到 kotlin 位图中:
var bfo = BitmapFactory.Options()
bfo.outHeight = 20
bfo.outWidth = 20
bfo.inJustDecodeBounds = false
val fRawBmp = File(qrCodeRawFilePath)
val rawBmp = BitmapFactory.decodeFile(fRawBmp.absolutePath, bfo)
.outHeight
和 .outWidth
似乎对尺寸没有任何影响(可能用于屏幕渲染?)。 rawBmp
object具有以下特点:
- 82x82 像素
- 总字节数:26896
- 每行字节数:328
- 每像素字节数:4
由于宽度太小,必须按比例缩放:
if(inBmp.width < 264) {
val startTime = System.nanoTime()
qrBmp = Bitmap.createScaledBitmap(inBmp, 264, 264, true)
val endTime = System.nanoTime()
val duration = endTime - startTime
wasScaled = true
}
这会将特征更改为
- 264x264px
- 总字节数 278784
- 每行字节数 1056
- 字节每像素 4
因为宽度是 8 的倍数,所以不需要填充。
然后我设置 GS v 0 header:
val bytesPerLine = ceil((widthInPx.toFloat() / 8f).toDouble()).toInt()
val m = 0 // 0-3
val xH = bytesPerLine / 256
val xL = bytesPerLine - xH * 256
val yH = heightInPx / 256
val yL = heightInPx - yH * 256
val imageBytes = ByteArray(8 + bytesPerLine * heightInPx)
System.arraycopy(byteArrayOf(0x1D, 0x76, 0x30, m.toByte(), xL.toByte(), xH.toByte(), yL.toByte(), yH.toByte()), 0, imageBytes, 0, 8)
每个像素必须有 1 位,否则图像会失真。我是这样实现的(改编自 ESCPOS-ThermalPrinter):
var i = 8
for (posY in 0 until heightInPx) {
var jj = 0
while (jj < widthInPx) {
val stringBinary = StringBuilder()
for (k in 0..7) {
val posX = jj + k
if (posX < widthInPx) {
val color: Int = qrBmp.getPixel(posX, posY)
val r = color shr 16 and 0xff
val g = color shr 8 and 0xff
val b = color and 0xff
if (r > 160 && g > 160 && b > 160) {
stringBinary.append("0")
} else {
stringBinary.append("1")
}
} else {
stringBinary.append("0")
}
}
imageBytes[i++] = stringBinary.toString().toInt(2).toByte()
jj += 8
}
}
最终参数为:
- 米: 0
- xL: 33 字节
- xH: 0 字节
- yL: 8 点
- yH: 1 点
- k: 8712
- 数据:8720 字节 (8+k)
然后我将它发送到蓝牙插座的 OutputStream,打印机在图像上扼流圈。
我正在使用具有不同 Android 版本、ABI、蓝牙版本和架构的多台设备进行测试 - 偶尔 它会在一台或另一台设备上打印,必须它大多失败了。
如果使用来自网络的一些演示应用程序,打印机会打印图像,所以我认为我做错了什么。
也许图片对于缓冲区来说太大了?
编辑 1
在使用 text1 + image + text2 的简单测试中,如果我刷新流,它将打印 text1 和 image;但不会打印 text2,即:
bt.outStream!!.write(byteArrayOf(0x1B, 0x74, 0x02)) // ESC t codepage PC437 USA Standard Europe
bt.outStream?.write("text1\n".toByteArray(Charsets.ISO_8859_1))
br.outStream?.flush()
var bfo = BitmapFactory.Options()
bfo.inJustDecodeBounds = false
val fRawBmp = File(path2file)
val rawBmp = BitmapFactory.decodeFile(fRawBmp.absolutePath, bfo)
bt.outStream?.write(bmp2Bytes(rawBmp))
bt.outStream?.flush()
bt.outStream?.write("text2\n\n\n".toByteArray(Charsets.ISO_8859_1))
bt.outStream?.flush()
bt.outStream?.close()
bt.inStream?.close()
bt.socket?.close()
QR 代码可读,但我仍必须重新启动打印机。所以我一定是溢出了一些东西...
原来问题不在打印机缓冲区,缺少 ESC/POS 命令或数据大小。
关闭蓝牙套接字之前必须等待,否则可能有未发送的数据。
所以,
Thread.sleep(400) // 200ms is enough for _most_ devices I tested
bt.outStream?.write("text2\n\n\n".toByteArray(Charsets.ISO_8859_1))
bt.outStream?.flush()
bt.outStream?.close()
bt.inStream?.close()
bt.socket?.close()
我有两台蓝牙热敏打印机和一台集成设备。
其中一台打印机不支持通过 GS ( k .. 49, so I'm printing by loading a file.bmp into a Bitmap kotlin class and then sending as image via GS v 0 的二维码。
我面临的问题是,当我打印 QR 图像时,另一台打印机停止运行 mid-image。
我必须重启打印机才能正常工作,否则会打印乱码。
源文件具有以下特点:
- 82x82 像素
- 2.9x2.9 cm 打印尺寸(需为 3 cm)
- 每像素 24 位
- 2 种颜色
它像这样加载到 kotlin 位图中:
var bfo = BitmapFactory.Options()
bfo.outHeight = 20
bfo.outWidth = 20
bfo.inJustDecodeBounds = false
val fRawBmp = File(qrCodeRawFilePath)
val rawBmp = BitmapFactory.decodeFile(fRawBmp.absolutePath, bfo)
.outHeight
和 .outWidth
似乎对尺寸没有任何影响(可能用于屏幕渲染?)。 rawBmp
object具有以下特点:
- 82x82 像素
- 总字节数:26896
- 每行字节数:328
- 每像素字节数:4
由于宽度太小,必须按比例缩放:
if(inBmp.width < 264) {
val startTime = System.nanoTime()
qrBmp = Bitmap.createScaledBitmap(inBmp, 264, 264, true)
val endTime = System.nanoTime()
val duration = endTime - startTime
wasScaled = true
}
这会将特征更改为
- 264x264px
- 总字节数 278784
- 每行字节数 1056
- 字节每像素 4
因为宽度是 8 的倍数,所以不需要填充。
然后我设置 GS v 0 header:
val bytesPerLine = ceil((widthInPx.toFloat() / 8f).toDouble()).toInt()
val m = 0 // 0-3
val xH = bytesPerLine / 256
val xL = bytesPerLine - xH * 256
val yH = heightInPx / 256
val yL = heightInPx - yH * 256
val imageBytes = ByteArray(8 + bytesPerLine * heightInPx)
System.arraycopy(byteArrayOf(0x1D, 0x76, 0x30, m.toByte(), xL.toByte(), xH.toByte(), yL.toByte(), yH.toByte()), 0, imageBytes, 0, 8)
每个像素必须有 1 位,否则图像会失真。我是这样实现的(改编自 ESCPOS-ThermalPrinter):
var i = 8
for (posY in 0 until heightInPx) {
var jj = 0
while (jj < widthInPx) {
val stringBinary = StringBuilder()
for (k in 0..7) {
val posX = jj + k
if (posX < widthInPx) {
val color: Int = qrBmp.getPixel(posX, posY)
val r = color shr 16 and 0xff
val g = color shr 8 and 0xff
val b = color and 0xff
if (r > 160 && g > 160 && b > 160) {
stringBinary.append("0")
} else {
stringBinary.append("1")
}
} else {
stringBinary.append("0")
}
}
imageBytes[i++] = stringBinary.toString().toInt(2).toByte()
jj += 8
}
}
最终参数为:
- 米: 0
- xL: 33 字节
- xH: 0 字节
- yL: 8 点
- yH: 1 点
- k: 8712
- 数据:8720 字节 (8+k)
然后我将它发送到蓝牙插座的 OutputStream,打印机在图像上扼流圈。
我正在使用具有不同 Android 版本、ABI、蓝牙版本和架构的多台设备进行测试 - 偶尔 它会在一台或另一台设备上打印,必须它大多失败了。
如果使用来自网络的一些演示应用程序,打印机会打印图像,所以我认为我做错了什么。
也许图片对于缓冲区来说太大了?
编辑 1
在使用 text1 + image + text2 的简单测试中,如果我刷新流,它将打印 text1 和 image;但不会打印 text2,即:
bt.outStream!!.write(byteArrayOf(0x1B, 0x74, 0x02)) // ESC t codepage PC437 USA Standard Europe
bt.outStream?.write("text1\n".toByteArray(Charsets.ISO_8859_1))
br.outStream?.flush()
var bfo = BitmapFactory.Options()
bfo.inJustDecodeBounds = false
val fRawBmp = File(path2file)
val rawBmp = BitmapFactory.decodeFile(fRawBmp.absolutePath, bfo)
bt.outStream?.write(bmp2Bytes(rawBmp))
bt.outStream?.flush()
bt.outStream?.write("text2\n\n\n".toByteArray(Charsets.ISO_8859_1))
bt.outStream?.flush()
bt.outStream?.close()
bt.inStream?.close()
bt.socket?.close()
QR 代码可读,但我仍必须重新启动打印机。所以我一定是溢出了一些东西...
原来问题不在打印机缓冲区,缺少 ESC/POS 命令或数据大小。
关闭蓝牙套接字之前必须等待,否则可能有未发送的数据。
所以,
Thread.sleep(400) // 200ms is enough for _most_ devices I tested
bt.outStream?.write("text2\n\n\n".toByteArray(Charsets.ISO_8859_1))
bt.outStream?.flush()
bt.outStream?.close()
bt.inStream?.close()
bt.socket?.close()