diff --git a/src/main/java/com/ffii/core/utils/CanonPrinterUtil.kt b/src/main/java/com/ffii/core/utils/CanonPrinterUtil.kt new file mode 100644 index 0000000..fd0e8e9 --- /dev/null +++ b/src/main/java/com/ffii/core/utils/CanonPrinterUtil.kt @@ -0,0 +1,138 @@ +package com.ffii.core.utils + +import org.apache.pdfbox.pdmodel.PDDocument +import java.io.File +import java.io.OutputStream +import java.net.Socket + +/** + * Utility class for generating and sending print jobs to a Canon printer using PDF. + * This class requires the 'org.apache.pdfbox:pdfbox' dependency to be included in your project. + */ +open class CanonPrinterUtil { + + // Enum to represent valid duplex print modes + enum class DuplexMode { + SIMPLEX, // Single-sided printing + DUPLEX_LONG_EDGE, // Double-sided, flip on long edge (portrait binding) + DUPLEX_SHORT_EDGE // Double-sided, flip on short edge (landscape binding) + } + + companion object { + /** + * Detects if a PDF document is in landscape (horizontal) orientation. + * + * @param pdfFile The PDF file to check. + * @return true if the document is landscape, false if portrait. + */ + fun isLandscape(pdfFile: File): Boolean { + if (!pdfFile.exists() || !pdfFile.canRead()) { + return false + } + + try { + PDDocument.load(pdfFile).use { document -> + if (document.numberOfPages > 0) { + val page = document.getPage(0) + val mediaBox = page.mediaBox + val width = mediaBox.width + val height = mediaBox.height + + // Landscape if width > height + return width > height + } + } + } catch (e: Exception) { + println("DEBUG: Error detecting PDF orientation: ${e.message}") + } + return false + } + + /** + * Sends a PDF document directly to the specified Canon printer with duplex printing support. + * + * @param pdfFile The PDF file to be printed. + * @param printerIp The IP address of the Canon printer. + * @param printerPort The port of the Canon printer, typically 9100. + * @param printQty The qty of print, default 1 + * @param duplexMode Valid values: SIMPLEX (single-sided), DUPLEX_LONG_EDGE (portrait), DUPLEX_SHORT_EDGE (landscape) + * @throws Exception if there is an error during file processing or printing. + */ + fun printPdfToCanon(pdfFile: File, printerIp: String, printerPort: Int, printQty: Int? = 1, duplexMode: DuplexMode = DuplexMode.DUPLEX_LONG_EDGE) { + + // 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. Build PJL (Printer Job Language) commands for Canon printers + val pjlCommands = StringBuilder() + + // PJL job start + pjlCommands.append("\u001B%-12345X@PJL\n") + pjlCommands.append("@PJL JOB NAME=\"PDF Print\"\n") + + // 2. Set duplex mode + when (duplexMode) { + DuplexMode.SIMPLEX -> { + pjlCommands.append("@PJL SET DUPLEX=OFF\n") + } + DuplexMode.DUPLEX_LONG_EDGE -> { + pjlCommands.append("@PJL SET DUPLEX=ON\n") + pjlCommands.append("@PJL SET BINDING=LONGEDGE\n") + } + DuplexMode.DUPLEX_SHORT_EDGE -> { + pjlCommands.append("@PJL SET DUPLEX=ON\n") + pjlCommands.append("@PJL SET BINDING=SHORTEDGE\n") + } + } + + // 3. Set number of copies + if ((printQty ?: 1) > 1) { + pjlCommands.append("@PJL SET COPIES=${printQty ?: 1}\n") + } + + // 4. Set paper size to A4 + pjlCommands.append("@PJL SET PAPER=A4\n") + + // 5. Enter PDF language mode + pjlCommands.append("@PJL ENTER LANGUAGE=PDF\n") + + // PJL job end marker (before PDF content) + pjlCommands.append("\u001B%-12345X\n") + + // 6. Read PDF file content + val pdfContent = pdfFile.readBytes() + + println("DEBUG: PDF file size: ${pdfContent.size} bytes") + + // Print each copy for the specified quantity + repeat(printQty ?: 1) { copyIndex -> + println("DEBUG: Printing copy ${copyIndex + 1} of ${printQty ?: 1}") + + // 7. Send to printer via TCP/IP socket + Socket(printerIp, printerPort).use { socket -> + val os: OutputStream = socket.getOutputStream() + + // Send PJL commands + val printData = pjlCommands.toString().toByteArray(Charsets.US_ASCII) + os.write(printData) + + // Send PDF content + os.write(pdfContent) + + // Send job end marker + os.write("\u001B%-12345X".toByteArray(Charsets.US_ASCII)) + + os.flush() + println("DEBUG: Copy ${copyIndex + 1} sent to printer") + } + } + } catch (e: Exception) { + // Re-throw the exception with a more descriptive message + throw Exception("Error processing print job for PDF: ${e.message}", e) + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt index 87cce23..1a737c8 100644 --- a/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt @@ -71,6 +71,7 @@ import com.ffii.fpsms.modules.stock.entity.SuggestPickLotRepository import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService // 添加这行 import com.ffii.fpsms.modules.deliveryOrder.web.models.* import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository // 添加 +import com.ffii.core.utils.CanonPrinterUtil @Service open class DeliveryOrderService( @@ -928,36 +929,53 @@ open class DeliveryOrderService( ) } - //Print Delivery Note - @Transactional - open fun printDeliveryNote(request: PrintDeliveryNoteRequest) { - //val printer = printerService.findById(request.printerId) ?: throw java.util.NoSuchElementException("No such printer") - - val pdf = exportDeliveryNote( - ExportDeliveryNoteRequest( - doPickOrderId = request.doPickOrderId, - numOfCarton = request.numOfCarton, - isDraft = request.isDraft - ) + //Print Delivery Note + @Transactional + open fun printDeliveryNote(request: PrintDeliveryNoteRequest) { + val printer = printerService.findById(request.printerId) ?: throw java.util.NoSuchElementException("No such printer") + + val pdf = exportDeliveryNote( + ExportDeliveryNoteRequest( + doPickOrderId = request.doPickOrderId, + numOfCarton = request.numOfCarton, + isDraft = request.isDraft ) + ) - val jasperPrint = pdf["report"] as JasperPrint + val jasperPrint = pdf["report"] as JasperPrint - val tempPdfFile = File.createTempFile("print_job_", ".pdf") + val tempPdfFile = File.createTempFile("print_job_", ".pdf") - try { - JasperExportManager.exportReportToPdfFile(jasperPrint, tempPdfFile.absolutePath) + try { + JasperExportManager.exportReportToPdfFile(jasperPrint, tempPdfFile.absolutePath) - //val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty - //printer.ip?.let { ip -> printer.port?.let { port -> - // ZebraPrinterUtil.printPdfToZebra(tempPdfFile, ip, port, printQty, ZebraPrinterUtil.PrintDirection.ROTATED) - //}} - } finally { - //tempPdfFile.delete() + val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty + + val duplexMode = if (CanonPrinterUtil.isLandscape(tempPdfFile)) { + CanonPrinterUtil.DuplexMode.DUPLEX_SHORT_EDGE // Landscape: flip on short edge + } else { + CanonPrinterUtil.DuplexMode.DUPLEX_LONG_EDGE // Portrait: flip on long edge } + println("DEBUG: PDF orientation detected - Landscape: ${CanonPrinterUtil.isLandscape(tempPdfFile)}, Duplex mode: $duplexMode") + + printer.ip?.let { ip -> + printer.port?.let { port -> + CanonPrinterUtil.printPdfToCanon( + tempPdfFile, + ip, + port, + printQty, + duplexMode + ) + } + } + } finally { + //tempPdfFile.delete() } + } + //Carton Labels open fun exportDNLabels(request: ExportDNLabelsRequest): Map { val DNLABELS_PDF = "DeliveryNote/DeliveryNoteCartonLabelsPDF.jrxml" diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt index 89daab1..3df4c29 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt @@ -2,6 +2,7 @@ package com.ffii.fpsms.modules.jobOrder.service import com.ffii.core.exception.BadRequestException import com.ffii.core.response.RecordsRes +import com.ffii.core.utils.CanonPrinterUtil import com.ffii.core.utils.GsonUtils import com.ffii.core.utils.PdfUtils import com.ffii.core.utils.QrCodeUtil @@ -395,7 +396,7 @@ open class JobOrderService( params["FGCode"] = pickRecordInfo.firstOrNull()?.get("fgCode") as? String ?: "N/A" params["FGName"] = pickRecordInfo.firstOrNull()?.get("fgName") as? String ?: "N/A" - /*// Debug UOM information + /* Debug UOM information val bomItemUomIdRaw = pickRecordInfo.firstOrNull()?.get("bomItemUomId") val bomItemId = pickRecordInfo.firstOrNull()?.get("bomItemId") val uomCode = pickRecordInfo.firstOrNull()?.get("uomCode") as? String @@ -420,9 +421,10 @@ open class JobOrderService( ) } + //Print Pick Record @Transactional open fun printPickRecord(request: PrintPickRecordRequest){ - //val printer = printerService.findById(request.printerId) ?: throw java.util.NoSuchElementException("No such printer") + val printer = printerService.findById(request.printerId) ?: throw java.util.NoSuchElementException("No such printer") val pdf = exportPickRecord( ExportPickRecordRequest( @@ -437,8 +439,30 @@ open class JobOrderService( try{ JasperExportManager.exportReportToPdfFile(jasperPrint,tempPdfFile.absolutePath) + val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty + + // Auto-detect orientation and set duplex mode accordingly + val duplexMode = if (CanonPrinterUtil.isLandscape(tempPdfFile)) { + CanonPrinterUtil.DuplexMode.DUPLEX_SHORT_EDGE // Landscape: flip on short edge + } else { + CanonPrinterUtil.DuplexMode.DUPLEX_LONG_EDGE // Portrait: flip on long edge + } + + println("DEBUG: PDF orientation detected - Landscape: ${CanonPrinterUtil.isLandscape(tempPdfFile)}, Duplex mode: $duplexMode") + + printer.ip?.let { ip -> + printer.port?.let { port -> + CanonPrinterUtil.printPdfToCanon( + tempPdfFile, + ip, + port, + printQty, + duplexMode + ) + } + } } finally { - //tempPdfFile.delete + //tempPdfFile.delete() } } diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt index d5cb103..e4f65f1 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/BomService.kt @@ -492,7 +492,7 @@ open class BomService( // val folder = File(folderPath) val resolver = PathMatchingResourcePatternResolver() // val excels = resolver.getResources("bomImport/*.xlsx") - val excels = resolver.getResources("file:C:/Users/ffii_/Downloads/bom/new/*.xlsx") + val excels = resolver.getResources("file:C:/Users/Kelvin YAU/Downloads/bom/*.xlsx") // val excels = resolver.getResources("file:C:/Users/2Fi/Desktop/Third Wave of BOM Excel/*.xlsx") println("size: ${excels.size}") val logExcel = ClassPathResource("excelTemplate/bom_excel_issue_log.xlsx")