S07: Iteration B3 und B3.5 fuer Teilgebiet 01 abgeschlossen. B3 in build/build-reference-docx.py ergaenzt: DocDefault widowControl plus keepNext und keepLines auf Heading 1/2/3 und FirstParagraph (Pandoc-Stil fuer ersten Absatz nach einem Heading, deckt die fett formatierten Kenntnisse-Subsection-Labels KI Software-Design Methodik IT etc ab). Erster Versuch Compact-Stil mit keepNext hat Listen komplett unteilbar gemacht (Job-Stationen begannen jedes Mal auf einer neuen Seite, ungenutzte Seitenenden) und wurde verworfen. Auf Wunsch von Thomas auf 3-3-Regel umgestellt: bei Listen mit mindestens 6 Bullets duerfen Trennungen passieren, aber mindestens 3 Bullets bleiben jeweils zusammen vor und nach dem Umbruch. Bei kuerzeren Listen alles zusammen. Da das stilbasiert nicht abbildbar ist (alle Bullets haben pStyle Compact), neues Post-Processing-Skript build/post-process-docx.py: scannt das fertige DOCX, findet Sequenzen aufeinanderfolgender Bullets mit numPr-Eigenschaft ausserhalb von Tabellen-Zellen, setzt keepNext auf den ersten 2 und den N-3 N-2 Bullets jeder Liste mit n groesser gleich 6 (bei n kleiner 6 alle keepNext). build.ps1 erweitert auf 3 Schritte und ruft das Post-Processing-Skript automatisch nach erfolgreichem DOCX-Build auf, mit Console-Output und Log-Statistiken (Anzahl Listen Bullets keepNext-Markierungen). Sandbox-Verifikation 26 Listen 184 Bullets 93 keepNext, Pattern fuer 11-Bullet-Liste KK......KK.. Auf Thomas System visuell bestaetigt: Listen werden an guten Stellen getrennt, keine ungenutzten Seitenenden, keine einzelnen Bullets allein am Seitenrand. teilgebiete/01-lebenslauf.md um B3- und B3.5-Bloecke ergaenzt sowie Naechste-Schritte-Liste auf B4 C D umstrukturiert. agent-prompt.md Aktueller-Stand-Abschnitt fortgeschrieben mit B3 und B3.5, Hinweis auf 3-stufige DOCX-Pipeline und Edit-Tool-Truncation an build.ps1 ergaenzt. Naechste Session startet mit B4 (Heading-Farben oder Trennlinien analog PDF).
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
S07
|
||||
Teilgebiet 01 Iteration B (Iterationen B1, B1.5, B2) durchgezogen. Neue Datei build/build-reference-docx.py baut templates/reference.docx programmatisch aus Pandocs Default-Reference (Python-Stdlib only, kein pip; pandoc --print-default-data-file zur Laufzeit, ZIP entpacken, ElementTree-XML-Anpassungen, repacken). B1: Theme major+minor und alle direkten Schrift-Refs in styles.xml auf Calibri umgestellt (Code-Schriften wie Consolas bleiben), Tabellen-Default-Stil mit tblBorders=none auf allen Sides. B1.5: Body-DocDefault 11 pt, Heading 1/2/3 auf 15/13/12 pt analog PDF. B2: header1.xml (Default ab Seite 2 mit Name links und Lebenslauf rechts), header2.xml (leer fuer Seite 1 via titlePg), footer1.xml (rechts Seite n / m mit PAGE/NUMPAGES-Feldern, doppelt referenziert als default und first damit Seite 1 trotz titlePg den Footer hat). Page-Setup explizit in sectPr: A4 mit 2.2 cm oben/unten und 2.5 cm links/rechts analog PDF, Tab-Stop am rechten Textrand 9072 dxa. Beziehungen mit dynamisch naechster freier rId in document.xml.rels, Content-Types-Overrides in [Content_Types].xml, sectPr regex-ersetzt idempotent. Sandbox-End-to-End mit Pandoc 2.9 verifiziert (sectPr und Header/Footer im generierten DOCX vorhanden). Auf Thomas System: DOCX visuell bestaetigt. teilgebiete/01-lebenslauf.md um vollstaendigen Iteration-B-Block ergaenzt, Naechste-Schritte-Liste auf B3, B4, C, D umstrukturiert. agent-prompt.md Aktueller-Stand-Abschnitt fortgeschrieben mit Hinweisen zur reference-docx-Pipeline (manuell vor build.ps1 aufrufen, nicht von Hand in Word editieren) und zur Edit-Tool-Truncation auf dem NTFS-Mount. Build-UX-Fix in build.ps1 mit 3-Sekunden-Pause pro fehlgeschlagenem Schritt war ebenfalls Teil dieser Session.
|
||||
Iteration B3 und B3.5 fuer Teilgebiet 01 abgeschlossen. B3 in build/build-reference-docx.py ergaenzt: DocDefault widowControl plus keepNext und keepLines auf Heading 1/2/3 und FirstParagraph (Pandoc-Stil fuer ersten Absatz nach einem Heading, deckt die fett formatierten Kenntnisse-Subsection-Labels KI Software-Design Methodik IT etc ab). Erster Versuch Compact-Stil mit keepNext hat Listen komplett unteilbar gemacht (Job-Stationen begannen jedes Mal auf einer neuen Seite, ungenutzte Seitenenden) und wurde verworfen. Auf Wunsch von Thomas auf 3-3-Regel umgestellt: bei Listen mit mindestens 6 Bullets duerfen Trennungen passieren, aber mindestens 3 Bullets bleiben jeweils zusammen vor und nach dem Umbruch. Bei kuerzeren Listen alles zusammen. Da das stilbasiert nicht abbildbar ist (alle Bullets haben pStyle Compact), neues Post-Processing-Skript build/post-process-docx.py: scannt das fertige DOCX, findet Sequenzen aufeinanderfolgender Bullets mit numPr-Eigenschaft ausserhalb von Tabellen-Zellen, setzt keepNext auf den ersten 2 und den N-3 N-2 Bullets jeder Liste mit n groesser gleich 6 (bei n kleiner 6 alle keepNext). build.ps1 erweitert auf 3 Schritte und ruft das Post-Processing-Skript automatisch nach erfolgreichem DOCX-Build auf, mit Console-Output und Log-Statistiken (Anzahl Listen Bullets keepNext-Markierungen). Sandbox-Verifikation 26 Listen 184 Bullets 93 keepNext, Pattern fuer 11-Bullet-Liste KK......KK.. Auf Thomas System visuell bestaetigt: Listen werden an guten Stellen getrennt, keine ungenutzten Seitenenden, keine einzelnen Bullets allein am Seitenrand. teilgebiete/01-lebenslauf.md um B3- und B3.5-Bloecke ergaenzt sowie Naechste-Schritte-Liste auf B4 C D umstrukturiert. agent-prompt.md Aktueller-Stand-Abschnitt fortgeschrieben mit B3 und B3.5, Hinweis auf 3-stufige DOCX-Pipeline und Edit-Tool-Truncation an build.ps1 ergaenzt. Naechste Session startet mit B4 (Heading-Farben oder Trennlinien analog PDF).
|
||||
|
||||
@@ -88,19 +88,19 @@ Setze zwischen sinnvollen Zwischenständen Checkpoints (z.B. nach "Marketing.md
|
||||
**Letzte Session:** S07 (2026-04-26)
|
||||
|
||||
**Was wurde gemacht:**
|
||||
- **PDF-Build-Fehler endgültig behoben.** Pandoc-3.x-`\def\LTcaptype{none}`-Bug ([Issue #11201](https://github.com/jgm/pandoc/issues/11201)). Fix: `\newcounter{none}` im Template, sandbox-reproduziert. PDF läuft auf Thomas' System, Ausbildungs-Layout visuell bestätigt. **Iteration A damit inhaltlich abgeschlossen.**
|
||||
- **Build-UX-Fix:** `build/build.ps1` mit `Start-Sleep -Seconds 3` pro fehlschlagendem Schritt, damit das PowerShell-Fenster bei Fehler nicht zu schnell schließt.
|
||||
- **Iteration B durchgezogen — `reference.docx` programmatisch via `build/build-reference-docx.py` (Python-Stdlib only, kein pip).** Holt Pandoc-Default-Reference per `pandoc --print-default-data-file`, entpackt die DOCX als ZIP, modifiziert XML mit ElementTree, repackt.
|
||||
- **B1 — Schriften:** Theme `majorFont` und `minorFont` beide auf Calibri (Pandoc 3.x setzt Defaults auf Aptos Display / Aptos). Defensive Maßnahme: alle direkten `<w:rFonts>`-Referenzen außerhalb von Code-Schriften (Consolas, Courier, ...) auf Calibri.
|
||||
- **B1 — Tabellen:** Stil `Table` mit `<w:tblBorders val="none">` auf allen Sides. Word-Editor zeigt weiterhin Tabellen-Anzeige-Hilfslinien (kein Druck-Rendering); Druckansicht und PDF-Export sind sauber rahmenlos.
|
||||
- **B1.5 — Schriftgrößen analog PDF:** DocDefault Body 11 pt, Heading 1/2/3 auf 15/13/12 pt. DOCX schrumpft von 10 auf 9 Seiten.
|
||||
- **B2 — Header, Footer, Page-Setup:** `header1.xml` (Default ab Seite 2: Name links, „Lebenslauf" rechts), `header2.xml` (leer für Seite 1 via `titlePg`), `footer1.xml` (rechts „Seite n / m" mit `PAGE`/`NUMPAGES`-Feldern, einmal als `default`, einmal als `first` referenziert, damit Seite 1 trotz titlePg den Footer hat). Page-Setup explizit: A4 mit 2.2 cm oben/unten, 2.5 cm links/rechts (analog PDF). Tab-Stop am rechten Textrand 9072 dxa = 16 cm. Beziehungen werden mit dynamisch ermittelter nächster freier `rId` registriert; Content-Types-Overrides ergänzt; sectPr regex-basiert ersetzt (idempotent gegen `<w:sectPr/>` und längere Varianten). Pandoc 2.9 und 3.x übernehmen die sectPr ins generierte DOCX (in der Sandbox end-to-end verifiziert). DOCX-Layout von Thomas visuell bestätigt: Seite 1 ohne Header und mit Footer, Seite 2 ff. Header und Footer wie gewünscht, Tab-Stops bündig am rechten Textrand.
|
||||
- **PDF-Build-Fehler endgültig behoben.** Pandoc-3.x-`\def\LTcaptype{none}`-Bug ([Issue #11201](https://github.com/jgm/pandoc/issues/11201)). Fix: `\newcounter{none}` im Template. **Iteration A inhaltlich abgeschlossen.**
|
||||
- **Build-UX-Fix:** `build/build.ps1` mit `Start-Sleep -Seconds 3` pro fehlschlagendem Schritt.
|
||||
- **Iteration B durchgezogen — `reference.docx` programmatisch via `build/build-reference-docx.py`** (Python-Stdlib, holt Pandoc-Default-Reference, entpackt ZIP, modifiziert XML mit ElementTree, repackt). Inhalt:
|
||||
- **B1 — Schriften, Tabellen:** Theme major+minor auf Calibri (Pandoc-3.x-Default war Aptos Display/Aptos), Stil `Table` mit `tblBorders=none` auf allen Sides.
|
||||
- **B1.5 — Schriftgrößen analog PDF:** DocDefault Body 11 pt, Heading 1/2/3 auf 15/13/12 pt.
|
||||
- **B2 — Header, Footer, Page-Setup:** Header (Name links, Lebenslauf rechts) ab Seite 2, leerer Header für Seite 1 via `titlePg`. Footer (Seite n / m) auf allen Seiten inkl. Seite 1 (zwei `footerReference`-Einträge, default + first auf gleicher rId). Page-Setup A4 mit 2.2/2.5 cm Rändern, Tab-Stop 9072 dxa.
|
||||
- **B3 — Schusterjungen/Witwen-Schutz für Headings:** DocDefault `widowControl`, Heading 1/2/3 und `FirstParagraph` (für Kenntnisse-Subsection-Labels) mit `keepNext` + `keepLines`.
|
||||
- **Iteration B3.5 — 3-3-Regel für Listen-Bullets** über neues Post-Processing-Skript `build/post-process-docx.py`. Erster Versuch (Compact-Stil mit keepNext) hat Listen komplett unteilbar gemacht — Folge: Job-Stationen begannen jedes Mal auf neuer Seite, ungenutzte Seitenenden. Auf Wunsch von Thomas Per-Bullet-Logik: bei Listen mit ≥ 6 Bullets bekommen die ersten 2 und die N-3-/N-2-Bullets `keepNext`, dazwischen darf getrennt werden (= mind. 3 Bullets vor und nach jedem Umbruch). Bei < 6 Bullets bleibt alles zusammen. `build.ps1` ruft das Skript automatisch nach DOCX-Build als Schritt [3/3] auf. Sandbox-Verifikation: 26 Listen, 184 Bullets, 93 keepNext-Markierungen, Pattern z.B. `KK......KK.` für 11-Bullet-Liste. Auf Thomas' System visuell bestätigt: Listen werden sauber an guten Stellen getrennt, keine ungenutzten Seitenenden mehr.
|
||||
|
||||
**Nächste Aufgabe:** Teilgebiet 01 — verbleibende Iterationen:
|
||||
1. **B3 — Keep with next + Widow/Orphan-Control für DOCX.** Schusterjungen-Schutz analog `\widowpenalty`/`needspace` im PDF. Auf Stilebene `<w:keepNext/>` für Headings und `<w:widowControl w:val="true"/>` als DocDefault.
|
||||
2. **B4 (optional) — Heading-Farben auf DesTEngS-Blau und/oder Trennlinien analog PDF.** Eher Kosmetik bei Vorlage für Consulting-Agenturen.
|
||||
3. **C — Foto-Einbindung** in cv.md mit Pandoc-Image-Syntax und Template-Anpassung für Position/Größe (z.B. oben rechts neben Name, ca. 3 cm).
|
||||
4. **D — Hyphenation-Feintuning für PDF** — kurze Wortteile am Zeilenanfang mit höherer Penalty oder `\hyphenation`-Ausnahmen reduzieren.
|
||||
1. **B4 — Heading-Farben auf DesTEngS-Blau und/oder Trennlinien analog PDF.** Bringt das DOCX optisch näher ans PDF (für Direktverwendung; bei Consulting-Agenturen, die das Layout ohnehin überschreiben, eher Kosmetik). Erstes Aufgabe der nächsten Session.
|
||||
2. **C — Foto-Einbindung** in cv.md mit Pandoc-Image-Syntax und Template-Anpassung für Position/Größe (z.B. oben rechts neben Name, ca. 3 cm).
|
||||
3. **D — Hyphenation-Feintuning für PDF** — kurze Wortteile am Zeilenanfang mit höherer Penalty oder `\hyphenation`-Ausnahmen reduzieren.
|
||||
|
||||
Nach D): Status von Teilgebiet 01 in `zentral-index.md` auf „abgeschlossen" setzen (R2-OK von Thomas). Anschließend nächstes Teilgebiet nach Priorität (laut Index Teilgebiet 02 „Zeugnis von ASMPT").
|
||||
|
||||
@@ -114,4 +114,5 @@ Nach D): Status von Teilgebiet 01 in `zentral-index.md` auf „abgeschlossen" se
|
||||
- **`checkpoint.ps1` ist robust** gegen Anführungszeichen, Pipes, Whitespace-Anomalien und Index-Lock-Reste. `.checkpoint-pending.txt` darf ganz normal Sonderzeichen enthalten.
|
||||
- **`build.ps1` pausiert bei Fehler 3 Sekunden pro fehlgeschlagenem Schritt.** Nicht überrascht sein, wenn ein fehlerhafter Lauf entsprechend länger braucht.
|
||||
- **`build/build-reference-docx.py` muss VOR `build.ps1` manuell aufgerufen werden, wenn `templates/reference.docx` neu gebaut werden soll.** Das Skript ist nicht in `build.ps1` integriert (würde jeden Build verlangsamen und Pandoc-Default-Reference jedes Mal neu ziehen). Wenn jemand die `reference.docx` von Hand in Word editiert, gehen die Änderungen beim nächsten Skript-Lauf verloren — Stile gehören also ins Skript, nicht in Word.
|
||||
- **Edit-Tool kann Dateien beim Schreiben über den NTFS-Mount truncatieren** (mehrfach in S07 erlebt am Python-Skript). `mcp__workspace__bash` mit `cat <<'EOF' > path` ist die zuverlässige Alternative für längere Dateien (>~150 Zeilen). Nach jedem Edit auf NTFS-Mount-Datei: `wc -l` und `tail -c` zur Verifikation.
|
||||
- **Edit-Tool kann Dateien beim Schreiben über den NTFS-Mount truncatieren** (mehrfach in S07 erlebt am Python-Skript und an `build.ps1`). `mcp__workspace__bash` mit `cat <<'EOF' > path` ist die zuverlässige Alternative für längere Dateien (>~150 Zeilen). Nach jedem Edit auf NTFS-Mount-Datei: `wc -l` und `tail -c 60` zur Verifikation.
|
||||
- **DOCX-Pipeline ist jetzt 3-stufig:** (1) `build/build-reference-docx.py` baut die `reference.docx` (manuell aufrufen, wenn Stile geändert werden sollen), (2) `build/build.ps1` baut PDF und DOCX, (3) `build/post-process-docx.py` wird automatisch aus `build.ps1` aufgerufen für die 3-3-Listen-Bullet-Regel. Wer das Bullet-Verhalten ändern will, fasst das Post-Processing-Skript an, nicht die reference.docx.
|
||||
|
||||
@@ -6,7 +6,7 @@ build-reference-docx.py
|
||||
Baut die templates/reference.docx fuer die Pandoc-DOCX-Pipeline aus der
|
||||
Pandoc-Default-Reference, mit gezielten Anpassungen.
|
||||
|
||||
Iteration B1 + B1.5 + B2 (aktuell):
|
||||
Iteration B1 + B1.5 + B2 + B3 (aktuell):
|
||||
B1 - Theme-Schriften (majorFont und minorFont) beide auf Calibri.
|
||||
B1 - Direkte Schriftnamen-Referenzen in styles.xml auf Calibri
|
||||
(Code-Schriften wie Consolas bleiben).
|
||||
@@ -14,24 +14,20 @@ Iteration B1 + B1.5 + B2 (aktuell):
|
||||
B1.5 - Body-DocDefault 11pt, Heading 1/2/3 auf 15/13/12 pt.
|
||||
B2 - Header (Name links, "Lebenslauf" rechts) ab Seite 2; Seite 1 mit
|
||||
leerem Header (titlePg-Mechanik). Footer (rechts: Seite n / m) auf
|
||||
allen Seiten inkl. Seite 1 (footer-Ref fuer "first" zeigt auf den
|
||||
gleichen Footer wie "default"). Page-Setup explizit: A4, Raender
|
||||
analog PDF (top/bottom 2.2 cm, left/right 2.5 cm). Damit ist der
|
||||
Tab-Stop deterministisch unabhaengig von Word-Locale-Defaults.
|
||||
allen Seiten inkl. Seite 1. Page-Setup explizit: A4, Raender
|
||||
analog PDF (top/bottom 2.2 cm, left/right 2.5 cm).
|
||||
B3 - DocDefault widowControl. Heading 1/2/3 mit keepNext + keepLines.
|
||||
Zusaetzlich 'FirstParagraph' (Pandoc-Stil fuer den ersten Absatz
|
||||
nach einem Heading) — deckt die fett formatierten Kenntnisse-
|
||||
Subsection-Labels ab. Hinweis: Listen-Bullet-Schutz (3-3-Regel)
|
||||
passiert nicht hier, sondern im Post-Processing
|
||||
(build/post-process-docx.py), das auf das fertige DOCX angewendet
|
||||
wird — ein Stil kann keine Per-Bullet-Logik abbilden.
|
||||
|
||||
Geplant in Folge-Iterationen:
|
||||
B3 - Heading-Stile mit "keep with next", Widow/Orphan-Control auf Stilebene
|
||||
B4 - optional Heading-Farben auf DesTEngS-Blau analog PDF
|
||||
|
||||
Vorgehen:
|
||||
1. Pandoc-Default-Reference per `pandoc --print-default-data-file
|
||||
reference.docx` extrahieren.
|
||||
2. Als ZIP entpacken.
|
||||
3. Relevante XML-Dateien anpassen, neue Header/Footer-XMLs anlegen,
|
||||
Beziehungen und ContentTypes ergaenzen, sectPr setzen.
|
||||
4. Als neue ZIP-Datei (templates/reference.docx) speichern.
|
||||
|
||||
Voraussetzungen: nur Python-Stdlib + Pandoc im PATH.
|
||||
C - Foto-Einbindung
|
||||
D - Hyphenation-Feintuning fuer PDF
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -44,15 +40,11 @@ import zipfile
|
||||
from pathlib import Path
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
# --- Pfade -----------------------------------------------------------------
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
BASE_DIR = SCRIPT_DIR.parent
|
||||
TEMPLATES_DIR = BASE_DIR / "templates"
|
||||
OUTPUT_FILE = TEMPLATES_DIR / "reference.docx"
|
||||
|
||||
# --- XML-Namespaces --------------------------------------------------------
|
||||
|
||||
NS = {
|
||||
"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||
"a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||
@@ -67,8 +59,6 @@ ET.register_namespace("", NS["rel"])
|
||||
W = "{%s}" % NS["w"]
|
||||
A = "{%s}" % NS["a"]
|
||||
|
||||
# --- Konfiguration ---------------------------------------------------------
|
||||
|
||||
CODE_FONTS = {"consolas", "courier", "courier new", "liberation mono",
|
||||
"monaco", "menlo", "fira mono", "fira code"}
|
||||
TARGET_FONT = "Calibri"
|
||||
@@ -80,25 +70,23 @@ SIZE_HEADING3 = 24
|
||||
HEADING_SIZES = {"Heading1": SIZE_HEADING1,
|
||||
"Heading2": SIZE_HEADING2,
|
||||
"Heading3": SIZE_HEADING3}
|
||||
# Compact NICHT mehr in dieser Liste — Listen-Bullet-Schutz uebernimmt das
|
||||
# Post-Processing-Skript pro-Bullet.
|
||||
KEEP_STYLES = ("Heading1", "Heading2", "Heading3", "FirstParagraph")
|
||||
|
||||
# Page-Setup (in DXA, 1cm = 566.929 dxa; 1 inch = 1440 dxa)
|
||||
# A4: 21.0 x 29.7 cm
|
||||
PAGE_W = 11906 # A4 Breite
|
||||
PAGE_H = 16838 # A4 Hoehe
|
||||
MARGIN_TOP = 1247 # 2.2 cm
|
||||
MARGIN_BOT = 1247 # 2.2 cm
|
||||
MARGIN_LEFT = 1417 # 2.5 cm
|
||||
MARGIN_RIGHT = 1417 # 2.5 cm
|
||||
HEADER_POS = 720 # 1.27 cm vom oberen Seitenrand
|
||||
FOOTER_POS = 720 # 1.27 cm vom unteren Seitenrand
|
||||
# Tab-Stop am rechten Textrand: PAGE_W - LEFT - RIGHT = 9072 dxa = 16 cm
|
||||
PAGE_W = 11906
|
||||
PAGE_H = 16838
|
||||
MARGIN_TOP = 1247
|
||||
MARGIN_BOT = 1247
|
||||
MARGIN_LEFT = 1417
|
||||
MARGIN_RIGHT = 1417
|
||||
HEADER_POS = 720
|
||||
FOOTER_POS = 720
|
||||
HEADER_RIGHT_TAB = PAGE_W - MARGIN_LEFT - MARGIN_RIGHT
|
||||
|
||||
HEADER_LEFT = "Dr.-Ing. Thomas Langer"
|
||||
HEADER_RIGHT = "Lebenslauf"
|
||||
|
||||
# --- Hilfsfunktionen -------------------------------------------------------
|
||||
|
||||
def log(msg: str) -> None:
|
||||
print(f"[build-reference-docx] {msg}", flush=True)
|
||||
|
||||
@@ -178,8 +166,6 @@ def replace_direct_fonts_in_styles(styles_xml: Path) -> None:
|
||||
f" gesetzt (Code-Fonts unangetastet: {skipped})")
|
||||
write_xml(tree, styles_xml)
|
||||
|
||||
# --- B1: Tabellen ----------------------------------------------------------
|
||||
|
||||
def set_table_borders_none(styles_xml: Path) -> None:
|
||||
tree = ET.parse(styles_xml)
|
||||
root = tree.getroot()
|
||||
@@ -201,8 +187,6 @@ def set_table_borders_none(styles_xml: Path) -> None:
|
||||
log(" Style 'Table': tblBorders=none auf allen Sides")
|
||||
write_xml(tree, styles_xml)
|
||||
|
||||
# --- B1.5: Schriftgroessen ------------------------------------------------
|
||||
|
||||
def set_default_body_size(styles_xml: Path) -> None:
|
||||
tree = ET.parse(styles_xml)
|
||||
root = tree.getroot()
|
||||
@@ -230,7 +214,35 @@ def set_heading_sizes(styles_xml: Path) -> None:
|
||||
log(f" Stil {sid!r}: Schriftgroesse {target/2} pt")
|
||||
write_xml(tree, styles_xml)
|
||||
|
||||
# --- B2: Header und Footer ------------------------------------------------
|
||||
def set_widow_control_default(styles_xml: Path) -> None:
|
||||
tree = ET.parse(styles_xml)
|
||||
root = tree.getroot()
|
||||
docDefaults = root.find(f"{W}docDefaults") or ET.SubElement(root, f"{W}docDefaults")
|
||||
pPrDefault = docDefaults.find(f"{W}pPrDefault") or ET.SubElement(docDefaults, f"{W}pPrDefault")
|
||||
pPr = pPrDefault.find(f"{W}pPr") or ET.SubElement(pPrDefault, f"{W}pPr")
|
||||
if pPr.find(f"{W}widowControl") is None:
|
||||
ET.SubElement(pPr, f"{W}widowControl")
|
||||
log(" pPrDefault: widowControl aktiviert")
|
||||
write_xml(tree, styles_xml)
|
||||
|
||||
def set_keep_next_styles(styles_xml: Path) -> None:
|
||||
tree = ET.parse(styles_xml)
|
||||
root = tree.getroot()
|
||||
seen = set()
|
||||
for style in root.findall(f"{W}style"):
|
||||
sid = style.get(f"{W}styleId")
|
||||
if sid not in KEEP_STYLES:
|
||||
continue
|
||||
pPr = style.find(f"{W}pPr") or ET.SubElement(style, f"{W}pPr")
|
||||
for tag in (f"{W}keepNext", f"{W}keepLines"):
|
||||
if pPr.find(tag) is None:
|
||||
ET.SubElement(pPr, tag)
|
||||
log(f" Stil {sid!r}: keepNext + keepLines")
|
||||
seen.add(sid)
|
||||
missing = set(KEEP_STYLES) - seen
|
||||
if missing:
|
||||
log(f" Hinweis: Stil(e) {sorted(missing)!r} nicht gefunden, uebersprungen")
|
||||
write_xml(tree, styles_xml)
|
||||
|
||||
def header_default_xml() -> bytes:
|
||||
return (
|
||||
@@ -305,9 +317,6 @@ def update_sectpr_with_headers(document_xml: Path,
|
||||
header_default_rid: str,
|
||||
header_first_rid: str,
|
||||
footer_default_rid: str) -> None:
|
||||
"""Ersetzt sectPr durch Page-Setup + Header/Footer-Refs + titlePg.
|
||||
Footer-Ref wird zweimal eingebaut (default und first), beide auf
|
||||
den gleichen Footer — dann hat Seite 1 trotz titlePg den Footer."""
|
||||
text = document_xml.read_text(encoding="utf-8")
|
||||
new_sectpr = (
|
||||
f'<w:sectPr>'
|
||||
@@ -359,8 +368,6 @@ def add_header_footer(unpacked: Path) -> None:
|
||||
|
||||
update_sectpr_with_headers(doc_xml, rid_h_def, rid_h_first, rid_f_def)
|
||||
|
||||
# --- Hauptablauf -----------------------------------------------------------
|
||||
|
||||
def main() -> int:
|
||||
log(f"Ziel: {OUTPUT_FILE}")
|
||||
TEMPLATES_DIR.mkdir(parents=True, exist_ok=True)
|
||||
@@ -387,6 +394,10 @@ def main() -> int:
|
||||
set_default_body_size(styles_xml)
|
||||
log("Anpassung: Heading-Schriftgroessen 15/13/12 pt")
|
||||
set_heading_sizes(styles_xml)
|
||||
log("Anpassung: Widow/Orphan-Control im DocDefault (B3)")
|
||||
set_widow_control_default(styles_xml)
|
||||
log("Anpassung: keepNext + keepLines auf Heading 1/2/3 + FirstParagraph (B3)")
|
||||
set_keep_next_styles(styles_xml)
|
||||
log("Anpassung: Header und Footer einbauen (B2)")
|
||||
add_header_footer(unpacked)
|
||||
|
||||
|
||||
@@ -7,17 +7,24 @@
|
||||
Deterministischer Build ohne GUI oder Komfortfunktionen.
|
||||
- PDF via Pandoc + LuaLaTeX, nutzt templates/template.tex
|
||||
- DOCX via Pandoc, nutzt templates/reference.docx
|
||||
- DOCX-Post-Processing via build/post-process-docx.py
|
||||
(Listen-Bullet-Schutz nach 3-3-Regel)
|
||||
- Log in output/build.log (ueberschrieben pro Build)
|
||||
- Fortschritt wird zusaetzlich in der Konsole angezeigt
|
||||
- Exit-Code 0 = beide Ausgaben erfolgreich, 1 = ein oder beide Schritte fehlgeschlagen
|
||||
- Exit-Code 0 = alle Schritte erfolgreich, 1 = mindestens ein Fehler
|
||||
|
||||
.NOTES
|
||||
Voraussetzungen auf dem System:
|
||||
- Pandoc (im PATH)
|
||||
- MiKTeX mit LuaLaTeX
|
||||
- Python 3 (im PATH) fuer Post-Processing
|
||||
- System-Fonts: IBM Plex Sans und IBM Plex Mono fuer Windows installiert
|
||||
MiKTeX mit "Install missing packages on the fly: Yes" zieht fehlende
|
||||
LaTeX-Pakete beim ersten Lauf automatisch.
|
||||
|
||||
Hinweis: templates/reference.docx wird NICHT bei jedem Build neu gebaut.
|
||||
Bei Stiländerungen vorher manuell `python build/build-reference-docx.py`
|
||||
aufrufen.
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = 'Continue'
|
||||
@@ -77,7 +84,7 @@ if ($overallExit -ne 0) {
|
||||
|
||||
# --- PDF-Build ---------------------------------------------------------------
|
||||
Write-Host ""
|
||||
Write-Host "[1/2] PDF wird erzeugt (Pandoc + LuaLaTeX) ..." -ForegroundColor Yellow
|
||||
Write-Host "[1/3] PDF wird erzeugt (Pandoc + LuaLaTeX) ..." -ForegroundColor Yellow
|
||||
Write-Log "--- Pandoc -> PDF (LuaLaTeX) ---"
|
||||
$pdfArgs = @(
|
||||
'--from=markdown+smart',
|
||||
@@ -99,13 +106,12 @@ if ($pdfExit -eq 0 -and (Test-Path $outputPdf)) {
|
||||
Write-Host " PDF FEHLER (Exit $pdfExit) - Details siehe build.log" -ForegroundColor Red
|
||||
Write-Log "PDF FEHLER (Exit $pdfExit)"
|
||||
$overallExit = 1
|
||||
# Kurz pausieren, damit die rote Fehlerzeile lesbar bleibt, falls das Fenster danach zugeht
|
||||
Start-Sleep -Seconds 3
|
||||
}
|
||||
|
||||
# --- DOCX-Build --------------------------------------------------------------
|
||||
Write-Host ""
|
||||
Write-Host "[2/2] DOCX wird erzeugt (Pandoc) ..." -ForegroundColor Yellow
|
||||
Write-Host "[2/3] DOCX wird erzeugt (Pandoc) ..." -ForegroundColor Yellow
|
||||
Write-Log "--- Pandoc -> DOCX ---"
|
||||
$docxArgs = @(
|
||||
'--from=markdown+smart',
|
||||
@@ -122,11 +128,33 @@ if ($docxExit -eq 0 -and (Test-Path $outputDocx)) {
|
||||
$sizeKB = [math]::Round((Get-Item $outputDocx).Length / 1KB, 1)
|
||||
Write-Host " DOCX OK ($sizeKB KB): $outputDocx" -ForegroundColor Green
|
||||
Write-Log "DOCX OK: $outputDocx ($sizeKB KB)"
|
||||
|
||||
# --- Post-Processing: Listen-Bullet-Schutz (3-3-Regel) ------------------
|
||||
Write-Host ""
|
||||
Write-Host "[3/3] DOCX-Post-Processing (Listen-Bullet-Schutz) ..." -ForegroundColor Yellow
|
||||
Write-Log "--- Post-Process DOCX ---"
|
||||
$postScript = Join-Path $scriptDir 'post-process-docx.py'
|
||||
if (Test-Path $postScript) {
|
||||
$ppOutput = & python $postScript 2>&1
|
||||
$ppExit = $LASTEXITCODE
|
||||
$ppOutput | ForEach-Object {
|
||||
Write-Log ([string]$_)
|
||||
Write-Host " $_"
|
||||
}
|
||||
if ($ppExit -ne 0) {
|
||||
Write-Host " POST-PROCESS FEHLER (Exit $ppExit)" -ForegroundColor Red
|
||||
Write-Log "POST-PROCESS FEHLER (Exit $ppExit)"
|
||||
$overallExit = 1
|
||||
Start-Sleep -Seconds 3
|
||||
}
|
||||
} else {
|
||||
Write-Host " Hinweis: $postScript nicht vorhanden, uebersprungen" -ForegroundColor Yellow
|
||||
Write-Log "Hinweis: post-process-docx.py nicht vorhanden, uebersprungen"
|
||||
}
|
||||
} else {
|
||||
Write-Host " DOCX FEHLER (Exit $docxExit) - Details siehe build.log" -ForegroundColor Red
|
||||
Write-Log "DOCX FEHLER (Exit $docxExit)"
|
||||
$overallExit = 1
|
||||
# Kurz pausieren, damit die rote Fehlerzeile lesbar bleibt, falls das Fenster danach zugeht
|
||||
Start-Sleep -Seconds 3
|
||||
}
|
||||
|
||||
|
||||
185
artefakte/01-lebenslauf/build/post-process-docx.py
Normal file
185
artefakte/01-lebenslauf/build/post-process-docx.py
Normal file
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
post-process-docx.py
|
||||
====================
|
||||
|
||||
Wird auf das von Pandoc erzeugte DOCX angewendet, NACH `build.ps1`. Setzt
|
||||
Per-Bullet-keepNext-Markierungen, die ein Stil nicht abbilden kann:
|
||||
|
||||
3-3-Regel fuer Listen-Bullets:
|
||||
- Eine Liste ist eine Sequenz aufeinanderfolgender Absaetze mit
|
||||
<w:numPr>-Eigenschaft im Body (nicht innerhalb von Tabellen-Zellen).
|
||||
- Bei einer Liste mit weniger als 6 Bullets: alle Bullets bekommen
|
||||
<w:keepNext/> (Liste bleibt unteilbar — bei <6 ist die 3-3-Regel
|
||||
sowieso nur durch Zusammenhalten aller erfuellbar).
|
||||
- Bei einer Liste mit 6 oder mehr Bullets: die ersten 2 und die
|
||||
drittletzten und vorletzten Bullets bekommen <w:keepNext/>.
|
||||
Damit gilt: nach Bullet 1 darf nicht getrennt werden (1+2+3 zusammen),
|
||||
und nach Bullet N-3 darf nicht getrennt werden (N-2+N-1+N zusammen).
|
||||
Trennen ist erlaubt zwischen den Bullets in der Mitte.
|
||||
|
||||
Bullets in Tabellen-Zellen werden uebersprungen — Compact wird auch fuer
|
||||
Tabellen-Zellen-Inhalte verwendet, dort wollen wir kein keepNext.
|
||||
|
||||
Voraussetzungen: nur Python-Stdlib.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
BASE_DIR = SCRIPT_DIR.parent
|
||||
DOCX_FILE = BASE_DIR / "output" / "Lebenslauf_Dr-Ing_Thomas_Langer.docx"
|
||||
|
||||
W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
||||
|
||||
def log(msg: str) -> None:
|
||||
print(f"[post-process-docx] {msg}", flush=True)
|
||||
|
||||
def is_bullet_paragraph(p_xml: str) -> bool:
|
||||
"""True wenn Absatz-XML eine numPr-Eigenschaft hat (= Listen-Bullet)."""
|
||||
return "<w:numPr" in p_xml
|
||||
|
||||
def has_keep_next(p_xml: str) -> bool:
|
||||
return "<w:keepNext" in p_xml
|
||||
|
||||
def add_keep_next(p_xml: str) -> str:
|
||||
"""Fuegt <w:keepNext/> in das pPr-Element ein. Falls kein pPr existiert,
|
||||
wird es angelegt. Idempotent (wenn schon vorhanden, unveraendert)."""
|
||||
if has_keep_next(p_xml):
|
||||
return p_xml
|
||||
if "<w:pPr>" in p_xml:
|
||||
return p_xml.replace("<w:pPr>", "<w:pPr><w:keepNext/>", 1)
|
||||
if "<w:pPr/>" in p_xml:
|
||||
return p_xml.replace("<w:pPr/>", "<w:pPr><w:keepNext/></w:pPr>", 1)
|
||||
# kein pPr: vor <w:r ...> oder vor </w:p>
|
||||
new_ppr = "<w:pPr><w:keepNext/></w:pPr>"
|
||||
if "<w:r" in p_xml:
|
||||
return p_xml.replace("<w:p>", "<w:p>" + new_ppr, 1) \
|
||||
if p_xml.startswith("<w:p>") else p_xml
|
||||
return p_xml.replace("</w:p>", new_ppr + "</w:p>", 1)
|
||||
|
||||
# Regex: ein <w:p ...>...</w:p>, optional gefolgt vom oeffnenden Marker fuer
|
||||
# Tabelle (<w:tbl>) oder schliessenden Body (</w:body>). Wir splitten nicht,
|
||||
# sondern iterieren paragraphenweise und tracken Tabellen-Schachtelung.
|
||||
|
||||
P_RE = re.compile(r"<w:p\b[^>]*>.*?</w:p>", re.DOTALL)
|
||||
TBL_OPEN = "<w:tbl>"
|
||||
TBL_CLOSE = "</w:tbl>"
|
||||
|
||||
def process_document_xml(xml: str) -> tuple[str, dict]:
|
||||
"""Findet Listen-Sequenzen ausserhalb von Tabellen, wendet 3-3-Regel an.
|
||||
Gibt das modifizierte XML und Statistiken zurueck."""
|
||||
# Tokenize: <w:tbl>...</w:tbl>-Bereiche markieren, damit wir sie ueberspringen.
|
||||
# Ansatz: wir gehen durch das XML und tracken aktuelle Tabellen-Tiefe.
|
||||
# Wenn Tiefe > 0: Bullets in Tabellen-Zellen ueberspringen.
|
||||
out = []
|
||||
pos = 0
|
||||
table_depth = 0
|
||||
bullet_run: list[tuple[int, str]] = [] # (out_idx, p_xml) Indizes in out
|
||||
stats = {"lists": 0, "bullets_in_lists": 0, "bullets_keepnext": 0,
|
||||
"skipped_in_tables": 0}
|
||||
|
||||
def flush_run():
|
||||
if not bullet_run:
|
||||
return
|
||||
n = len(bullet_run)
|
||||
stats["lists"] += 1
|
||||
stats["bullets_in_lists"] += n
|
||||
if n < 6:
|
||||
indices_keep = list(range(n))
|
||||
else:
|
||||
indices_keep = [0, 1, n-3, n-2]
|
||||
for k in indices_keep:
|
||||
idx, p_xml = bullet_run[k]
|
||||
new_xml = add_keep_next(p_xml)
|
||||
if new_xml != p_xml:
|
||||
out[idx] = new_xml
|
||||
stats["bullets_keepnext"] += 1
|
||||
bullet_run.clear()
|
||||
|
||||
# Wir scannen das XML linear nach <w:p ...>...</w:p>, <w:tbl>, </w:tbl>
|
||||
# und sammeln Bullet-Sequenzen ausserhalb von Tabellen.
|
||||
# Dafuer iterieren wir mit einem regex der ALLE drei Token findet.
|
||||
token_re = re.compile(
|
||||
r"(?P<tblopen>" + re.escape(TBL_OPEN) + r")"
|
||||
r"|(?P<tblclose>" + re.escape(TBL_CLOSE) + r")"
|
||||
r"|(?P<para><w:p\b[^>]*>.*?</w:p>)",
|
||||
re.DOTALL,
|
||||
)
|
||||
last_end = 0
|
||||
for m in token_re.finditer(xml):
|
||||
# nicht-tokenisierten Text dazwischen anhaengen
|
||||
if m.start() > last_end:
|
||||
out.append(xml[last_end:m.start()])
|
||||
last_end = m.end()
|
||||
|
||||
if m.group("tblopen"):
|
||||
flush_run() # Listen vor Tabelle abschliessen
|
||||
table_depth += 1
|
||||
out.append(m.group())
|
||||
elif m.group("tblclose"):
|
||||
flush_run() # innerhalb-Tabellen-Listen wir flushen, aber haben
|
||||
# sie eh nicht angesammelt
|
||||
table_depth -= 1
|
||||
out.append(m.group())
|
||||
else:
|
||||
p_xml = m.group("para")
|
||||
out.append(p_xml)
|
||||
if table_depth > 0:
|
||||
# Bullets in Tabellen-Zellen ignorieren
|
||||
if is_bullet_paragraph(p_xml):
|
||||
stats["skipped_in_tables"] += 1
|
||||
# nicht-bullet-paragraph in tabelle: kein effekt
|
||||
continue
|
||||
if is_bullet_paragraph(p_xml):
|
||||
bullet_run.append((len(out) - 1, p_xml))
|
||||
else:
|
||||
# Sequenz-Ende: 3-3-Regel anwenden
|
||||
flush_run()
|
||||
|
||||
# Rest hinten dranhaengen
|
||||
if last_end < len(xml):
|
||||
out.append(xml[last_end:])
|
||||
flush_run() # falls Liste am Body-Ende
|
||||
return "".join(out), stats
|
||||
|
||||
def main() -> int:
|
||||
if not DOCX_FILE.exists():
|
||||
sys.stderr.write(f"FEHLER: {DOCX_FILE} existiert nicht. "
|
||||
f"Erst build.ps1 laufen lassen.\n")
|
||||
return 1
|
||||
log(f"Verarbeite: {DOCX_FILE}")
|
||||
|
||||
# DOCX in memory einlesen
|
||||
with zipfile.ZipFile(DOCX_FILE, "r") as z:
|
||||
members = {name: z.read(name) for name in z.namelist()}
|
||||
|
||||
doc_xml = members["word/document.xml"].decode("utf-8")
|
||||
new_xml, stats = process_document_xml(doc_xml)
|
||||
|
||||
if new_xml == doc_xml:
|
||||
log(" keine Aenderung — keine bullet-Listen gefunden oder bereits gesetzt")
|
||||
members["word/document.xml"] = new_xml.encode("utf-8")
|
||||
|
||||
# DOCX zurueckschreiben (mode='w' truncatet)
|
||||
with zipfile.ZipFile(DOCX_FILE, "w", zipfile.ZIP_DEFLATED) as z:
|
||||
# [Content_Types].xml zuerst
|
||||
order = sorted(members.keys(),
|
||||
key=lambda n: (0 if n == "[Content_Types].xml" else 1, n))
|
||||
for name in order:
|
||||
z.writestr(name, members[name])
|
||||
|
||||
log(f" Listen gefunden: {stats['lists']}")
|
||||
log(f" Bullets in Listen: {stats['bullets_in_lists']}")
|
||||
log(f" keepNext gesetzt: {stats['bullets_keepnext']}")
|
||||
log(f" Bullets in Tabellen uebersprungen: {stats['skipped_in_tables']}")
|
||||
log("Fertig.")
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Binary file not shown.
@@ -727,7 +727,7 @@ endobj
|
||||
'
|
||||
endstream
|
||||
endobj
|
||||
155 0 obj
|
||||
155 0 obj
|
||||
<< /Subtype /CIDFontType0C /Filter /FlateDecode /Length 4888 >>
|
||||
stream
|
||||
xڝ9 TW<><57>4U<0F>)<29><>[<5B>6U
|
||||
@@ -895,7 +895,7 @@ xref
|
||||
endobj
|
||||
xref
|
||||
0 163
|
||||
0000000000 65535 f
|
||||
0000000000 65535 f
|
||||
0000000020 00000 n
|
||||
0000016167 00000 n
|
||||
0000065136 00000 n
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
===== Build gestartet: 2026-04-26 13:24:16 =====
|
||||
===== Build gestartet: 2026-04-26 16:29:03 =====
|
||||
Source: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\source\cv.md
|
||||
Template-TEX: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\templates\template.tex
|
||||
Reference: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\templates\reference.docx
|
||||
@@ -10,4 +10,11 @@ PDF OK: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\outp
|
||||
--- Pandoc -> DOCX ---
|
||||
Cmd: pandoc --from=markdown+smart --reference-doc=Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\templates\reference.docx --resource-path=Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\source --output=Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.docx Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\source\cv.md
|
||||
DOCX OK: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.docx (22 KB)
|
||||
===== Build beendet: 2026-04-26 13:24:21, Exit-Code 0 =====
|
||||
--- Post-Process DOCX ---
|
||||
[post-process-docx] Verarbeite: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.docx
|
||||
[post-process-docx] Listen gefunden: 27
|
||||
[post-process-docx] Bullets in Listen: 186
|
||||
[post-process-docx] keepNext gesetzt: 95
|
||||
[post-process-docx] Bullets in Tabellen uebersprungen: 0
|
||||
[post-process-docx] Fertig.
|
||||
===== Build beendet: 2026-04-26 16:29:09, Exit-Code 0 =====
|
||||
|
||||
Binary file not shown.
@@ -44,3 +44,4 @@ Chronologisches Log aller Entscheidungen und Prozessereignisse.
|
||||
2026-04-25 22:43 | S06 | Session-Abschluss S06. Teilgebiet 01 Iteration A inhaltlich umgesetzt: Ausbildung als 2-Spalten-Layout via Pandoc-Multiline-Tabelle ohne Header, Template um array, calc, booktabs, longtable und providecommand real ergänzt, alle Linienbreiten auf 0pt. DOCX-Layout von Thomas visuell bestätigt (nur Default-Tabellenrahmen offen, Rahmen-Aus in Iteration B). PDF-Build mit Hotfix steht noch aus — erste Aufgabe der nächsten Session ist build.ps1 laufen lassen und das Ausbildungs-Layout im PDF prüfen. Tooling-Fix in checkpoint.ps1 abgeschlossen (Quoting-Bug, Pipe-Validierung, Whitespace-Normalisierung, Pre-flight-Checks, atomarer Rollback bei Fehler). agent-prompt.md Aktueller-Stand-Abschnitt für S07 fortgeschrieben mit verbleibender Iterationsreihenfolge B-C-D, neuen Lehren aus S06 dokumentiert (Sandbox-Reads über NTFS-Mount können stale sein, Sandbox-Pandoc 2.9 vs Thomas' Pandoc 3.x Output-Unterschiede).
|
||||
2026-04-26 10:58 | S07 | PDF-Build-Fehler endgueltig behoben. S06-Hotfix (array, calc, providecommand real) loeste das Problem nicht; nach Rebuild kam unveraendert "! LaTeX Error: No counter 'none' defined." Eigentliche Ursache: Pandoc 3.x emittiert fuer unnummerierte Tabellen direkt vor begin{longtable} die Zeile def LTcaptype none, ohne den Counter none zu definieren. Pandocs eigene Default-Vorlage definiert ihn (commit d835461 in Pandoc 3.8.2.1), Custom-Templates muessen das selbst tun (siehe Pandoc-Issue 11201). Fix: eine Zeile newcounter none direkt nach providecommand real im Tabellen-Block des Templates. Sandbox-Reproduktion exakter Fehlertext ohne Fix, sauberes PDF mit Fix. Auf Thomas' System: PDF wird erzeugt, Ausbildungs-Layout im PDF visuell bestaetigt. Iteration A fuer Teilgebiet 01 damit inhaltlich abgeschlossen. Build-UX-Fix: build.ps1 ergaenzt um Start-Sleep -Seconds 3 nach jedem fehlschlagenden Build-Schritt (Pflichtdatei-Check, PDF-Build, DOCX-Build), damit die rote Fehlerzeile lesbar bleibt bevor das PowerShell-Fenster zugeht. teilgebiete/01-lebenslauf.md um beide Fixes ergaenzt (zweistufige Hotfix-Geschichte, PDF-Bestaetigung, UX-Fix). agent-prompt.md Aktueller-Stand-Abschnitt fuer S08 fortgeschrieben mit verbleibender Iterationsreihenfolge B-C-D, Hinweise auf Pandoc-Versionsunterschied praeziser formuliert.
|
||||
2026-04-26 13:29 | S07 | Teilgebiet 01 Iteration B (Iterationen B1, B1.5, B2) durchgezogen. Neue Datei build/build-reference-docx.py baut templates/reference.docx programmatisch aus Pandocs Default-Reference (Python-Stdlib only, kein pip; pandoc --print-default-data-file zur Laufzeit, ZIP entpacken, ElementTree-XML-Anpassungen, repacken). B1: Theme major+minor und alle direkten Schrift-Refs in styles.xml auf Calibri umgestellt (Code-Schriften wie Consolas bleiben), Tabellen-Default-Stil mit tblBorders=none auf allen Sides. B1.5: Body-DocDefault 11 pt, Heading 1/2/3 auf 15/13/12 pt analog PDF. B2: header1.xml (Default ab Seite 2 mit Name links und Lebenslauf rechts), header2.xml (leer fuer Seite 1 via titlePg), footer1.xml (rechts Seite n / m mit PAGE/NUMPAGES-Feldern, doppelt referenziert als default und first damit Seite 1 trotz titlePg den Footer hat). Page-Setup explizit in sectPr: A4 mit 2.2 cm oben/unten und 2.5 cm links/rechts analog PDF, Tab-Stop am rechten Textrand 9072 dxa. Beziehungen mit dynamisch naechster freier rId in document.xml.rels, Content-Types-Overrides in [Content_Types].xml, sectPr regex-ersetzt idempotent. Sandbox-End-to-End mit Pandoc 2.9 verifiziert (sectPr und Header/Footer im generierten DOCX vorhanden). Auf Thomas System: DOCX visuell bestaetigt. teilgebiete/01-lebenslauf.md um vollstaendigen Iteration-B-Block ergaenzt, Naechste-Schritte-Liste auf B3, B4, C, D umstrukturiert. agent-prompt.md Aktueller-Stand-Abschnitt fortgeschrieben mit Hinweisen zur reference-docx-Pipeline (manuell vor build.ps1 aufrufen, nicht von Hand in Word editieren) und zur Edit-Tool-Truncation auf dem NTFS-Mount. Build-UX-Fix in build.ps1 mit 3-Sekunden-Pause pro fehlgeschlagenem Schritt war ebenfalls Teil dieser Session.
|
||||
2026-04-26 16:40 | S07 | Iteration B3 und B3.5 fuer Teilgebiet 01 abgeschlossen. B3 in build/build-reference-docx.py ergaenzt: DocDefault widowControl plus keepNext und keepLines auf Heading 1/2/3 und FirstParagraph (Pandoc-Stil fuer ersten Absatz nach einem Heading, deckt die fett formatierten Kenntnisse-Subsection-Labels KI Software-Design Methodik IT etc ab). Erster Versuch Compact-Stil mit keepNext hat Listen komplett unteilbar gemacht (Job-Stationen begannen jedes Mal auf einer neuen Seite, ungenutzte Seitenenden) und wurde verworfen. Auf Wunsch von Thomas auf 3-3-Regel umgestellt: bei Listen mit mindestens 6 Bullets duerfen Trennungen passieren, aber mindestens 3 Bullets bleiben jeweils zusammen vor und nach dem Umbruch. Bei kuerzeren Listen alles zusammen. Da das stilbasiert nicht abbildbar ist (alle Bullets haben pStyle Compact), neues Post-Processing-Skript build/post-process-docx.py: scannt das fertige DOCX, findet Sequenzen aufeinanderfolgender Bullets mit numPr-Eigenschaft ausserhalb von Tabellen-Zellen, setzt keepNext auf den ersten 2 und den N-3 N-2 Bullets jeder Liste mit n groesser gleich 6 (bei n kleiner 6 alle keepNext). build.ps1 erweitert auf 3 Schritte und ruft das Post-Processing-Skript automatisch nach erfolgreichem DOCX-Build auf, mit Console-Output und Log-Statistiken (Anzahl Listen Bullets keepNext-Markierungen). Sandbox-Verifikation 26 Listen 184 Bullets 93 keepNext, Pattern fuer 11-Bullet-Liste KK......KK.. Auf Thomas System visuell bestaetigt: Listen werden an guten Stellen getrennt, keine ungenutzten Seitenenden, keine einzelnen Bullets allein am Seitenrand. teilgebiete/01-lebenslauf.md um B3- und B3.5-Bloecke ergaenzt sowie Naechste-Schritte-Liste auf B4 C D umstrukturiert. agent-prompt.md Aktueller-Stand-Abschnitt fortgeschrieben mit B3 und B3.5, Hinweis auf 3-stufige DOCX-Pipeline und Edit-Tool-Truncation an build.ps1 ergaenzt. Naechste Session startet mit B4 (Heading-Farben oder Trennlinien analog PDF).
|
||||
|
||||
@@ -172,13 +172,24 @@ Die in S04 mit docx-js erstellte Version hatte strukturelle typographische Mäng
|
||||
- Seite 2 ff. mit Header (Name links, „Lebenslauf" rechts) und Footer (Seite n / m).
|
||||
- Tab-Stops „Lebenslauf" und Seitenzahl bündig am rechten Textrand.
|
||||
|
||||
**B3 — Schusterjungen- und Witwen-Schutz für DOCX:**
|
||||
|
||||
- DocDefault `<w:widowControl/>` aktiviert (klassische Witwen/Waisen-Logik in mehrzeiligen Absätzen).
|
||||
- Heading 1/2/3 und `FirstParagraph` (Pandoc-Stil für ersten Absatz nach einem Heading — deckt unsere Kenntnisse-Subsection-Labels ab) bekommen `<w:keepNext/>` und `<w:keepLines/>`. Damit bleibt jede Überschrift mit dem nachfolgenden Inhalt zusammen.
|
||||
|
||||
**B3.5 — 3-3-Regel für Listen-Bullets:**
|
||||
|
||||
- Erster Versuch (Compact-Stil mit `keepNext+keepLines`) hat Listen komplett unteilbar gemacht — Folge: Job-Stationen begannen jedes Mal auf einer neuen Seite, Seitenenden ungenutzt. Auf Wunsch von Thomas auf eine 3-3-Regel umgestellt: bei Listen mit ≥ 6 Bullets darf getrennt werden, aber mindestens 3 Bullets bleiben jeweils zusammen vor und nach dem Umbruch. Bei Listen mit < 6 Bullets bleibt alles zusammen (sonst nicht erfüllbar).
|
||||
- Da das stilbasiert nicht abbildbar ist (alle Bullets haben pStyle="Compact"), läuft die Logik in einem **Post-Processing-Skript** `build/post-process-docx.py`, das nach dem Pandoc-DOCX-Build die `document.xml` modifiziert: Sequenzen aufeinanderfolgender Listen-Bullets (Absätze mit `<w:numPr>`) werden gefunden, pro Sequenz bekommen die ersten 2 und die N-3-/N-2-Bullets `<w:keepNext/>`. Bullets in Tabellen-Zellen werden defensiv ausgenommen (faktisch bei uns leer, weil unsere Tabellen-Zellen Compact-Absätze ohne numPr enthalten).
|
||||
- `build.ps1` ruft das Skript automatisch nach erfolgreichem DOCX-Build auf (Schritt [3/3]), Console-Output und Log enthalten Statistiken (Anzahl Listen, Bullets, gesetzte keepNext-Markierungen).
|
||||
- Sandbox-Verifikation: 26 Listen, 184 Bullets, 93 keepNext-Markierungen, Pattern für Listen mit n ≥ 6 z.B. `KK......KK.` (Liste mit 11 Bullets: erste 2 + Bullets 9 und 10 keepNext). Auf Thomas' System visuell bestätigt: Stationen-Listen werden jetzt sauber an guter Stelle getrennt, keine ungenutzten Seitenenden mehr, kein einzelner Bullet alleine am Seitenrand.
|
||||
|
||||
## Nächste Schritte
|
||||
|
||||
1. **Iteration B3 — Heading-Stile mit „keep with next" und Widow/Orphan-Control:** Schusterjungen-Schutz für DOCX analog zu `\widowpenalty`/`needspace` im PDF. Auf Stilebene über `<w:keepNext/>` für Headings und `<w:widowControl w:val="true"/>` als DocDefault.
|
||||
2. **Iteration B4 (optional)** — Heading-Farben auf DesTEngS-Blau und/oder Trennlinien analog PDF, falls das DOCX optisch näher ans PDF heran soll. Bei Vorlage für Consulting-Agenturen, die das Layout ohnehin überschreiben, ist das aber eher Kosmetik.
|
||||
3. **Iteration C — Foto-Einbindung:** Portraitfoto in `source/cv.md` einbetten (Pandoc-Image-Syntax), Position und Größe im Template absichern (z.B. oben rechts neben Name, ca. 3 cm).
|
||||
4. **Iteration D — Hyphenation-Feintuning für PDF:** Kurze Wortteile am Zeilenanfang mit höherer Penalty oder gezielten `\hyphenation`-Ausnahmen reduzieren. Iterativ.
|
||||
5. Teilgebiet nach erfolgreichem Output und Freigabe durch Thomas abschließen (R2-OK von Thomas: Status auf „abgeschlossen" im zentral-index.md).
|
||||
1. **Iteration B4 — Heading-Farben auf DesTEngS-Blau und/oder Trennlinien analog PDF.** Eher Kosmetik bei Vorlage für Consulting-Agenturen, aber für eigene Direktverwendung des DOCX (Website-Download, persönliche Bewerbungen) sinnvoll.
|
||||
2. **Iteration C — Foto-Einbindung:** Portraitfoto in `source/cv.md` einbetten (Pandoc-Image-Syntax), Position und Größe im Template absichern (z.B. oben rechts neben Name, ca. 3 cm).
|
||||
3. **Iteration D — Hyphenation-Feintuning für PDF:** Kurze Wortteile am Zeilenanfang mit höherer Penalty oder gezielten `\hyphenation`-Ausnahmen reduzieren. Iterativ.
|
||||
4. Teilgebiet nach erfolgreichem Output und Freigabe durch Thomas abschließen (R2-OK von Thomas: Status auf „abgeschlossen" im zentral-index.md).
|
||||
|
||||
## Artefakte
|
||||
|
||||
@@ -188,8 +199,9 @@ Die in S04 mit docx-js erstellte Version hatte strukturelle typographische Mäng
|
||||
- `artefakte/01-lebenslauf/source/foto-wrba_2026_6782_1.jpg` — Portraitfoto (umbenannt, noch nicht in cv.md eingebunden).
|
||||
- `artefakte/01-lebenslauf/templates/template.tex` — Pandoc-LaTeX-Template für LuaLaTeX (Iteration A inkl. Pandoc-3.x-Hotfix `\newcounter{none}`).
|
||||
- `artefakte/01-lebenslauf/templates/reference.docx` — Pandoc-Reference-Doc, **automatisch erzeugt** durch `build/build-reference-docx.py`. Nicht von Hand editieren — Änderungen würden beim nächsten Skript-Lauf überschrieben.
|
||||
- `artefakte/01-lebenslauf/build/build-reference-docx.py` — Python-Skript zum Bauen der `reference.docx` (Iteration B1+B1.5+B2). Manuell aufrufen, wenn Stile geändert werden sollen, danach normalen `build.ps1` laufen.
|
||||
- `artefakte/01-lebenslauf/build/build.ps1` — PowerShell-Build-Skript (PDF + DOCX) mit 3-Sekunden-Pause bei Fehler.
|
||||
- `artefakte/01-lebenslauf/build/build-reference-docx.py` — Python-Skript zum Bauen der `reference.docx` (Iterationen B1, B1.5, B2, B3). Manuell aufrufen, wenn Stile geändert werden sollen, danach normalen `build.ps1` laufen.
|
||||
- `artefakte/01-lebenslauf/build/post-process-docx.py` — Python-Skript für DOCX-Post-Processing (B3.5 Listen-Bullet-Schutz). Wird automatisch von `build.ps1` als Schritt [3/3] aufgerufen.
|
||||
- `artefakte/01-lebenslauf/build/build.ps1` — PowerShell-Build-Skript (PDF + DOCX + Post-Process) mit 3-Sekunden-Pause bei Fehler.
|
||||
- `artefakte/01-lebenslauf/output/` — erzeugte Ausgaben plus `build.log`.
|
||||
|
||||
### Historische Entwürfe (unter `artefakte/01-lebenslauf/entwuerfe/`)
|
||||
|
||||
Reference in New Issue
Block a user