Text Auto-Resize
Der Auto-Resize Mechanismus passt die Schriftgröße automatisch an, damit Text in den Container passt, ohne Wörter zu umbrechen.
Problemstellung
Wenn ein Benutzer Text wie "DEIN FERNSTUDIUM ENTDECKEN" in einen 170px breiten Container eingibt:
- Das Wort "FERNSTUDIUM" ist bei 33px Schriftgröße breiter als der Container
- Ohne Auto-Resize würde das Wort mitten im Wort umbrechen → unschön
Lösung: Zwei-Phasen-Algorithmus
Phase 1: Word-Fit Check
Prüft, ob jedes einzelne Wort in den Container passt:
Input: "DEIN FERNSTUDIUM ENTDECKEN" @ 33px, Container: 170px
Wort-Breiten:
- "DEIN" = 52px ✅
- "FERNSTUDIUM" = 185px ❌ (> 170px)
- "ENTDECKEN" = 128px ✅
Ergebnis: Word-Fit FAILED → Schriftgröße muss reduziert werden
Phase 2: Binary Search für optimale Größe
Findet die größte Schriftgröße, bei der alle Wörter passen:
Binary Search: low=8, high=33
Iteration 1: mid=20 → "FERNSTUDIUM"=112px ✅ → low=20
Iteration 2: mid=26 → "FERNSTUDIUM"=145px ✅ → low=26
Iteration 3: mid=29 → "FERNSTUDIUM"=160px ✅ → low=29
Iteration 4: mid=31 → "FERNSTUDIUM"=172px ❌ → high=31
Iteration 5: mid=30 → "FERNSTUDIUM"=166px ✅ → low=30
Ergebnis: 30px (größte Größe bei der alles passt)
Max-Lines Constraint
Wenn maxLines gesetzt ist, wird zusätzlich geprüft, ob der Text in X Zeilen passt:
Text: "BILDUNG, DIE DICH BEGLEITET"
Container: 170px
maxLines: 2
Bei 33px: 3 Zeilen → zu viele
Bei 25px: 2 Zeilen → passt ✅
Wichtig: Word-Fit wird IMMER zuerst geprüft, auch wenn maxLines gesetzt ist!
Implementierungen
Frontend (Browser)
Datei: frontend/src/lib/utils/textAutoResize.ts
export function calculateOptimalFontSize(
text: string,
containerWidth: number,
initialFontSize: number,
fontFamily: string,
fontWeight: string,
maxLines?: number
): number
Text-Messung: Canvas API (context.measureText())
Python Renderer
Datei: renderer/src/utils/text_utils.py
class TextUtils:
def calculate_optimal_font_size(
self, text, max_width, initial_font_size,
font_family, font_weight, font_style, max_lines
) -> int
def calculate_optimal_font_size_for_word_fit(
self, text, max_width, initial_font_size,
font_family, font_weight, font_style
) -> int
Text-Messung: PIL/Pillow (font.getbbox())
Browser vs PIL Unterschiede
PIL (FreeType) misst Text ~2-3% breiter als Browser:
| Text | Browser | PIL | Differenz |
|---|---|---|---|
| "FERNSTUDIUM" @ 33px | 180px | 185px | +2.7% |
Lösung: 2% Toleranz in der Word-Fit Prüfung:
tolerance = max_width * 0.02 # 2%
if word_width > max_width + tolerance:
return False # Wort passt nicht
Ohne diese Toleranz würde der Python Renderer kleinere Schriftgrößen verwenden als das Frontend.
Flowchart
┌─────────────────────────────────────┐
│ Auto-Resize Start │
│ (initial_font_size = 33px) │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Phase 1: Check Word-Fit │
│ Passt jedes Wort in Container? │
└──────────────┬──────────────────── ──┘
│
┌─────┴─────┐
│ │
YES NO
│ │
▼ ▼
┌─────────────┐ ┌─────────────────────────┐
│ Keep Size │ │ Binary Search: │
│ │ │ Find largest size where │
│ │ │ all words fit │
└──────┬──────┘ └───────────┬─────────────┘
│ │
└──────────┬──────────┘
│
▼
┌─────────────────────────────────────┐
│ Phase 2: Check Max-Lines │
│ (nur wenn maxLines gesetzt) │
└──────────────┬──────────────────────┘
│
┌─────┴─────┐
│ │
lines ≤ max lines > max
│ │
▼ ▼
┌─────────────┐ ┌─────────────────────────┐
│ Done │ │ Binary Search: │
│ │ │ Find largest size where │
│ │ │ lines ≤ maxLines │
└─────────────┘ └─────────────────────────┘
Edge Cases
1. Leerer Text
if not text or not text.strip():
return initial_font_size # Keine Änderung
2. Einzelnes sehr langes Wort
Wenn ein Wort selbst bei minimaler Schriftgröße (8px) nicht passt, wird es trotzdem angezeigt (overflow).
3. BBCode Formatierung
BBCode-Tags ([b], [/b]) werden vor der Messung entfernt:
plain_text = re.sub(r'\[[biu]\]|\[\/[biu]\]', '', text)
4. Windows-Zeilenenden
\r\n und \r werden zu \n normalisiert:
text = text.replace('\r\n', '\n').replace('\r', '\n')
Debugging
Bei Problemen mit Auto-Resize:
- Prüfe Container-Breite: Ist sie korrekt gesetzt?
- Prüfe Font-Loading: Wird die richtige Schriftart geladen?
- Vergleiche Browser vs Renderer: Unterschiedliche Schriftgrößen?
- Aktiviere Logging:
print(f"Word '{word}' width: {word_width}px, max: {max_width}px")
Siehe auch
- Layout Engine - Wie Container-Höhen berechnet werden
- Rendering Architecture - Frontend vs Python Renderer