| @@ -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() | |||||
| } | |||||
| } | |||||
| } | |||||