Logo-Downloadseite /logo mit Erzeuger-Skript

Damit die Abteilungen das Wappen selbst holen können, statt jedes Mal zu fragen:
drei Varianten (farbig, einfarbig schwarz, einfarbig weiß) in SVG, PDF, PNG und JPG,
dazu ein ZIP über alles und die Vereinsfarben. Verlinkt nur in der Fußzeile.

Bisher lag das Logo genau einmal im Projekt, als eine einzige SVG. Es gab kein PNG,
kein JPG, kein PDF und keine einfarbige Fassung.

bin/logo-build.php erzeugt alle Dateien aus einer Master-SVG unter storage/logo-quellen/.
Gerastert wird mit headless Chrome, weil GD kein SVG lesen kann und weder Inkscape noch
rsvg-convert noch ImageMagick verfügbar sind; das PDF ist dabei echter Vektor. Das Skript
läuft einmalig lokal, nichts davon im Request-Pfad. bin/preflight.php meldet als Fehler,
wenn eine Download-Datei fehlt: die Komponente ließe den Knopf sonst still weg.

Die beiden einfarbigen Fassungen sind exakte Negative voneinander, nur Linien, alle
Flächen frei. EPS gibt es bewusst nicht, dafür fehlt jedes Werkzeug; SVG und PDF decken
die Druckereien ab. Frühere Vereinslogos sind vorbereitet, aber im Projekt liegt bisher
kein einziges historisches Wappen vor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J9mubC8gtpjtJntbhSJBRS
This commit is contained in:
2026-09-17 10:10:08 +02:00
parent e6ac00b248
commit 75fce4f680
35 changed files with 1141 additions and 2 deletions

View File

@@ -60,6 +60,7 @@ selbst gehosteter Stack ohne Abhängigkeiten von Drittanbietern.
| Veranstaltungen (`/veranstaltungen` + Detailseiten) | `data/veranstaltungen.json` + `public/assets/img/veranstaltungen/` | Termin, Texte, Programm, Turnierpläne (`turniere[]`, nur fürs Heft), Abschnitte von Claude im Chat gepflegt — **außer `plakate` und `galerie`: die schreibt ausschließlich `bin/veranstaltung-bilder.php`**. Adresse nie in die Texte schreiben (steht in `club.json`) |
| News (`/news` + optionale Detailseiten) | `data/news.json` (redaktionelle Meldungen; `artikel[]` = Fließtext-Blöcke, `status` = Entwurf/veröffentlicht) — Instagram-Posts und Ergebnisse kommen weiterhin nur aus `data/instagram.json`/`data/matchcenter.json`, werden auf der News-Seite nur gemergt | Von Claude im Chat gepflegt wie `events.json`/`teams.json` — **keine** Automatisierung, kein Bin-Skript. Instagram/Ergebnisse nie hier duplizieren. `text` bleibt der Teaser (Feed, Meta-Description, Article-Schema), der Artikel steht in `artikel[]` |
| Dateiablage `/dateien` (Konfiguration) | `public/dateien/_filesconfig.php` (Rechte pro Konto: `storage/dateien/system/users/admin/config.php`) | **`storage/dateien/system/config/config.php` von Hand editieren** — die App generiert sie selbst und überschreibt sie bei jedem Update. Passwörter/Lizenz gehören in `config.php` (`dateien.*`) |
| Logo-Downloads (`/logo`) | Texte: `data/logo.json` · Master: `storage/logo-quellen/*.svg` | Die Dateien unter `public/assets/downloads/logo/` sind **maschinenverwaltet von `bin/logo-build.php`** — nie von Hand skalieren, nie von Hand umfärben, nie einzeln nachlegen. Auch keine Dateiliste in die JSON schreiben: welche Formate es gibt, entscheidet das Skript, Größen liest die Seite von der Platte |
| Icons | `public/assets/icons/` (Bootstrap Icons, lokal) via `icon()`-Helper | Inline-SVG-Pfade von Hand ins Markup, Emoji/Unicode-Glyphen, gezeichnete CSS-Icons, Icon-Font/CDN |
Per-Page-Meta (Title/Description/OG) lebt in der jeweiligen Page-Datei (`app/pages/*.php`) im `$meta`-Array.
@@ -239,7 +240,7 @@ Funktion, sie ist die einzige Stelle, an der aus JSON HTML entsteht.
Web wie CLI. Kein `error_log()`, kein `file_put_contents()` auf eine Logdatei, keine eigene
Log-Closure in einem neuen Skript.
- Kanal = Dateiname unter `storage/logs/`: `mail` · `spam` · `instagram` · `matchcenter` ·
`stadionzeitung` · `veranstaltungen` · `chronik` · `filesgallery` · `logrotate`
`stadionzeitung` · `veranstaltungen` · `chronik` · `filesgallery` · `logo` · `logrotate`
(letzterer nur für den Übersprungen-Hinweis des Cron-Endpoints, der seinen Kanal aus dem
Job-Namen bildet)
(`php-errors.log` schreibt PHP selbst, eigenes Format).
@@ -1180,6 +1181,66 @@ funktionieren hier **nicht** — sie brauchen eine öffentliche URL, unser `stor
`public/dateien/.htaccess` (braucht `'unsafe-inline'` für Scripts/Styles: die App interpoliert
Inline-Code per PHP und kennt keine Nonces) — sie gilt nur dort, die Seiten behalten die strikte.
## Logo-Downloadseite `/logo`
Damit die Abteilungen das Wappen selbst holen können, statt jedes Mal zu fragen: eine
öffentliche Seite mit jeder Variante in jedem gängigen Format, dazu die Farbwerte und die
Nutzungsregeln. Verlinkt **nur in der Fußzeile** (`navigation.json` → `legal`), nicht im
Hauptmenü. Die Dateien werden **einmalig lokal** erzeugt und committet; nichts davon läuft
auf dem Server, es gibt keinen Cron und keine Laufzeit-Abhängigkeit.
**Drei Varianten, alle aus EINER Master-SVG** (`storage/logo-quellen/aktuell.svg`, identisch
zum Website-Asset `public/assets/img/logo.svg`): `farbig`, `einfarbig-schwarz` und
`einfarbig-weiss`. Die Umfärbung passiert durch Austausch der Farbklassen im `<defs>`-Block
der CorelDRAW-Datei (`.fil0`/`.fil3` schwarz, `.fil1` weiß, `.fil2` rot) —
`bin/logo-build.php` bricht ab, wenn eine erwartete Klasse fehlt, sonst entstünde stillschweigend
dreimal dasselbe Logo.
**Die beiden einfarbigen Fassungen sind exakte Negative voneinander:** nur Linien, ALLE
Flächen frei. Zwei Irrwege dahin, beide am 17.09.2026 korrigiert: eine Fassung „für dunklen
Grund" behielt das rote Schild und war damit keine einfarbige, sondern ein farbiges Logo mit
weißen Konturen; und `einfarbig-schwarz` ersetzte Rot durch **Weiß**, wodurch im PNG eine
deckend weiße Schildfläche steckte (auf farbigem Papier ein weißer Klotz). **Einfarbig heißt
einfarbig:** ein Vollton, der Rest transparent. Nachweisbar über die Volltonfarben im PNG,
je Variante darf genau eine vorkommen.
**Gerastert wird mit headless Chrome, nicht mit GD.** `bin/img-resize.php` kann keine SVG lesen,
und Inkscape, rsvg-convert oder ImageMagick gibt es hier nicht (auf dem Shared Hosting erst
recht nicht). Chrome kann beides, was gebraucht wird: transparent rastern (`--screenshot` +
`--default-background-color=00000000`) und ein **echtes Vektor-PDF** drucken (`--print-to-pdf`,
nachgemessen: Kurven-Operatoren im Inhaltsstrom, kein eingebettetes Pixelbild).
**Kein `--user-data-dir`** — der naheliegende eigene Profilordner lässt Chrome 152 beim ersten
Aufruf endlos hängen, ohne Bild und ohne Meldung. Ohne den Schalter läuft jeder Aufruf in
Sekunden durch, auch bei geöffnetem Chrome. Wer das ändert, misst die Laufzeit nach.
**Der JPG-Hintergrund gehört zur Variante**, nicht zum Format: JPG kann keine Transparenz, und
die weiße Fassung auf weißem Grund wäre ein leeres Blatt. `einfarbig-weiss` wird deshalb auf
Anthrazit (`#222`) flachgelegt, alle anderen auf Weiß (`jpg_bg` je Eintrag in `$OUTPUTS`).
**EPS gibt es bewusst nicht.** Dafür bräuchte es Illustrator, Inkscape oder Ghostscript; ein
Composer-Paket verbietet Regel 1. Druckereien nehmen PDF oder SVG, beides liegt als Vektor
bereit. Steht die Frage doch im Raum, beantwortet sie die FAQ auf der Seite.
**Retro-Logos:** `retro[]` in `data/logo.json` ist vorbereitet und vorerst leer — im Projekt
existiert kein einziges historisches Wappen (weder im Repo noch in `.context/`, der alten
CMS-Datenbank oder der Chronik-Sammlung). Kommt ein Scan, wird er eine weitere Datei in
`storage/logo-quellen/` plus ein Eintrag in `$OUTPUTS`; liegt er nur als Pixelbild vor, erzeugt
das Skript PNG und JPG über `resize_image_variants()` und lässt SVG und PDF weg.
**Die Seite ist bewusst karg** (Felix, 17.09.2026): Einleitungstext, Nutzungshinweis
(„nicht verzerren, nicht einfärben"), die Abschnittsüberschrift über den Karten und eine
fünfteilige FAQ waren gebaut und sind alle wieder entfernt worden; das ZIP über alle
Varianten hatte ebenfalls eine eigene Sektion samt Überschrift „Alles auf einmal" und ist
jetzt nur noch ein Knopf unter dem Karten-Raster (Prop `paket` der Komponente) — „alles
davon" gehört direkt unter das, worauf es sich bezieht. Eine Downloadseite ist ein
Regal, kein Text: Wappen, Knöpfe, Farben, fertig. Die Variantenbeschreibungen sind auf drei
Wörter gekürzt („Stempel, Gravur, einfarbiger Druck."). Die Überschrift steht noch als
`visually-hidden` im DOM, damit die Heading-Kette nicht von `h1` auf `h3` springt.
**Nichts davon wieder hinzuerklären.**
`bin/preflight.php` prüft, dass zu jeder Variante die Dateien existieren (**Fehler**, nicht
Warnung: die Komponente lässt einen Knopf ohne Datei still weg, man sähe es der Seite nicht an)
und warnt, wenn eine Master-SVG neuer ist als das Erzeugte.
## Chronik 2028 (Materialsammlung fürs Jubiläum)
Sammelt Fundstücke für die Vereinschronik zum 120-jährigen Bestehen. **Reine Recherche-Werkzeuge,