furtka/website/tools/brand-assets.py
Daniel Maksymilian Syrnicki 91aa33b891
All checks were successful
CI / lint (pull_request) Successful in 30s
CI / validate-json (pull_request) Successful in 24s
CI / markdown-links (pull_request) Successful in 16s
CI / test (pull_request) Successful in 1m47s
CI / lint (push) Successful in 28s
CI / test (push) Successful in 1m56s
CI / validate-json (push) Successful in 24s
CI / markdown-links (push) Successful in 21s
Deploy site / deploy (push) Successful in 5s
Build ISO / build-iso (push) Successful in 20m27s
feat(brand): Mark M is the Furtka logo — website, installer, box UI, console, boot splash
The logo vote on furtka.org closed with M 29 · R 13 (42 votes).

- website/tools/brand-assets.py generates every brand file from the
  logo-assets.py geometry: favicons (small-size tier, accent with a
  dark-mode variant) for website / webinstaller / on-box UI, 180 px
  apple-touch-icon, 1200x630 default share card, 640x480 syslinux splash,
  and docs/brand/ (mark, small mark, lockups, 20-column console art)
- website: header mark, favicon, touch icon, default og:image; /logo/ now
  shows the final result (static counts, winner highlighted, no voting
  UI or poll.js); homepage link reads 'meet our new logo'
- webinstaller: M in the header + tab icon
- on-box UI: .brand mark via CSS mask in brand red (not the UI's green),
  favicon on every page, brand line on the login page
- console: M art above the text of /etc/issue on the installed box
  (furtka-welcome) and the live ISO (furtka-update-issue)
- ISO: build.sh swaps releng's Arch splash for the Furtka one
- README shows the lockup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wq1hzUp4ZmCKHDuLKCnu73
2026-09-28 15:08:49 +02:00

104 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""Generate the Furtka brand files from the winning logo (Mark M) everywhere
it ships: website, live installer, on-box UI, ISO boot splash, repo docs.
python3 website/tools/brand-assets.py
Reuses the geometry and rasteriser of logo-assets.py (the poll showcase), so
the mark is drawn from one source. Needs the same build-only deps: cairosvg,
Pillow, fontTools. Output files are committed; re-run after a geometry change.
"""
import importlib.util
import os
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
REPO = os.path.abspath(os.path.join(HERE, "..", ".."))
_spec = importlib.util.spec_from_file_location("logo_assets", os.path.join(HERE, "logo-assets.py"))
la = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(la)
K = "m"
COLOR = la.MARKS[K]["color"] # light-theme accent
COLOR_DARK = "#ff6b56" # dark-theme accent used by the site and the box UI
INK = la.MARKS[K]["ink"]
def out(*parts):
path = os.path.join(REPO, *parts)
os.makedirs(os.path.dirname(path), exist_ok=True)
return path
def favicon_svg():
"""Small-size tier, accent colour, lighter accent on dark browser chrome."""
inner = la.body(K, "min", "currentColor")
x, y, w, h = la.MARKS[K]["vb"]
return (
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="{x} {y} {w} {h}">'
f"<style>svg{{color:{COLOR}}}"
f"@media (prefers-color-scheme:dark){{svg{{color:{COLOR_DARK}}}}}</style>"
f"{inner}</svg>\n"
)
def mark_svg(tier, color):
return (
la.svg(K, tier, color, standalone=True)
.replace(' class="pm"', "")
.replace(' aria-hidden="true" focusable="false"', "")
+ "\n"
)
def lockup_svg(color):
s = la.lockup(K).replace("currentColor", color)
return (
s.replace('<svg class="pm"', f"<svg {la.XMLNS}").replace(
' aria-hidden="true" focusable="false"', ' role="img" aria-label="Furtka"'
)
+ "\n"
)
def write(path, text):
with open(path, "w") as f:
f.write(text)
def main():
fav = favicon_svg()
for p in (
("website", "static", "favicon.svg"),
("webinstaller", "static", "favicon.svg"),
("assets", "www", "favicon.svg"),
):
write(out(*p), fav)
la.tile(K, 180, pad=0.2).convert("RGB").save(
out("website", "static", "apple-touch-icon.png"), optimize=True
)
# Default share card for pages without their own og:image.
la.lockup_png(K, 1200, 630, False, 230).convert("RGB").save(
out("website", "static", "img", "brand", "social.png"), optimize=True
)
# syslinux (BIOS boot menu) wants a 640x480 PNG and draws its menu over
# the middle of it, so the lockup sits small in the top band — same
# layout as releng's Arch splash, which build.sh replaces with this.
splash_band = la.lockup_png(K, 640, 120, False, 44)
splash = la.Image.new("RGBA", (640, 480), INK)
splash.alpha_composite(splash_band, (0, 8))
splash.convert("RGB").save(out("iso", "overlay", "splash.png"), optimize=True)
write(out("docs", "brand", "furtka-mark.svg"), mark_svg("flat", COLOR))
write(out("docs", "brand", "furtka-mark-small.svg"), mark_svg("min", COLOR))
write(out("docs", "brand", "furtka-lockup.svg"), lockup_svg(COLOR))
write(out("docs", "brand", "furtka-lockup-white.svg"), lockup_svg("#ffffff"))
write(out("docs", "brand", "console-banner.txt"), la.banner(K, cols=20) + "\n")
print("brand assets ok")
if __name__ == "__main__":
sys.exit(main())