import os import zipfile import gradio as gr import pandas as pd from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import inch import barcode from barcode.writer import ImageWriter # Directories for generated files LABELS_DIR = "labels" BARCODES_DIR = "barcodes" os.makedirs(LABELS_DIR, exist_ok=True) os.makedirs(BARCODES_DIR, exist_ok=True) # Required CSV/Excel columns REQUIRED_COLUMNS = [ "Order ID", "Order Date", "Shipper Name", "Shipper Address", "Shipper Phone", "Receiver Name", "Receiver Address", "Receiver Phone", "Package Weight (kg)", "Shipping Method", "Tracking Number", "Estimated Delivery Date" ] ############################################################################### # 1) Draw the static collage (top portion) for a 4x6 USPS label ############################################################################### def draw_label_collage(c, width, height): """ Draws the top portion: big 'F', USPS text, placeholder QR code, 'USPS FIRST-CLASS PKG' title, plus a top horizontal line. """ # Big "F" near top-left c.setFont("Helvetica-Bold", 80) c.drawString(15, height - 85, "F") # USPS postage info block (small text under "F") c.setFont("Helvetica", 6) c.drawString(70, height - 20, "US POSTAGE AND FEES PAID") c.drawString(70, height - 30, "Apr 12 2018") c.drawString(70, height - 40, "Mailed from ZIP 77024") c.drawString(70, height - 50, "First-Class Pkg Svc") c.drawString(70, height - 60, "CommercialBasePrice") # Placeholder for QR code in top-right c.setFont("Helvetica", 6) c.drawString(width - 50, height - 20, "QR CODE") # "USPS FIRST-CLASS PKG" in bold, centered near top c.setFont("Helvetica-Bold", 14) c.drawCentredString(width / 2, height - 110, "USPS FIRST-CLASS PKG") # Horizontal line across the label, just below the header c.setLineWidth(1) c.line(15, height - 115, width - 15, height - 115) ############################################################################### # 2) Overlay dynamic text + horizontal lines to mimic the sample layout ############################################################################### def draw_label_text(c, row, width, height): """ Places: - Warehouse & Order info, then a horizontal line - Recipient address, then another line - "USPS TRACKING #" heading, then another line - Tracking number below that line """ # Convert row to local variables (with fallback) order_id = row.get("Order ID", "") order_date = row.get("Order Date", "") shipper_name = row.get("Shipper Name", "") shipper_addr = row.get("Shipper Address", "") shipper_phone = row.get("Shipper Phone", "") receiver_name = row.get("Receiver Name", "") receiver_addr = row.get("Receiver Address", "") receiver_phone = row.get("Receiver Phone", "") weight = row.get("Package Weight (kg)", "") ship_method = row.get("Shipping Method", "") tracking_num = row.get("Tracking Number", "") eta_date = row.get("Estimated Delivery Date", "") # We'll start below the top line from the collage top_line_y = height - 115 ############################ # Warehouse + Order Info ############################ # Left: Warehouse lines c.setFont("Helvetica-Bold", 9) c.drawString(15, top_line_y - 15, shipper_name or "WAREHOUSE 2") c.setFont("Helvetica", 9) c.drawString(15, top_line_y - 30, shipper_addr or "11919 WINK RD") c.drawString(15, top_line_y - 45, f"{shipper_phone}" or "HOUSTON TX 77024-7134") # Right: "Order: 286" c.setFont("Helvetica", 9) c.drawRightString(width - 15, top_line_y - 15, f"Order: {order_id}") c.drawRightString(width - 15, top_line_y - 30, f"Date: {order_date}") # Horizontal line under Warehouse/Order block warehouse_line_y = top_line_y - 60 c.line(15, warehouse_line_y, width - 15, warehouse_line_y) ############################ # Recipient Block ############################ # Bold name, address, phone c.setFont("Helvetica-Bold", 11) c.drawString(15, warehouse_line_y - 15, receiver_name) c.setFont("Helvetica", 9) c.drawString(15, warehouse_line_y - 30, receiver_addr) c.drawString(15, warehouse_line_y - 45, f"Phone: {receiver_phone}") # Possibly show shipping details to the right c.setFont("Helvetica-Bold", 9) c.drawRightString(width - 15, warehouse_line_y - 15, f"Weight: {weight} kg") c.drawRightString(width - 15, warehouse_line_y - 30, f"Method: {ship_method}") c.drawRightString(width - 15, warehouse_line_y - 45, f"ETA: {eta_date}") # Another line below the recipient block recipient_line_y = warehouse_line_y - 60 c.line(15, recipient_line_y, width - 15, recipient_line_y) ############################ # USPS TRACKING # ############################ # Place "USPS TRACKING #" in bold, centered c.setFont("Helvetica-Bold", 12) c.drawCentredString(width / 2, recipient_line_y - 20, "USPS TRACKING #") # Another line under "USPS TRACKING #" tracking_line_y = recipient_line_y - 35 c.line(15, tracking_line_y, width - 15, tracking_line_y) # Tracking number below that line, centered c.setFont("Helvetica-Bold", 10) c.drawCentredString(width / 2, tracking_line_y - 20, tracking_num) ############################################################################### # 3) Create the 4x6 USPS-style label ############################################################################### def create_usps_label(row, index=1): """ 1) Generate a Code128 barcode from 'Tracking Number' 2) Draw collage 3) Draw dynamic text & lines 4) Place barcode near the bottom """ # Barcode tracking_number = row.get("Tracking Number", "0000000000") barcode_filename = os.path.join(BARCODES_DIR, f"barcode_{index}.png") code128 = barcode.get_barcode_class("code128") code128_obj = code128(tracking_number, writer=ImageWriter()) code128_obj.save(barcode_filename) # 4x6 inches width, height = 4 * inch, 6 * inch label_filename = os.path.join(LABELS_DIR, f"label_{index}.pdf") c = canvas.Canvas(label_filename, pagesize=(width, height)) # Draw the collage draw_label_collage(c, width, height) # Overlay text & lines draw_label_text(c, row, width, height) # Place barcode near bottom, centered horizontally if os.path.exists(barcode_filename): barcode_w = 3 * inch barcode_h = 0.8 * inch x_pos = (width - barcode_w) / 2 y_pos = 0.5 * inch # ~0.5 inch from bottom c.drawImage(barcode_filename, x_pos, y_pos, width=barcode_w, height=barcode_h) else: c.setFont("Helvetica", 10) c.drawString(15, 0.5 * inch, "Barcode generation failed.") c.showPage() c.save() return label_filename ############################################################################### # 4) Gradio function to process file uploads ############################################################################### def process_file(file_obj): """ Reads CSV/Excel, checks columns, generates one label per row, zips them. """ try: # Clean out old labels/barcodes for f in os.listdir(LABELS_DIR): os.remove(os.path.join(LABELS_DIR, f)) for f in os.listdir(BARCODES_DIR): os.remove(os.path.join(BARCODES_DIR, f)) # Read the uploaded file if file_obj.name.endswith(".csv"): df = pd.read_csv(file_obj) elif file_obj.name.endswith((".xls", ".xlsx")): df = pd.read_excel(file_obj) else: return "Invalid file format. Please upload a .csv, .xls, or .xlsx file.", None # Check required columns missing = [col for col in REQUIRED_COLUMNS if col not in df.columns] if missing: return f"Missing columns: {', '.join(missing)}", None # Generate labels for i, row in df.iterrows(): create_usps_label(row.to_dict(), index=i+1) # Zip them up zip_filename = "usps_labels.zip" with zipfile.ZipFile(zip_filename, "w") as zf: for f in os.listdir(LABELS_DIR): if f.endswith(".pdf"): zf.write(os.path.join(LABELS_DIR, f), f) return "Labels generated successfully.", zip_filename except Exception as e: return f"Error: {str(e)}", None ############################################################################### # 5) Gradio Interface for Hugging Face ############################################################################### iface = gr.Interface( fn=process_file, inputs=gr.File(type="filepath"), outputs=[gr.Text(label="Status"), gr.File(label="Download Zipped Labels")], title="USPS-Style Label Generator (4x6)", description=( "Upload a CSV/XLS/XLSX file with columns:\n" "Order ID, Order Date, Shipper Name, Shipper Address, Shipper Phone,\n" "Receiver Name, Receiver Address, Receiver Phone, Package Weight (kg),\n" "Shipping Method, Tracking Number, Estimated Delivery Date\n\n" "Generates a 4x6 USPS-style PDF label per row, including lines that match the sample." ), ) if __name__ == "__main__": iface.launch()