#!/usr/bin/env python3 """ Génère les deux images de l'installeur NSIS à partir du logo de l'app. python scripts/installer-art.py NSIS impose du BMP à des tailles fixes : · sidebar.bmp 164 x 314 — panneau de gauche (accueil et fin) · header.bmp 150 x 57 — bandeau des pages intermédiaires Les images sont versionnées dans src-tauri/installer/ : ce script n'est à relancer que si le logo ou les couleurs de marque changent (il demande Pillow, qui n'est PAS une dépendance du projet). """ from pathlib import Path from PIL import Image, ImageDraw, ImageFont ROOT = Path(__file__).resolve().parent.parent OUT = ROOT / "src-tauri" / "installer" ICON = ROOT / "src-tauri" / "icons" / "128x128@2x.png" # Dégradé du logo (public/logo.svg) INDIGO = (99, 102, 241) TEAL = (20, 184, 166) INK = (16, 16, 24) INK_SOFT = (30, 29, 48) def font(size: int, bold: bool = False): """Segoe UI si disponible, repli sur la police par défaut de Pillow.""" for name in (["segoeuib.ttf", "seguisb.ttf"] if bold else ["segoeui.ttf"]): try: return ImageFont.truetype(f"C:/Windows/Fonts/{name}", size) except OSError: continue return ImageFont.load_default() def lerp(a, b, t): return tuple(round(a[i] + (b[i] - a[i]) * t) for i in range(3)) def vertical_gradient(size, top, bottom): w, h = size img = Image.new("RGB", size) d = ImageDraw.Draw(img) for y in range(h): d.line([(0, y), (w, y)], fill=lerp(top, bottom, y / max(h - 1, 1))) return img def horizontal_gradient(size, left, right): w, h = size img = Image.new("RGB", size) d = ImageDraw.Draw(img) for x in range(w): d.line([(x, 0), (x, h)], fill=lerp(left, right, x / max(w - 1, 1))) return img def logo(px: int) -> Image.Image: return Image.open(ICON).convert("RGBA").resize((px, px), Image.LANCZOS) # --------------------------------------------------------------------- # Panneau latéral 164 x 314 — fond sombre, logo, nom, liseré de marque # --------------------------------------------------------------------- def sidebar() -> Image.Image: W, H = 164, 314 img = vertical_gradient((W, H), INK_SOFT, INK) d = ImageDraw.Draw(img, "RGBA") # Halo indigo diffus derrière le logo, pour éviter le fond plat. cx, cy = W // 2, 96 for r in range(92, 0, -4): alpha = int(30 * (1 - r / 92) ** 2) d.ellipse([cx - r, cy - r, cx + r, cy + r], fill=(*INDIGO, alpha)) mark = logo(76) img.paste(mark, (cx - 38, cy - 38), mark) title = font(15, bold=True) sub = font(11) d.text((cx, 168), "GameDev", font=title, fill=(255, 255, 255), anchor="mm") d.text((cx, 186), "Tracker", font=title, fill=(255, 255, 255), anchor="mm") d.text((cx, 210), "Gestion de projet", font=sub, fill=(150, 148, 170), anchor="mm") d.text((cx, 225), "de jeu vidéo", font=sub, fill=(150, 148, 170), anchor="mm") # Liseré dégradé en pied de panneau : la signature visuelle de la marque. bar = horizontal_gradient((W, 4), INDIGO, TEAL) img.paste(bar, (0, H - 4)) return img # --------------------------------------------------------------------- # Bandeau 150 x 57 — fond SOMBRE, accordé à MUI_BGCOLOR du template # --------------------------------------------------------------------- def header() -> Image.Image: W, H = 150, 57 img = Image.new("RGB", (W, H), INK) d = ImageDraw.Draw(img) mark = logo(34) img.paste(mark, (W - 34 - 12, (H - 34) // 2), mark) name = font(11, bold=True) d.text((W - 56, H // 2 - 7), "GameDev", font=name, fill=(255, 255, 255), anchor="rm") d.text((W - 56, H // 2 + 7), "Tracker", font=name, fill=(255, 255, 255), anchor="rm") bar = horizontal_gradient((W, 3), INDIGO, TEAL) img.paste(bar, (0, H - 3)) return img OUT.mkdir(parents=True, exist_ok=True) for name, image in (("sidebar", sidebar()), ("header", header())): path = OUT / f"{name}.bmp" # BMP 24 bits sans alpha : le seul format que NSIS accepte partout. image.convert("RGB").save(path, "BMP") print(f" {path.relative_to(ROOT)} {image.width}x{image.height}") print("\nImages générées.")