feat: Symbole zentralisieren + Fragment-Ueberschriften filtern

Perceptual-Hash-basierte Symbol-/Duplikaterkennung in lib/pdf_extract.py:

- average_hash() (aHash) + Hamming-Distanz statt exaktem Byte-Hash: erkennt
  visuell gleiche Bilder trotz unterschiedlicher Skalierung/Kompression
- Symbole (klein <=260px UND mehrfach vorkommend, z.B. Warndreiecke) werden
  EINMAL zentral nach assets/shared/symbols/symbol_<hash>.png abgelegt und im
  RST als .. image:: (12mm, zentriert) auf die eine Quelle referenziert -
  dokumentuebergreifend (registry). Ein Warndreieck: 1x gespeichert statt 58x
- grossformatige Inhaltsbild-Duplikate ebenfalls ueber aHash zusammengefuehrt
- Fragment-"Ueberschriften" (POS, li., re., "bar, max. 10 bar") unterdrueckt
- Altbestand assets/shared/symbols/img_*.png (durcheinander benannt) entfernt

Wirkung: omniflo images/ 83 -> 62, 14 zentrale Symbole statt zig Duplikate.
Voller PDF-Build sauber (lualatex exit 0, 719 Seiten).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-09-23 14:10:33 +02:00
co-authored by Claude Opus 4.8
parent c912b1e9d5
commit bbfe17ec37
31 changed files with 173 additions and 24 deletions
+15 -4
View File
@@ -87,7 +87,8 @@ CHAPTER_DOC_TITLE = {
# ---------------------------------------------------------------------------
def convert_manual(item: dict, source_root: Path, chapters_dir: Path,
dry_run: bool) -> None:
dry_run: bool, symbols_dir: Path | None = None,
symbol_registry: dict | None = None) -> None:
"""Konvertiert eine Hauptanleitung nach chapters/<kapitel>/."""
src_pdf = source_root / item["rel_path"]
chapter_dir = chapters_dir / item["chapter"]
@@ -118,8 +119,9 @@ def convert_manual(item: dict, source_root: Path, chapters_dir: Path,
# Text + Bilder positionsbasiert extrahieren (verwoben in Lesereihenfolge,
# PDF-Ueberschriftennummern entfernt, Bilder als .. figure:: referenziert).
doc_title = CHAPTER_DOC_TITLE.get(item["chapter"], slugify(item["filename"]))
blocks, img_count = pdf_extract.extract_manual(src_pdf, rst_path, img_dir,
title=doc_title)
blocks, img_count = pdf_extract.extract_manual(
src_pdf, rst_path, img_dir, title=doc_title,
symbols_dir=symbols_dir, symbol_registry=symbol_registry)
log.info(" %d Textbloecke, %d Bilder (referenziert) -> %s",
blocks, img_count, rst_path.name)
@@ -268,8 +270,17 @@ def main() -> None:
if args.only in (None, "convert"):
conv = [i for i in items if i["action"] == "convert"]
log.info("--- %d Hauptanleitungen konvertieren ---", len(conv))
# Symbole dokumentuebergreifend zentral in assets/shared/symbols/ ablegen
# und nur einmal referenzieren (Warndreiecke etc. tauchen in mehreren
# Anleitungen auf). Registry ueber alle Dokumente hinweg teilen.
symbols_dir = chapters_dir.parent / "assets" / "shared" / "symbols"
symbol_registry: dict = {}
for it in conv:
convert_manual(it, source_root, chapters_dir, dry_run)
convert_manual(it, source_root, chapters_dir, dry_run,
symbols_dir=symbols_dir, symbol_registry=symbol_registry)
if not dry_run:
log.info("Symbole zentral: %d eindeutige -> %s",
len(symbol_registry), symbols_dir)
# 2. Verzeichnislisten
if args.only in (None, "list"):