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