Abgespalten aus #224 (Entscheidung E3 vom 2026-10-13). Erstes Glied der Kette „Große Dokumente“.
Revidiert durch #231 (Betreiber, 2026-10-14): Die Prüfung an zehn weiteren PDFs zeigte, dass die Kennzeichnung jeder sichtbaren Form zu laut ist (bis +24 % Text) und falsche Durchstreichungen erzeugt. <mark data-farbe> entfällt; Kennzeichnung nur noch als ~~/**/* mit eigenen Regeln („Struktur zuerst“). Gliederung, Wortlaut-Treue und Kopfaufbau aus diesem Paket bleiben. Der Bildseiten-CHECK wird dort präzisiert. Die Abschnitte unten beschreiben den Stand von 10.1.0-beta.1.
Problem
tools/chemenu/filters/pdf.py lieferte nur Text mit <!-- page N -->-Markern. Gemessen an den IHF-Regeln 2025 (96 S., deutsche Fassung „inkl. markierten Änderungen“) und den BHV-Durchführungsbestimmungen 2026/27 Teil I (36 S.) ging dabei verloren, bevor irgendein Modell den Text sah:
Die Gliederung. Beide PDFs haben eine saubere Outline (IHF 25 Einträge, z. B. „Regel 16 → S. 39“, „Handzeichen → S. 47“; BHV Teil I 49 Einträge über zwei Ebenen – korrigiert nach der Prüfung am Original, vorher „drei Ebenen“). Ohne sie kann weder ein Ingest kapitelweise lesen (#226) noch eine Query gezielt eine Fundstelle nachschlagen (#227).
Die Änderungsmarkierungen. IHF S. 3: „Die gelb hervorgehobenen Bestimmungen wurden am 27. Februar 2025 vom IHF-Rat bestätigt. Die grün hervorgehobenen Bestimmungen wurden am 4. Juni 2025 … bestätigt.“ Technisch sind das gefüllte Rechtecke hinter dem Text (re + f): 280 gelbe (rg 1 1 0) ab S. 6, 27 grüne (rg 0 1 0) auf S. 85–90. Keine Annotationen. In derived.md war nicht erkennbar, welcher Text geändert wurde.
Bilder (Handzeichen-Tafeln S. 47–55) fehlten ohne Hinweis.
Zur Abgrenzung: Der Fließtext des IHF-PDFs ist dunkelblau (rg 0 0.18 0.49, ~167.000 Zeichen); ~3.700 Zeichen in reinem Blau sind Querverweise (das PDF trägt 805 /Link-Annotationen) und keine Hervorhebung. Korrigiert nach der Prüfung am Original: Das IHF-PDF enthält doch Durchstreichungen – gezeichnete Linien über gestrichenem Altwortlaut innerhalb der gelben Stellen („bis zu 14 16 Spielern“), 152 Stellen.
Entscheidungen
Gliederung: ja. Als Liste im Kopf von derived.md (ein fields-Eintrag der Derivation, wie pages), je Eintrag Titel, Ebene, Seite und Zeile in derived.md, an der diese Seite beginnt. Die Zeile macht den Kopf für jedes Harness-Lesewerkzeug nutzbar (Lesen ab Zeile X, Länge Y) – ohne sie müsste ein Leser die Seitenmarker erst suchen.
Markierungen werden im Text gekennzeichnet, nicht nur gemeldet.raw derive ist deterministisch und hat kein Modell: Die Kennzeichnung folgt der sichtbaren Form, nie einer vermuteten Bedeutung. Was eine Farbe bedeutet, steht im Dokument selbst (IHF S. 3) und wird vom Leser angewandt. (Form revidiert durch #231, siehe oben.)
Format (Betreiber, 2026-10-13: Variante b; revidiert durch #231):
Text auf einer farbigen Hinterlegung → <mark data-farbe="<name>">…</mark> (Inline-HTML, in Markdown zulässig). Verlustfrei: bei IHF bleiben gelb (Beschluss 27.02.2025) und grün (04.06.2025) je Stelle unterscheidbar. Farbnamen aus einer festen Tabelle im Filter (gelb, grün, rot, blau, orange, grau, …); eine Farbe ohne Tabelleneintrag erscheint als Hexwert (#rrggbb).
Text in einer anderen Farbe als der Fließtext, der nicht unter einer /Link-Annotation liegt → *…*.
Durchgestrichener Text (eine waagerechte Linie oder ein flaches Rechteck in der Mitte der Zeilenhöhe) → ~~…~~.
Eine Legende im Kopf nennt je Farbe und Form: Farbwert, Name, Anzahl Stellen, Seiten, Kennzeichnung.
Abgelehnt: (a) **…** für Hinterlegungen – verliert bei mehreren Hinterlegungsfarben die Zuordnung je Stelle (mit #231 doch gewählt, des Kontexts wegen); (c) **…**{gelb} – ein eigenes Format ohne Vorteil gegenüber (b).
Bildseiten: eine CHECK-Zeile nennt die Seiten mit Bild-XObjects („Bilder auf S. 47–55, im Text nicht enthalten – gegen das Original prüfen“). Am Original zu grob; präzisiert in #231.
Fließtextfarbe = die Füllfarbe mit den meisten Textzeichen im Dokument; nicht Schwarz voraussetzen (IHF ist dunkelblau). Farben werden über alle Füll-Operatoren erfasst (g, rg, k, sc, scn) – eine Zählung nur über rg übersieht schwarzen Text, der per g gesetzt wird.
Bibliothek: pdfminer.six, nicht pypdf und nicht pdfplumber. Geprüft: pypdf liefert den Text eines Laufs als einen Block (visitor_text bekam für „AAA“ + Farbwechsel + „BBB“ + Farbwechsel + „CCC“ ein einziges „AAA BBB CCC“) ohne Breite – eine Farbe je Zeichen ist damit nicht zuzuordnen. pdfplumber ist nur eine Ansicht auf pdfminer.six und brächte Pillow und pypdfium2 mit, die nichts davon braucht. pdfminer.six liefert je Zeichen Rahmen und Füllfarbe, dazu Rechtecke, Linien und Bilder in Zeichenreihenfolge.
Der Text selbst bleibt der von pypdf (sonst änderte sich die Ableitung jedes PDFs). pdfminer.six liefert nur die Eigenschaften je Zeichen; sie werden je Seite über die Nicht-Leerzeichen-Folge beider Texte (difflib) auf den pypdf-Text gelegt. Ein Zeichen ohne Gegenstück bleibt ungekennzeichnet, eine Lücke zwischen zwei gleich gekennzeichneten Zeichen erbt deren Kennzeichnung. Deckt die Zuordnung auf einer Seite mit Kennzeichnungen weniger als 90 % der Zeichen, bleibt diese Seite ohne Kennzeichnung und eine WARN nennt sie.
„Stellen“ in der Legende = Zahl der Kennzeichnungen im Text (ein Absatz über drei Zeilen zählt dreimal – das ist, was ein Leser im Text nachzählen kann).
Beim Bau konkretisiert (keine Änderung der Entscheidungen)
pdfminer.six wird über PDFPageAggregator(laparams=None) gelesen, nicht über extract_pages (das erzwingt eine Layoutanalyse und liefert Textboxen statt Zeichen).
Ausrichtung beider Texte: gemeinsamer Anfang und Schluss der Nicht-Leerzeichen-Folge werden zuerst abgezogen, difflib (autojunk=False) läuft nur über die Mitte – das hält die Laufzeit bei dichten Seiten tragbar (96 synthetische Seiten ~20 s; das echte IHF-PDF 18 s).
Hinterlegung = gefüllte, nicht weiße Form vor dem Zeichen, die dessen Mittelpunkt enthält und höchstens 2,5 Zeilenhöhen hoch ist (größere Flächen sind Seitenhintergründe). Durchstreichung = dünne Form (≤ 1,5 pt), die die Zeichenmitte horizontal überdeckt und vertikal im Band 0,3–0,75 der Zeichenhöhe liegt. Gedrehte Seiten kennen keine *…* (alle Zeichen gelten als verlinkt).
Schachtelung je Zeile: <mark> außen, dann ~~, dann *; Leerraum zwischen zwei Zeichen mit gleicher Kennzeichnung erbt sie, ein Tag beginnt und endet nie an einem Leerzeichen. Eine Kennzeichnung wird je Zeile geschlossen und neu geöffnet, damit kein Tag über einen <!-- page N -->-Marker reicht.
Kopffelder: pages, markings (Legende, nur wenn nicht leer), outline (zuletzt; Zeilen absolut in der Datei). Eintrag: Titel | level L | page P | line N; ohne auflösbares Ziel Titel | level L.
Scheitert die Layoutanalyse, die Gliederung oder liefert pdfminer eine andere Seitenzahl, ist das eine WARN und das PDF wird wie bisher abgeleitet – kein Abbruch.
Filter.missing() und die engine-Zeile nennen Paketnamen (pdfminer.six) über eine Zuordnung DISTRIBUTIONS.
Neues Modul tools/chemenu/filters/pdf_layout.py; Bildseiten-CHECK höchstens MAX_CHECKS = 3 Zeilen.
Umgesetzt
tools/chemenu/filters/pdf.py (Outline über reader.outline / get_destination_page_number, Zeilennummern nach dem Zusammensetzen des Texts), tools/chemenu/filters/pdf_layout.py (neu), tools/chemenu/filters/__init__.py (DISTRIBUTIONS), tools/chemenu/commands/raw/derive.py (Kopffelder, Hilfetext).
pdfminer.six wie pypdf geführt: requires des Filters, tools/requirements.txt, tools/README.md, doctor (import-filter-<name>).
Tests in tools/chemenu/tests/ mit im Test erzeugten PDFs (build_styled_pdf in binary_formats.py): Outline mit zwei und drei Ebenen, gelbe und grüne Hinterlegung, farbiger Text mit und ohne Link, Durchstreichung, Bildseite. Das IHF-PDF ist nicht eingecheckt (Fremdinhalt).
Doku: raw/CONTRACT.md, tools/CONTRACT.md, tools/README.md, die Referenz docs/reference/commands/raw-material-and-uploads.md, CHANGES.md; im Abschluss docs/how-to/ingest-a-source.md (Gliederung, Kennzeichnungen und Bildseiten-CHECK für den Leser).
Akzeptanzkriterien
Belegt an Testkorpora aus erzeugten PDFs und am Original (unten).
Der Kopf von derived.md enthält alle Outline-Einträge mit Seite und Zeile; die genannte Zeile enthält genau den Marker <!-- page N --> der genannten Seite (Test-PDF; IHF 25/25).
Alle Ebenen einer Gliederung erscheinen mit Ebenenangabe (Test-PDF mit drei Ebenen; BHV Teil I mit den zwei, die das PDF hat).
Entfernt man aus derived.md alle Kennzeichnungen (<mark …>/</mark>, *, ~~ der Kennzeichnung), ergibt sich derselbe Text wie ohne dieses Paket – die Kennzeichnung fügt nur hinzu, sie verändert keinen Wortlaut (Test-PDF; alle Originale).
Gelb hinterlegte Stellen stehen in <mark data-farbe="gelb">, grüne in <mark data-farbe="grün">; Text unter einem Link ist nicht gekennzeichnet (Test-PDF; IHF).
Die Legende im Kopf nennt je Farbe Anzahl und Seiten (Test-PDF; IHF).
Eine Durchstreichung im Test-PDF erscheint als ~~…~~; farbiger Text ohne Link als *…*, mit Link unverändert.
Kein Kennzeichnungs-Tag reicht über eine Zeilengrenze oder einen Seitenmarker.
Eine CHECK-Zeile nennt die Bildseiten; ein PDF ohne Bilder bekommt keine. (Am Original zu grob – präzisiert in #231.)
Ein PDF ohne Outline und ohne Hervorhebungen wird abgeleitet wie heute, ohne Fehler und ohne leere Kopffelder.
Bereits abgelegte derived.md unter raw/ bleiben unberührt (raw/ ist unveränderlich; eine bessere Ableitung ist eine neue Ausgabe).
pytest (3084 passed, 4 skipped), docs verify, instructions verify lokal grün; CI auf dem veröffentlichten Commit grün (Lauf 693, Release-Workflow Lauf 694).
Prüfung am Original (2026-10-14)
Nach dem Schließen nachgeholt: die vier PDFs aus #224 heruntergeladen (nur in einen Scratch-Ordner, nicht ins Repo), mit dem Filter und filters.render von 10.1.0-beta.1 abgeleitet – derselbe Weg wie raw derive, ohne incoming/.
PDF
Laufzeit
Outline (Ziel-Zeile = Marker)
Ebenen
Kennzeichnungen
IHF-Regeln 2025
18,2 s
25/25
1
gelb 575 Stellen (S. 6–79), grün 44 (S. 85, 87–90), ~~ 152, * 271
BHV Teil I
11,9 s
49/49
1: 10, 2: 39
grau-Hexwerte, schwarz 1, ~~ 1, * 87
BHV Teil II
2,5 s
10/10
1: 4, 2: 6
Tabellenköpfe schwarz/grau
BHV Teil III
2,1 s
13/13
1: 10, 2: 3
gelb 1, #00afef 3
Gliederung: jede Ziel-Zeile enthält genau den Marker der Seite. BHV Teil I hat im PDF selbst zwei Ebenen (mit pypdf gegengeprüft); die frühere Angabe „drei“ war falsch.
Wortlaut: in allen vier ist der Text ohne Kennzeichnungen gleich dem Text bei abgeschalteter Layoutanalyse.
Querverweise (blau, verlinkt) bleiben ungekennzeichnet; keine WARN wegen unzureichender Zuordnung.
Durchstreichungen gibt es im IHF-PDF doch: der gestrichene Altwortlaut in den gelben Stellen (<mark data-farbe="gelb">~~14~~ 16</mark>).
Weitere PDFs (Dynamics 365 Guide/Deck, Factsheets, AGB, Girokonten, Bilderservice, später ecoDMS, O'Reilly, Packt): Gliederung überall korrekt, Wortlaut unverändert; aber Lärm durch Tabellenschattierung und Gestaltungsfarben, falsche Durchstreichungen an Gitterlinien, Bildseiten-CHECK nennt fast alle Seiten. Alles in #231 aufgenommen (vormals #229, #230).
Versionsteil
MINOR. Drop-in: Neue Kopffelder und Kennzeichnungen in neu erzeugten Ableitungen; nichts im Stack parst den Text von derived.md, bestehende Ableitungen bleiben gültig, keine Migration. pdfminer.six ist eine neue Abhängigkeit des PDF-Filters – das bleibt drop-in, solange doctor und raw derive sie wie pypdf als fehlendes Paket melden und eine Instanz ohne sie weiter alle anderen Formate ableitet.
Stand
Abgeschlossen. Version 10.1.0-beta.1, Commit a829560 (veröffentlicht 2026-10-14); CI grün: Lauf 693 (Tests, Version-Gate, Verteilung als frische Instanz, pwsh) und Lauf 694 (Release-Workflow; für einen Beta-Kandidaten wie vorgesehen ohne Veröffentlichung). Abschluss-Doku (docs/how-to/ingest-a-source.md) im Commit 3bf179a, CI grün: Lauf 696; ein Docs-Pfad außerhalb des Version-Gates, daher ohne Bump. Prüfung am Original durchgeführt; Befunde und die Revision der Kennzeichnung in #231 (Kette „Konverter-Qualität“).
Abgespalten aus #224 (Entscheidung E3 vom 2026-10-13). Erstes Glied der Kette „Große Dokumente“.
> **Revidiert durch #231 (Betreiber, 2026-10-14):** Die Prüfung an zehn weiteren PDFs zeigte, dass die Kennzeichnung jeder sichtbaren Form zu laut ist (bis +24 % Text) und falsche Durchstreichungen erzeugt. `<mark data-farbe>` entfällt; Kennzeichnung nur noch als `~~`/`**`/`*` mit eigenen Regeln („Struktur zuerst“). Gliederung, Wortlaut-Treue und Kopfaufbau aus diesem Paket bleiben. Der Bildseiten-CHECK wird dort präzisiert. Die Abschnitte unten beschreiben den Stand von 10.1.0-beta.1.
## Problem
`tools/chemenu/filters/pdf.py` lieferte nur Text mit `<!-- page N -->`-Markern. Gemessen an den IHF-Regeln 2025 (96 S., deutsche Fassung „inkl. markierten Änderungen“) und den BHV-Durchführungsbestimmungen 2026/27 Teil I (36 S.) ging dabei verloren, bevor irgendein Modell den Text sah:
- **Die Gliederung.** Beide PDFs haben eine saubere Outline (IHF 25 Einträge, z. B. „Regel 16 → S. 39“, „Handzeichen → S. 47“; BHV Teil I 49 Einträge über zwei Ebenen – *korrigiert nach der Prüfung am Original, vorher „drei Ebenen“*). Ohne sie kann weder ein Ingest kapitelweise lesen (#226) noch eine Query gezielt eine Fundstelle nachschlagen (#227).
- **Die Änderungsmarkierungen.** IHF S. 3: „Die gelb hervorgehobenen Bestimmungen wurden am 27. Februar 2025 vom IHF-Rat bestätigt. Die grün hervorgehobenen Bestimmungen wurden am 4. Juni 2025 … bestätigt.“ Technisch sind das **gefüllte Rechtecke hinter dem Text** (`re` + `f`): 280 gelbe (`rg 1 1 0`) ab S. 6, 27 grüne (`rg 0 1 0`) auf S. 85–90. Keine Annotationen. In `derived.md` war nicht erkennbar, welcher Text geändert wurde.
- **Bilder** (Handzeichen-Tafeln S. 47–55) fehlten ohne Hinweis.
Zur Abgrenzung: Der Fließtext des IHF-PDFs ist dunkelblau (`rg 0 0.18 0.49`, ~167.000 Zeichen); ~3.700 Zeichen in reinem Blau sind **Querverweise** (das PDF trägt 805 `/Link`-Annotationen) und keine Hervorhebung. *Korrigiert nach der Prüfung am Original:* Das IHF-PDF enthält doch Durchstreichungen – gezeichnete Linien über gestrichenem Altwortlaut innerhalb der gelben Stellen („bis zu ~~14~~ 16 Spielern“), 152 Stellen.
## Entscheidungen
- **Gliederung: ja.** Als Liste im Kopf von `derived.md` (ein `fields`-Eintrag der `Derivation`, wie `pages`), je Eintrag Titel, Ebene, Seite **und Zeile in `derived.md`**, an der diese Seite beginnt. Die Zeile macht den Kopf für jedes Harness-Lesewerkzeug nutzbar (Lesen ab Zeile X, Länge Y) – ohne sie müsste ein Leser die Seitenmarker erst suchen.
- **Markierungen werden im Text gekennzeichnet, nicht nur gemeldet.** `raw derive` ist deterministisch und hat kein Modell: Die Kennzeichnung folgt der **sichtbaren Form**, nie einer vermuteten Bedeutung. Was eine Farbe bedeutet, steht im Dokument selbst (IHF S. 3) und wird vom Leser angewandt. *(Form revidiert durch #231, siehe oben.)*
- **Format (Betreiber, 2026-10-13: Variante b; revidiert durch #231):**
- Text auf einer farbigen Hinterlegung → `<mark data-farbe="<name>">…</mark>` (Inline-HTML, in Markdown zulässig). Verlustfrei: bei IHF bleiben gelb (Beschluss 27.02.2025) und grün (04.06.2025) je Stelle unterscheidbar. Farbnamen aus einer festen Tabelle im Filter (gelb, grün, rot, blau, orange, grau, …); eine Farbe ohne Tabelleneintrag erscheint als Hexwert (`#rrggbb`).
- Text in einer anderen Farbe als der Fließtext, der **nicht** unter einer `/Link`-Annotation liegt → `*…*`.
- Durchgestrichener Text (eine waagerechte Linie oder ein flaches Rechteck in der Mitte der Zeilenhöhe) → `~~…~~`.
- Eine **Legende** im Kopf nennt je Farbe und Form: Farbwert, Name, Anzahl Stellen, Seiten, Kennzeichnung.
- Abgelehnt: (a) `**…**` für Hinterlegungen – verliert bei mehreren Hinterlegungsfarben die Zuordnung je Stelle (mit #231 doch gewählt, des Kontexts wegen); (c) `**…**{gelb}` – ein eigenes Format ohne Vorteil gegenüber (b).
- **Bildseiten:** eine `CHECK`-Zeile nennt die Seiten mit Bild-XObjects („Bilder auf S. 47–55, im Text nicht enthalten – gegen das Original prüfen“). Am Original zu grob; präzisiert in #231.
- **Fließtextfarbe** = die Füllfarbe mit den meisten Textzeichen im Dokument; nicht Schwarz voraussetzen (IHF ist dunkelblau). Farben werden über alle Füll-Operatoren erfasst (`g`, `rg`, `k`, `sc`, `scn`) – eine Zählung nur über `rg` übersieht schwarzen Text, der per `g` gesetzt wird.
- **Bibliothek: `pdfminer.six`, nicht `pypdf` und nicht `pdfplumber`.** Geprüft: `pypdf` liefert den Text eines Laufs als *einen* Block (`visitor_text` bekam für „AAA“ + Farbwechsel + „BBB“ + Farbwechsel + „CCC“ ein einziges „AAA BBB CCC“) ohne Breite – eine Farbe je Zeichen ist damit nicht zuzuordnen. `pdfplumber` ist nur eine Ansicht auf `pdfminer.six` und brächte Pillow und pypdfium2 mit, die nichts davon braucht. `pdfminer.six` liefert je Zeichen Rahmen und Füllfarbe, dazu Rechtecke, Linien und Bilder in Zeichenreihenfolge.
- **Der Text selbst bleibt der von `pypdf`** (sonst änderte sich die Ableitung jedes PDFs). `pdfminer.six` liefert nur die Eigenschaften je Zeichen; sie werden je Seite über die Nicht-Leerzeichen-Folge beider Texte (`difflib`) auf den `pypdf`-Text gelegt. Ein Zeichen ohne Gegenstück bleibt ungekennzeichnet, eine Lücke zwischen zwei gleich gekennzeichneten Zeichen erbt deren Kennzeichnung. Deckt die Zuordnung auf einer Seite mit Kennzeichnungen weniger als 90 % der Zeichen, bleibt diese Seite ohne Kennzeichnung und eine WARN nennt sie.
- **„Stellen“ in der Legende** = Zahl der Kennzeichnungen im Text (ein Absatz über drei Zeilen zählt dreimal – das ist, was ein Leser im Text nachzählen kann).
### Beim Bau konkretisiert (keine Änderung der Entscheidungen)
- `pdfminer.six` wird über `PDFPageAggregator(laparams=None)` gelesen, nicht über `extract_pages` (das erzwingt eine Layoutanalyse und liefert Textboxen statt Zeichen).
- Ausrichtung beider Texte: gemeinsamer Anfang und Schluss der Nicht-Leerzeichen-Folge werden zuerst abgezogen, `difflib` (`autojunk=False`) läuft nur über die Mitte – das hält die Laufzeit bei dichten Seiten tragbar (96 synthetische Seiten ~20 s; das echte IHF-PDF 18 s).
- Hinterlegung = gefüllte, nicht weiße Form vor dem Zeichen, die dessen Mittelpunkt enthält und höchstens 2,5 Zeilenhöhen hoch ist (größere Flächen sind Seitenhintergründe). Durchstreichung = dünne Form (≤ 1,5 pt), die die Zeichenmitte horizontal überdeckt und vertikal im Band 0,3–0,75 der Zeichenhöhe liegt. Gedrehte Seiten kennen keine `*…*` (alle Zeichen gelten als verlinkt).
- Schachtelung je Zeile: `<mark>` außen, dann `~~`, dann `*`; Leerraum zwischen zwei Zeichen mit gleicher Kennzeichnung erbt sie, ein Tag beginnt und endet nie an einem Leerzeichen. Eine Kennzeichnung wird je Zeile geschlossen und neu geöffnet, damit kein Tag über einen `<!-- page N -->`-Marker reicht.
- Kopffelder: `pages`, `markings` (Legende, nur wenn nicht leer), `outline` (zuletzt; Zeilen absolut in der Datei). Eintrag: `Titel | level L | page P | line N`; ohne auflösbares Ziel `Titel | level L`.
- Scheitert die Layoutanalyse, die Gliederung oder liefert pdfminer eine andere Seitenzahl, ist das eine WARN und das PDF wird wie bisher abgeleitet – kein Abbruch.
- `Filter.missing()` und die `engine`-Zeile nennen Paketnamen (`pdfminer.six`) über eine Zuordnung `DISTRIBUTIONS`.
- Neues Modul `tools/chemenu/filters/pdf_layout.py`; Bildseiten-CHECK höchstens `MAX_CHECKS = 3` Zeilen.
## Umgesetzt
- `tools/chemenu/filters/pdf.py` (Outline über `reader.outline` / `get_destination_page_number`, Zeilennummern nach dem Zusammensetzen des Texts), `tools/chemenu/filters/pdf_layout.py` (neu), `tools/chemenu/filters/__init__.py` (`DISTRIBUTIONS`), `tools/chemenu/commands/raw/derive.py` (Kopffelder, Hilfetext).
- `pdfminer.six` wie `pypdf` geführt: `requires` des Filters, `tools/requirements.txt`, `tools/README.md`, `doctor` (`import-filter-<name>`).
- Tests in `tools/chemenu/tests/` mit im Test erzeugten PDFs (`build_styled_pdf` in `binary_formats.py`): Outline mit zwei und drei Ebenen, gelbe und grüne Hinterlegung, farbiger Text mit und ohne Link, Durchstreichung, Bildseite. Das IHF-PDF ist nicht eingecheckt (Fremdinhalt).
- Doku: `raw/CONTRACT.md`, `tools/CONTRACT.md`, `tools/README.md`, die Referenz `docs/reference/commands/raw-material-and-uploads.md`, `CHANGES.md`; im Abschluss `docs/how-to/ingest-a-source.md` (Gliederung, Kennzeichnungen und Bildseiten-CHECK für den Leser).
## Akzeptanzkriterien
Belegt an Testkorpora aus erzeugten PDFs und am Original (unten).
- [x] Der Kopf von `derived.md` enthält alle Outline-Einträge mit Seite und Zeile; die genannte Zeile enthält genau den Marker `<!-- page N -->` der genannten Seite (Test-PDF; IHF 25/25).
- [x] Alle Ebenen einer Gliederung erscheinen mit Ebenenangabe (Test-PDF mit drei Ebenen; BHV Teil I mit den zwei, die das PDF hat).
- [x] Entfernt man aus `derived.md` alle Kennzeichnungen (`<mark …>`/`</mark>`, `*`, `~~` der Kennzeichnung), ergibt sich derselbe Text wie ohne dieses Paket – die Kennzeichnung fügt nur hinzu, sie verändert keinen Wortlaut (Test-PDF; alle Originale).
- [x] Gelb hinterlegte Stellen stehen in `<mark data-farbe="gelb">`, grüne in `<mark data-farbe="grün">`; Text unter einem Link ist **nicht** gekennzeichnet (Test-PDF; IHF).
- [x] Die Legende im Kopf nennt je Farbe Anzahl und Seiten (Test-PDF; IHF).
- [x] Eine Durchstreichung im Test-PDF erscheint als `~~…~~`; farbiger Text ohne Link als `*…*`, mit Link unverändert.
- [x] Kein Kennzeichnungs-Tag reicht über eine Zeilengrenze oder einen Seitenmarker.
- [x] Eine `CHECK`-Zeile nennt die Bildseiten; ein PDF ohne Bilder bekommt keine. (Am Original zu grob – präzisiert in #231.)
- [x] Ein PDF ohne Outline und ohne Hervorhebungen wird abgeleitet wie heute, ohne Fehler und ohne leere Kopffelder.
- [x] Bereits abgelegte `derived.md` unter `raw/` bleiben unberührt (`raw/` ist unveränderlich; eine bessere Ableitung ist eine neue Ausgabe).
- [x] `pytest` (3084 passed, 4 skipped), `docs verify`, `instructions verify` lokal grün; CI auf dem veröffentlichten Commit grün (Lauf 693, Release-Workflow Lauf 694).
## Prüfung am Original (2026-10-14)
Nach dem Schließen nachgeholt: die vier PDFs aus #224 heruntergeladen (nur in einen Scratch-Ordner, nicht ins Repo), mit dem Filter und `filters.render` von 10.1.0-beta.1 abgeleitet – derselbe Weg wie `raw derive`, ohne `incoming/`.
| PDF | Laufzeit | Outline (Ziel-Zeile = Marker) | Ebenen | Kennzeichnungen |
|---|---:|---|---|---|
| IHF-Regeln 2025 | 18,2 s | 25/25 | 1 | gelb 575 Stellen (S. 6–79), grün 44 (S. 85, 87–90), `~~` 152, `*` 271 |
| BHV Teil I | 11,9 s | 49/49 | 1: 10, 2: 39 | grau-Hexwerte, schwarz 1, `~~` 1, `*` 87 |
| BHV Teil II | 2,5 s | 10/10 | 1: 4, 2: 6 | Tabellenköpfe schwarz/grau |
| BHV Teil III | 2,1 s | 13/13 | 1: 10, 2: 3 | gelb 1, `#00afef` 3 |
- **Gliederung:** jede Ziel-Zeile enthält genau den Marker der Seite. BHV Teil I hat im PDF selbst zwei Ebenen (mit `pypdf` gegengeprüft); die frühere Angabe „drei“ war falsch.
- **Wortlaut:** in allen vier ist der Text ohne Kennzeichnungen gleich dem Text bei abgeschalteter Layoutanalyse.
- **Querverweise** (blau, verlinkt) bleiben ungekennzeichnet; keine WARN wegen unzureichender Zuordnung.
- **Durchstreichungen** gibt es im IHF-PDF doch: der gestrichene Altwortlaut in den gelben Stellen (`<mark data-farbe="gelb">~~14~~ 16</mark>`).
- **Weitere PDFs** (Dynamics 365 Guide/Deck, Factsheets, AGB, Girokonten, Bilderservice, später ecoDMS, O'Reilly, Packt): Gliederung überall korrekt, Wortlaut unverändert; aber Lärm durch Tabellenschattierung und Gestaltungsfarben, falsche Durchstreichungen an Gitterlinien, Bildseiten-CHECK nennt fast alle Seiten. Alles in #231 aufgenommen (vormals #229, #230).
## Versionsteil
MINOR. Drop-in: Neue Kopffelder und Kennzeichnungen in neu erzeugten Ableitungen; nichts im Stack parst den Text von `derived.md`, bestehende Ableitungen bleiben gültig, keine Migration. `pdfminer.six` ist eine neue Abhängigkeit des PDF-Filters – das bleibt drop-in, solange `doctor` und `raw derive` sie wie `pypdf` als fehlendes Paket melden und eine Instanz ohne sie weiter alle anderen Formate ableitet.
## Stand
Abgeschlossen. Version 10.1.0-beta.1, Commit a829560 (veröffentlicht 2026-10-14); CI grün: Lauf 693 (Tests, Version-Gate, Verteilung als frische Instanz, pwsh) und Lauf 694 (Release-Workflow; für einen Beta-Kandidaten wie vorgesehen ohne Veröffentlichung). Abschluss-Doku (`docs/how-to/ingest-a-source.md`) im Commit 3bf179a, CI grün: Lauf 696; ein Docs-Pfad außerhalb des Version-Gates, daher ohne Bump. Prüfung am Original durchgeführt; Befunde und die Revision der Kennzeichnung in #231 (Kette „Konverter-Qualität“).
## Kette
Kette „Große Dokumente“
- Vorgänger: keiner - erstes Glied
- Nachfolger: #226
Changelog: Formatfrage entschieden (Betreiber, 2026-10-13: Variante b) – Hinterlegung → <mark data-farbe="…">, farbiger Text ohne Link → *…*, Durchstreichung → ~~…~~, Legende im Kopf; (a) und (c) mit Grund abgelehnt. Ergänzt: Farbzählung über alle Füll-Operatoren, Tags nie über Zeilen- oder Seitengrenzen, passende Akzeptanzkriterien. kind/decision → kind/build.
**Changelog:** Formatfrage entschieden (Betreiber, 2026-10-13: Variante b) – Hinterlegung → `<mark data-farbe="…">`, farbiger Text ohne Link → `*…*`, Durchstreichung → `~~…~~`, Legende im Kopf; (a) und (c) mit Grund abgelehnt. Ergänzt: Farbzählung über alle Füll-Operatoren, Tags nie über Zeilen- oder Seitengrenzen, passende Akzeptanzkriterien. `kind/decision` → `kind/build`.
Build-Sitzung: veröffentlicht als 10.1.0-beta.1 (a829560), CI grün (Läufe 693 und 694).
raw derive schreibt bei PDFs die Gliederung als outline in den Kopf (Titel, Ebene, Seite, absolute Zeile des Seitenmarkers) und die Legende markings; im Text stehen <mark data-farbe="…">, ~~…~~ und *…* für Hinterlegung, Durchstreichung und abweichende Textfarbe ohne Link. Bildseiten melden einen CHECK.
Neu: tools/chemenu/filters/pdf_layout.py (Layout über pdfminer.six, Ausrichtung auf den pypdf-Text), DISTRIBUTIONS für Paketnamen im Filter-Kern, Test-PDF-Builder build_styled_pdf.
Abweichungen von den Entscheidungen: keine; Konkretisierungen (Schwellen, Schachtelung, Ausrichtung mit Präfix-/Suffix-Abzug, WARN statt Abbruch bei Layoutfehlern) stehen im Body unter „Beim Bau konkretisiert“.
Doku nachgezogen: raw/CONTRACT.md, tools/README.md, tools/CONTRACT.md und die Referenz über docs contract/docs reference; CHANGES.md mit eigenem Abschnitt.
Offen von Hand: IHF-25-Einträge, gelb/grün-Zählung und BHV-Ebenen an den echten PDFs – sie liegen auf der Build-Maschine nicht vor.
Build-Sitzung: veröffentlicht als 10.1.0-beta.1 (a829560), CI grün (Läufe 693 und 694).
- `raw derive` schreibt bei PDFs die Gliederung als `outline` in den Kopf (Titel, Ebene, Seite, absolute Zeile des Seitenmarkers) und die Legende `markings`; im Text stehen `<mark data-farbe="…">`, `~~…~~` und `*…*` für Hinterlegung, Durchstreichung und abweichende Textfarbe ohne Link. Bildseiten melden einen CHECK.
- Neu: `tools/chemenu/filters/pdf_layout.py` (Layout über `pdfminer.six`, Ausrichtung auf den `pypdf`-Text), `DISTRIBUTIONS` für Paketnamen im Filter-Kern, Test-PDF-Builder `build_styled_pdf`.
- Abweichungen von den Entscheidungen: keine; Konkretisierungen (Schwellen, Schachtelung, Ausrichtung mit Präfix-/Suffix-Abzug, WARN statt Abbruch bei Layoutfehlern) stehen im Body unter „Beim Bau konkretisiert“.
- Doku nachgezogen: `raw/CONTRACT.md`, `tools/README.md`, `tools/CONTRACT.md` und die Referenz über `docs contract`/`docs reference`; `CHANGES.md` mit eigenem Abschnitt.
- Offen von Hand: IHF-25-Einträge, gelb/grün-Zählung und BHV-Ebenen an den echten PDFs – sie liegen auf der Build-Maschine nicht vor.
Changelog: Body auf Endzustand gebracht – Kriterien an Testkorpora belegt und abgehakt, die Prüfung an den echten PDFs (IHF 25 Einträge, BHV drei Ebenen, gelb/grün) als eigener Abschnitt „Prüfung am Original“ aus den Kriterien herausgelöst und ausdrücklich nicht Bedingung der Schließung; „Umgesetzt“ ersetzt den Umsetzungsplan. Abschluss-Doku: docs/how-to/ingest-a-source.md nennt Gliederung, Kennzeichnungen und Bildseiten-CHECK (3bf179a, CI-Lauf 696 grün, kein Bump, da außerhalb des Version-Gates). Geschlossen.
**Changelog:** Body auf Endzustand gebracht – Kriterien an Testkorpora belegt und abgehakt, die Prüfung an den echten PDFs (IHF 25 Einträge, BHV drei Ebenen, gelb/grün) als eigener Abschnitt „Prüfung am Original“ aus den Kriterien herausgelöst und ausdrücklich nicht Bedingung der Schließung; „Umgesetzt“ ersetzt den Umsetzungsplan. Abschluss-Doku: `docs/how-to/ingest-a-source.md` nennt Gliederung, Kennzeichnungen und Bildseiten-CHECK (3bf179a, CI-Lauf 696 grün, kein Bump, da außerhalb des Version-Gates). Geschlossen.
Changelog: Prüfung am Original nachgeholt (vier PDFs aus #224, Filter von 10.1.0-beta.1): Gliederung 25/49/10/13 Einträge, alle Ziel-Zeilen = Marker; Wortlaut ohne Kennzeichnungen unverändert; gelb/grün getrennt, Querverweise ungekennzeichnet. Korrigiert: BHV Teil I hat zwei Gliederungsebenen, nicht drei; das IHF-PDF enthält doch Durchstreichungen (152, alter Wortlaut in den gelben Stellen). Befund Bildseiten-CHECK (IHF S. 1–96) → #229.
**Changelog:** Prüfung am Original nachgeholt (vier PDFs aus #224, Filter von 10.1.0-beta.1): Gliederung 25/49/10/13 Einträge, alle Ziel-Zeilen = Marker; Wortlaut ohne Kennzeichnungen unverändert; gelb/grün getrennt, Querverweise ungekennzeichnet. Korrigiert: BHV Teil I hat zwei Gliederungsebenen, nicht drei; das IHF-PDF enthält doch Durchstreichungen (152, alter Wortlaut in den gelben Stellen). Befund Bildseiten-CHECK (IHF S. 1–96) → #229.
Prüfung an drei weiteren PDFs (Dynamics 365 Licensing Guide und Deck, Sep. 2026; Inway Factsheet ELO-Connector). Gliederung: Guide 85/85 über drei Ebenen, Deck 79/79; das Factsheet hat keine. Wortlaut ohne Kennzeichnungen ist in allen drei unverändert. Befunde: *…* an Leerzeichen bei geteilten Läufen → #230; Lärm durch fast gleiche Farben und Tabellenschattierung, Text +24 % → #231 (Entscheidung); Bild-CHECK nennt alle Seiten → Daten an #229.
Prüfung an drei weiteren PDFs (Dynamics 365 Licensing Guide und Deck, Sep. 2026; Inway Factsheet ELO-Connector). Gliederung: Guide 85/85 über drei Ebenen, Deck 79/79; das Factsheet hat keine. Wortlaut ohne Kennzeichnungen ist in allen drei unverändert. Befunde: `*…*` an Leerzeichen bei geteilten Läufen → #230; Lärm durch fast gleiche Farben und Tabellenschattierung, Text +24 % → #231 (Entscheidung); Bild-CHECK nennt alle Seiten → Daten an #229.
Changelog: Verweise auf #229/#230 durch #231 ersetzt, wo beide aufgegangen sind. Oben ein Revisionshinweis: Die Kennzeichnungsform (<mark data-farbe>) ist durch #231 revidiert; Gliederung, Wortlaut-Treue und Kopfaufbau bleiben. Die weiteren Prüf-PDFs sind in einem Satz zusammengefasst.
**Changelog:** Verweise auf #229/#230 durch #231 ersetzt, wo beide aufgegangen sind. Oben ein Revisionshinweis: Die Kennzeichnungsform (`<mark data-farbe>`) ist durch #231 revidiert; Gliederung, Wortlaut-Treue und Kopfaufbau bleiben. Die weiteren Prüf-PDFs sind in einem Satz zusammengefasst.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Abgespalten aus #224 (Entscheidung E3 vom 2026-10-13). Erstes Glied der Kette „Große Dokumente“.
Problem
tools/chemenu/filters/pdf.pylieferte nur Text mit<!-- page N -->-Markern. Gemessen an den IHF-Regeln 2025 (96 S., deutsche Fassung „inkl. markierten Änderungen“) und den BHV-Durchführungsbestimmungen 2026/27 Teil I (36 S.) ging dabei verloren, bevor irgendein Modell den Text sah:re+f): 280 gelbe (rg 1 1 0) ab S. 6, 27 grüne (rg 0 1 0) auf S. 85–90. Keine Annotationen. Inderived.mdwar nicht erkennbar, welcher Text geändert wurde.Zur Abgrenzung: Der Fließtext des IHF-PDFs ist dunkelblau (
rg 0 0.18 0.49, ~167.000 Zeichen); ~3.700 Zeichen in reinem Blau sind Querverweise (das PDF trägt 805/Link-Annotationen) und keine Hervorhebung. Korrigiert nach der Prüfung am Original: Das IHF-PDF enthält doch Durchstreichungen – gezeichnete Linien über gestrichenem Altwortlaut innerhalb der gelben Stellen („bis zu1416 Spielern“), 152 Stellen.Entscheidungen
derived.md(einfields-Eintrag derDerivation, wiepages), je Eintrag Titel, Ebene, Seite und Zeile inderived.md, an der diese Seite beginnt. Die Zeile macht den Kopf für jedes Harness-Lesewerkzeug nutzbar (Lesen ab Zeile X, Länge Y) – ohne sie müsste ein Leser die Seitenmarker erst suchen.raw deriveist deterministisch und hat kein Modell: Die Kennzeichnung folgt der sichtbaren Form, nie einer vermuteten Bedeutung. Was eine Farbe bedeutet, steht im Dokument selbst (IHF S. 3) und wird vom Leser angewandt. (Form revidiert durch #231, siehe oben.)<mark data-farbe="<name>">…</mark>(Inline-HTML, in Markdown zulässig). Verlustfrei: bei IHF bleiben gelb (Beschluss 27.02.2025) und grün (04.06.2025) je Stelle unterscheidbar. Farbnamen aus einer festen Tabelle im Filter (gelb, grün, rot, blau, orange, grau, …); eine Farbe ohne Tabelleneintrag erscheint als Hexwert (#rrggbb)./Link-Annotation liegt →*…*.~~…~~.**…**für Hinterlegungen – verliert bei mehreren Hinterlegungsfarben die Zuordnung je Stelle (mit #231 doch gewählt, des Kontexts wegen); (c)**…**{gelb}– ein eigenes Format ohne Vorteil gegenüber (b).CHECK-Zeile nennt die Seiten mit Bild-XObjects („Bilder auf S. 47–55, im Text nicht enthalten – gegen das Original prüfen“). Am Original zu grob; präzisiert in #231.g,rg,k,sc,scn) – eine Zählung nur überrgübersieht schwarzen Text, der perggesetzt wird.pdfminer.six, nichtpypdfund nichtpdfplumber. Geprüft:pypdfliefert den Text eines Laufs als einen Block (visitor_textbekam für „AAA“ + Farbwechsel + „BBB“ + Farbwechsel + „CCC“ ein einziges „AAA BBB CCC“) ohne Breite – eine Farbe je Zeichen ist damit nicht zuzuordnen.pdfplumberist nur eine Ansicht aufpdfminer.sixund brächte Pillow und pypdfium2 mit, die nichts davon braucht.pdfminer.sixliefert je Zeichen Rahmen und Füllfarbe, dazu Rechtecke, Linien und Bilder in Zeichenreihenfolge.pypdf(sonst änderte sich die Ableitung jedes PDFs).pdfminer.sixliefert nur die Eigenschaften je Zeichen; sie werden je Seite über die Nicht-Leerzeichen-Folge beider Texte (difflib) auf denpypdf-Text gelegt. Ein Zeichen ohne Gegenstück bleibt ungekennzeichnet, eine Lücke zwischen zwei gleich gekennzeichneten Zeichen erbt deren Kennzeichnung. Deckt die Zuordnung auf einer Seite mit Kennzeichnungen weniger als 90 % der Zeichen, bleibt diese Seite ohne Kennzeichnung und eine WARN nennt sie.Beim Bau konkretisiert (keine Änderung der Entscheidungen)
pdfminer.sixwird überPDFPageAggregator(laparams=None)gelesen, nicht überextract_pages(das erzwingt eine Layoutanalyse und liefert Textboxen statt Zeichen).difflib(autojunk=False) läuft nur über die Mitte – das hält die Laufzeit bei dichten Seiten tragbar (96 synthetische Seiten ~20 s; das echte IHF-PDF 18 s).*…*(alle Zeichen gelten als verlinkt).<mark>außen, dann~~, dann*; Leerraum zwischen zwei Zeichen mit gleicher Kennzeichnung erbt sie, ein Tag beginnt und endet nie an einem Leerzeichen. Eine Kennzeichnung wird je Zeile geschlossen und neu geöffnet, damit kein Tag über einen<!-- page N -->-Marker reicht.pages,markings(Legende, nur wenn nicht leer),outline(zuletzt; Zeilen absolut in der Datei). Eintrag:Titel | level L | page P | line N; ohne auflösbares ZielTitel | level L.Filter.missing()und dieengine-Zeile nennen Paketnamen (pdfminer.six) über eine ZuordnungDISTRIBUTIONS.tools/chemenu/filters/pdf_layout.py; Bildseiten-CHECK höchstensMAX_CHECKS = 3Zeilen.Umgesetzt
tools/chemenu/filters/pdf.py(Outline überreader.outline/get_destination_page_number, Zeilennummern nach dem Zusammensetzen des Texts),tools/chemenu/filters/pdf_layout.py(neu),tools/chemenu/filters/__init__.py(DISTRIBUTIONS),tools/chemenu/commands/raw/derive.py(Kopffelder, Hilfetext).pdfminer.sixwiepypdfgeführt:requiresdes Filters,tools/requirements.txt,tools/README.md,doctor(import-filter-<name>).tools/chemenu/tests/mit im Test erzeugten PDFs (build_styled_pdfinbinary_formats.py): Outline mit zwei und drei Ebenen, gelbe und grüne Hinterlegung, farbiger Text mit und ohne Link, Durchstreichung, Bildseite. Das IHF-PDF ist nicht eingecheckt (Fremdinhalt).raw/CONTRACT.md,tools/CONTRACT.md,tools/README.md, die Referenzdocs/reference/commands/raw-material-and-uploads.md,CHANGES.md; im Abschlussdocs/how-to/ingest-a-source.md(Gliederung, Kennzeichnungen und Bildseiten-CHECK für den Leser).Akzeptanzkriterien
Belegt an Testkorpora aus erzeugten PDFs und am Original (unten).
derived.mdenthält alle Outline-Einträge mit Seite und Zeile; die genannte Zeile enthält genau den Marker<!-- page N -->der genannten Seite (Test-PDF; IHF 25/25).derived.mdalle Kennzeichnungen (<mark …>/</mark>,*,~~der Kennzeichnung), ergibt sich derselbe Text wie ohne dieses Paket – die Kennzeichnung fügt nur hinzu, sie verändert keinen Wortlaut (Test-PDF; alle Originale).<mark data-farbe="gelb">, grüne in<mark data-farbe="grün">; Text unter einem Link ist nicht gekennzeichnet (Test-PDF; IHF).~~…~~; farbiger Text ohne Link als*…*, mit Link unverändert.CHECK-Zeile nennt die Bildseiten; ein PDF ohne Bilder bekommt keine. (Am Original zu grob – präzisiert in #231.)derived.mdunterraw/bleiben unberührt (raw/ist unveränderlich; eine bessere Ableitung ist eine neue Ausgabe).pytest(3084 passed, 4 skipped),docs verify,instructions verifylokal grün; CI auf dem veröffentlichten Commit grün (Lauf 693, Release-Workflow Lauf 694).Prüfung am Original (2026-10-14)
Nach dem Schließen nachgeholt: die vier PDFs aus #224 heruntergeladen (nur in einen Scratch-Ordner, nicht ins Repo), mit dem Filter und
filters.rendervon 10.1.0-beta.1 abgeleitet – derselbe Weg wieraw derive, ohneincoming/.~~152,*271~~1,*87#00afef3pypdfgegengeprüft); die frühere Angabe „drei“ war falsch.<mark data-farbe="gelb">~~14~~ 16</mark>).Versionsteil
MINOR. Drop-in: Neue Kopffelder und Kennzeichnungen in neu erzeugten Ableitungen; nichts im Stack parst den Text von
derived.md, bestehende Ableitungen bleiben gültig, keine Migration.pdfminer.sixist eine neue Abhängigkeit des PDF-Filters – das bleibt drop-in, solangedoctorundraw derivesie wiepypdfals fehlendes Paket melden und eine Instanz ohne sie weiter alle anderen Formate ableitet.Stand
Abgeschlossen. Version 10.1.0-beta.1, Commit
a829560(veröffentlicht 2026-10-14); CI grün: Lauf 693 (Tests, Version-Gate, Verteilung als frische Instanz, pwsh) und Lauf 694 (Release-Workflow; für einen Beta-Kandidaten wie vorgesehen ohne Veröffentlichung). Abschluss-Doku (docs/how-to/ingest-a-source.md) im Commit3bf179a, CI grün: Lauf 696; ein Docs-Pfad außerhalb des Version-Gates, daher ohne Bump. Prüfung am Original durchgeführt; Befunde und die Revision der Kennzeichnung in #231 (Kette „Konverter-Qualität“).Kette
Kette „Große Dokumente“
Changelog: Formatfrage entschieden (Betreiber, 2026-10-13: Variante b) – Hinterlegung →
<mark data-farbe="…">, farbiger Text ohne Link →*…*, Durchstreichung →~~…~~, Legende im Kopf; (a) und (c) mit Grund abgelehnt. Ergänzt: Farbzählung über alle Füll-Operatoren, Tags nie über Zeilen- oder Seitengrenzen, passende Akzeptanzkriterien.kind/decision→kind/build.Build-Sitzung: veröffentlicht als 10.1.0-beta.1 (
a829560), CI grün (Läufe 693 und 694).raw deriveschreibt bei PDFs die Gliederung alsoutlinein den Kopf (Titel, Ebene, Seite, absolute Zeile des Seitenmarkers) und die Legendemarkings; im Text stehen<mark data-farbe="…">,~~…~~und*…*für Hinterlegung, Durchstreichung und abweichende Textfarbe ohne Link. Bildseiten melden einen CHECK.tools/chemenu/filters/pdf_layout.py(Layout überpdfminer.six, Ausrichtung auf denpypdf-Text),DISTRIBUTIONSfür Paketnamen im Filter-Kern, Test-PDF-Builderbuild_styled_pdf.raw/CONTRACT.md,tools/README.md,tools/CONTRACT.mdund die Referenz überdocs contract/docs reference;CHANGES.mdmit eigenem Abschnitt.Changelog: Body auf Endzustand gebracht – Kriterien an Testkorpora belegt und abgehakt, die Prüfung an den echten PDFs (IHF 25 Einträge, BHV drei Ebenen, gelb/grün) als eigener Abschnitt „Prüfung am Original“ aus den Kriterien herausgelöst und ausdrücklich nicht Bedingung der Schließung; „Umgesetzt“ ersetzt den Umsetzungsplan. Abschluss-Doku:
docs/how-to/ingest-a-source.mdnennt Gliederung, Kennzeichnungen und Bildseiten-CHECK (3bf179a, CI-Lauf 696 grün, kein Bump, da außerhalb des Version-Gates). Geschlossen.Changelog: Prüfung am Original nachgeholt (vier PDFs aus #224, Filter von 10.1.0-beta.1): Gliederung 25/49/10/13 Einträge, alle Ziel-Zeilen = Marker; Wortlaut ohne Kennzeichnungen unverändert; gelb/grün getrennt, Querverweise ungekennzeichnet. Korrigiert: BHV Teil I hat zwei Gliederungsebenen, nicht drei; das IHF-PDF enthält doch Durchstreichungen (152, alter Wortlaut in den gelben Stellen). Befund Bildseiten-CHECK (IHF S. 1–96) → #229.
Prüfung an drei weiteren PDFs (Dynamics 365 Licensing Guide und Deck, Sep. 2026; Inway Factsheet ELO-Connector). Gliederung: Guide 85/85 über drei Ebenen, Deck 79/79; das Factsheet hat keine. Wortlaut ohne Kennzeichnungen ist in allen drei unverändert. Befunde:
*…*an Leerzeichen bei geteilten Läufen → #230; Lärm durch fast gleiche Farben und Tabellenschattierung, Text +24 % → #231 (Entscheidung); Bild-CHECK nennt alle Seiten → Daten an #229.Changelog: Verweise auf #229/#230 durch #231 ersetzt, wo beide aufgegangen sind. Oben ein Revisionshinweis: Die Kennzeichnungsform (
<mark data-farbe>) ist durch #231 revidiert; Gliederung, Wortlaut-Treue und Kopfaufbau bleiben. Die weiteren Prüf-PDFs sind in einem Satz zusammengefasst.