5.7 KiB
Doc-Converter Memory
SPRACHE: Alle Antworten auf Deutsch!
WICHTIG: Visuelle Prüfung immer mit Chromium!
chromium --headless --disable-gpu --no-sandbox --screenshot=/tmp/screenshot.png --window-size=1200,3000 "URL"
Topic-Dateien (Details)
- hybrid-architecture.md — 3-Phasen-Pipeline (Phase 1→2a→2b)
- normalize-content-structure.md — Post-Processing Steps
- 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_percentundfooter_top_percentals 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 <p> befreien (html.parser-Bug-Fix)
- Excessive Margins (>30pt, ≥5%) + justify entfernen
- Bold
<p>mit Sektionsnummern → h2/h3/h4, Heading-Level-Korrektur - Silbentrennung reparieren
- Serif-Fonts → Arial
- Font-Family auf ALLE Elemente
- Gebrochene Absätze zusammenführen
- Em-Dash-Listen → disc bullets
- Padding von Paragraphen/Headings außerhalb Tabellen entfernen 8b. Hierarchische Einrückung (hängende Einzüge für h2/h3/h4 + Content)
- Tabellen-Borders + Backgrounds (Definition-Tables 9b, Category-Tables 9c) 9d. Seitenübergreifende Tabellen zusammenführen (gleiche Spaltenanzahl, keine Überschrift dazwischen)
- Wingdings → Unicode ♦
- Fehlende CSS-Semikolons reparieren
- 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 <body> Tag!
Fix: body = soup.find('body') or soup — dann funktioniert body.children auch ohne <body>.
<tbody> bei Tabellen (Step 9)
table.find_all('tr', recursive=False) gibt 0 zurück wenn <tbody> 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 <p> (2026-02-10)
BS4 html.parser schließt <p> NICHT automatisch vor <div>!
<p>text<div><img src="visual_layout_43.png"></div></p>
→ BS4 parsed als: <p> enthält <div> als Kind!
Effekt: Step 6 current.clear() zerstört das nested <div><img>.
Fix: Step 0 extrahiert Block-Elemente aus <p> Tags:
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:
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
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-<div>s absteigen (ohne class),
da merge_chunked_html() + Titel-Insertion Content in <div> wrappen kann.