162 lines
5.4 KiB
Python
162 lines
5.4 KiB
Python
#!/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()
|