"""Brand image templates (Pillow). 1080x1350 portrait, two brand styles. Deterministic, template-driven graphics — deliberately not AI-generated media (DECISIONS: nothing here trips AI-media disclosure requirements; it is typeset design). Fonts: Pillow's bundled default (scalable), so no system font deps. """ from __future__ import annotations import os from PIL import Image, ImageDraw, ImageFont WIDTH, HEIGHT = 1080, 1350 _BRANDS = { # bitcoinhotels — warm gold on near-black "bh": { "bg": (20, 18, 16), "accent": (201, 168, 76), "text": (242, 236, 224), "muted": (150, 140, 120), }, # hardcap — bitcoin orange on cool black "hc": { "bg": (13, 13, 16), "accent": (247, 147, 26), "text": (250, 250, 250), "muted": (120, 120, 130), }, } def _font(size: int, bold: bool = False): # Pillow >=10: load_default(size=...) returns a scalable bundled font return ImageFont.load_default(size=size) def _vgrad(top: tuple, bottom: tuple) -> Image.Image: """Simple two-color vertical gradient.""" img = Image.new("RGB", (WIDTH, HEIGHT)) for y in range(HEIGHT): t = y / (HEIGHT - 1) row = tuple(int(top[i] + (bottom[i] - top[i]) * t) for i in range(3)) ImageDraw.Draw(img).line([(0, y), (WIDTH, y)], fill=row) return img def render_brand_image( *, brand: str = "bh", kicker: str | None, lines: list[str], tagline: list[str] | None = None, url: str | None = None, out_path: str, ) -> str: """Render one branded poster. Returns out_path. Raises on unknown brand.""" if brand not in _BRANDS: raise ValueError(f"unknown brand {brand!r} (have: {sorted(_BRANDS)})") palette = _BRANDS[brand] img = _vgrad(palette["bg"], tuple(max(0, c - 8) for c in palette["bg"])) draw = ImageDraw.Draw(img) x, y = 90, 240 if kicker: draw.text((x, y), kicker, font=_font(40), fill=palette["muted"]) y += 110 for line in lines: draw.text((x, y), line, font=_font(96, bold=True), fill=palette["text"]) y += 118 y += 24 for t in tagline or []: draw.text((x, y), t, font=_font(38), fill=palette["muted"]) y += 58 draw.rectangle([x, y + 30, x + 470, y + 33], fill=palette["accent"]) if url: draw.text((x, y + 110), url, font=_font(44), fill=palette["accent"]) os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True) img.save(out_path, "JPEG", quality=92) return out_path