diff --git a/build.gradle b/build.gradle index 4300c01..b5e486b 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,8 @@ dependencies { implementation 'org.springframework.security:spring-security-ldap' implementation 'org.liquibase:liquibase-core' implementation 'com.google.code.gson:gson:2.8.5' + implementation("org.apache.pdfbox:pdfbox:2.0.29") + implementation("org.apache.pdfbox:fontbox:2.0.34") // // https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui // implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.8") diff --git a/src/main/java/com/ffii/core/utils/ZebraPrinterUtil.kt b/src/main/java/com/ffii/core/utils/ZebraPrinterUtil.kt new file mode 100644 index 0000000..12c21ca --- /dev/null +++ b/src/main/java/com/ffii/core/utils/ZebraPrinterUtil.kt @@ -0,0 +1,109 @@ +package com.ffii.core.utils + +import org.apache.pdfbox.pdmodel.PDDocument +import org.apache.pdfbox.rendering.PDFRenderer +import org.apache.pdfbox.rendering.ImageType +import java.awt.image.BufferedImage +import java.io.File +import java.io.OutputStream +import java.net.Socket +import java.util.* + +/** + * Utility class for generating and sending print jobs to a Zebra printer using ZPL. + * This class requires the 'org.apache.pdfbox:pdfbox' dependency to be included in your project. + */ +open class ZebraPrinterUtil { + + companion object { + + /** + * Converts the first page of a PDF document into a ZPL command and sends it to the specified printer. + * + * @param pdfFile The PDF file to be printed. + * @param printerIp The IP address of the Zebra printer. + * @param printerPort The port of the Zebra printer, typically 9100. + * @throws Exception if there is an error during file processing or printing. + */ + @JvmStatic + fun printPdfToZebra(pdfFile: File, printerIp: String, printerPort: Int) { + + // Check if the file exists and is readable + if (!pdfFile.exists() || !pdfFile.canRead()) { + throw IllegalArgumentException("Error: File not found or not readable at path: ${pdfFile.absolutePath}") + } + + try { + // 1. Load the PDF document + PDDocument.load(pdfFile).use { document -> + val renderer = PDFRenderer(document) + + // 2. Render the first page of the PDF as a monochrome image + // The '300 / 72f' scales the image to 300 DPI. + val image = renderer.renderImage(0, 300 / 72f, ImageType.BINARY) + + // 3. Convert the image to a ZPL format string + val zplCommand = convertImageToZpl(image) + + // 4. Send the ZPL command to the printer via a network socket + val printData = zplCommand.toByteArray() + Socket(printerIp, printerPort).use { socket -> + val os: OutputStream = socket.getOutputStream() + os.write(printData) + os.flush() + } + } + } catch (e: Exception) { + // Re-throw the exception with a more descriptive message + throw Exception("Error processing print job for PDF: ${e.message}", e) + } + } + + /** + * Converts a BufferedImage (monochrome) to a ZPL string using the ^GFA command. + * This function handles the conversion of pixel data to a compressed hex string. + * + * @param image The BufferedImage to convert. + * @return A ZPL-formatted string ready to be sent to the printer. + */ + private fun convertImageToZpl(image: BufferedImage): String { + val zpl = StringBuilder() + zpl.append("^XA\n") + + val width = image.width + val height = image.height + + // ZPL format for a graphical image is ^GFA + val bytesPerRow = (width + 7) / 8 + val totalBytes = bytesPerRow * height + + zpl.append("^FO0,0^GFA,").append(totalBytes).append(",").append(totalBytes).append(",").append(bytesPerRow).append(",") + + // Extract pixel data and convert to ZPL hex string + for (y in 0 until height) { + val rowBits = BitSet(width) + for (x in 0 until width) { + // Check for a black pixel (0xFF000000 in ARGB) + if (image.getRGB(x, y) == 0xFF000000.toInt()) { + rowBits.set(x) + } + } + + for (i in 0 until bytesPerRow) { + var byteValue = 0 + for (bit in 0 until 8) { + if (rowBits.get(i * 8 + bit)) { + byteValue = byteValue or (1 shl (7 - bit)) + } + } + zpl.append(String.format("%02X", byteValue)) + } + } + + zpl.append("^FS\n") + zpl.append("^XZ\n") + + return zpl.toString() + } + } +}