package com.utopiaindustries.service; import com.google.zxing.BarcodeFormat; import com.itextpdf.io.font.constants.StandardFonts; import com.itextpdf.io.image.ImageData; import com.itextpdf.kernel.colors.ColorConstants; import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.kernel.pdf.PdfPage; import com.itextpdf.kernel.pdf.canvas.PdfCanvas; import com.itextpdf.layout.element.AreaBreak; import com.itextpdf.layout.element.Paragraph; import com.itextpdf.layout.property.AreaBreakType; import com.itextpdf.layout.property.TextAlignment; import com.utopiaindustries.dao.ctp.*; import com.utopiaindustries.model.ctp.*; import com.utopiaindustries.util.BarcodeUtils; import com.utopiaindustries.util.StringUtils; import com.zebra.sdk.comm.Connection; import com.zebra.sdk.comm.TcpConnection; import com.zebra.sdk.graphics.internal.ZebraImage; import com.zebra.sdk.printer.ZebraPrinter; import com.zebra.sdk.printer.ZebraPrinterFactory; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.PDFRenderer; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.itextpdf.io.image.ImageDataFactory; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.geom.PageSize; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Image; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.net.Socket; import java.nio.file.Path; import java.nio.file.FileSystems; import java.awt.*; import java.awt.Font; import java.io.*; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import com.google.zxing.EncodeHintType; import com.google.zxing.qrcode.QRCodeWriter; import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; import java.io.ByteArrayInputStream; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; @Service public class BarcodeService { @Value("${ctp.printer.ipAdd}") private String ipAddress; @Value("${ctp.printer.port}") private int port; @Value("${ctp.printer.bundleIpAdd}") private String bundleIpAddress; @Value("${ctp.printer.bundlePort}") private int bundlePort; @Value("${ctp.printer.stitchQRPath}") private String stitchPath; @Value("${ctp.printer.bundlePath}") private String bundlePath; private final BundleDAO bundleDAO; private final JobCardDAO jobCardDAO; private final MasterBundleDAO masterBundleDAO; private final FinishedItemDAO finishedItemDAO; private final StitchingOfflineItemDAO stitchingOfflineItemDAO; private final InventoryAccountDAO inventoryAccountDAO; private final JobCardItemDAO jobCardItemDAO; public BarcodeService(BundleDAO bundleDAO, JobCardDAO jobCardDAO, MasterBundleDAO masterBundleDAO, FinishedItemDAO finishedItemDAO, StitchingOfflineItemDAO stitchingOfflineItemDAO, InventoryAccountDAO inventoryAccountDAO, JobCardItemDAO jobCardItemDAO) { this.bundleDAO = bundleDAO; this.jobCardDAO = jobCardDAO; this.masterBundleDAO = masterBundleDAO; this.finishedItemDAO = finishedItemDAO; this.stitchingOfflineItemDAO = stitchingOfflineItemDAO; this.inventoryAccountDAO = inventoryAccountDAO; this.jobCardItemDAO = jobCardItemDAO; } /* * generate barcodes here * */ public void generateBarcodes(List ids, String artifactType) throws Exception { String size = BarcodeStickerSize.SIZE_4_X_7.name(); BarcodeStickerSize stickerSize = BarcodeStickerSize.getSize(size); List list = new ArrayList<>(); if (StringUtils.compare(artifactType, Bundle.class.getSimpleName())) { list = bundleDAO.findByIds(ids); getBarcodeImages(list, stickerSize, artifactType); } else if (StringUtils.compare(artifactType, MasterBundle.class.getSimpleName())) { list = masterBundleDAO.findByIds(ids); getBarcodeImages(list, stickerSize, artifactType); } else if (StringUtils.compare(artifactType, FinishedItem.class.getSimpleName())) { String sizeForStitchItem = BarcodeStickerSize.SIZE_1_X_2.name(); BarcodeStickerSize stickerSizeForStitchItem = BarcodeStickerSize.getSize(sizeForStitchItem); list = stitchingOfflineItemDAO.findByIds(ids); getBarcodeImagesForStitchItems(list, stickerSizeForStitchItem); } } public void getBarcodeImages(List artifacts, BarcodeStickerSize stickerSize, String artifactType) throws Exception { int pageWidth = 900; // A4 Width int pageHeight = 1200; // A4 Height int rows = 3; int cols = 2; int totalStickersPerPage = rows * cols; int totalPages = (int) Math.ceil((double) artifacts.size() / totalStickersPerPage); // Total required pages List bufferedImages = new ArrayList<>(); for (int page = 0; page < totalPages; page++) { // Create a Blank A4 Page Image BufferedImage a4Image = new BufferedImage(pageWidth, pageHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = a4Image.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, pageWidth, pageHeight); g2d.setColor(Color.BLACK); Font barcodeFont = new Font("Helvetica", Font.BOLD, stickerSize.getTextSize()+8); Font detailFont = new Font("Helvetica", Font.PLAIN, stickerSize.getTextSize()+3); for (int i = 0; i < totalStickersPerPage; i++) { int artifactIndex = page * totalStickersPerPage + i; if (artifactIndex >= artifacts.size()) break; InventoryArtifact artifact = artifacts.get(artifactIndex); int row = i / cols; int col = i % cols; int x = col * (pageWidth / cols); int y = row * (pageHeight / rows); Font stickerFont = new Font("Helvetica", Font.BOLD, stickerSize.getTextSize() + 10); g2d.setFont(stickerFont); // Draw Sticker Border g2d.drawRect(x, y, pageWidth / cols, pageHeight / rows); this.drawCenteredText(g2d, artifactType.equals("Bundle")?"Sub-Bundle":"Master-Bundle", detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop()-20,stickerSize.getMarginLeft()-165); // Draw SKU g2d.setFont(barcodeFont); String sku = artifact.getSku(); FontMetrics fontMetrics = g2d.getFontMetrics(); int textWidth = fontMetrics.stringWidth(sku); g2d.drawString(sku, x + ((pageWidth / cols) - textWidth) / 2, y + fontMetrics.getAscent()+ stickerSize.getMarginTop()+10); // Generate Barcode Image g2d.setFont(barcodeFont); byte[] imgBytes = BarcodeUtils.getBarcodeImageByteArray( artifact.getBarcode(), BarcodeFormat.CODE_128, stickerSize.getImageWidthBarcode(), stickerSize.getImageHeightBarcode()); BufferedImage barcodeImage = ImageIO.read(new ByteArrayInputStream(imgBytes)); int originalBarcodeWidth = barcodeImage.getWidth(); int originalBarcodeHeight = barcodeImage.getHeight(); double scaleX = (double)(pageWidth / cols) / originalBarcodeWidth; double scaleY = (double)(pageHeight / rows) / originalBarcodeHeight; double scaleFactor = Math.min(scaleX, scaleY); int scaledWidth = (int)(originalBarcodeWidth * scaleFactor) - 30; int scaledHeight = (int)(originalBarcodeHeight * scaleFactor) - 10; BufferedImage scaledBarcodeImage = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g2dBarcode = scaledBarcodeImage.createGraphics(); g2dBarcode.drawImage(barcodeImage, 0, 0, scaledWidth, scaledHeight, null); g2dBarcode.dispose(); int barcodeX = x + ((pageWidth / cols) - scaledBarcodeImage.getWidth()) / 2; int barcodeY = y + fontMetrics.getAscent() + stickerSize.getMarginTop() + 20; g2d.drawImage(scaledBarcodeImage, barcodeX, barcodeY, null); //barcode text g2d.setFont(barcodeFont); String barcode = artifact.getBarcode(); FontMetrics barcodeFontMatrix = g2d.getFontMetrics(); int barcodeText = fontMetrics.stringWidth(barcode); g2d.drawString(barcode, (x + ((pageWidth / cols) - barcodeText) / 2), y + barcodeFontMatrix.getAscent() + stickerSize.getMarginTop() + 120); //draw item-id DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm"); this.drawCenteredText(g2d, "Item-ID: "+artifact.getItemId(), detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop() + 150,stickerSize.getMarginLeft()); this.drawCenteredText(g2d, "Created-By: "+artifact.getCreatedBy(), detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop() + 170,stickerSize.getMarginLeft()); this.drawCenteredText(g2d, "Created-At: "+formatter.format(artifact.getCreatedAt()), detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop() + 190,stickerSize.getMarginLeft()); //draw job-card details JobCard jobCard = jobCardDAO.find(artifact.getJobCardId()); this.drawCenteredText(g2d, "Job-Card: " + jobCard.getCode(), detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop() + 210, stickerSize.getMarginLeft() ); this.drawCenteredText(g2d, "Purchase-Order: " + jobCard.getPurchaseOrderId(), detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop() + 230, stickerSize.getMarginLeft()); this.drawCenteredText(g2d, "Lot-Number: " + jobCard.getLotNumber(), detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop() + 250, stickerSize.getMarginLeft()); if((artifactType.equals("Bundle"))){ JobCardItem jobCardItem = jobCardItemDAO.findByCardId(jobCard.getId()).get(0); InventoryAccount inventoryAccount = inventoryAccountDAO.find(jobCardItem.getAccountId()); this.drawCenteredText(g2d, "Pieces: " + artifact.getWrapQuantity(), detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop() + 270, stickerSize.getMarginLeft()); this.drawCenteredText(g2d, "Cutting-Account: " + inventoryAccount.getTitle() , detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop() + 290, stickerSize.getMarginLeft()); }else { List bundles = bundleDAO.findByMasterId(artifact.getId()); String concatenatedIds = bundles.stream() .map(bundle -> String.valueOf(bundle.getId())) .collect(Collectors.joining("\\")); int totalWrappedQuantity = bundles.stream() .mapToInt(e->e.getWrapQuantity().intValue()) .sum(); this.drawCenteredText(g2d, "Total-Pieces: " + totalWrappedQuantity, detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop() + 270, stickerSize.getMarginLeft()); this.drawCenteredText(g2d, "Sub-Bundles: " + concatenatedIds, detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop() + 290, stickerSize.getMarginLeft()); } this.drawCenteredText(g2d, String.valueOf(artifact.getId()), detailFont, x, y, pageWidth, cols, stickerSize.getMarginTop() + 330, 0); } g2d.dispose(); bufferedImages.add(a4Image); } saveA4ImageToPDF(bufferedImages,bundlePath); try { printPDF(bundleIpAddress, bundlePath); } catch (Exception e) { e.printStackTrace(); } } public void printPDF(String printerIP, String pdfFilePath) throws Exception { File pdfFile = new File(pdfFilePath); FileInputStream fis = new FileInputStream(pdfFile); Socket printerSocket = new Socket(printerIP, port); OutputStream out = printerSocket.getOutputStream(); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } out.flush(); out.close(); fis.close(); printerSocket.close(); } public void saveA4ImageToPDF(List a4Images, String outputFilePath) throws Exception { PdfWriter writer = new PdfWriter(outputFilePath); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc, PageSize.A4); document.setMargins(0, 0, 0, 0); for (int i = 0; i < a4Images.size(); i++) { if (i > 0) { document.add(new AreaBreak(AreaBreakType.NEXT_PAGE)); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(a4Images.get(i), "png", baos); byte[] imageBytes = baos.toByteArray(); ImageData imageData = ImageDataFactory.create(imageBytes); Image image = new Image(imageData); // Scale and position correctly image.scaleToFit(pdfDoc.getDefaultPageSize().getWidth(), pdfDoc.getDefaultPageSize().getHeight()); image.setFixedPosition(0, 30); document.add(image); } document.close(); } private void drawCenteredText(Graphics2D g2d, String text, Font font, int x, int y, int pageWidth, int cols, int marginTop, int marginLeft) { g2d.setFont(font); FontMetrics fontMetrics = g2d.getFontMetrics(); int adjustedX = (x + ((pageWidth / cols) ) / 2) - marginLeft; int adjustedY = y + fontMetrics.getAscent() + marginTop; g2d.drawString(text, adjustedX, adjustedY); } public void getBarcodeImagesForStitchItems(List artifacts, BarcodeStickerSize stickerSize) throws Exception { Path pdfPath = FileSystems.getDefault().getPath(stitchPath); PdfWriter writer = new PdfWriter(pdfPath.toFile()); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); pdfDoc.setDefaultPageSize(new PageSize(stickerSize.getWidth(), stickerSize.getHeight())); for (InventoryArtifact artifact : artifacts) { JobCard jobCard = jobCardDAO.find(artifact.getJobCardId()); PdfPage page = pdfDoc.addNewPage(); PdfCanvas canvas = new PdfCanvas(page); canvas.setFillColor(ColorConstants.WHITE); canvas.rectangle(0, 0, stickerSize.getWidth(), stickerSize.getHeight()); canvas.fill(); // Draw QR Code on Left Side byte[] imgBytes = generateQRCodeImageByteArray(artifact.getBarcode(), stickerSize.getImageWidthBarcode(), stickerSize.getImageHeightBarcode()); Image qrCodeImage = new Image(ImageDataFactory.create(imgBytes)); float qrY =stickerSize.getMarginLeft()+50 ; float qrX = stickerSize.getMarginTop(); qrCodeImage.setFixedPosition(qrX - 16, qrY-40); document.add(qrCodeImage); float textX = stickerSize.getMarginLeft() + 40; float textY = stickerSize.getMarginTop() + 40; String sku = artifact.getSku(); String jobCardCode = jobCard.getCode(); String combinedText = sku + " \\ " + jobCardCode + " \\ " + artifact.getBundleId(); combinedText = combinedText.replaceAll("\\s+", ""); int maxLength = 14; List lines = new ArrayList<>(); for (int i = 0; i < 5; i++) { int start = i * maxLength; if (start < combinedText.length()) { String part = combinedText.substring(start, Math.min(start + maxLength, combinedText.length())).replaceAll("\\s+", ""); lines.add(part); } } float labelWidth = 67.69f; float textBoxWidth = 220; float textY1 = textY-23; float textXCenter = textX + (labelWidth / 2) - (textBoxWidth / 2)-50; for (String line : lines) { Paragraph rotatedText = new Paragraph(line) .setFontColor(ColorConstants.BLACK) .setFontSize(stickerSize.getTextSize() + 2) .setTextAlignment(TextAlignment.CENTER) .setFixedPosition(textXCenter, textY1-18, textBoxWidth); document.add(rotatedText); textY1 -= 6; } PdfFont font = PdfFontFactory.createFont(StandardFonts.COURIER_OBLIQUE); String id = String.valueOf(artifact.getId()); document.add(new Paragraph(id) .setFont(font) .setBold() .setFontColor(ColorConstants.BLACK) .setFontSize(stickerSize.getTextSize() + 8) .setTextAlignment(TextAlignment.LEFT) .setFixedPosition(textX - 25, textY + 13, 100)); float dottedLine = textY - 65; for(int i= 0 ;i<16;i++){ document.add(new Paragraph("|") .setFontSize(stickerSize.getTextSize()) .setFontColor(ColorConstants.BLACK) .setBold() .setRotationAngle(-Math.PI / 2) .setTextAlignment(TextAlignment.LEFT) .setFixedPosition(dottedLine,textX+45, 100)); dottedLine += 7; } float dottedLine2 = textY - 65; for(int i= 0 ;i<16;i++){ document.add(new Paragraph("|") .setFontSize(stickerSize.getTextSize()) .setFontColor(ColorConstants.BLACK) .setBold() .setRotationAngle(-Math.PI / 2) .setTextAlignment(TextAlignment.LEFT) .setFixedPosition(dottedLine2,textX+80, 100)); dottedLine2 += 7; } if (!artifact.equals(artifacts.get(artifacts.size() - 1))) { document.add(new AreaBreak()); } } if (pdfDoc.getNumberOfPages() > artifacts.size()) { pdfDoc.removePage(pdfDoc.getNumberOfPages()); } document.close(); sendPdfToZebraPrinter(pdfPath.toFile()); } public void sendPdfToZebraPrinter(File pdfFile) throws Exception { PDDocument pdDocument = PDDocument.load(pdfFile); PDFRenderer renderer = new PDFRenderer(pdDocument); for (int pageIndex = 0; pageIndex < pdDocument.getNumberOfPages(); pageIndex++) { BufferedImage pageImage = renderer.renderImageWithDPI(pageIndex, 320); printLabel(pageImage); } pdDocument.close(); } public byte[] generateQRCodeImageByteArray(String data, int width, int height) { try { QRCodeWriter qrCodeWriter = new QRCodeWriter(); Map hintMap = new HashMap<>(); hintMap.put(EncodeHintType.MARGIN, 0); BitMatrix bitMatrix = qrCodeWriter.encode(data, BarcodeFormat.QR_CODE, width, height, hintMap); BufferedImage qrImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); qrImage.createGraphics().fillRect(0, 0, width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { qrImage.setRGB(x, y, bitMatrix.get(x, y) ? 0x000000 : 0xFFFFFF); // Black and white } } ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(qrImage, "PNG", baos); return baos.toByteArray(); } catch (WriterException | IOException e) { e.printStackTrace(); return new byte[0]; } } public void printLabel(BufferedImage bufferedImage) throws Exception { Connection connection = new TcpConnection(ipAddress, port); connection.open(); ZebraPrinter printer = ZebraPrinterFactory.getInstance(connection); ZebraImage zebraImage = new ZebraImage(bufferedImage); printer.printImage(zebraImage, 0, 0, 0, 0, false); connection.close(); } }