#!/usr/bin/env python3 """Generiert die PWA-Icons (icon-192/512.png): weißes Umschlag-Logo auf blauem Verlauf. Reproduzierbar — bei Logo-Änderungen einfach neu laufen lassen. python3 web/_gen_icons.py """ from PIL import Image, ImageDraw SS = 4 # Supersampling-Faktor für weiche Kanten GRAD_TL = (91, 155, 255) # #5b9bff oben links GRAD_BR = (26, 63, 176) # #1a3fb0 unten rechts INK = (255, 255, 255, 255) # Umschlag in Weiß # Logo im 48er-Koordinatensystem (identisch zu favicon.svg / Homepage-SVG) VB = 48.0 LOGO_CX, LOGO_CY = 24.0, 24.75 # Mittelpunkt der Logo-Bounding-Box LOGO_W = 34.0 # Breite der Bounding-Box FILL_FRAC = 0.60 # Logo füllt 60 % der Icon-Breite (Maskable-safe) def diagonal_gradient(size): g = Image.new("RGB", (64, 64)) px = g.load() for y in range(64): for x in range(64): t = (x + y) / 126.0 px[x, y] = tuple(round(a + (b - a) * t) for a, b in zip(GRAD_TL, GRAD_BR)) return g.resize((size, size), Image.BILINEAR) def render(size): W = size * SS img = diagonal_gradient(W).convert("RGBA") d = ImageDraw.Draw(img) scale = (FILL_FRAC * W) / LOGO_W # px pro viewBox-Einheit cx = cy = W / 2.0 def T(vx, vy): # viewBox -> Pixel (zentriert) return (cx + (vx - LOGO_CX) * scale, cy + (vy - LOGO_CY) * scale) sw = max(1, round(3.0 * scale)) # Strichstärke r = 4.5 * scale # Eckenradius Umschlag # Umschlag-Körper (abgerundetes Rechteck) x0, y0 = T(7, 16); x1, y1 = T(41, 40) d.rounded_rectangle([x0, y0, x1, y1], radius=r, outline=INK, width=sw) # Klappe (V) d.line([T(8, 18.5), T(24, 31), T(40, 18.5)], fill=INK, width=sw, joint="curve") # Kapuze (oberer Ellipsenbogen) ax0, ay0 = T(17, 9.5); ax1, ay1 = T(31, 22.5) d.arc([ax0, ay0, ax1, ay1], start=180, end=360, fill=INK, width=sw) return img.resize((size, size), Image.LANCZOS) for s in (192, 512): render(s).save(f"web/icon-{s}.png") print(f"web/icon-{s}.png geschrieben")