问题描述
其中一台打印机不支持通过 GS ( k .. 49 发送 QR 码,因此我通过将 file.bmp 加载到 Bitmap kotlin 类中然后通过 GS v 0 作为图像发送来进行打印。>
我面临的问题是,当我打印 QR 图像时,另一台打印机停止在图像中间。
我必须重新启动打印机才能正常工作,否则会打印垃圾。
源文件具有以下特点:
- 82x82 像素
- 2.9x2.9 厘米的打印尺寸(必须为 3 厘米)
- 每像素 24 位
- 2 种颜色
它被加载到一个 kotlin Bitmap 中:
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
对象具有以下特征:
- 82x82 像素
- 总字节数:26896
- 每行字节数:328
- 每像素字节数:4
由于宽度太小,必须缩放:
if(inBmp.width < 264) {
val startTime = System.nanoTime()
qrBmp = Bitmap.createScaledBitmap(inBmp,264,true)
val endTime = System.nanoTime()
val duration = endTime - startTime
wasScaled = true
}
这将特性更改为
- 264x264 像素
- 总字节数 278784
- 每行字节数 1056
- 每像素字节数 4
由于宽度是 8 的倍数,所以不需要填充。
然后我设置 GS v 0 标头:
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()),imageBytes,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()
二维码是可读的,但我仍然必须重新启动打印机。所以我一定是溢出了一些东西......
解决方法
结果证明问题不在打印机缓冲区、缺少 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()