Cover-Spielerfoto: Varianten statt 12-MB-Original (iOS Safari)
Das Cover blieb am iPhone leer, während Chrome am Desktop es zeigte. Ursache war kein fehlendes Bild: das freigestellte Spielerfoto hatte 2584x5093 Pixel und 12,3 MB. iOS Safari verwirft Bilder, deren dekodierte Größe sein Budget übersteigt — hier gut 53 MB, und auf einer Ausgabe liegen 86 Bilder. Dargestellt wird das Foto mit 98cqi Höhe, bei 480px Seitenbreite also etwa 238x470 CSS-Pixel. Es gibt jetzt 320/640/1000 als PNG mit erhaltener Transparenz (0,2 / 0,8 / 1,9 MB); sizes nennt die echte Breite, sonst zöge ein Handy mit DPR 3 die größte Variante. Das Original ist gelöscht. Dafür kann resize_image_variants() nun Alpha erhalten ($alpha = true schreibt PNG statt JPG) und die img-Komponente eine Endung ($image['ext']). Der Default bleibt JPG, weil bestehende Aufrufer PNG-Quellen absichtlich zu JPG verkleinern. bin/stadionzeitung-add.php erzeugt die Varianten künftig selbst. Dort stand "unverändert kopieren, Felix liefert bereits ein web-taugliches Foto" — genau diese Annahme ist gerissen. Außerdem: smtp.password stand in der Produktiv-Config noch auf dem Platzhalter POSTFACH_PASSWORT (von preflight auf dem Server gefunden, einziger harter Fehler). Übernommen wurde das Passwort aus der Dev-Config, mit dem der Versand am 27.07.2026 verifiziert wurde; Host, Postfach und Empfänger sind identisch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
This commit is contained in:
@@ -481,23 +481,49 @@ if ($total % 4 !== 0) {
|
||||
$log("Seitenzahl {$total} ist kein Vielfaches von 4 — die Druckfassung füllt mit " . (4 - $total % 4) . ' weißen Seiten auf. Besser gemeinsam lösen (z. B. eine Anzeige mehr).', 'WARN');
|
||||
}
|
||||
|
||||
// --- Spielerfoto fürs Cover: unverändert kopieren (kein resize_image_variants(),
|
||||
// das würde über imagejpeg() die Transparenz zerstören — Felix liefert bereits
|
||||
// ein web-taugliches Foto). Optional, kein Fehlschlag bei Fehlen. ---
|
||||
// --- Spielerfoto fürs Cover: zuschneiden UND verkleinern. ---
|
||||
// Hier stand früher „unverändert kopieren, Felix liefert bereits ein web-taugliches
|
||||
// Foto" — die Annahme stimmte nicht. Das Hutschdorf-Foto hatte 2584x5093 Pixel und
|
||||
// 12 MB; iOS Safari verwirft Bilder, deren dekodierte Größe sein Budget übersteigt
|
||||
// (hier gut 53 MB), und auf einer Ausgabe liegen rund 86 Bilder. Ergebnis: das Cover
|
||||
// blieb am iPhone leer, während Chrome am Desktop es zeigte (31.07.2026).
|
||||
// Dargestellt wird das Foto mit 98cqi Höhe, bei 480px Seitenbreite also etwa
|
||||
// 238x470 CSS-Pixel — die 1000er Variante deckt Druck und dreifache Pixeldichte ab.
|
||||
// resize_image_variants() erhält bei $alpha = true die Freistellung (PNG statt JPG).
|
||||
// Optional, kein Fehlschlag bei Fehlen.
|
||||
$playerImage = null;
|
||||
if ($spieler !== null) {
|
||||
$ext = strtolower(pathinfo($spieler, PATHINFO_EXTENSION));
|
||||
$playerDest = "{$destDir}/spieler.{$ext}";
|
||||
// PNG: auf den Inhalt zuschneiden (Freistellungen kommen oft mit viel
|
||||
// leerem Rand aus dem Export-Tool). Andere Formate (kein Alpha) unverändert.
|
||||
$copied = $ext === 'png'
|
||||
? crop_transparent_png_to_content($spieler, $playerDest)
|
||||
: copy($spieler, $playerDest);
|
||||
if ($copied) {
|
||||
$playerImage = "img/stadionzeitung/{$slug}/spieler.{$ext}";
|
||||
$log('Spielerfoto übernommen' . ($ext === 'png' ? ' (auf Inhalt zugeschnitten).' : ' (unverändert kopiert).'));
|
||||
} else {
|
||||
$log("Spielerfoto konnte nicht kopiert werden: {$spieler}", 'WARN');
|
||||
$istPng = $ext === 'png';
|
||||
// PNG: erst auf den Inhalt zuschneiden (Freistellungen kommen oft mit viel
|
||||
// leerem Rand aus dem Export-Tool), dann verkleinern. Der Zuschnitt geht in eine
|
||||
// Zwischendatei, damit das riesige Original nicht im Ausgaben-Ordner landet.
|
||||
$quelle = $spieler;
|
||||
$tmpCrop = "{$destDir}/spieler-zuschnitt.png";
|
||||
if ($istPng && crop_transparent_png_to_content($spieler, $tmpCrop)) {
|
||||
$quelle = $tmpCrop;
|
||||
}
|
||||
try {
|
||||
$varianten = resize_image_variants($quelle, "{$destDir}/spieler", [320, 640, 1000], 82, $istPng);
|
||||
$playerImage = [
|
||||
'base' => "img/stadionzeitung/{$slug}/spieler",
|
||||
'widths' => array_map(static fn (array $v): int => $v['width'], $varianten),
|
||||
'ext' => $istPng ? 'png' : 'jpg',
|
||||
'alt' => '',
|
||||
];
|
||||
$groesse = array_sum(array_map(static fn (array $v): int => $v['bytes'], $varianten));
|
||||
$log(sprintf(
|
||||
'Spielerfoto übernommen: %d Varianten%s, zusammen %.1f MB (Original %.1f MB).',
|
||||
count($varianten),
|
||||
$istPng ? ' mit Transparenz, auf Inhalt zugeschnitten' : '',
|
||||
$groesse / 1048576,
|
||||
filesize($spieler) / 1048576
|
||||
));
|
||||
} catch (Throwable $e) {
|
||||
$log("Spielerfoto konnte nicht verarbeitet werden: {$spieler}", 'WARN');
|
||||
}
|
||||
if (is_file($tmpCrop)) {
|
||||
unlink($tmpCrop);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user