#!/usr/bin/env python3 from __future__ import annotations import argparse import base64 import json import shutil import subprocess import sys import tempfile from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "renderer")) from app.renderer import render_documents from app.theme_catalog import THEMES from app.validation import validate_report def command(name: str, override: str | None = None) -> str: value = override or shutil.which(name) if not value: raise SystemExit(f"Benötigtes Programm nicht gefunden: {name}") return value def print_pdf(engine: str, executable: str, html_path: Path, pdf_path: Path) -> None: if engine == "weasyprint": subprocess.run( [executable, str(html_path), str(pdf_path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=90, ) return subprocess.run( [ executable, "--headless=new", "--no-sandbox", "--disable-gpu", "--disable-dev-shm-usage", "--disable-background-networking", "--disable-extensions", "--no-first-run", f"--user-data-dir={pdf_path.parent / ('.chrome-' + pdf_path.stem)}", f"--print-to-pdf={pdf_path.resolve()}", "--no-pdf-header-footer", html_path.resolve().as_uri(), ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=90, ) def first_page_png(pdftoppm: str, pdf_path: Path, png_path: Path) -> None: prefix = png_path.with_suffix("") subprocess.run( [pdftoppm, "-f", "1", "-singlefile", "-scale-to", "760", "-png", str(pdf_path), str(prefix)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) def make_gallery(cards: list[dict[str, str]], output: Path) -> None: blocks = [] for card in cards: image_data = base64.b64encode(Path(card["image"]).read_bytes()).decode("ascii") swatches = "".join(f'' for color in card["colors"].split(",")) blocks.append( f'''
{card['name']}
{card['category']} · {card['layout']}

{card['name']}

{card['id']}
{swatches}

{card['description']}

''' ) output.write_text( '''Monthly Report Kit - Theme-Katalog

Monthly Report Kit

28 Designs in 10 Layoutfamilien

Auswahl über design.theme

''' + "".join(blocks) + '''
''', encoding="utf-8", ) def main() -> int: parser = argparse.ArgumentParser(description="Rendert alle Themes und erzeugt einen PDF-Theme-Katalog.") parser.add_argument("--input", type=Path, default=ROOT / "examples" / "sample-report.json") parser.add_argument("--output-dir", type=Path, default=ROOT / "output" / "theme-catalog") parser.add_argument("--engine", choices=["auto", "chromium", "weasyprint"], default="auto") parser.add_argument("--chromium", default=None) parser.add_argument("--keep-theme-pdfs", action="store_true") parser.add_argument("--themes", nargs="*", default=None, help="Optional: nur diese Theme-IDs rendern") args = parser.parse_args() if args.engine == "weasyprint" or (args.engine == "auto" and shutil.which("weasyprint")): engine = "weasyprint" renderer = command("weasyprint") else: engine = "chromium" renderer = command("chromium", args.chromium) pdftoppm = command("pdftoppm") payload = json.loads(args.input.read_text(encoding="utf-8")) errors = validate_report(payload) if errors: print(json.dumps(errors, ensure_ascii=False, indent=2), file=sys.stderr) return 2 args.output_dir.mkdir(parents=True, exist_ok=True) preview_dir = args.output_dir / "previews" preview_dir.mkdir(exist_ok=True) pdf_dir = args.output_dir / "pdfs" pdf_dir.mkdir(exist_ok=True) cards: list[dict[str, str]] = [] selected = args.themes or list(THEMES) unknown = sorted(set(selected) - set(THEMES)) if unknown: raise SystemExit(f"Unbekannte Themes: {', '.join(unknown)}") for theme_id in selected: theme = THEMES[theme_id] payload["design"]["theme"] = theme_id payload["design"].pop("palette_mode", None) html, _ = render_documents(payload) html_path = args.output_dir / f"{theme_id}.html" pdf_path = pdf_dir / f"{theme_id}.pdf" png_path = preview_dir / f"{theme_id}.png" html_path.write_text(html, encoding="utf-8") print_pdf(engine, renderer, html_path, pdf_path) first_page_png(pdftoppm, pdf_path, png_path) palette = theme["palette"] cards.append({ "id": theme_id, "name": theme["name"], "layout": theme["layout"], "category": theme["category"], "description": theme["description"], "image": str(png_path), "colors": ",".join([palette["primary_color"], palette["secondary_color"], palette["accent_color"], palette["surface_color"]]), }) print(f"{theme_id}: OK") if set(selected) == set(THEMES): gallery_html = args.output_dir / "theme-gallery.html" gallery_pdf = args.output_dir / "theme-gallery.pdf" make_gallery(cards, gallery_html) print_pdf(engine, renderer, gallery_html, gallery_pdf) print(gallery_pdf) if not args.keep_theme_pdfs and pdf_dir.exists(): shutil.rmtree(pdf_dir) return 0 if __name__ == "__main__": raise SystemExit(main())