From dd4df6fd70c5aa2769ae6ac213a4c039dbfa765f Mon Sep 17 00:00:00 2001 From: "vluk@2fi-solutions.com.hk" Date: Mon, 22 Dec 2025 12:20:59 +0800 Subject: [PATCH] Added borther pdf printer utils --- .../com/ffii/core/utils/BrotherPrinterUtil.kt | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/main/java/com/ffii/core/utils/BrotherPrinterUtil.kt diff --git a/src/main/java/com/ffii/core/utils/BrotherPrinterUtil.kt b/src/main/java/com/ffii/core/utils/BrotherPrinterUtil.kt new file mode 100644 index 0000000..4a0a395 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/BrotherPrinterUtil.kt @@ -0,0 +1,101 @@ +package com.ffii.core.utils + +import org.apache.pdfbox.pdmodel.PDDocument +import org.apache.pdfbox.rendering.PDFRenderer +import java.awt.image.BufferedImage +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.OutputStream +import java.net.Socket +import javax.imageio.ImageIO + +class BrotherPrinterUtil { + + companion object { + /** + * //Usage + * BrotherPrinterUtil.printToBrother( + pdfFile = File("document.pdf"), + printerIp = "192.168.1.50", + printQty = 1 + ) + + * Sends a PDF to a Brother DCP-1610W by rendering it to a PCL-compatible format. + */ + fun printToBrother(pdfFile: File, printerIp: String, printerPort: Int = 9100, printQty: Int = 1) { + if (!pdfFile.exists()) throw IllegalArgumentException("File not found.") + + PDDocument.load(pdfFile).use { document -> + val renderer = PDFRenderer(document) + + Socket(printerIp, printerPort).use { socket -> + val os = socket.getOutputStream() + + // 1. Start PJL Job + os.write(getPjlHeader(printQty)) + + // 2. Render each page as a 300 DPI image + for (pageIndex in 0 until document.numberOfPages) { + val image = renderer.renderImageWithDPI(pageIndex, 300f, org.apache.pdfbox.rendering.ImageType.BINARY) + + // 3. Convert Image to PCL Raster Data + val pclData = convertImageToPcl(image) + os.write(pclData) + + // Form Feed (Move to next page) + os.write("\u000C".toByteArray()) + } + + // 4. End Job + os.write("\u001B%-12345X".toByteArray(Charsets.US_ASCII)) + os.flush() + } + } + } + + private fun getPjlHeader(qty: Int): ByteArray { + val pjl = StringBuilder() + pjl.append("\u001B%-12345X@PJL\r\n") + pjl.append("@PJL SET COPIES=$qty\r\n") + pjl.append("@PJL ENTER LANGUAGE=PCL\r\n") // Tell printer to expect PCL graphics, not PDF + return pjl.toString().toByteArray(Charsets.US_ASCII) + } + + /** + * Converts a BufferedImage into PCL Level 3/5 Raster Graphics. + * This is the "magic" that allows a budget printer to print a PDF. + */ + private fun convertImageToPcl(image: BufferedImage): ByteArray { + val out = ByteArrayOutputStream() + val width = image.width + val height = image.height + + // PCL: Start Graphics at 300 DPI + out.write("\u001B*t300R".toByteArray()) + // PCL: Start Raster Graphics + out.write("\u001B*r1A".toByteArray()) + + for (y in 0 until height) { + val rowBytes = (width + 7) / 8 + val data = ByteArray(rowBytes) + + for (x in 0 until width) { + // Check if pixel is dark (threshold) + val color = image.getRGB(x, y) + val isBlack = (color and 0xFF) < 128 + if (isBlack) { + data[x / 8] = (data[x / 8].toInt() or (0x80 ushr (x % 8))).toByte() + } + } + + // PCL: Transfer Raster Data (Esc * b # W [data]) + out.write("\u001B*b${rowBytes}W".toByteArray()) + out.write(data) + } + + // PCL: End Raster Graphics + out.write("\u001B*rB".toByteArray()) + return out.toByteArray() + } + } +} \ No newline at end of file