Files
heos/tools/make_icons.py
T
franzzandClaude Opus 5 aa1b734b3a Use the HEOS mark, in white, for the header and the icon
The word HEOS at the top of the panel becomes the mark itself, and the
home-screen icons are rendered from the same file so there is one place
to change it. All white on transparent: the panel is dark, and iOS lays
a transparent apple-touch-icon over black.

make_icons.py no longer draws its own glyph -- it rasterises logo.svg
with headless Chromium, which is heavier than the old arithmetic but the
only way to render real paths without a system SVG library. The PNGs are
committed, so it only has to run when the logo changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 21:45:33 +02:00

67 lines
2.2 KiB
Python

#!/usr/bin/env python3
"""Render static/logo.svg into the home-screen icons.
python3 tools/make_icons.py
Writes static/icon-180.png (what iOS uses on the home screen) and
static/icon-512.png (Android / the web manifest): the white mark on a
transparent background, which iOS lays over black on the home screen.
The PNGs are committed, so this only needs running if the logo changes.
It rasterises with headless Chromium, which is a heavy thing to install
for one job -- if you have librsvg to hand, this does the same:
rsvg-convert -w 512 -h 512 static/logo.svg -o static/icon-512.png
Otherwise: pip install playwright && playwright install chromium
"""
import sys
from pathlib import Path
STATIC = Path(__file__).resolve().parent.parent / "static"
LOGO = STATIC / "logo.svg"
SIZES = (180, 512)
MARGIN = 0.14 # breathing room, so iOS's rounded mask never clips it
PAGE = """<!doctype html>
<style>
html, body {{ margin: 0; background: transparent; }}
body {{ width: {size}px; height: {size}px; display: grid; place-items: center; }}
img {{ width: {inner}px; height: {inner}px; object-fit: contain; }}
</style>
<img src="logo.svg">
"""
def main():
try:
from playwright.sync_api import sync_playwright
except ImportError:
sys.exit("needs playwright: pip install playwright && playwright install chromium")
if not LOGO.exists():
sys.exit(f"no logo at {LOGO}")
scratch = STATIC / "_icon.html"
try:
with sync_playwright() as p:
browser = p.chromium.launch(args=["--no-sandbox"])
for size in SIZES:
inner = round(size * (1 - 2 * MARGIN))
scratch.write_text(PAGE.format(size=size, inner=inner))
page = browser.new_page(viewport={"width": size, "height": size})
page.goto(scratch.as_uri())
page.wait_for_timeout(120) # let the SVG paint
target = STATIC / f"icon-{size}.png"
page.screenshot(path=target, omit_background=True)
page.close()
print(f"wrote {target} ({target.stat().st_size} bytes)")
browser.close()
finally:
scratch.unlink(missing_ok=True)
if __name__ == "__main__":
main()