# -*- coding: utf-8 -*- """Klärt die verbleibenden Layout-Fragen vor dem Bau des Parsers. Prüft: exakte Spaltengrenzen, Kreuz-in-Checkbox-Erkennung, Unterstreichungen (Kernelemente der Musterantworten), Kapitelkopfzeilen und seitenübergreifende Fragen. """ import io import re import sys from collections import Counter, defaultdict import fitz sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") PDF = r"C:\Antigravity\Waffensachkundeprüfung\Fragenkatalog_sachkunde_mitAntworten.pdf" FIRST_PAGE = 5 # 0-basiert: PDF-Seite 6 doc = fitz.open(PDF) # ---------------------------------------------------------------- Spalten vlines = Counter() hlines_full = Counter() box_x = Counter() for pno in range(FIRST_PAGE, doc.page_count): for d in doc[pno].get_drawings(): for item in d["items"]: if item[0] == "l": p1, p2 = item[1], item[2] if abs(p1.x - p2.x) < 0.7 and abs(p1.y - p2.y) > 3: vlines[round(p1.x)] += 1 elif item[0] == "re": r = item[1] if r.width < 1.6 and r.height > 3: vlines[round(r.x0)] += 1 if 6 < r.width < 20 and abs(r.width - r.height) < 4: box_x[round(r.x0)] += 1 print("Vertikale Tabellenlinien (x -> Häufigkeit), Top 12:") for x, n in vlines.most_common(12): print(f" x={x:4d} {n}") print(f"\nCheckbox-x-Positionen: {box_x.most_common(6)}") # ------------------------------------------------- Kreuz-in-Checkbox-Test page = doc[FIRST_PAGE] boxes, diagonals = [], [] for d in page.get_drawings(): for item in d["items"]: if item[0] == "re": r = item[1] if 6 < r.width < 20 and abs(r.width - r.height) < 4 and r.x0 > 480: boxes.append(r) elif item[0] == "l": p1, p2 = item[1], item[2] if abs(p1.x - p2.x) > 2 and abs(p1.y - p2.y) > 2 and p1.x > 480: diagonals.append((p1, p2)) print(f"\nSeite 6: {len(boxes)} Checkboxen, {len(diagonals)} Diagonalsegmente") for b in sorted(boxes, key=lambda r: r.y0): inside = sum(1 for p1, p2 in diagonals if b.x0 - 2 <= p1.x <= b.x1 + 2 and b.y0 - 2 <= p1.y <= b.y1 + 2) words = page.get_text("words", clip=fitz.Rect(290, b.y0 - 4, 500, b.y0 + 14)) label = " ".join(w[4] for w in words)[:46] print(f" y={b.y0:6.1f} Diagonalen={inside} -> {'ANGEKREUZT' if inside >= 2 else 'leer '} {label!r}") # --------------------------------------------------------- Unterstreichung print("\n--- Unterstreichungs-Kandidaten (dünne Füllrechtecke) ---") thin = Counter() for pno in range(FIRST_PAGE, doc.page_count): for d in doc[pno].get_drawings(): if d.get("fill") is None: continue for item in d["items"]: if item[0] == "re": r = item[1] if 0.3 < r.height < 1.6 and r.width > 4: thin[round(r.height, 2)] += 1 print(f"Höhenverteilung dünner Rechtecke: {sorted(thin.items())}") # Beispielseite mit Musterantwort-Hervorhebung (Frage 1.01 auf Seite 6) p6 = doc[FIRST_PAGE] for d in p6.get_drawings(): for item in d["items"]: if item[0] == "re": r = item[1] if 0.6 < r.height < 1.6 and r.width > 4 and r.x0 > 280: above = p6.get_text("words", clip=fitz.Rect(r.x0 - 2, r.y0 - 12, r.x1 + 2, r.y0)) print(f" Unterstrichen y={r.y0:.1f} x={r.x0:.0f}-{r.x1:.0f}: {' '.join(w[4] for w in above)!r}") # ------------------------------------------------------------ Kapitelköpfe print("\n--- Kapitelkopfzeilen (aus dem Seitenkopf) ---") heads = defaultdict(list) for pno in range(FIRST_PAGE, doc.page_count): head = doc[pno].get_text("text", clip=fitz.Rect(60, 30, 540, 95)).strip() lines = [l.strip() for l in head.splitlines() if l.strip()] heads[tuple(lines[:3])].append(pno + 1) for key, pages in heads.items(): print(f" Seiten {pages[0]}-{pages[-1]} ({len(pages)}): {key}") # ------------------------------------------- Fragennummern und Seitenlage print("\n--- Fragennummern je Seite (Spalte 1) ---") num_re = re.compile(r"^\d{1,3}(?:\.\d{2,3})?$") total = 0 per_page_first = {} for pno in range(FIRST_PAGE, doc.page_count): words = doc[pno].get_text("words", clip=fitz.Rect(60, 92, 103, 800)) nums = [w[4] for w in sorted(words, key=lambda w: w[1]) if num_re.match(w[4])] total += len(nums) if nums: per_page_first[pno + 1] = (nums[0], nums[-1], len(nums)) print(f"Gefundene Fragennummern gesamt: {total}") sample = list(per_page_first.items()) for pg, v in sample[:5] + sample[-5:]: print(f" Seite {pg}: {v[0]} … {v[1]} ({v[2]} Fragen)")