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.
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -88,14 +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.** Der S06-Hotfix (array, calc, providecommand real) hat das Problem nicht beseitigt — nach Rebuild auf Thomas' System kam unverändert `! LaTeX Error: No counter 'none' defined.` Eigentliche Ursache: Pandoc 3.x emittiert für unnummerierte Tabellen direkt vor `\begin{longtable}` die Zeile `\def\LTcaptype{none}`, ohne den Counter `none` zu definieren. Sobald longtable intern `\refstepcounter{\LTcaptype}` aufruft, bricht LaTeX ab. Pandocs eigene Default-Vorlage definiert den Counter (commit d835461 in 3.8.2.1), Custom-Templates müssen das selbst tun. Siehe [Pandoc-Issue #11201](https://github.com/jgm/pandoc/issues/11201). Fix: eine Zeile `\newcounter{none}` direkt nach `\providecommand{\real}` im Tabellen-Block des Templates. In der Sandbox sauber reproduziert (gleicher Fehlertext ohne Fix, sauberes PDF mit Fix). Auf Thomas' System: PDF wird erzeugt, Ausbildungs-Layout im PDF visuell bestätigt.
|
||||
- **Iteration A für Teilgebiet 01 damit inhaltlich abgeschlossen** (Tabellen-Variante mit zwei Spalten ca. 14 % / 80 %, rahmenlos im PDF, native Word-Tabelle im DOCX — Rahmen-Aus folgt in Iteration B).
|
||||
- **Build-UX-Fix:** `build/build.ps1` ergänzt um `Start-Sleep -Seconds 3` nach jedem fehlschlagenden Build-Schritt (Pflichtdatei-Check, PDF-Build, DOCX-Build). Vorher schloss sich das PowerShell-Fenster bei Doppelklick auf das Skript so schnell, dass die rote Fehlerzeile nicht lesbar war. Bei mehreren Fehlern in einem Lauf akkumulieren sich die Pausen — gewollt.
|
||||
- **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.
|
||||
|
||||
**Nächste Aufgabe:** Teilgebiet 01 — drei verbleibende Iterationen in dieser Reihenfolge:
|
||||
1. **B) `templates/reference.docx` in Word polieren** — Header/Footer setzen, Schriften auf Calibri vereinheitlichen, Listen-Schutz „Keep with next" und Widow-Control via Word-Stile, Tabellen-Stile so konfigurieren, dass die Ausbildungs-Tabelle ohne Rahmen rendert (Default-Tabellenstil oder benannter Stil).
|
||||
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 gezielten `\hyphenation`-Ausnahmen reduzieren. Iterativ.
|
||||
**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.
|
||||
|
||||
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").
|
||||
|
||||
@@ -108,3 +113,5 @@ Nach D): Status von Teilgebiet 01 in `zentral-index.md` auf „abgeschlossen" se
|
||||
- **Sandbox kann nichts an `.git/` schreiben** (NTFS-Permission-Issue): Lock-Files, korrupte Index — alles muss von PowerShell aus repariert werden.
|
||||
- **`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.
|
||||
|
||||
401
artefakte/01-lebenslauf/build/build-reference-docx.py
Normal file
401
artefakte/01-lebenslauf/build/build-reference-docx.py
Normal file
@@ -0,0 +1,401 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
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):
|
||||
B1 - Theme-Schriften (majorFont und minorFont) beide auf Calibri.
|
||||
B1 - Direkte Schriftnamen-Referenzen in styles.xml auf Calibri
|
||||
(Code-Schriften wie Consolas bleiben).
|
||||
B1 - Tabellen-Default-Stil "Table" mit tblBorders=none.
|
||||
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.
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
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",
|
||||
"r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||
"rel": "http://schemas.openxmlformats.org/package/2006/relationships",
|
||||
"ct": "http://schemas.openxmlformats.org/package/2006/content-types",
|
||||
}
|
||||
for prefix, uri in NS.items():
|
||||
ET.register_namespace(prefix, uri)
|
||||
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"
|
||||
|
||||
SIZE_BODY = 22
|
||||
SIZE_HEADING1 = 30
|
||||
SIZE_HEADING2 = 26
|
||||
SIZE_HEADING3 = 24
|
||||
HEADING_SIZES = {"Heading1": SIZE_HEADING1,
|
||||
"Heading2": SIZE_HEADING2,
|
||||
"Heading3": SIZE_HEADING3}
|
||||
|
||||
# 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
|
||||
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)
|
||||
|
||||
XML_DECL = b'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n'
|
||||
|
||||
def write_xml(tree: ET.ElementTree, dest: Path) -> None:
|
||||
body = ET.tostring(tree.getroot(), encoding="utf-8")
|
||||
dest.write_bytes(XML_DECL + body)
|
||||
|
||||
def write_xml_bytes(content: bytes, dest: Path) -> None:
|
||||
dest.write_bytes(XML_DECL + content)
|
||||
|
||||
def fetch_pandoc_default(dest: Path) -> None:
|
||||
log("Pandoc-Default-Reference extrahieren ...")
|
||||
result = subprocess.run(
|
||||
["pandoc", "--print-default-data-file", "reference.docx"],
|
||||
capture_output=True, check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
sys.stderr.write(result.stderr.decode("utf-8", errors="replace"))
|
||||
raise SystemExit(f"pandoc liefert Exit-Code {result.returncode}")
|
||||
dest.write_bytes(result.stdout)
|
||||
log(f" -> {dest} ({dest.stat().st_size} Bytes)")
|
||||
|
||||
def unpack_docx(src: Path, dest_dir: Path) -> None:
|
||||
with zipfile.ZipFile(src, "r") as z:
|
||||
z.extractall(dest_dir)
|
||||
|
||||
def repack_docx(src_dir: Path, dest: Path) -> None:
|
||||
files = []
|
||||
for path in src_dir.rglob("*"):
|
||||
if path.is_file():
|
||||
arcname = path.relative_to(src_dir).as_posix()
|
||||
files.append((path, arcname))
|
||||
files.sort(key=lambda t: (0 if t[1] == "[Content_Types].xml" else 1, t[1]))
|
||||
with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as z:
|
||||
for path, arcname in files:
|
||||
z.write(path, arcname)
|
||||
|
||||
def is_code_font(name: str) -> bool:
|
||||
return (name or "").strip().lower() in CODE_FONTS
|
||||
|
||||
# --- B1: Schriften ---------------------------------------------------------
|
||||
|
||||
def set_theme_fonts_to_calibri(theme_xml: Path) -> None:
|
||||
tree = ET.parse(theme_xml)
|
||||
root = tree.getroot()
|
||||
for kind in ("majorFont", "minorFont"):
|
||||
font = root.find(f".//{A}{kind}")
|
||||
if font is None:
|
||||
raise RuntimeError(f"{kind}-Element nicht im Theme")
|
||||
latin = font.find(f"{A}latin")
|
||||
if latin is None:
|
||||
raise RuntimeError(f"{kind}/latin-Element nicht gefunden")
|
||||
old = latin.get("typeface")
|
||||
latin.set("typeface", TARGET_FONT)
|
||||
log(f" Theme {kind}/latin: {old!r} -> {TARGET_FONT!r}")
|
||||
write_xml(tree, theme_xml)
|
||||
|
||||
def replace_direct_fonts_in_styles(styles_xml: Path) -> None:
|
||||
tree = ET.parse(styles_xml)
|
||||
root = tree.getroot()
|
||||
changed = 0
|
||||
skipped = 0
|
||||
for rfonts in root.iter(f"{W}rFonts"):
|
||||
for attr in (f"{W}ascii", f"{W}hAnsi", f"{W}cs", f"{W}eastAsia"):
|
||||
val = rfonts.get(attr)
|
||||
if val is None:
|
||||
continue
|
||||
if is_code_font(val):
|
||||
skipped += 1
|
||||
continue
|
||||
if val != TARGET_FONT:
|
||||
rfonts.set(attr, TARGET_FONT)
|
||||
changed += 1
|
||||
log(f" styles.xml: {changed} direkte Font-Attribute auf {TARGET_FONT!r}"
|
||||
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()
|
||||
style = next((s for s in root.findall(f"{W}style")
|
||||
if s.get(f"{W}styleId") == "Table"), None)
|
||||
if style is None:
|
||||
raise RuntimeError("Style 'Table' nicht in styles.xml")
|
||||
tbl_pr = style.find(f"{W}tblPr") or ET.SubElement(style, f"{W}tblPr")
|
||||
existing = tbl_pr.find(f"{W}tblBorders")
|
||||
if existing is not None:
|
||||
tbl_pr.remove(existing)
|
||||
borders = ET.SubElement(tbl_pr, f"{W}tblBorders")
|
||||
for side in ("top", "left", "bottom", "right", "insideH", "insideV"):
|
||||
e = ET.SubElement(borders, f"{W}{side}")
|
||||
e.set(f"{W}val", "none")
|
||||
e.set(f"{W}sz", "0")
|
||||
e.set(f"{W}space", "0")
|
||||
e.set(f"{W}color", "auto")
|
||||
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()
|
||||
docDefaults = root.find(f"{W}docDefaults") or ET.SubElement(root, f"{W}docDefaults")
|
||||
rPrDefault = docDefaults.find(f"{W}rPrDefault") or ET.SubElement(docDefaults, f"{W}rPrDefault")
|
||||
rPr = rPrDefault.find(f"{W}rPr") or ET.SubElement(rPrDefault, f"{W}rPr")
|
||||
for tag in (f"{W}sz", f"{W}szCs"):
|
||||
elem = rPr.find(tag) or ET.SubElement(rPr, tag)
|
||||
elem.set(f"{W}val", str(SIZE_BODY))
|
||||
log(f" DocDefault Body-Schriftgroesse: {SIZE_BODY/2} pt")
|
||||
write_xml(tree, styles_xml)
|
||||
|
||||
def set_heading_sizes(styles_xml: Path) -> None:
|
||||
tree = ET.parse(styles_xml)
|
||||
root = tree.getroot()
|
||||
for style in root.findall(f"{W}style"):
|
||||
sid = style.get(f"{W}styleId")
|
||||
if sid not in HEADING_SIZES:
|
||||
continue
|
||||
target = HEADING_SIZES[sid]
|
||||
rPr = style.find(f"{W}rPr") or ET.SubElement(style, f"{W}rPr")
|
||||
for tag in (f"{W}sz", f"{W}szCs"):
|
||||
elem = rPr.find(tag) or ET.SubElement(rPr, tag)
|
||||
elem.set(f"{W}val", str(target))
|
||||
log(f" Stil {sid!r}: Schriftgroesse {target/2} pt")
|
||||
write_xml(tree, styles_xml)
|
||||
|
||||
# --- B2: Header und Footer ------------------------------------------------
|
||||
|
||||
def header_default_xml() -> bytes:
|
||||
return (
|
||||
b'<w:hdr xmlns:w="' + NS["w"].encode() + b'">\n'
|
||||
b' <w:p>\n'
|
||||
b' <w:pPr>\n'
|
||||
b' <w:tabs>\n'
|
||||
b' <w:tab w:val="right" w:pos="' + str(HEADER_RIGHT_TAB).encode() + b'"/>\n'
|
||||
b' </w:tabs>\n'
|
||||
b' </w:pPr>\n'
|
||||
b' <w:r><w:t xml:space="preserve">' + HEADER_LEFT.encode() + b'</w:t></w:r>\n'
|
||||
b' <w:r><w:tab/><w:t xml:space="preserve">' + HEADER_RIGHT.encode() + b'</w:t></w:r>\n'
|
||||
b' </w:p>\n'
|
||||
b'</w:hdr>\n'
|
||||
)
|
||||
|
||||
def header_first_blank_xml() -> bytes:
|
||||
return (
|
||||
b'<w:hdr xmlns:w="' + NS["w"].encode() + b'">\n'
|
||||
b' <w:p/>\n'
|
||||
b'</w:hdr>\n'
|
||||
)
|
||||
|
||||
def footer_default_xml() -> bytes:
|
||||
return (
|
||||
b'<w:ftr xmlns:w="' + NS["w"].encode() + b'">\n'
|
||||
b' <w:p>\n'
|
||||
b' <w:pPr>\n'
|
||||
b' <w:tabs>\n'
|
||||
b' <w:tab w:val="right" w:pos="' + str(HEADER_RIGHT_TAB).encode() + b'"/>\n'
|
||||
b' </w:tabs>\n'
|
||||
b' </w:pPr>\n'
|
||||
b' <w:r><w:tab/><w:t xml:space="preserve">Seite </w:t></w:r>\n'
|
||||
b' <w:fldSimple w:instr="PAGE">\n'
|
||||
b' <w:r><w:t>1</w:t></w:r>\n'
|
||||
b' </w:fldSimple>\n'
|
||||
b' <w:r><w:t xml:space="preserve"> / </w:t></w:r>\n'
|
||||
b' <w:fldSimple w:instr="NUMPAGES">\n'
|
||||
b' <w:r><w:t>1</w:t></w:r>\n'
|
||||
b' </w:fldSimple>\n'
|
||||
b' </w:p>\n'
|
||||
b'</w:ftr>\n'
|
||||
)
|
||||
|
||||
REL_HEADER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"
|
||||
REL_FOOTER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
|
||||
CT_HEADER = "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"
|
||||
CT_FOOTER = "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"
|
||||
|
||||
def next_free_rel_id(rels_xml: Path) -> int:
|
||||
text = rels_xml.read_text(encoding="utf-8")
|
||||
ids = [int(m.group(1)) for m in re.finditer(r'Id="rId(\d+)"', text)]
|
||||
return (max(ids) + 1) if ids else 1
|
||||
|
||||
def add_relationship(rels_xml: Path, rid: str, rtype: str, target: str) -> None:
|
||||
text = rels_xml.read_text(encoding="utf-8")
|
||||
new_rel = f'<Relationship Type="{rtype}" Id="{rid}" Target="{target}" />'
|
||||
if new_rel in text:
|
||||
return
|
||||
text = text.replace("</Relationships>", new_rel + "</Relationships>")
|
||||
rels_xml.write_text(text, encoding="utf-8")
|
||||
|
||||
def add_content_type_override(ct_xml: Path, part_name: str, ct: str) -> None:
|
||||
text = ct_xml.read_text(encoding="utf-8")
|
||||
new_override = f'<Override PartName="{part_name}" ContentType="{ct}"/>'
|
||||
if part_name in text:
|
||||
return
|
||||
text = text.replace("</Types>", new_override + "</Types>")
|
||||
ct_xml.write_text(text, encoding="utf-8")
|
||||
|
||||
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>'
|
||||
f'<w:headerReference w:type="default" r:id="{header_default_rid}"/>'
|
||||
f'<w:headerReference w:type="first" r:id="{header_first_rid}"/>'
|
||||
f'<w:footerReference w:type="default" r:id="{footer_default_rid}"/>'
|
||||
f'<w:footerReference w:type="first" r:id="{footer_default_rid}"/>'
|
||||
f'<w:pgSz w:w="{PAGE_W}" w:h="{PAGE_H}"/>'
|
||||
f'<w:pgMar w:top="{MARGIN_TOP}" w:right="{MARGIN_RIGHT}"'
|
||||
f' w:bottom="{MARGIN_BOT}" w:left="{MARGIN_LEFT}"'
|
||||
f' w:header="{HEADER_POS}" w:footer="{FOOTER_POS}" w:gutter="0"/>'
|
||||
f'<w:titlePg/>'
|
||||
f'</w:sectPr>'
|
||||
)
|
||||
new_text, n = re.subn(
|
||||
r'<w:sectPr\s*/>|<w:sectPr>.*?</w:sectPr>',
|
||||
new_sectpr, text, flags=re.DOTALL,
|
||||
)
|
||||
if n == 0:
|
||||
new_text = text.replace("</w:body>", new_sectpr + "</w:body>")
|
||||
document_xml.write_text(new_text, encoding="utf-8")
|
||||
log(f" document.xml sectPr: pgSz/pgMar (A4, 2.2/2.5cm Raender), Header"
|
||||
f" default+first, Footer default+first auf gleicher rId, titlePg")
|
||||
|
||||
def add_header_footer(unpacked: Path) -> None:
|
||||
word_dir = unpacked / "word"
|
||||
rels_xml = word_dir / "_rels" / "document.xml.rels"
|
||||
ct_xml = unpacked / "[Content_Types].xml"
|
||||
doc_xml = word_dir / "document.xml"
|
||||
|
||||
write_xml_bytes(header_default_xml(), word_dir / "header1.xml")
|
||||
write_xml_bytes(header_first_blank_xml(), word_dir / "header2.xml")
|
||||
write_xml_bytes(footer_default_xml(), word_dir / "footer1.xml")
|
||||
log(" word/header1.xml (default), header2.xml (first blank),"
|
||||
" footer1.xml geschrieben")
|
||||
|
||||
next_id = next_free_rel_id(rels_xml)
|
||||
rid_h_def, rid_h_first, rid_f_def = (f"rId{next_id+i}" for i in range(3))
|
||||
add_relationship(rels_xml, rid_h_def, REL_HEADER, "header1.xml")
|
||||
add_relationship(rels_xml, rid_h_first, REL_HEADER, "header2.xml")
|
||||
add_relationship(rels_xml, rid_f_def, REL_FOOTER, "footer1.xml")
|
||||
log(f" Beziehungen: {rid_h_def}=header1, {rid_h_first}=header2,"
|
||||
f" {rid_f_def}=footer1")
|
||||
|
||||
add_content_type_override(ct_xml, "/word/header1.xml", CT_HEADER)
|
||||
add_content_type_override(ct_xml, "/word/header2.xml", CT_HEADER)
|
||||
add_content_type_override(ct_xml, "/word/footer1.xml", CT_FOOTER)
|
||||
log(" [Content_Types].xml: Override-Eintraege fuer header1/2 und footer1")
|
||||
|
||||
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)
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix="refdocx-") as tmp:
|
||||
tmp_dir = Path(tmp)
|
||||
default_docx = tmp_dir / "pandoc-default.docx"
|
||||
unpacked = tmp_dir / "unpacked"
|
||||
|
||||
fetch_pandoc_default(default_docx)
|
||||
unpacked.mkdir()
|
||||
unpack_docx(default_docx, unpacked)
|
||||
|
||||
theme_xml = unpacked / "word" / "theme" / "theme1.xml"
|
||||
styles_xml = unpacked / "word" / "styles.xml"
|
||||
|
||||
log("Anpassung: Theme major+minor auf Calibri")
|
||||
set_theme_fonts_to_calibri(theme_xml)
|
||||
log("Anpassung: Direkte Font-Referenzen in styles.xml -> Calibri")
|
||||
replace_direct_fonts_in_styles(styles_xml)
|
||||
log("Anpassung: Tabellen-Default ohne Rahmen")
|
||||
set_table_borders_none(styles_xml)
|
||||
log("Anpassung: Body-Schriftgroesse 11 pt (DocDefault)")
|
||||
set_default_body_size(styles_xml)
|
||||
log("Anpassung: Heading-Schriftgroessen 15/13/12 pt")
|
||||
set_heading_sizes(styles_xml)
|
||||
log("Anpassung: Header und Footer einbauen (B2)")
|
||||
add_header_footer(unpacked)
|
||||
|
||||
log("Repack als reference.docx")
|
||||
repack_docx(unpacked, OUTPUT_FILE)
|
||||
log(f" -> {OUTPUT_FILE} ({OUTPUT_FILE.stat().st_size} Bytes)")
|
||||
|
||||
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 10:41:37 =====
|
||||
===== Build gestartet: 2026-04-26 13:24:16 =====
|
||||
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
|
||||
@@ -9,5 +9,5 @@ Cmd: pandoc --from=markdown+smart --pdf-engine=lualatex --template=Q:\DesTEngS\P
|
||||
PDF OK: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.pdf (68.2 KB)
|
||||
--- 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 (20.1 KB)
|
||||
===== Build beendet: 2026-04-26 10:41:42, Exit-Code 0 =====
|
||||
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 =====
|
||||
|
||||
Binary file not shown.
@@ -43,3 +43,4 @@ Chronologisches Log aller Entscheidungen und Prozessereignisse.
|
||||
2026-04-25 22:35 | S06 | Tooling-Fix: checkpoint.ps1 robust gegen problematische Eingabedaten gemacht. Commit-Message wird jetzt via Temp-Datei und git commit -F uebergeben statt via -m mit String, damit doppelte Anfuehrungszeichen im Summary nicht mehr das Argument zerlegen (Ursache des fehlgeschlagenen Hotfix-Commits zuvor). Pipe-Zeichen im Summary werden vorab abgelehnt, da sie mit dem Changelog-Format Timestamp Pipe Session Pipe Summary kollidieren. Whitespace und Zeilenumbrueche im Summary werden zu einem einzelnen Leerzeichen normalisiert. Pre-flight-Checks ergaenzt: Existenz von .git, verwaiste .git/index.lock mit klarer Anleitung melden, changelog.md muss vor dem Lauf clean sein. Atomarer Rollback bei Fehler im Hauptablauf: changelog.md wird auf Original-Stand zurueckgeschrieben und Index-Stagung von changelog.md per git restore --staged oder git reset HEAD zurueckgesetzt, sodass der naechste Lauf nicht doppelt anhaengt. Cleanup-Robustheit: Temp-Message-Datei wird in finally geloescht, das Loeschen der Pending-Datei ist nicht mehr fatal sondern nur eine Warnung.
|
||||
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.
|
||||
|
||||
@@ -140,12 +140,45 @@ Die in S04 mit docx-js erstellte Version hatte strukturelle typographische Mäng
|
||||
|
||||
**Build-UX-Fix (S07):** `build/build.ps1` ergänzt um `Start-Sleep -Seconds 3` nach jedem fehlschlagenden Build-Schritt (Pflichtdatei-Check, PDF, DOCX). Bei Doppelklick auf `checkpoint.cmd`-artigen Aufruf schließt sich das PowerShell-Fenster sonst sofort und Fehlermeldungen sind nicht lesbar. Bei mehreren Fehlern in einem Lauf akkumulieren sich die Pausen — gewollt.
|
||||
|
||||
## Iteration B (S07) — `reference.docx` programmatisch bauen
|
||||
|
||||
**Ansatz:** Anstatt die `reference.docx` manuell in Word zu pflegen (nicht versionierbar, nicht reproduzierbar), wird sie durch ein Python-Skript `build/build-reference-docx.py` aus Pandocs Default-Reference erzeugt und gezielt angepasst. Nur Python-Stdlib (`zipfile`, `xml.etree.ElementTree`, `subprocess`, `re`) — keine pip-Abhängigkeit. Das Skript läuft unter Sandbox-Pandoc 2.9 und Thomas' Pandoc 3.x gleichermaßen, weil es die Pandoc-Default-Reference per `pandoc --print-default-data-file reference.docx` zur Laufzeit zieht. Manueller Aufruf vor jedem `build.ps1`, wenn Stile geändert wurden.
|
||||
|
||||
**B1 — Schriften und Tabellen:**
|
||||
|
||||
- Theme-Schriften `majorFont` und `minorFont` beide auf `Calibri` umgestellt (Pandoc 3.x setzt sie als Default auf `Aptos Display` und `Aptos`, Sandbox-Pandoc 2.9 auf `Calibri` und `Cambria`).
|
||||
- Defensive Maßnahme: alle direkten Schriftnamen-Referenzen in `styles.xml` (z.B. `<w:rFonts w:ascii="..." />`) auf Calibri umgestellt, ausgenommen Code-Schriften (Consolas, Courier, ...). In der Pandoc-3.x-Variante kommt das mit 0 Treffern aus, in zukünftigen Pandoc-Versionen mit direkten Heading-Schriftreferenzen würde es greifen.
|
||||
- Tabellen-Default-Stil `Table` bekommt explizite `<w:tblBorders>` mit `val="none"` auf allen Sides (`top`, `left`, `bottom`, `right`, `insideH`, `insideV`). Word-Editor zeigt die Default-„Tabellenbegrenzungen" weiterhin als Anzeige-Hilfe an (kein Druck-Rendering), Druckansicht und PDF-Export sind sauber rahmenlos.
|
||||
|
||||
**B1.5 — Schriftgrößen analog PDF:**
|
||||
|
||||
- DocDefault `<w:sz>` auf 22 (= 11 pt Body, analog `template.tex`).
|
||||
- Heading 1/2/3 explizit auf 30/26/24 (= 15/13/12 pt). Damit ist die Heading-Hierarchie visuell ähnlich zum PDF, ohne den Word-Default-Sprung von 20 pt nach 12 pt.
|
||||
- Effekt: DOCX schrumpft von 10 auf 9 Seiten (im PDF sind es 7).
|
||||
|
||||
**B2 — Header, Footer, Page-Setup:**
|
||||
|
||||
- `word/header1.xml` (Default ab Seite 2): links „Dr.-Ing. Thomas Langer", rechts „Lebenslauf" (Tab-Stop am rechten Textrand).
|
||||
- `word/header2.xml` (erste Seite): leerer `<w:p/>` über `<w:titlePg/>` aktiviert.
|
||||
- `word/footer1.xml`: rechtsbündig „Seite n / m" mit Word-Feldern `PAGE` und `NUMPAGES`. Wird über zwei `footerReference`-Einträge (`type="default"` und `type="first"`) auf alle Seiten inkl. Seite 1 angewendet — ohne den `type="first"`-Eintrag würde `titlePg` Seite 1 ohne Footer lassen.
|
||||
- Page-Setup explizit in `<w:sectPr>`: A4 (`pgSz w:w="11906" w:h="16838"`), Ränder 2.2 cm oben/unten, 2.5 cm links/rechts (analog PDF). Damit ist der Tab-Stop an `9072 dxa` (= 16 cm Textbreite) deterministisch unabhängig von Word-Locale-Defaults; ohne explizites Page-Setup waren die Tab-Stops vorher etwa 5 mm zu weit links.
|
||||
- Beziehungen werden in `word/_rels/document.xml.rels` mit dynamisch ermittelter nächster freier `rId` registriert; Content-Types-Overrides in `[Content_Types].xml` ergänzt; `<w:sectPr>` in `word/document.xml` regex-basiert ersetzt (idempotent gegenüber Pandoc-Defaults `<w:sectPr/>` und längeren Varianten). Pandoc übernimmt die letzte sectPr aus der reference.docx ins generierte DOCX — End-to-End-Test in der Sandbox bestätigt: alle Header/Footer-Refs, pgMar und titlePg sind im finalen DOCX vorhanden.
|
||||
|
||||
**Visuelle Bestätigung im Word (S07):**
|
||||
|
||||
- Body: Calibri 11 pt; Headings 1/2/3: Calibri 15/13/12 pt.
|
||||
- Ausbildungs-Tabelle in Druckansicht und PDF-Export rahmenlos.
|
||||
- Seite 1 ohne Header, mit Footer.
|
||||
- Seite 2 ff. mit Header (Name links, „Lebenslauf" rechts) und Footer (Seite n / m).
|
||||
- Tab-Stops „Lebenslauf" und Seitenzahl bündig am rechten Textrand.
|
||||
|
||||
## Nächste Schritte
|
||||
|
||||
1. **Iteration B — `templates/reference.docx` in Word polieren:** Header/Footer setzen, Schriften auf Calibri vereinheitlichen, Listen-Schutz „Keep with next" und Widow-Control via Word-Stile, Stile `DefinitionTerm` und `Definition` für 2-Spalten-Verhalten konfigurieren (z.B. fester linker Einzug auf `Definition`, hängende Einrückung).
|
||||
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).
|
||||
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).
|
||||
|
||||
## Artefakte
|
||||
|
||||
@@ -153,10 +186,11 @@ Die in S04 mit docx-js erstellte Version hatte strukturelle typographische Mäng
|
||||
|
||||
- `artefakte/01-lebenslauf/source/cv.md` — **Aktive Quelldatei** (aufbauend auf V10, Draft-Marker entfernt).
|
||||
- `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 (erstes Grundgerüst).
|
||||
- `artefakte/01-lebenslauf/templates/reference.docx` — Pandoc-Default-Reference-Doc als Ausgangsbasis (Styles noch anzupassen).
|
||||
- `artefakte/01-lebenslauf/build/build.ps1` — PowerShell-Build-Skript (PDF + DOCX).
|
||||
- `artefakte/01-lebenslauf/output/` — erzeugte Ausgaben plus `build.log` (leer bis zum ersten erfolgreichen Build).
|
||||
- `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/output/` — erzeugte Ausgaben plus `build.log`.
|
||||
|
||||
### Historische Entwürfe (unter `artefakte/01-lebenslauf/entwuerfe/`)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user