#!/usr/bin/env python3 """Generate the JIMMY KIMMEL LIVE banner texture for the hero facade. The three event banners are flat panels; the lettering is text and cannot be read as geometry at 45 m. An image model would mangle a specific trademark string, so the text is drawn here with Pillow and a system font instead: deterministic, exact, and (unlike Qwen-Image 2.1) not research-only. The banner face is 1.35 m wide x 6.2 m tall (aspect 1:4.59). The texture is written at 512 x 2304 (aspect 1:4.5), a 2% stretch that is invisible at the review distance. python3 Tools/gen/generate_banner_texture.py python3 Tools/gen/generate_banner_texture.py --out Art/textures/generated/T_Banner_KIMMEL.png Requires Pillow (host-only tool, not a gate; standard library is not enough to rasterise a font). The text is a trademark reproduced for internal development review only - record it in THIRD_PARTY_NOTICES.md before any public build. """ from __future__ import annotations import argparse import os from PIL import Image, ImageDraw, ImageFont # Banner face aspect: 1.35 m wide x 6.2 m tall. 512 x 2304 keeps that to within 2 %. WIDTH = 512 HEIGHT = 2304 # sRGB colours. The banner is a deep event red, the lettering is gold; both match the # photographs (and the owner's "less dark, more bright" direction for the elevation). BANNER = (186, 24, 24) GOLD = (232, 198, 90) FONT_BOLD = "/usr/share/fonts/liberation-sans-fonts/LiberationSans-Bold.ttf" FONT_REGULAR = "/usr/share/fonts/liberation-sans-fonts/LiberationSans-Regular.ttf" # Three stacked words, as the real vertical banners arrange the title. `size` is the # font height in texture pixels; the block is centred vertically in the banner. LINES = [ ("JIMMY", 150, FONT_BOLD), ("KIMMEL", 150, FONT_BOLD), ("LIVE", 120, FONT_REGULAR), ] LINE_GAP = 70 # pixels between baselines beyond the font ascent def _font(path: str, size: int) -> ImageFont.FreeTypeFont: if not os.path.exists(path): raise SystemExit(f"font missing: {path}") return ImageFont.truetype(path, size) def draw_banner(out: str) -> None: img = Image.new("RGB", (WIDTH, HEIGHT), BANNER) draw = ImageDraw.Draw(img) fonts = [(text, size, _font(path, size)) for text, size, path in LINES] # Measure the whole block so it sits centred vertically in the 6.2 m banner. ascents = [f.getbbox(text)[3] for text, _, f in fonts] block_h = sum(ascents) + LINE_GAP * (len(fonts) - 1) y = (HEIGHT - block_h) // 2 for text, size, font in fonts: bbox = font.getbbox(text) text_w = bbox[2] - bbox[0] x = (WIDTH - text_w) // 2 - bbox[0] draw.text((x, y), text, font=font, fill=GOLD) y += bbox[3] + LINE_GAP os.makedirs(os.path.dirname(os.path.abspath(out)) or ".", exist_ok=True) img.save(out) print(f"wrote {out} ({WIDTH}x{HEIGHT})") def main() -> None: parser = argparse.ArgumentParser() parser.add_argument( "--out", default="Art/textures/generated/T_Banner_KIMMEL.png") args = parser.parse_args() draw_banner(args.out) if __name__ == "__main__": main()