From 8d4eb8635c6bbc1466d4d43408b995b48e9c1c5a Mon Sep 17 00:00:00 2001 From: root Date: Tue, 10 Feb 2026 22:26:17 +0000 Subject: [PATCH] Update: MEMORY.md mit aktuellen Post-Processing Fixes Co-Authored-By: Claude Opus 4.6 --- MEMORY.md | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 MEMORY.md diff --git a/MEMORY.md b/MEMORY.md new file mode 100644 index 0000000..8611485 --- /dev/null +++ b/MEMORY.md @@ -0,0 +1,133 @@ +# Doc-Converter Memory + +## SPRACHE: Alle Antworten auf Deutsch! + +## WICHTIG: Visuelle Prüfung immer mit Chromium! +```bash +chromium --headless --disable-gpu --no-sandbox --screenshot=/tmp/screenshot.png --window-size=1200,3000 "URL" +``` + +## Topic-Dateien (Details) +- [hybrid-architecture.md](hybrid-architecture.md) — 3-Phasen-Pipeline (Phase 1→2a→2b) +- [normalize-content-structure.md](normalize-content-structure.md) — Post-Processing Steps +- [image-extraction.md](image-extraction.md) — Bild-Cropping, Vektorgrafiken, Fullpage + +--- + +## 3-Phasen-Hybrid-Architektur (2026-02-10) + +**Kurzfassung:** Phase 1 (Strukturerkennung + Crop-Koordinaten via Gemini, 3 Content-Seiten) → `crop_page_images()` → Phase 2a (TOC/Footer) → Phase 2b (Content mit gecroppten Bildern, KEINE Header/Footer-Referenzbilder). Pre-Content-Seiten werden NICHT an Content-Gemini gesendet. + +### Gemini-basierte Crop-Erkennung (2026-02-10) +- Phase 1 sendet **3 Content-Seiten** (nicht 2) an Gemini +- RECOGNITION_PROMPT: "Finde wiederkehrende Elemente auf mind. 2 Seiten am oberen/unteren Rand" +- Gemini liefert `header_bottom_percent` und `footer_top_percent` als **physische Crop-Grenzen** +- `detect_footer_boundary()` wird NICHT mehr für Crop-Override verwendet (nur noch Gemini) +- `crop_page_images()` schneidet alle Seitenbilder physisch zu → Gemini sieht NIE Header/Footer +- Phase 2b Prompts: "Kopf-/Fußzeile bereits entfernt, konvertiere ALLES" + +### 50-Seiten-Testergebnisse (test50_v3, 2026-02-10) +| Metrik | Vorher (test50) | Nachher (test50_v3) | +|--------|----------------|---------------------| +| Kosten | $0.471 | $0.392 | +| visual_layouts | 3 | 1 | +| Footer-Artefakte | vorhanden | 0 | + +--- + +## Content-Normalisierung (2026-02-10) + +`normalize_content_structure(html_content, detected_type="standard")` — Steps: +0. **Block-Elemente aus `

` befreien** (html.parser-Bug-Fix) +1. Excessive Margins (>30pt, ≥5%) + justify entfernen +2. Bold `

` mit Sektionsnummern → h2/h3/h4, Heading-Level-Korrektur +3. Silbentrennung reparieren +4. Serif-Fonts → Arial +5. Font-Family auf ALLE Elemente +6. Gebrochene Absätze zusammenführen +7. Em-Dash-Listen → disc bullets +8. Padding von Paragraphen/Headings außerhalb Tabellen entfernen +8b. **Hierarchische Einrückung** (hängende Einzüge für h2/h3/h4 + Content) +9. Tabellen-Borders + Backgrounds (Definition-Tables 9b, Category-Tables 9c) +9d. **Seitenübergreifende Tabellen zusammenführen** (gleiche Spaltenanzahl, keine Überschrift dazwischen) +10. Wingdings → Unicode ♦ +11. Fehlende CSS-Semikolons reparieren +12. Doppelte Semikolons bereinigen + +**Pipeline:** strip_invalid_image_refs → strip_toc_from_content → normalize_content_structure → normalize_font_sizes → make_content_left_aligned + +**Font-Sizes:** h1=16pt, h2=12pt, h3/h4=11pt, p/li=11pt, footer=8pt + +**Tabellen-Farben:** detected_type="va" → #E6B8B7, detected_type="standard" → #B8CCE4 + +**Gemini-Kontext:** Erste Tabellen-Hintergrundfarbe wird an Folgeseiten-Prompts übergeben (±5% Toleranz) + +**Dokumenttitel:** h1 aus Titelseite (text_by_page[1]) + Untertitel 11pt, KEINE Bilder. +Ausnahme: "1. Inhaltsverzeichnis" im TOC → kein h1, Titel wird h2 "1. ". + +--- + +## Bekannte Pitfalls + +### KRITISCH: soup.find('body') bei HTML-Fragmenten +`merge_chunked_html()` liefert HTML OHNE `` Tag! +Fix: `body = soup.find('body') or soup` — dann funktioniert `body.children` auch ohne ``. + +### `` bei Tabellen (Step 9) +`table.find_all('tr', recursive=False)` gibt 0 zurück wenn `` existiert! +Fix: `tbody = table.find('tbody', recursive=False); row_container = tbody if tbody else table` + +### strip_toc_from_content — Absolute Positioning +Rule 5: NICHT `.decompose()`! Nur CSS-Properties `position: absolute`, `top`, `left`, `right`, `bottom` strippen. + +### CSS-Semikolons (Step 11) +Gemini generiert `text-align: left font-weight: bold` (fehlt `;`). +Regex erkennt CSS-Properties ohne vorangehendes `;`. + +### KRITISCH: html.parser nestet Block-Elemente in `

` (2026-02-10) +BS4 `html.parser` schließt `

` NICHT automatisch vor `

`! +```html +

text

+→ BS4 parsed als:

enthält

als Kind! +``` +**Effekt:** Step 6 `current.clear()` zerstört das nested `
`. +**Fix:** Step 0 extrahiert Block-Elemente aus `

` Tags: +```python +block_tags = {'div', 'table', 'h1',...,'blockquote'} +for p_tag in soup.find_all('p'): + block_children = [c for c in p_tag.children if c.name in block_tags] + for block in block_children: + block.extract(); p_tag.insert_after(block) +``` +Zusätzlich: Safety-Guards in Step 3 + Step 6: +```python +if current.find(['div', 'table', 'img'], recursive=False): continue +``` + +### BeautifulSoup decomposed Elements +Fix: `if hasattr(el, 'decomposed') and el.decomposed or el.parent is None: continue` + +### Isoliertes Post-Processing Testen +```python +docker exec n8n-doc-converter python3 -c " +import sys; sys.path.insert(0, '/app') +from server import normalize_content_structure +html = open('/data/bsp/7787ddc0_test15/content.html').read() +result = normalize_content_structure(html, detected_type='standard') +open('/data/bsp/7787ddc0_test15/content.html', 'w').write(result) +" +``` +Spart ~$0.17 pro Test. + +### Step 8b: Hierarchische Einrückung +``` +HEADING_INDENT = { + 'h2': margin-left:0, padding-left:10pt, text-indent:-10pt + 'h3': margin-left:10pt, padding-left:12pt, text-indent:-12pt + 'h4': margin-left:22pt, padding-left:14pt, text-indent:-14pt +} +CONTENT_MARGIN = { 'h2':10pt, 'h3':22pt, 'h4':36pt } +``` +Iteriert über direkte Children von body/soup, trackt `current_heading_level`. +**PITFALL:** `collect_elements()` muss in Wrapper-`

`s absteigen (ohne class), +da `merge_chunked_html()` + Titel-Insertion Content in `
` wrappen kann.