alpinux.site.2026/scripts/build-assets.py
Cédrix 14cb68c4c5 fix: logo wiki servi depuis docs/assets/ (hors git, généré par build-assets.py)
overrides/partials/logo.html : remplace l'URL static.alpinux.org par
{{ base_url }}/assets/alpinux-logo.png — le logo est inclus dans le
build MkDocs, pas servi depuis le CDN.

scripts/build-assets.py : sépare les deux destinations :
- docs/assets/alpinux-logo.png (200×200, pour le wiki)
- /tmp/alpinux-static-assets/ (512px + favicons → static.alpinux.org/logo/)
Plus d'argument --out ; les chemins sont fixes et documentés.

docs/technique/deploiement-wiki.md :
- schéma mis à jour (SVG → build-assets.py → PNG → mkdocs build)
- ajout de l'étape 3 "Générer le logo" dans la procédure manuelle
- script deploy-wiki.sh inclut python3 scripts/build-assets.py
- tableau résumé mis à jour
- prérequis : ajout pillow et chromium

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 10:05:12 +02:00

162 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
Génère les assets binaires (logo PNG + favicons) depuis la source SVG.
Dépendances : Pillow, chromium
Usage :
python3 scripts/build-assets.py
Sorties :
docs/assets/alpinux-logo.png → logo 200×200 inclus dans le wiki (MkDocs)
/tmp/alpinux-static-assets/ → favicons + logo 512px à uploader sur static.alpinux.org/logo/
"""
import subprocess
import tempfile
import os
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
REPO = Path(__file__).resolve().parent.parent
SVG = REPO / "docs/assets/alpinux-logo.svg"
DOCS_OUT = REPO / "docs/assets"
CDN_OUT = Path("/tmp/alpinux-static-assets")
FONT_R = "/usr/share/fonts/truetype/msttcorefonts/arial.ttf"
FONT_B = "/usr/share/fonts/truetype/msttcorefonts/arialbd.ttf"
COLOR = (15, 78, 143) # #0f4e8f
def render_shapes(svg_path: Path, width: int, height: int) -> Image.Image:
"""Render SVG shapes via chromium headless (no text)."""
html = f"""<!DOCTYPE html><html>
<head><meta charset="utf-8">
<style>*{{margin:0;padding:0}}html,body{{width:{width}px;height:{height}px;overflow:hidden;background:transparent}}</style>
</head>
<body><img src="file://{svg_path}" width="{width}" height="{height}"></body>
</html>"""
with tempfile.NamedTemporaryFile(suffix=".html", mode="w", delete=False) as f:
f.write(html)
tmp_html = f.name
out_png = tmp_html.replace(".html", ".png")
subprocess.run([
"chromium", "--headless", "--disable-gpu", "--no-sandbox",
f"--screenshot={out_png}", f"--window-size={width},{height}",
f"file://{tmp_html}"
], capture_output=True)
img = Image.open(out_png).convert("RGBA")
os.unlink(tmp_html)
os.unlink(out_png)
return img
def add_text(canvas: Image.Image) -> Image.Image:
"""Composite Alpinux text with correct weights onto the canvas."""
draw = ImageDraw.Draw(canvas)
size = 30
f_reg = ImageFont.truetype(FONT_R, size)
f_bld = ImageFont.truetype(FONT_B, size)
parts = [("A", False), ("l", True), ("p", False), ("inux", True)]
widths = []
for char, bold in parts:
f = f_bld if bold else f_reg
bb = f.getbbox(char)
widths.append(bb[2] - bb[0])
total_w = sum(widths)
x = (canvas.width - total_w) // 2
y = 164 + (36 - size) // 2 + 1
for (char, bold), w in zip(parts, widths):
f = f_bld if bold else f_reg
bb = f.getbbox(char)
draw.text((x - bb[0], y - bb[1]), char, font=f, fill=COLOR)
x += w
return canvas
def build_logo_wiki(docs_out: Path):
"""Build 200×200 logo PNG → docs/assets/ (servi par MkDocs, non commité)."""
shapes = render_shapes(SVG, 200, 164)
canvas = Image.new("RGBA", (200, 200), (255, 255, 255, 255))
canvas.paste(shapes, (0, 0))
canvas = add_text(canvas)
path = docs_out / "alpinux-logo.png"
canvas.convert("RGB").save(path)
print(f" {path} (200×200) → wiki MkDocs")
def build_logo_cdn(cdn_out: Path):
"""Build 512×512 logo PNG → CDN (static.alpinux.org/logo/)."""
shapes512 = render_shapes(SVG, 512, 421)
canvas512 = Image.new("RGBA", (512, 512), (255, 255, 255, 255))
canvas512.paste(shapes512, (0, 0))
draw = ImageDraw.Draw(canvas512)
size = 77
f_reg = ImageFont.truetype(FONT_R, size)
f_bld = ImageFont.truetype(FONT_B, size)
parts = [("A", False), ("l", True), ("p", False), ("inux", True)]
widths = [f_bld.getbbox(c)[2]-f_bld.getbbox(c)[0] if b else f_reg.getbbox(c)[2]-f_reg.getbbox(c)[0] for c,b in parts]
total_w = sum(widths)
x = (512 - total_w) // 2
y = 421 + (91 - size) // 2 + 2
for (char, bold), w in zip(parts, widths):
f = f_bld if bold else f_reg
bb = f.getbbox(char)
draw.text((x - bb[0], y - bb[1]), char, font=f, fill=COLOR)
x += w
path512 = cdn_out / "alpinux-logo-512.png"
canvas512.convert("RGB").save(path512)
print(f" {path512} (512×512) → static.alpinux.org/logo/")
def build_favicons(cdn_out: Path):
"""Build favicon PNG set + .ico → CDN (static.alpinux.org/logo/)."""
icon_src = render_shapes(SVG, 200, 164).crop((0, 0, 164, 164))
sizes = {
"favicon-16.png": 16,
"favicon-32.png": 32,
"favicon.png": 48,
"favicon-96.png": 96,
"favicon-192.png": 192,
}
imgs = {}
for name, sz in sizes.items():
img = icon_src.resize((sz, sz), Image.LANCZOS)
bg = Image.new("RGBA", (sz, sz), (255, 255, 255, 255))
bg.paste(img, (0, 0))
p = cdn_out / name
bg.save(p)
imgs[sz] = bg
print(f" {p} ({sz}×{sz}) → static.alpinux.org/logo/")
ico = cdn_out / "favicon.ico"
imgs[16].save(ico, format="ICO", sizes=[(16,16),(32,32),(48,48)],
append_images=[imgs[32], imgs[48]])
print(f" {ico} (multi-size: 16+32+48) → static.alpinux.org/logo/")
def main():
DOCS_OUT.mkdir(parents=True, exist_ok=True)
CDN_OUT.mkdir(parents=True, exist_ok=True)
print("Génération des assets Alpinux...")
print()
print("── Logo wiki (docs/assets/) ──")
build_logo_wiki(DOCS_OUT)
print()
print("── CDN static.alpinux.org/logo/ ──")
build_logo_cdn(CDN_OUT)
build_favicons(CDN_OUT)
print()
print(f"Fichiers CDN dans : {CDN_OUT}")
print("À uploader sur : static.alpinux.org/logo/")
print()
print("Ensuite : mkdocs build")
if __name__ == "__main__":
main()