feat: Fortna-Extraktion nach chapters/ + Sphinx-PDF-Build

Skripte zum Befuellen der Kapitelstruktur aus dem Fortna-Rohmaterial und
Aufbau eines vollstaendigen Sphinx/LaTeX-Builds (nur Code/Config/Doku,
generierte RST/Bilder/PDFs und Rohmaterial via .gitignore ausgeschlossen).

- lib/fortna_inventory.py: Rohmaterial scannen, Sprache+Zielkapitel+Aktion
  zuordnen, results/fortna_inventory.json schreiben
- lib/fortna_convert_chapters.py: 5 Hauptanleitungen volltextig (Text+Bilder)
  konvertieren, Verzeichnislisten (index.de.rst) fuer uebrige Kapitel
- lib/fortna_init_project.py: projekte/fortna/ (anlage.json, overrides, images)
- lib/build_index.py: index.rst mit toctrees aus chapters/**/*.de.rst generieren
- lib/extract_products.py: Symbolfont-Zeichen (PUA) auf Unicode mappen (clean_pua)
- conf.py: Sphinx-Build (lualatex, babel statt polyglossia, iconpackage=none)
- bin/: .bat/.sh-Wrapper fuer alle Skripte + build_pdf
- doc/: Konzept-Plan (plan_fortna_extraktion.md) und lib-Skript-Uebersicht
- requirements.txt: Sphinx + myst-parser ergaenzt

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-09-23 13:23:32 +02:00
co-authored by Claude Opus 4.8
parent eb9237217e
commit e7aa3b4e4b
30 changed files with 2095 additions and 1 deletions
+31 -1
View File
@@ -29,6 +29,36 @@ PDF_TARGETS: list[tuple[str, str]] = [
# rST Ueberschriften-Zeichen nach Level
RST_HEADING_CHARS = {1: "=", 2: "-", 3: "~", 4: "^"}
# Private-Use-Area-Zeichen aus Word-Symbolfonts (Wingdings/Symbol) auf sinnvolle
# Unicode-Aequivalente abbilden. Diese Zeichen haben in Standard-LaTeX-Fonts keine
# Glyphe (erscheinen sonst als leere Kaestchen im PDF).
PUA_REPLACEMENTS = {
"": "•", # bullet -> •
"": "•", # square bullet -> •
"": "•", # bullet variante
"": "→", # rightwards arrow -> →
"": "→", # rightwards arrow -> →
"": "←", # leftwards arrow -> ←
"": "✓", # checkmark -> ✓
"": " ", # space
}
_PUA_RE = re.compile("|".join(re.escape(k) for k in PUA_REPLACEMENTS))
# Restliche Private-Use-Area-Zeichen (U+E000..U+F8FF), die keine bekannte
# Entsprechung haben, werden entfernt - sie haben in LaTeX-Fonts keine Glyphe.
_PUA_REST_RE = re.compile(r"[-]")
def clean_pua(text: str) -> str:
"""Ersetzt Symbolfont-Zeichen (Private Use Area) durch Unicode-Aequivalente.
Bekannte Symbole (Bullet, Pfeile, Haken) werden gemappt, alle uebrigen
PUA-Zeichen entfernt, damit im PDF keine leeren Kaestchen erscheinen.
"""
text = _PUA_RE.sub(lambda m: PUA_REPLACEMENTS[m.group(0)], text)
text = _PUA_REST_RE.sub("", text)
return text
def find_target(pdf_name: str) -> str | None:
"""Findet den Zielordner-Namen fuer eine PDF-Datei."""
@@ -202,7 +232,7 @@ def extract_text_to_rst(pdf_path: Path, rst_path: Path) -> int:
text = ""
max_size = 0.0
for span in line.get("spans", []):
span_text = span.get("text", "").strip()
span_text = clean_pua(span.get("text", "")).strip()
if span_text:
text += span_text + " "
size = span.get("size", 0)