Newer
Older
gnexus-creds / tools / rasterize_icons.py
"""Rasterize frontend/public/logo.svg into PWA icons.

Outputs (committed):
  frontend/public/icons/icon-192.png / icon-512.png          — any purpose
  frontend/public/icons/icon-192-maskable.png / icon-512-maskable.png

Maskable variants keep the full-bleed background with the logo scaled to the
80% safe zone, so Android's circular adaptive-icon cut never clips the shield.

Usage: python tools/rasterize_icons.py   (needs the project .venv's playwright)
"""

from pathlib import Path
from tempfile import TemporaryDirectory

from playwright.sync_api import sync_playwright

PROJECT_ROOT = Path(__file__).resolve().parent.parent
SVG_PATH = PROJECT_ROOT / "frontend" / "public" / "logo.svg"
OUT_DIR = PROJECT_ROOT / "frontend" / "public" / "icons"

SIZES = [(192, False), (512, False), (192, True), (512, True)]
SAFE_SCALE = 0.8

HTML = """<!DOCTYPE html><html><head><meta charset="utf-8"><style>
  html, body {{ margin: 0; padding: 0; background: #16161E; }}
  .frame {{ width: {size}px; height: {size}px; background: #16161E;
            display: flex; align-items: center; justify-content: center;
            overflow: hidden; }}
  .frame svg {{ width: {inner}px; height: {inner}px; }}
</style></head><body><div class="frame">{svg}</div></body></html>"""


def write_icons() -> None:
    svg = SVG_PATH.read_text(encoding="utf-8")
    OUT_DIR.mkdir(parents=True, exist_ok=True)

    with sync_playwright() as p, TemporaryDirectory() as tmp:
        browser = p.chromium.launch(
            executable_path="/usr/bin/chromium",
            args=["--no-sandbox", "--force-color-profile=srgb"],
        )
        page = browser.new_page()
        for size, maskable in SIZES:
            inner = int(size * SAFE_SCALE) if maskable else size
            html = HTML.format(size=size, inner=inner, svg=svg)
            html_path = Path(tmp) / f"icon-{size}-{int(maskable)}.html"
            html_path.write_text(html, encoding="utf-8")
            page.set_viewport_size({"width": size, "height": size})
            page.goto(f"file://{html_path}")
            name = f"icon-{size}{'-maskable' if maskable else ''}.png"
            page.screenshot(path=str(OUT_DIR / name), omit_background=False)
            print(f"wrote {name}")
        browser.close()


if __name__ == "__main__":
    write_icons()