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>
67 lines
2.2 KiB
Python
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()
|