| 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 |
|
|
| |
| LABELS_DIR = "labels" |
| BARCODES_DIR = "barcodes" |
| os.makedirs(LABELS_DIR, exist_ok=True) |
| os.makedirs(BARCODES_DIR, exist_ok=True) |
|
|
| |
| 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" |
| ] |
|
|
| |
| |
| |
| 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. |
| """ |
| |
| c.setFont("Helvetica-Bold", 80) |
| c.drawString(15, height - 85, "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") |
|
|
| |
| c.setFont("Helvetica", 6) |
| c.drawString(width - 50, height - 20, "QR CODE") |
|
|
| |
| c.setFont("Helvetica-Bold", 14) |
| c.drawCentredString(width / 2, height - 110, "USPS FIRST-CLASS PKG") |
|
|
| |
| c.setLineWidth(1) |
| c.line(15, height - 115, width - 15, height - 115) |
|
|
| |
| |
| |
| 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 |
| """ |
| |
| 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", "") |
|
|
| |
| top_line_y = height - 115 |
|
|
| |
| |
| |
| |
| 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") |
|
|
| |
| 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}") |
|
|
| |
| warehouse_line_y = top_line_y - 60 |
| c.line(15, warehouse_line_y, width - 15, warehouse_line_y) |
|
|
| |
| |
| |
| |
| 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}") |
|
|
| |
| 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}") |
|
|
| |
| recipient_line_y = warehouse_line_y - 60 |
| c.line(15, recipient_line_y, width - 15, recipient_line_y) |
|
|
| |
| |
| |
| |
| c.setFont("Helvetica-Bold", 12) |
| c.drawCentredString(width / 2, recipient_line_y - 20, "USPS TRACKING #") |
|
|
| |
| tracking_line_y = recipient_line_y - 35 |
| c.line(15, tracking_line_y, width - 15, tracking_line_y) |
|
|
| |
| c.setFont("Helvetica-Bold", 10) |
| c.drawCentredString(width / 2, tracking_line_y - 20, tracking_num) |
|
|
| |
| |
| |
| 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 |
| """ |
| |
| 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) |
|
|
| |
| 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_label_collage(c, width, height) |
| |
| draw_label_text(c, row, width, height) |
|
|
| |
| 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 |
| 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 |
|
|
| |
| |
| |
| def process_file(file_obj): |
| """ |
| Reads CSV/Excel, checks columns, |
| generates one label per row, zips them. |
| """ |
| try: |
| |
| 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)) |
|
|
| |
| 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 |
|
|
| |
| missing = [col for col in REQUIRED_COLUMNS if col not in df.columns] |
| if missing: |
| return f"Missing columns: {', '.join(missing)}", None |
|
|
| |
| for i, row in df.iterrows(): |
| create_usps_label(row.to_dict(), index=i+1) |
|
|
| |
| 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 |
|
|
| |
| |
| |
| 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() |