Files
ScarriffleandClaude Opus 5 dd2c33725f feat: Ein Markenlogo je Vertrag
Am Logo findet man den Vertrag schneller als am Namen: Netflix erkennt man am
roten N, nicht am Wort – und erst recht nicht an einem Zahnrad aus der
Symbolauswahl. Die bleibt als Rueckfall fuer alles, wozu es kein Logo gibt.

Hochgeladen wird, was das Telefon hergibt; gespeichert ein PNG von hoechstens
128 Pixeln Kantenlaenge. Aus drei Megabyte werden ein paar Kilobyte, und das
Seitenverhaeltnis bleibt – ein verzerrtes Logo sieht falscher aus als ein
kleines.

Immer PNG, egal was hereinkam. Das erspart der Anzeige jede
Fallunterscheidung und schliesst SVG aus: Eine SVG-Datei kann Skript
enthalten, und ein Bild, das der Nutzer hochlaedt und der Browser ausfuehrt,
ist genau die Art Falle, die man sich nicht in ein Finanzprogramm legt.

Eigene Tabelle, nicht eine Spalte an den Vertraegen: Sonst zoege jede
Listenabfrage saemtliche Bilder mit heraus, obwohl die Liste sie einzeln
nachlaedt.

ZWEI FEHLER IM SICHERN, die dabei aufgefallen sind:

Binaeres liess sich gar nicht sichern – die Ausgabe scheiterte an den Bytes,
und zwar erst beim Schreiben, also nachdem der Nutzer glaubt, ein Backup zu
haben. Jetzt Base64.

Und "Ergaenzen" legte Zeilen von Tabellen mit natuerlichem Schluessel ein
zweites Mal an, statt sie wiederzuerkennen. Beim Logo kippte damit die ganze
Wiederherstellung. Betraf bisher keine Tabelle, weil noch keine solche einen
Vergleichsschluessel hatte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 19:35:37 +02:00

64 lines
2.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Ein hochgeladenes Bild zu einem brauchbaren Logo machen.
Was aus einem Formular kommt, ist selten das, was man anzeigen will: ein
Screenshot vom Telefon, drei Megabyte, 3000 Pixel breit, für eine Kachel von
dreissig. Hier wird daraus ein kleines PNG.
Immer PNG, egal was hereinkam. Das erspart der Anzeige jede Fallunterscheidung
und schliesst SVG aus – eine SVG-Datei kann Skript enthalten, und ein Bild,
das der Nutzer hochlädt und der Browser ausführt, ist genau die Art Falle, die
man sich nicht in ein Finanzprogramm legt.
"""
from __future__ import annotations
import io
from PIL import Image, UnidentifiedImageError
# Was hochgeladen werden darf. Grosszügig – gerechnet wird ohnehin klein.
MAX_UPLOAD = 4 * 1024 * 1024
# Kantenlänge des gespeicherten Bilds. 128 statt 64: Auf einem Bildschirm mit
# doppelter Pixeldichte ist eine 64er-Kachel sonst sichtbar unscharf.
MAX_KANTE = 128
MIME = "image/png"
class LogoFehler(ValueError):
"""Das Bild lässt sich nicht verwenden – mit einem Grund für den Nutzer."""
def mach_logo(daten: bytes) -> bytes:
"""Aus hochgeladenen Bytes ein kleines PNG machen.
Das Seitenverhältnis bleibt: Ein verzerrtes Logo sieht falscher aus als
ein kleines.
"""
if not daten:
raise LogoFehler("Die Datei ist leer.")
if len(daten) > MAX_UPLOAD:
raise LogoFehler(
f"Das Bild ist zu gross ({len(daten) // 1024} KB, erlaubt sind "
f"{MAX_UPLOAD // 1024 // 1024} MB)."
)
try:
bild = Image.open(io.BytesIO(daten))
bild.load()
except (UnidentifiedImageError, OSError) as e:
raise LogoFehler(
"Das ist kein lesbares Bild. PNG, JPEG, WEBP oder GIF gehen."
) from e
# Durchsichtigkeit erhalten – Logos sind meist freigestellt, und auf
# dunklem Grund fiele ein weisser Kasten sofort auf.
if bild.mode not in ("RGBA", "LA"):
bild = bild.convert("RGBA")
bild.thumbnail((MAX_KANTE, MAX_KANTE), Image.LANCZOS)
ziel = io.BytesIO()
bild.save(ziel, format="PNG", optimize=True)
return ziel.getvalue()