Reputation: 883
I have two Bluetooth thermal printers as well as an integrated device.
One of the printers doesn't support QR codes via 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.
The problem I'm facing is that when I print the QR image the other printer stalls mid-image.
I must restart the printer for it to work properly, otherwise it'll print garbage.
The source file has the following characteristics:
It's loaded into a kotlin Bitmap as such:
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
and .outWidth
don't seem to have any effect on dimensions (probably used for screen rendering?). The rawBmp
object has the following characteristics:
Since the width is too small it must be scaled with:
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
}
This changes the characteristics to
Since the width is a multiple of 8 it doesn't need to be padded.
I then setup the 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)
I must have 1 bit per pixel or the image will be distorted. I achieve it with this (adapted from 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
}
}
The final parameters are:
I then send it fo the OutputStream of the Bluetooth socket and the printer chokes on the image.
I'm testing with multiple devices with different Android versions, ABIs, Bluetooth versions and architectures - occasionally it'll print on one device or another, must it mostly fails.
If using some demo apps from the net, the printer does print images, so I assume I'm doing something wrong.
Perhaps the image is too big for the buffer?
Edit 1
On a simple test using text1 + image + text2, it'll print text1 and image if i flush the stream; but won't print text2, i.e.:
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()
The QR code is readable but i must still restart the printer. So I must be overflowing something...
Upvotes: 1
Views: 1583
Reputation: 883
Turns out the problem wasn't in the printer buffer, missing ESC/POS command or data size.
I must wait before closing the Bluetooth socket otherwise there may be unsent data.
So,
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()
Upvotes: 3