|
|
|
@@ -42,31 +42,102 @@ open class PlasticBagPrinterService( |
|
|
|
): ByteArray { |
|
|
|
val baos = ByteArrayOutputStream() |
|
|
|
ZipOutputStream(baos).use { zos -> |
|
|
|
|
|
|
|
// Reduced heights by ~80% to hit the 12KB-17KB file size target |
|
|
|
// These heights (100-180) will produce much smaller BMP files |
|
|
|
val productBmp = createMonochromeBitmap(productName, 100) |
|
|
|
val codeBmp = createMonochromeBitmap(itemCode, 180) |
|
|
|
val expBmp = createMonochromeBitmap(expiryDate, 180) |
|
|
|
val qrBmp = createQrCodeBitmap("$itemCode|$lotNo|$expiryDate", 250) |
|
|
|
|
|
|
|
val nameFile = "${lotNo}_product.bmp" |
|
|
|
val codeFile = "${lotNo}_code.bmp" |
|
|
|
val expFile = "${lotNo}_expiry.bmp" |
|
|
|
val qrFile = "${lotNo}_qr.bmp" |
|
|
|
val productBmp = createMonochromeBitmap(productName, 300) |
|
|
|
val codeBmp = createMonochromeBitmap(itemCode, 200) |
|
|
|
val expBmp = createMonochromeBitmap(expiryDate, 180) |
|
|
|
val qrBmp = createQrCodeBitmap("$itemCode|$lotNo|$expiryDate", 600) |
|
|
|
val lotBmp = createMonochromeBitmap(lotNo, 180) |
|
|
|
|
|
|
|
// All file names now prefixed with "TT_$itemCode" |
|
|
|
val prefix = "TT_$itemCode" |
|
|
|
val nameFile = "${prefix}_product.bmp" |
|
|
|
val codeFile = "${prefix}_code.bmp" |
|
|
|
val expFile = "${prefix}_expiry.bmp" |
|
|
|
val qrFile = "${prefix}_qr.bmp" |
|
|
|
val lotFile = "${prefix}_lot.bmp" |
|
|
|
|
|
|
|
addToZip(zos, nameFile, productBmp.bytes) |
|
|
|
addToZip(zos, codeFile, codeBmp.bytes) |
|
|
|
addToZip(zos, expFile, expBmp.bytes) |
|
|
|
addToZip(zos, qrFile, qrBmp.bytes) |
|
|
|
|
|
|
|
// The XML Width/Height must match the ACTUAL generated bitmap dimensions |
|
|
|
val imageXml = """<?xml version="1.0" encoding="utf-8"?><Legend type="Image.V1" PaperColor="White" BackgroundColor="DarkGray"><DeviceInfo DeviceURI="NGEDriver[SDX40c.cadb94e3-677c-4bcb-9c80-88bf9526d6fe]/"><DeviceName /></DeviceInfo><Description /><Mirrored>false</Mirrored><Orientation>DEG_0</Orientation><Width>5300</Width><Height>7100</Height><FieldList><Logo type="Logo.V1"><Name>LOGO</Name><ID>4</ID><Geometry><X>250</X><Y>500</Y><Rotation>0</Rotation></Geometry><FieldColor>BLACK</FieldColor><FileName>$nameFile</FileName><Width>${productBmp.width}</Width><Height>100</Height></Logo><Logo type="Logo.V1"><Name>LOGO_2</Name><ID>5</ID><Geometry><X>1000</X><Y>1250</Y><Rotation>0</Rotation></Geometry><FieldColor>BLACK</FieldColor><FileName>$codeFile</FileName><Width>${codeBmp.width}</Width><Height>180</Height></Logo><Logo type="Logo.V1"><Name>LOGO_3</Name><ID>6</ID><Geometry><X>500</X><Y>2500</Y><Rotation>0</Rotation></Geometry><FieldColor>BLACK</FieldColor><FileName>$expFile</FileName><Width>${expBmp.width}</Width><Height>180</Height></Logo><Logo type="Logo.V1"><Name>LOGO_4</Name><ID>7</ID><Geometry><X>500</X><Y>3750</Y><Rotation>0</Rotation></Geometry><FieldColor>BLACK</FieldColor><FileName>$qrFile</FileName><Width>${qrBmp.width}</Width><Height>250</Height></Logo></FieldList></Legend>""".trimIndent() |
|
|
|
|
|
|
|
addToZip(zos, "$lotNo.image", imageXml.toByteArray(Charsets.UTF_8)) |
|
|
|
|
|
|
|
val jobXml = """<?xml version="1.0" encoding="utf-8"?><Job><ImageFileName>$lotNo.image</ImageFileName></Job>""" |
|
|
|
addToZip(zos, "$lotNo.job", jobXml.toByteArray(Charsets.UTF_8)) |
|
|
|
addToZip(zos, lotFile, lotBmp.bytes) |
|
|
|
addToZip(zos, qrFile, qrBmp.bytes) |
|
|
|
|
|
|
|
val imageXml = """ |
|
|
|
<Legend type="Image.V1" PaperColor="White" BackgroundColor="DarkGray"> |
|
|
|
<DeviceInfo DeviceURI="NGEDriver[SDX40c.cadb94e3-677c-4bcb-9c80-88bf9526d6fe]/"> |
|
|
|
<DeviceName /> |
|
|
|
</DeviceInfo> |
|
|
|
<Description /> |
|
|
|
<Mirrored>false</Mirrored> |
|
|
|
<Orientation>DEG_0</Orientation> |
|
|
|
<Width>5300</Width> |
|
|
|
<Height>7100</Height> |
|
|
|
<FieldList> |
|
|
|
<Logo type="Logo.V1"> |
|
|
|
<Name>LOGO</Name> |
|
|
|
<ID>4</ID> |
|
|
|
<Geometry> |
|
|
|
<X>250</X> |
|
|
|
<Y>500</Y> |
|
|
|
<Rotation>0</Rotation> |
|
|
|
</Geometry> |
|
|
|
<FieldColor>BLACK</FieldColor> |
|
|
|
<FileName>$nameFile</FileName> |
|
|
|
<Width>4914</Width> |
|
|
|
<Height>845</Height> |
|
|
|
</Logo> |
|
|
|
<Logo type="Logo.V1"> |
|
|
|
<Name>LOGO_2</Name> |
|
|
|
<ID>5</ID> |
|
|
|
<Geometry> |
|
|
|
<X>1000</X> |
|
|
|
<Y>1250</Y> |
|
|
|
<Rotation>0</Rotation> |
|
|
|
</Geometry> |
|
|
|
<FieldColor>BLACK</FieldColor> |
|
|
|
<FileName>$codeFile</FileName> |
|
|
|
<Width>3205</Width> |
|
|
|
<Height>1173</Height> |
|
|
|
</Logo> |
|
|
|
<Logo type="Logo.V1"> |
|
|
|
<Name>LOGO_3</Name> |
|
|
|
<ID>6</ID> |
|
|
|
<Geometry> |
|
|
|
<X>500</X> |
|
|
|
<Y>2500</Y> |
|
|
|
<Rotation>0</Rotation> |
|
|
|
</Geometry> |
|
|
|
<FieldColor>BLACK</FieldColor> |
|
|
|
<FileName>$expFile</FileName> |
|
|
|
<Width>4203</Width> |
|
|
|
<Height>1173</Height> |
|
|
|
</Logo> |
|
|
|
<Logo type="Logo.V1"> |
|
|
|
<Name>LOGO_4</Name> |
|
|
|
<ID>7</ID> |
|
|
|
<Geometry> |
|
|
|
<X>500</X> |
|
|
|
<Y>3750</Y> |
|
|
|
<Rotation>0</Rotation> |
|
|
|
</Geometry> |
|
|
|
<FieldColor>BLACK</FieldColor> |
|
|
|
<FileName>$qrFile</FileName> |
|
|
|
<Width>4353</Width> |
|
|
|
<Height>3000</Height> |
|
|
|
</Logo> |
|
|
|
</FieldList> |
|
|
|
</Legend>""".trimIndent() |
|
|
|
|
|
|
|
// Image and Job files now also use TT_$itemCode |
|
|
|
addToZip(zos, "$prefix.image", imageXml.toByteArray(Charsets.UTF_8)) |
|
|
|
|
|
|
|
val jobXml = """ |
|
|
|
<Job> |
|
|
|
<ImageFileName>$prefix.image</ImageFileName> |
|
|
|
</Job>""".trimIndent() |
|
|
|
|
|
|
|
addToZip(zos, "$prefix.job", jobXml.toByteArray(Charsets.UTF_8)) |
|
|
|
} |
|
|
|
return baos.toByteArray() |
|
|
|
} |
|
|
|
@@ -137,22 +208,31 @@ open class PlasticBagPrinterService( |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private fun createQrCodeBitmap(content: String, size: Int): BitmapResult { |
|
|
|
private fun createQrCodeBitmap(content: String, contentSize: Int, totalSize: Int = contentSize + 80): BitmapResult { |
|
|
|
if (totalSize < contentSize) throw IllegalArgumentException("totalSize must be >= contentSize") |
|
|
|
|
|
|
|
val writer = QRCodeWriter() |
|
|
|
val bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, size, size) |
|
|
|
val image = BufferedImage(size, size, BufferedImage.TYPE_BYTE_BINARY) |
|
|
|
for (x in 0 until size) { |
|
|
|
for (y in 0 until size) { |
|
|
|
image.setRGB(x, y, if (bitMatrix.get(x, y)) Color.BLACK.rgb else Color.WHITE.rgb) |
|
|
|
val bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, contentSize, contentSize) |
|
|
|
|
|
|
|
val image = BufferedImage(totalSize, totalSize, BufferedImage.TYPE_BYTE_BINARY) |
|
|
|
val g = image.createGraphics() |
|
|
|
g.color = Color.WHITE |
|
|
|
g.fillRect(0, 0, totalSize, totalSize) |
|
|
|
|
|
|
|
val offset = (totalSize - contentSize) / 2 |
|
|
|
for (x in 0 until contentSize) { |
|
|
|
for (y in 0 until contentSize) { |
|
|
|
if (bitMatrix.get(x, y)) { |
|
|
|
image.setRGB(x + offset, y + offset, Color.BLACK.rgb) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Crop QR code (removes quiet zone excess if any) |
|
|
|
val cropped = cropToContent(image) |
|
|
|
g.dispose() |
|
|
|
|
|
|
|
val baos = ByteArrayOutputStream() |
|
|
|
ImageIO.write(cropped, "bmp", baos) |
|
|
|
return BitmapResult(baos.toByteArray(), cropped.width) |
|
|
|
ImageIO.write(image, "bmp", baos) |
|
|
|
|
|
|
|
return BitmapResult(baos.toByteArray(), image.width) |
|
|
|
} |
|
|
|
|
|
|
|
private fun cropToContent(img: BufferedImage): BufferedImage { |
|
|
|
|