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
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
104 lines
3.5 KiB
Python
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())
|