Zum Hauptinhalt springen

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:

TextBrowserPILDifferenz
"FERNSTUDIUM" @ 33px180px185px+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:

  1. Prüfe Container-Breite: Ist sie korrekt gesetzt?
  2. Prüfe Font-Loading: Wird die richtige Schriftart geladen?
  3. Vergleiche Browser vs Renderer: Unterschiedliche Schriftgrößen?
  4. Aktiviere Logging:
    print(f"Word '{word}' width: {word_width}px, max: {max_width}px")

Siehe auch