Files
jarl/scripts/logo.py
T
abrown38andClaude Opus 5.5 0f04dbb4b1 Fix the logo's pop-in with a traced vector logo
The logo was an 880 KB SVG wrapping a 1254px PNG, downloaded again on
every page load. Keep that PNG as the master (artwork/logo.png) and have
scripts/logo.py trace it with potrace into a 6 KB logo.svg (favicon,
README) and logo.js, which brand.js draws inline in the header and on the
login page so the logo arrives with the page. Remove the unused
logo-white.svg.

Static files now carry content ETags, so reloads get 304s instead of
downloading the UI again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:01:22 +00:00

73 lines
3.3 KiB
Python
Executable File

#!/usr/bin/env python3
# Builds the web UI's logo from the master image, artwork/logo.png (a blue
# and white figure on a transparent background), by tracing it into vector
# paths with potrace:
#
# internal/web/static/assets/logo.svg the favicon, and the README's logo
# internal/web/static/assets/logo.js the same paths for the web UI, which
# draws them inline (brand.js), so the
# logo appears together with the page
#
# Both are a few KB, where the PNG is 650 KB. Needs potrace and ImageMagick
# (convert). Run from the repository root after changing the master:
#
# python3 scripts/logo.py
import os
import re
import subprocess
import sys
import tempfile
MASTER = "artwork/logo.png"
OUT = "internal/web/static/assets"
BLUE = "#1d5287" # the master's blue
WHITE = "#fff"
PAD = 6 # pixels of the master around the figure
def run(*args):
return subprocess.run(args, check=True, capture_output=True, text=True).stdout
def trace(pbm):
"""Traces a bitmap into one SVG path (in the master's pixels, y up)."""
svg = run("potrace", "-b", "svg", "--flat", "-t", "4", "-a", "1.0", "-O", "0.4", "-u", "1", pbm, "-o", "-")
return " ".join(re.search(r'<path d="([^"]*)"', svg, re.S).group(1).split())
def main():
with tempfile.TemporaryDirectory() as tmp:
base, white = os.path.join(tmp, "base.pbm"), os.path.join(tmp, "white.pbm")
# The figure's whole shape (opaque pixels), drawn in blue...
run("convert", MASTER, "-alpha", "extract", "-threshold", "50%", "-negate", base)
# ...and its white parts on top; the blue lines show between them.
run("convert", MASTER, "-background", "black", "-alpha", "remove", "-alpha", "off",
"-colorspace", "gray", "-threshold", "75%", "-negate", white)
layers = [(BLUE, trace(base)), (WHITE, trace(white))]
height = int(run("identify", "-format", "%h", MASTER))
w, h, x, y = map(int, re.match(r"(\d+)x(\d+)\+(\d+)\+(\d+)",
run("convert", MASTER, "-alpha", "extract", "-threshold", "50%", "-format", "%@", "info:")).groups())
side = max(w, h) + 2 * PAD
view_box = f"{x + w / 2 - side / 2:g} {y + h / 2 - side / 2:g} {side:g} {side:g}"
transform = f"translate(0 {height}) scale(1 -1)"
paths = "".join(f'<path fill="{fill}" d="{d}"/>' for fill, d in layers)
with open(os.path.join(OUT, "logo.svg"), "w") as f:
f.write(f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="{view_box}">'
f'<!-- Generated by scripts/logo.py from {MASTER}. -->'
f'<g transform="{transform}">{paths}</g></svg>\n')
with open(os.path.join(OUT, "logo.js"), "w") as f:
f.write(f"// The Jarl logo, traced from {MASTER} by scripts/logo.py; don't edit.\n"
f"// brand.js draws it; logo.svg holds the same paths.\n"
f'export const viewBox = "{view_box}";\n'
f'export const transform = "{transform}";\n'
"export const layers = [\n"
+ "".join(f' ["{fill}", "{d}"],\n' for fill, d in layers)
+ "];\n")
for name in ("logo.svg", "logo.js"):
print(f"{OUT}/{name}: {os.path.getsize(os.path.join(OUT, name))} bytes")
if __name__ == "__main__":
sys.exit(main())