"""SVG packaging generator for Foodlinkk.""" from __future__ import annotations import base64 from io import BytesIO from typing import Any import svgwrite from barcode import Code128 from barcode.writer import SVGWriter MM_TO_PX = 3.7795275591 # 96 DPI conversion DEFAULT_BARCODE_VALUE = "8710000000012" FOODLINKK_BRAND = { "bg": "#0b1220", "panel": "#101a2d", "primary": "#00e5ff", "secondary": "#ffd700", "text": "#e2e8f0", "cut_line": "#ef4444", "fold_line": "#60a5fa", } def _mm(mm: float) -> float: return round(float(mm) * MM_TO_PX, 2) def _elements_enabled(elements: Any, name: str) -> bool: if isinstance(elements, dict): return bool(elements.get(name)) if isinstance(elements, list): return name in elements return False def _barcode_data_uri(value: str) -> str: barcode = Code128(value, writer=SVGWriter()) stream = BytesIO() barcode.write(stream) encoded = base64.b64encode(stream.getvalue()).decode("ascii") return f"data:image/svg+xml;base64,{encoded}" def generate_packaging(spec: dict[str, Any]) -> str: """Create an SVG packaging design based on a simple spec.""" ptype = (spec.get("type") or "folding_box").strip().lower() width_mm = float(spec.get("width_mm", 120)) height_mm = float(spec.get("height_mm", 80)) depth_mm = float(spec.get("depth_mm", 40)) elements = spec.get("elements", {}) brand = {**FOODLINKK_BRAND, **(spec.get("brand") or {})} if ptype == "folding_box": canvas_w = _mm((width_mm * 2) + (depth_mm * 2) + 20) canvas_h = _mm(height_mm + depth_mm + 20) elif ptype == "wrap": canvas_w = _mm(width_mm + 20) canvas_h = _mm(height_mm + 20) elif ptype == "round_label": diameter = max(min(width_mm, height_mm), 20) canvas_w = _mm(diameter + 20) canvas_h = _mm(diameter + 20) else: raise ValueError(f"Unsupported packaging type: {ptype}") dwg = svgwrite.Drawing(size=(canvas_w, canvas_h)) dwg.viewbox(0, 0, canvas_w, canvas_h) # Background and frame dwg.add(dwg.rect(insert=(0, 0), size=(canvas_w, canvas_h), fill=brand["bg"])) dwg.add( dwg.rect( insert=(4, 4), size=(canvas_w - 8, canvas_h - 8), fill=brand["panel"], rx=10, ry=10, stroke=brand["primary"], stroke_opacity=0.25, stroke_width=2, ) ) margin = 24 if ptype == "folding_box": body_w = _mm(width_mm) body_h = _mm(height_mm) depth_w = _mm(depth_mm) x0 = margin y0 = margin panels = [depth_w, body_w, depth_w, body_w] x = x0 for idx, panel_w in enumerate(panels): dwg.add( dwg.rect( insert=(x, y0), size=(panel_w, body_h), fill="none", stroke=brand["primary"] if idx % 2 else brand["secondary"], stroke_opacity=0.45, stroke_width=1.6, ) ) x += panel_w if _elements_enabled(elements, "fold_lines"): x = x0 + panels[0] for panel_w in panels[1:]: dwg.add( dwg.line( start=(x, y0), end=(x, y0 + body_h), stroke=brand["fold_line"], stroke_dasharray="8,6", stroke_width=1.2, ) ) x += panel_w if _elements_enabled(elements, "cut_lines"): dwg.add( dwg.rect( insert=(x0, y0), size=(sum(panels), body_h), fill="none", stroke=brand["cut_line"], stroke_dasharray="5,4", stroke_width=1.1, ) ) logo_x = x0 + panels[0] + (_mm(width_mm) * 0.12) logo_y = y0 + (_mm(height_mm) * 0.16) logo_w = _mm(width_mm) * 0.76 logo_h = _mm(height_mm) * 0.42 elif ptype == "wrap": body_w = _mm(width_mm) body_h = _mm(height_mm) x0 = margin y0 = margin dwg.add( dwg.rect( insert=(x0, y0), size=(body_w, body_h), fill="none", stroke=brand["primary"], stroke_width=2.2, ) ) if _elements_enabled(elements, "cut_lines"): dwg.add( dwg.rect( insert=(x0, y0), size=(body_w, body_h), fill="none", stroke=brand["cut_line"], stroke_dasharray="6,4", stroke_width=1.1, ) ) logo_x = x0 + (body_w * 0.14) logo_y = y0 + (body_h * 0.14) logo_w = body_w * 0.72 logo_h = body_h * 0.36 else: diameter = min(canvas_w, canvas_h) - (margin * 2) cx = canvas_w / 2 cy = canvas_h / 2 dwg.add( dwg.circle( center=(cx, cy), r=diameter / 2, fill="none", stroke=brand["primary"], stroke_width=2.4, ) ) if _elements_enabled(elements, "cut_lines"): dwg.add( dwg.circle( center=(cx, cy), r=(diameter / 2) - 4, fill="none", stroke=brand["cut_line"], stroke_dasharray="4,4", stroke_width=1.0, ) ) logo_w = diameter * 0.64 logo_h = diameter * 0.22 logo_x = cx - (logo_w / 2) logo_y = cy - (logo_h / 2) - 8 body_w = diameter body_h = diameter x0 = cx - (diameter / 2) y0 = cy - (diameter / 2) if _elements_enabled(elements, "logo_area"): dwg.add( dwg.rect( insert=(logo_x, logo_y), size=(logo_w, logo_h), rx=8, ry=8, fill="none", stroke=brand["secondary"], stroke_width=2, ) ) dwg.add( dwg.text( "FOODLINKK", insert=(logo_x + 12, logo_y + (logo_h / 2) + 5), fill=brand["text"], font_size=18, font_family="Arial, sans-serif", font_weight="bold", ) ) if _elements_enabled(elements, "barcode"): barcode_uri = _barcode_data_uri(str(spec.get("barcode_value") or DEFAULT_BARCODE_VALUE)) bar_w = max(140, body_w * 0.35) bar_h = max(50, body_h * 0.16) bar_x = x0 + body_w - bar_w - 14 bar_y = y0 + body_h - bar_h - 14 dwg.add(dwg.rect(insert=(bar_x - 4, bar_y - 4), size=(bar_w + 8, bar_h + 8), fill="#ffffff")) dwg.add(dwg.image(href=barcode_uri, insert=(bar_x, bar_y), size=(bar_w, bar_h))) return dwg.tostring()