Hilfskripte zum Extrakt aus dem Produktprogramm erstellt. Kernkonzepte in README.md und doc

This commit is contained in:
2026-04-30 15:30:43 +02:00
parent c4581f8da3
commit 61f1430821
5 changed files with 801 additions and 19 deletions
+91 -18
View File
@@ -77,6 +77,70 @@ def extract_images(pdf_path: Path, output_dir: Path) -> int:
return count
def _extract_toc_numbers(doc) -> dict[str, str]:
"""Extrahiert Kapitelnummern aus dem Inhaltsverzeichnis des PDFs.
Sucht TOC-Seiten (Seiten mit '....' Punkt-Fuellern) und ordnet
Ueberschriftentexte ihren Kapitelnummern zu.
Returns:
Dict title_text -> kapitelnummer, z.B. {"Typen von Weichen mit Bögen": "2.3.1"}
"""
import fitz
toc_entries: dict[str, str] = {}
for page_num in range(len(doc)):
page = doc[page_num]
text = page.get_text()
# Nur TOC-Seiten verarbeiten (enthalten "....")
if "...." not in text:
continue
lines = text.splitlines()
current_number = ""
i = 0
while i < len(lines):
line = lines[i].strip()
i += 1
if not line:
continue
# TOC-Zeile: "Text ........... Seitennr"
# Kapitelnummer steht oft auf separater Zeile davor
num_match = re.match(r"^(\d+(?:\.\d+)*)\s*$", line)
if num_match:
current_number = num_match.group(1)
continue
# Zeile mit Punkt-Fuellern = TOC-Eintrag
dot_match = re.search(r"\.{4,}", line)
if dot_match:
# Text vor den Punkten extrahieren
title = line[:dot_match.start()].strip()
# Seitennummer nach den Punkten
if title and current_number:
toc_entries[title] = current_number
current_number = ""
elif title:
# Nummer koennte am Anfang des Titels stehen
nm = re.match(r"^(\d+(?:\.\d+)*)\s+(.+)", title)
if nm:
toc_entries[nm.group(2).strip()] = nm.group(1)
# Auch inline Nummern: "1.4 Abkürzungen ..... 8"
for line in lines:
line = line.strip()
dot_match = re.search(r"\.{4,}", line)
if dot_match:
title_part = line[:dot_match.start()].strip()
nm = re.match(r"^(\d+(?:\.\d+)*)\s+(.+)", title_part)
if nm:
toc_entries[nm.group(2).strip()] = nm.group(1)
return toc_entries
def extract_text_to_rst(pdf_path: Path, rst_path: Path) -> int:
"""Extrahiert Text aus PDF und schreibt ihn als .rst.
@@ -86,16 +150,31 @@ def extract_text_to_rst(pdf_path: Path, rst_path: Path) -> int:
import fitz
doc = fitz.open(str(pdf_path))
# TOC-Nummern vorab extrahieren
toc_numbers = _extract_toc_numbers(doc)
raw_lines: list[dict] = []
for page_num in range(len(doc)):
page = doc[page_num]
page_height = page.rect.height
page_dict = page.get_text("dict", flags=fitz.TEXT_PRESERVE_WHITESPACE)
for block in page_dict.get("blocks", []):
if block.get("type") != 0:
continue
for line in block.get("lines", []):
bbox = line.get("bbox", [0, 0, 0, 0])
# Positionsbasierte Kopf-/Fusszeilen-Erkennung
# Kopfzeile: Y < 100pt (Logos, Kapitelname, OMNIFLO etc.)
if bbox[1] < 100:
continue
# Fusszeile: Y > page_height - 50pt
if bbox[3] > page_height - 50:
continue
text = ""
max_size = 0.0
for span in line.get("spans", []):
@@ -109,8 +188,6 @@ def extract_text_to_rst(pdf_path: Path, rst_path: Path) -> int:
text = text.strip()
if not text:
continue
if _is_header_footer(text, page_num + 1):
continue
raw_lines.append({
"text": text,
@@ -200,6 +277,18 @@ def extract_text_to_rst(pdf_path: Path, rst_path: Path) -> int:
has_dots = "." in num
is_heading = has_dots or entry["size"] >= 12.0
# Phase 3b: Nicht-nummerierte Texte mit grosser Schrift pruefen,
# ob sie im TOC eine Kapitelnummer haben (fehlende PDF-Nummerierung)
if not is_heading and entry["size"] >= 12.0:
# Text gegen TOC abgleichen
toc_num = toc_numbers.get(text)
if toc_num:
# Nummer aus TOC voranstellen
text = f"{toc_num} {text}"
hm = RE_HEADING.match(text)
is_heading = True
log.debug(" TOC-Nummer ergaenzt: %s", text)
if is_heading:
num = hm.group(1)
heading_text = f"{num} {hm.group(2)}"
@@ -229,22 +318,6 @@ def extract_text_to_rst(pdf_path: Path, rst_path: Path) -> int:
return block_count
def _is_header_footer(text: str, page_num: int) -> bool:
"""Erkennt typische Kopf-/Fusszeilen."""
lower = text.lower().strip()
# Copyright-Zeilen
if "copyright" in lower and "schönenberger" in lower:
return True
# Seitenzahlen
if re.match(r"^seite\s+\d+$", lower):
return True
# Version/Datum Fusszeilen
if re.match(r"^version\s+\d+", lower):
return True
# "Erstellt:" / "Freigabe:" Fusszeilen
if lower.startswith("erstellt:") or lower.startswith("freigabe:"):
return True
return False