S09: Teilgebiet 01 Iteration C Foto-Einbindung umgesetzt. Header als 2-Spalten-Grid-Table in cv.md mit Strich-Verhaeltnis 112:60 (= 65,1%/34,9% Spaltenbreite, ca. 10,15/5,43 cm bei 16 cm Textbreite). Foto rechts oben, 4,06x4,06 cm, beide Dimensionen explizit im Markdown um Pandocs Default-Wrapper keepaspectratio mit height=textheight zu vermeiden, der die Layout-Box auf 24cm Hoehe streckt und die Tabellen-Zeile zerschiesst. DOCX-Header-Spacing per neuer 4. Modifikation in build/post-process-docx.py (process_header_table): findet erste Tabelle, setzt Heading1-spacing-before=0 und Foto-Paragraph spacing-before=100 (=5pt) plus jc=right. PDF-Layout via neuem Pandoc-Lua-Filter build/header-image-wrap.lua: wrappt das Header-Foto im LaTeX-Output mit hfill+raisebox(-height)[0pt][0pt]{...}, hfill schiebt rechtsbuendig in raggedright-p-Spalte, raisebox setzt Bild-Top auf Cell-Top und reportet null Hoehe an die Tabellen-Zeile. Filter prueft FORMAT=latex und Image-Pfad enthaelt foto, DOCX bleibt unberuehrt. Lua-Filter-Erste-Version (Image durch RawInline ersetzt mit gebackenem Pfad) hat Pandocs Image-Resource-Resolution gebrochen und LuaLaTeX scheiterte mit File foto.jpg not found, Fix: Filter gibt Lua-Liste zurueck mit Original-img-Element zwischen RawInline-Wrappern. Template-Hotfixes fuer PDF: renewcommand-nolinkurl-zu-Plaintext (verhindert at-xverbatim-Bruch in longtable-Minipage durch URL-Display-Text-Verbatim-Mode), titlespacing-section-before=0pt fuer H1-Top-Alignment. cv.md: Pipe-Alignment in Grid Table programmatisch via Python ljust und Pipe-Position-Eindeutigkeitscheck (Pandoc 3.x ist beim Grid-Table-Pipe-Alignment streng, Sandbox-Pandoc 2.9 ist toleranter und damit irrefuehrend). build.ps1 erweitert um lua-filter-Argument in PDF und DOCX, plus Read-Host-Wait-on-Error entfernt (blockiert AI-Agents und CI), durch Start-Sleep 3s am Ende ersetzt. header-image-wrap.lua als Pflichtdatei in Test-Path-Check aufgenommen. Vier weitere Edit-Tool-Truncation-Vorfaelle in S09 (cv.md, template.tex zweimal, build.ps1), Lehre verschaerft Edit-Tool fuer jede nicht-triviale Modifikation auf NTFS-Mount-Dateien meiden. Sandbox-NTFS-Stale-Read auf DOCX-Output (DOCX-Datei als not a zip file, Workaround DOCX im Sandbox neu generieren). Sandbox-NTFS-Mount kann auch Datei-Schreiben mit open(w) verweigern obwohl os.path.exists True liefert, Workaround tmp-Datei plus os.rename. Build und visuelle Bestaetigung durch Thomas erfolgt fuer DOCX und PDF. teilgebiete/01-lebenslauf.md um Iteration-C-Block ergaenzt und Naechste-Schritte-Liste auf S10-Plan umgestellt (1 DOCX-Heading-Farben pruefen, 2 Doublecheck der generierten Texte mindestens elektrischer-Gehaeuse ist sinnverkehrt, 3 Buzzword-Kompetenzen brainstormen mindestens Umgang mit quantisierten LLMs fehlt, 4 PDF-Spacings H1/H2/Kontaktdaten und hellgraue Trennlinien korrigieren, 5 Hyphenation-Feintuning, 6 Teilgebiet abschliessen). agent-prompt.md Aktueller-Stand-Block fuer S10 fortgeschrieben.

This commit is contained in:
tlg
2026-04-27 18:51:18 +02:00
parent 93bf43301e
commit b26cfd0ab3
13 changed files with 3438 additions and 764 deletions

View File

@@ -1,2 +1,2 @@
S09
Teilgebiet 01 Iteration Links umgesetzt: alle URLs in cv.md auf explizite Markdown-Links migriert, damit Pandoc echte w:hyperlink-Elemente in die DOCX emittiert (vorher Plain-Text-only, Word zeigte sie nicht als Links und kein Hover funktionierte; im PDF wurden sie ueber Words eigene URL-Erkennung beim PDF-Export trotzdem klickbar, was die Inkonsistenz erklaerte). E-Mail als Pandoc-Autolink-Form mit spitzen Klammern (mailto), Telefon als tel:-Link mit Display-Spaces und URL-ohne-Spaces gem RFC 3966, Web-Links als doppelte-Bracket-Markdown-Syntax mit sichtbaren aeusseren eckigen Klammern als einheitlicher Anzeigetext-Stil. Display-Texte: Link zum Profil fuer LinkedIn und Freelance.de, destengs.de fuer Website (bewusster Wechsel von .com auf .de stimmiger zur deutschen Primaersprache), Dissertation fuer Promotion, Link zum Zertifikat funktioniert nur im Browser fuer TUEV-Zertifikat. TUEV-Link-Problem in Word diagnostiziert: certif-id.com liegt hinter Cloudflare-Bot-Schutz und blockiert Words urlmon-Pre-Flight-Anfrage mit 403; Direkt-Klick aus Word schlaegt mit Die angeforderten Informationen koennen nicht heruntergeladen werden fehl trotz funktionierender URL im Browser. Optionen A (destengs.de-Redirect), B (LinkedIn-Safety-Redirect), C (kein Link) abgewogen und verworfen, Option D gewaehlt: direkter TUEV-Link beibehalten mit erklaerendem Display-Text der den Empfaenger ueber die Word-Einschraenkung informiert. Thomas hat zusaetzlich eine kuerzere TUEV-Direkt-URL besorgt (perscert-tuv.certif-id.com/expert/public/share/7MR0WDzG106JDCqV_RW7) statt der urspruenglichen 130-Zeichen-Hash-URL. Zwischenfall: zweite Edit-Tool-Truncation in dieser Session auf cv.md beim Edit der TUEV- und Promotion-Zeile, die Schluss-Zeile Dissertation fuenf Veroeffentlichungen ein Patent eine Erfindungsmeldung wurde mitten im Wort abgeschnitten. Reparatur identisch zum S08-Pattern: git HEAD-Version als Input, alle 7 Link-Replacements in einem Python-Script atomar via os.replace zurueckgeschrieben mit count==1-Check pro Replacement. Lehre fuer kommende Sessions: Edit-Tool fuer cv.md generell nicht mehr verwenden, Python-aus-git-Pattern bevorzugen. Build und visuelle Bestaetigung durch Thomas erfolgt fuer DOCX und PDF. teilgebiete/01-lebenslauf.md um Iteration-Links-Block ergaenzt.
Teilgebiet 01 Iteration C Foto-Einbindung umgesetzt. Header als 2-Spalten-Grid-Table in cv.md mit Strich-Verhaeltnis 112:60 (= 65,1%/34,9% Spaltenbreite, ca. 10,15/5,43 cm bei 16 cm Textbreite). Foto rechts oben, 4,06x4,06 cm, beide Dimensionen explizit im Markdown um Pandocs Default-Wrapper keepaspectratio mit height=textheight zu vermeiden, der die Layout-Box auf 24cm Hoehe streckt und die Tabellen-Zeile zerschiesst. DOCX-Header-Spacing per neuer 4. Modifikation in build/post-process-docx.py (process_header_table): findet erste Tabelle, setzt Heading1-spacing-before=0 und Foto-Paragraph spacing-before=100 (=5pt) plus jc=right. PDF-Layout via neuem Pandoc-Lua-Filter build/header-image-wrap.lua: wrappt das Header-Foto im LaTeX-Output mit hfill+raisebox(-height)[0pt][0pt]{...}, hfill schiebt rechtsbuendig in raggedright-p-Spalte, raisebox setzt Bild-Top auf Cell-Top und reportet null Hoehe an die Tabellen-Zeile. Filter prueft FORMAT=latex und Image-Pfad enthaelt foto, DOCX bleibt unberuehrt. Lua-Filter-Erste-Version (Image durch RawInline ersetzt mit gebackenem Pfad) hat Pandocs Image-Resource-Resolution gebrochen und LuaLaTeX scheiterte mit File foto.jpg not found, Fix: Filter gibt Lua-Liste zurueck mit Original-img-Element zwischen RawInline-Wrappern. Template-Hotfixes fuer PDF: renewcommand-nolinkurl-zu-Plaintext (verhindert at-xverbatim-Bruch in longtable-Minipage durch URL-Display-Text-Verbatim-Mode), titlespacing-section-before=0pt fuer H1-Top-Alignment. cv.md: Pipe-Alignment in Grid Table programmatisch via Python ljust und Pipe-Position-Eindeutigkeitscheck (Pandoc 3.x ist beim Grid-Table-Pipe-Alignment streng, Sandbox-Pandoc 2.9 ist toleranter und damit irrefuehrend). build.ps1 erweitert um lua-filter-Argument in PDF und DOCX, plus Read-Host-Wait-on-Error entfernt (blockiert AI-Agents und CI), durch Start-Sleep 3s am Ende ersetzt. header-image-wrap.lua als Pflichtdatei in Test-Path-Check aufgenommen. Vier weitere Edit-Tool-Truncation-Vorfaelle in S09 (cv.md, template.tex zweimal, build.ps1), Lehre verschaerft Edit-Tool fuer jede nicht-triviale Modifikation auf NTFS-Mount-Dateien meiden. Sandbox-NTFS-Stale-Read auf DOCX-Output (DOCX-Datei als not a zip file, Workaround DOCX im Sandbox neu generieren). Sandbox-NTFS-Mount kann auch Datei-Schreiben mit open(w) verweigern obwohl os.path.exists True liefert, Workaround tmp-Datei plus os.rename. Build und visuelle Bestaetigung durch Thomas erfolgt fuer DOCX und PDF. teilgebiete/01-lebenslauf.md um Iteration-C-Block ergaenzt und Naechste-Schritte-Liste auf S10-Plan umgestellt (1 DOCX-Heading-Farben pruefen, 2 Doublecheck der generierten Texte mindestens elektrischer-Gehaeuse ist sinnverkehrt, 3 Buzzword-Kompetenzen brainstormen mindestens Umgang mit quantisierten LLMs fehlt, 4 PDF-Spacings H1/H2/Kontaktdaten und hellgraue Trennlinien korrigieren, 5 Hyphenation-Feintuning, 6 Teilgebiet abschliessen). agent-prompt.md Aktueller-Stand-Block fuer S10 fortgeschrieben.

View File

@@ -85,34 +85,43 @@ Setze zwischen sinnvollen Zwischenständen Checkpoints (z.B. nach "Marketing.md
## Aktueller Stand / Nächste Aufgabe
**Letzte Session:** S08 (2026-04-26)
**Letzte Session:** S09 (2026-04-27)
**Was wurde gemacht:** Iteration B fuer das DOCX abgeschlossen, plus zwei zusätzliche Anpassungen:
- **B4.1 — Heading-Farben in destengsblue** (`build/build-reference-docx.py` Funktion `set_heading_colors`, color val=`0B5394`, themeColor accent1 entfernt). H1, H2, H3 alle in destengsblue.
- **B4.2 — Heading-Trennlinien direkt am Stil verworfen.** Bottom-Border + Indent-Trick (`hanging`) erzeugte in Word linksbündige statt zentrierte Linien. Wichtige Lehre: Words `right`-Indent begrenzt sowohl Text als auch Border — eine Border *schmaler als der Heading-Text* ist über den Stil selbst nicht abbildbar.
- **B4.3 — Markdown-HRs aus cv.md entfernt** (21 alleinstehende `---`-Zeilen). Quelle der wahrgenommenen „Doppellinien": Pandoc-DOCX-Konvertierung von `---` zu VML-`<v:rect ... o:hr="t"/>` (Embossed-Look). Tabellen-Strich-Zeilen blieben unangetastet. **Vorfall:** Sandbox-NTFS-Stale-Read der cv.md (20043 statt 20201 Bytes) hätte fast die Live-Datei truncated, sofortige Wiederherstellung aus `git show HEAD` + HR-Removal erneut mit der git-Version als Input.
- **B4.4 — H2-Trennlinien via Post-Processing** (`build/post-process-docx.py` um Funktion erweitert): nach jedem H2-Absatz ein leerer Trenn-Absatz mit linksbündiger Bottom-Border, schwarz (`000000`), 8,6 cm Linienlänge (right-Indent 4196 dxa), 1,25 pt dick (sz=10).
- **B5 — Trainings als Tabelle** (cv.md): Trainings-Bullet-Liste durch Pandoc-Multiline-Tabelle ersetzt (analog Ausbildung), Inhalte normal nicht fett.
- **B6 — Bullet-Einzüge verkleinert** (`build/post-process-docx.py` modifiziert `numbering.xml` direkt; Pandoc IGNORIERT die `numbering.xml`-Werte der reference.docx, daher MUSS das im Post-Processing passieren). E1: Einzug 0,25 cm + Sondereinzug 0,35 cm (left=340/hanging=198 dxa); E2: Einzug 0,80 cm + Sondereinzug 0,40 cm (left=681/hanging=227 dxa). Word-Konvention: „Einzug links" = `(left - hanging)` = Bullet-Position; „Sondereinzug Hängend" = `hanging`.
**Was wurde in S09 gemacht:**
**Nächste Aufgabe:** Teilgebiet 01 — verbleibende Iterationen:
1. **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).
2. **D — Hyphenation-Feintuning für PDF** — kurze Wortteile am Zeilenanfang mit höherer Penalty oder `\hyphenation`-Ausnahmen reduzieren.
S09 — **Iteration „Links"** in cv.md: alle nackten URLs auf explizite Markdown-Link-Syntax migriert, damit Pandoc echte Hyperlink-Elemente in DOCX (`<w:hyperlink>`) und PDF (`\href`) emittiert. E-Mail als Pandoc-Autolink-Form `<email>`, Telefon als `[+49 ...](tel:+49...)`-Link, Web-Links als `[[text]](url)` mit sichtbaren äußeren eckigen Klammern. Display-Texte „Link zum Profil" für LinkedIn/Freelance.de, „destengs.de" für Website (`.com` → `.de`), „Dissertation" für Promotion, „Link zum Zertifikat (funktioniert nur im Browser)" für TÜV. TÜV-Link diagnostiziert: certif-id.com hinter Cloudflare-Bot-Schutz, Word-urlmon-Pre-Flight-Anfrage scheitert mit 403, Direkt-Klick aus Word funktioniert nicht trotz funktionierender URL im Browser. Optionen A (destengs.de-Redirect), B (LinkedIn-Safety-Redirect), C (kein Link) abgewogen, D gewählt: direkter Link mit erklärendem Display-Text. Detaillierte Doku siehe `teilgebiete/01-lebenslauf.md` Block „Iteration Links (S09)".
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").
S09 — **Iteration C: Foto-Einbindung via Grid Table.**
**Offene Punkte (unverändert seit S04):** Zuschnitt und Festpreise der KI-Produkte (marketing.md Abschnitt 2), KMU-Direkthonorarsatz festlegen (marketing.md Abschnitt 2), Vergütungsmodell-Wahl bei erstem konkreten Fall (Notiz in marketing.md Abschnitt 2).
- Header-Layout als 2-Spalten-Grid-Table in cv.md (Name+Kontaktdaten links, Foto rechts), Spalten-Verhältnis 65:35 (LEFT_W=112, RIGHT_W=60 Striche).
- Pandoc 3.x ist beim Pipe-Alignment in Grid Tables strikt (Pandoc 2.9 ist toleranter und damit irreführend für Sandbox-Tests). Grid Table programmatisch in Python aufbauen mit `ljust(LEFT_W)` und Pipe-Position-Eindeutigkeitscheck.
- DOCX-Header-Spacing per neuer 4. Modifikation in `build/post-process-docx.py` (`process_header_table`): H1-spacing-before = 0, Foto-Paragraph-spacing-before = 5 pt + jc=right.
- Foto-Größe 4,06 × 4,06 cm, beide Dimensionen explizit im Markdown — verhindert Pandoc-Default `keepaspectratio,height=\textheight`, der die Image-Layout-Box auf 24 cm Höhe streckt und das Tabellen-Layout zerschießt.
- PDF-Layout via Pandoc-Lua-Filter `build/header-image-wrap.lua`: wrappt das Header-Foto im LaTeX-Output mit `\hfill\raisebox{-\height}[0pt][0pt]{...}`. `\hfill` schiebt rechtsbündig in der `\raggedright`-p-Spalte, `\raisebox` setzt Bild-Top auf Cell-Top und reportet null Höhe an die Tabellen-Zeile. Filter prüft `FORMAT="latex"` und Image-Pfad enthält „foto"; DOCX bleibt unberührt.
- Lua-Filter-Erste-Version (Image durch RawInline ersetzt) hat Pandocs Image-Resource-Resolution gebrochen → `File 'foto.jpg' not found`. Fix: Filter gibt Lua-Liste zurück, in der das Original-`img`-Element zwischen RawInline-Wrappern erhalten bleibt.
- Template-Hotfixes für PDF: `\renewcommand{\nolinkurl}[1]{#1}` (verhindert `\@xverbatim`-Bruch in `longtable`-Minipage durch URL-Display-Text-Verbatim), `\titlespacing*{\section}{0pt}{0pt}{0.5em}` (H1-Top-Alignment).
- `build/build.ps1`: `--lua-filter=$luaFilter` zu PDF- und DOCX-Pandoc-Calls hinzugefügt; `Read-Host`-Wait-on-Error entfernt (blockiert AI-Agents/CI), durch `Start-Sleep -Seconds 3` am Ende bei Fehler ersetzt.
**Vier weitere Edit-Tool-Truncation-Vorfälle in S09** (cv.md, template.tex zweimal, build.ps1). Lehre verschärft: Edit-Tool für jede nicht-triviale Modifikation auf NTFS-Mount-Dateien meiden, generell Python-aus-git-HEAD- oder Python-aus-Disk-Pattern bevorzugen.
**Nächste Aufgabe (S10) in dieser Reihenfolge (von Thomas vorgegeben):**
1. **DOCX: Blau ist nicht DesTEngS-Blau.** Heading-Farben prüfen — Soll: `#0B5394`. `build/build-reference-docx.py` Funktion `set_heading_colors` setzt zwar `<w:color w:val="0B5394"/>`, aber Word rendert ggf. trotzdem den Theme-Aptos-Blau, weil das Heading-Style einen `themeColor`-Verweis behält oder weil das Theme dominiert. Prüfen, ob das `themeColor`-Attribut wirklich entfernt ist und ob die Farbe direkt am Run statt am Stil gesetzt werden muss.
2. **Doublecheck der neu generierten Texte in cv.md.** Mindestens „elektrischer Gehäuse" ist sinnverkehrt (vermutlich aus V9/V10-KI-Iterationen). cv.md komplett auf Sinn- und Sprachfehler durchgehen, vermutlich Konkordanz-/Stil-Fehler an mehreren Stellen.
3. **Buzzword-Kompetenzen-Brainstorm.** Kenntnisse-Abschnitt erweitern. Mindestens „Umgang mit quantisierten LLMs" ergänzen. Weitere KI-Begriffe für Agentur-Keyword-Matching identifizieren (Quantization, GGUF, GPTQ, AWQ, Inference-Optimierung, vLLM, llama.cpp, Ollama-Deployment etc.).
4. **PDF: Abstände H1 / H2 „Kontaktdaten" / Kontaktdaten-Bullet-Liste stimmen nicht** (Folge der C4c-Spacing-Änderung). Plus: hellgraue Trennlinien (rulegray = `#BFBFBF`) sind inakzeptabel — Farbe oder Linienführung überdenken (Trennlinien stehen nach H1 und H2 im titlesec-Format-Block).
**Hinweise für die nächste Session:**
- **Word-Indent-Konvention (S08-Lehre):** Word zeigt im Absatz-Dialog „Einzug links" als `(left - hanging)`, NICHT als `left`. „Sondereinzug Hängend" ist `hanging`. Daher gilt: `left = (gewünschter Einzug + gewünschter Hanging-Indent)` in dxa.
- **Word-Border-Limit (S08-Lehre):** Words paragraph-Border folgt den Indents; eine Border *schmaler als der Heading-Text* geht NICHT über `<w:ind right="...">` auf dem Heading-Stil, weil der right-Indent auch den Text begrenzt. Lösung wenn nötig: separater Trenn-Absatz nach dem Heading via Post-Processing.
- **Pandoc IGNORIERT die `numbering.xml` der reference.docx (S08-Lehre).** Wer Bullet-Einzüge ändern will, muss das im Post-Processing tun, nicht in `build-reference-docx.py`.
- **Pandoc generiert für Markdown-`---` im DOCX VML-Horizontal-Lines (`<v:rect o:hr="t"/>`)** — sehen aus wie Doppellinien (Embossed). Im PDF rendert Pandoc dieselbe Quelle als saubere zentrierte Halblinie. Wenn man saubere DOCX-Trennung braucht: HRs in cv.md entfernen oder Pandoc-Lua-Filter für DOCX-Build.
- **Sandbox-NTFS-Stale-Reads sind häufiger und kritischer als gedacht (S08-Lehre).** Sandbox kann auch git-Status, Datei-Existenz und Datei-Inhalte verzerrt sehen, ohne dass die echte Datei betroffen ist. **Bei jedem Sandbox-Schreiben auf eine grössere NTFS-Mount-Datei: ERST git-Version verifizieren und als Input verwenden, NICHT blind dem Sandbox-Read trauen.** Bei seltsamen git-status-Outputs aus der Sandbox: erst Thomas auf PowerShell `git status` ausführen lassen.
- **Edit-Tool truncated regelmäßig** (mehrfach in S07 und S08 erlebt — Skript-Dateien, `.checkpoint-pending.txt`, `agent-prompt.md`, `teilgebiete/01-lebenslauf.md`). Faustregel: Bei jedem Edit auf NTFS-Mount-Datei nach `Edit` mit `wc -c` und `tail -c 80` verifizieren. Bei längeren Edits oder wenn das Risiko hoch ist: lieber per Heredoc/Python aus der git-Version neu schreiben.
- **Sandbox-Pandoc ist 2.9.x, Thomas' System läuft Pandoc 3.x.** Output-Unterschiede zwischen den Versionen können Build-Probleme verursachen. Sandbox-Pandoc emittiert weder die calc-basierten Spaltenbreiten (`p{... * \real{...}}`) noch `\def\LTcaptype{none}` — beides Pandoc-3.x-Eigenheiten. Bei Fehlern, die nur auf Thomas' System auftreten, **synthetisch das Pandoc-3.x-Output-Fragment in einer Mini-Tex-Datei nachbauen** und damit gegen das Template kompilieren.
- **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.
- **DOCX-Pipeline ist jetzt 3-stufig mit drei Post-Processing-Modifikationen:** (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 und macht: 3-3-Listen-Bullet-Regel, H2-Trennlinien-Einfügung, Bullet-Einzug-Anpassung in `numbering.xml`. Wer das Bullet-Verhalten oder Trennlinien ändern will, fasst das Post-Processing-Skript an.
- **Pandoc 3.x ist beim Grid-Table-Pipe-Alignment strikt (S09-Lehre).** Grid Tables programmatisch bauen mit `ljust()`-Padding und Pipe-Position-Verifikation, nicht von Hand. Sandbox-Pandoc 2.9 ist toleranter und kann beim Testen den Eindruck erwecken, dass etwas funktioniert, was bei Thomas (Pandoc 3.x) nicht funktioniert.
- **Pandoc 3.x emittiert in p-Spalten mit nur einem Image-Element keine `\begin{minipage}`-Wrapper (S09-Lehre).** Das Image landet direkt in der `p{calc...}`-Spalte und unterliegt der `\parbox[t]`-Baseline-Logik (Image-Bottom = Baseline = Cell-Top, Image ragt nach oben heraus). Lösung: Lua-Filter mit `\raisebox{-\height}[0pt][0pt]{...}`.
- **Pandoc emittiert für href mit URL-Display-Text `\nolinkurl{}` (Verbatim-Mode, S09-Lehre).** In `longtable`-Minipage bricht das mit `! Paragraph ended before \@xverbatim was complete.` ab. Fix mit `\renewcommand{\nolinkurl}[1]{#1}` ist drin.
- **Pandoc-3.x-`keepaspectratio,height=\textheight`-Default ist tückisch (S09-Lehre).** Layout-Box wird auf textheight gestreckt selbst wenn das Bild visuell klein ist — beide Dimensionen explizit setzen, wenn das Bild in einer Tabelle ist.
- **Lua-Filter müssen das `img`-Element im AST belassen, sonst kein Resource-Path-Resolution (S09-Lehre).** Lua-Liste-Return mit `{RawInline, img, RawInline}` statt einzelnes RawInline mit gebackenem Pfad.
- **Edit-Tool-Truncation auf NTFS-Mount-Dateien ist nach S07/S08/S09 ein systematisches Problem** — durchgehend Python-aus-git-HEAD- oder Python-aus-Disk-Pattern verwenden (atomar via `os.replace`). Sandbox kann zudem stale-reads liefern (z.B. DOCX als „not a zip file"), bei Verdacht git-Version verifizieren oder Build neu erzeugen.
- **`build.ps1` ist jetzt nicht-blockierend (S09-Lehre).** AI-Agents und CI-Systeme können das Skript aufrufen ohne Read-Host-Hänger. Bei Fehler 3 s Pause am Ende plus 3 s pro fehlgeschlagenem Schritt.
- **Sandbox-Index-Lock-Issue:** Sandbox kann nichts an `.git/` schreiben (NTFS-Permission-Issue), Index-Lock-Reste muss Thomas auf PowerShell beseitigen. Bei dauerhaftem Lock-File-Vorhandensein zeigt `git status` eine Warning, aber lesende git-Operations (show, log) funktionieren weiter.
- **DOCX-Pipeline ist 3-stufig mit vier Post-Processing-Modifikationen:** (1) `build/build-reference-docx.py` baut die `reference.docx` (manuell aufrufen), (2) `build/build.ps1` baut PDF und DOCX, (3) `build/post-process-docx.py` macht: 3-3-Listen-Bullet-Regel, H2-Trennlinien, Bullet-Einzüge in `numbering.xml`, Header-Tabellen-H1-Spacing-und-Foto-Spacing.
- **Kein Hyphenation-Feintuning (Iteration D) in S09.** Steht in S10-Liste hinter den vier Korrektur-Punkten.
**Offene Punkte (unverändert seit S04):** Zuschnitt und Festpreise der KI-Produkte (marketing.md Abschnitt 2), KMU-Direkthonorarsatz festlegen (marketing.md Abschnitt 2), Vergütungsmodell-Wahl bei erstem konkreten Fall (Notiz in marketing.md Abschnitt 2).

View File

@@ -1,170 +1,190 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Baut den DesTEngS-Lebenslauf aus source/cv.md in PDF und DOCX.
.DESCRIPTION
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 = 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'
$PSNativeCommandUseErrorActionPreference = $false
# --- Pfade (alle relativ zum Speicherort dieses Skripts) ---------------------
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$baseDir = Split-Path -Parent $scriptDir
$sourceFile = Join-Path $baseDir 'source\cv.md'
$sourceDir = Join-Path $baseDir 'source'
$templateTex = Join-Path $baseDir 'templates\template.tex'
$referenceDoc = Join-Path $baseDir 'templates\reference.docx'
$outputDir = Join-Path $baseDir 'output'
$outputPdf = Join-Path $outputDir 'Lebenslauf_Dr-Ing_Thomas_Langer.pdf'
$outputDocx = Join-Path $outputDir 'Lebenslauf_Dr-Ing_Thomas_Langer.docx'
$logFile = Join-Path $outputDir 'build.log'
# --- Output-Ordner sicherstellen ---------------------------------------------
if (-not (Test-Path $outputDir)) {
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
}
# --- Log initialisieren (UTF-8 ohne BOM) -------------------------------------
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
function Write-Log {
param([string]$Line)
[System.IO.File]::AppendAllText($logFile, $Line + [Environment]::NewLine, $utf8NoBom)
}
[System.IO.File]::WriteAllText($logFile, '', $utf8NoBom)
$startTs = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
Write-Host ""
Write-Host "===== Build gestartet: $startTs =====" -ForegroundColor Cyan
Write-Log "===== Build gestartet: $startTs ====="
Write-Log "Source: $sourceFile"
Write-Log "Template-TEX: $templateTex"
Write-Log "Reference: $referenceDoc"
Write-Log "Output-Dir: $outputDir"
Write-Log ''
$overallExit = 0
# --- Pflichtdateien pruefen --------------------------------------------------
foreach ($f in @($sourceFile, $templateTex, $referenceDoc)) {
if (-not (Test-Path $f)) {
Write-Host "FEHLER: Pflichtdatei fehlt: $f" -ForegroundColor Red
Write-Log "FEHLER: Pflichtdatei fehlt: $f"
$overallExit = 1
}
}
if ($overallExit -ne 0) {
Write-Host "===== Abbruch: Pflichtdateien fehlen =====" -ForegroundColor Red
Write-Log "===== Abbruch: Pflichtdateien fehlen ====="
Start-Sleep -Seconds 3
exit $overallExit
}
# --- PDF-Build ---------------------------------------------------------------
Write-Host ""
Write-Host "[1/3] PDF wird erzeugt (Pandoc + LuaLaTeX) ..." -ForegroundColor Yellow
Write-Log "--- Pandoc -> PDF (LuaLaTeX) ---"
$pdfArgs = @(
'--from=markdown+smart',
'--pdf-engine=lualatex',
"--template=$templateTex",
"--resource-path=$sourceDir",
"--output=$outputPdf",
$sourceFile
)
Write-Log ('Cmd: pandoc ' + ($pdfArgs -join ' '))
$pdfOutput = & pandoc @pdfArgs 2>&1
$pdfExit = $LASTEXITCODE
$pdfOutput | ForEach-Object { Write-Log ([string]$_) }
if ($pdfExit -eq 0 -and (Test-Path $outputPdf)) {
$sizeKB = [math]::Round((Get-Item $outputPdf).Length / 1KB, 1)
Write-Host " PDF OK ($sizeKB KB): $outputPdf" -ForegroundColor Green
Write-Log "PDF OK: $outputPdf ($sizeKB KB)"
} else {
Write-Host " PDF FEHLER (Exit $pdfExit) - Details siehe build.log" -ForegroundColor Red
Write-Log "PDF FEHLER (Exit $pdfExit)"
$overallExit = 1
Start-Sleep -Seconds 3
}
# --- DOCX-Build --------------------------------------------------------------
Write-Host ""
Write-Host "[2/3] DOCX wird erzeugt (Pandoc) ..." -ForegroundColor Yellow
Write-Log "--- Pandoc -> DOCX ---"
$docxArgs = @(
'--from=markdown+smart',
"--reference-doc=$referenceDoc",
"--resource-path=$sourceDir",
"--output=$outputDocx",
$sourceFile
)
Write-Log ('Cmd: pandoc ' + ($docxArgs -join ' '))
$docxOutput = & pandoc @docxArgs 2>&1
$docxExit = $LASTEXITCODE
$docxOutput | ForEach-Object { Write-Log ([string]$_) }
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
Start-Sleep -Seconds 3
}
$endTs = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
Write-Host ""
if ($overallExit -eq 0) {
Write-Host "===== Build beendet: $endTs, Exit-Code $overallExit (OK) =====" -ForegroundColor Cyan
} else {
Write-Host "===== Build beendet: $endTs, Exit-Code $overallExit (FEHLER) =====" -ForegroundColor Red
}
Write-Log "===== Build beendet: $endTs, Exit-Code $overallExit ====="
exit $overallExit
#Requires -Version 5.1
<#
.SYNOPSIS
Baut den DesTEngS-Lebenslauf aus source/cv.md in PDF und DOCX.
.DESCRIPTION
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 = 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'
$PSNativeCommandUseErrorActionPreference = $false
# --- Pfade (alle relativ zum Speicherort dieses Skripts) ---------------------
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$baseDir = Split-Path -Parent $scriptDir
$sourceFile = Join-Path $baseDir 'source\cv.md'
$sourceDir = Join-Path $baseDir 'source'
$templateTex = Join-Path $baseDir 'templates\template.tex'
$referenceDoc = Join-Path $baseDir 'templates\reference.docx'
$outputDir = Join-Path $baseDir 'output'
$outputPdf = Join-Path $outputDir 'Lebenslauf_Dr-Ing_Thomas_Langer.pdf'
$outputDocx = Join-Path $outputDir 'Lebenslauf_Dr-Ing_Thomas_Langer.docx'
$logFile = Join-Path $outputDir 'build.log'
$luaFilter = Join-Path $scriptDir 'header-image-wrap.lua'
# --- Output-Ordner sicherstellen ---------------------------------------------
if (-not (Test-Path $outputDir)) {
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
}
# --- Log initialisieren (UTF-8 ohne BOM) -------------------------------------
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
function Write-Log {
param([string]$Line)
[System.IO.File]::AppendAllText($logFile, $Line + [Environment]::NewLine, $utf8NoBom)
}
[System.IO.File]::WriteAllText($logFile, '', $utf8NoBom)
$startTs = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
Write-Host ""
Write-Host "===== Build gestartet: $startTs =====" -ForegroundColor Cyan
Write-Log "===== Build gestartet: $startTs ====="
Write-Log "Source: $sourceFile"
Write-Log "Template-TEX: $templateTex"
Write-Log "Reference: $referenceDoc"
Write-Log "Output-Dir: $outputDir"
Write-Log ''
$overallExit = 0
# --- Pflichtdateien pruefen --------------------------------------------------
foreach ($f in @($sourceFile, $templateTex, $referenceDoc, $luaFilter)) {
if (-not (Test-Path $f)) {
Write-Host "FEHLER: Pflichtdatei fehlt: $f" -ForegroundColor Red
Write-Log "FEHLER: Pflichtdatei fehlt: $f"
$overallExit = 1
}
}
if ($overallExit -ne 0) {
Write-Host "===== Abbruch: Pflichtdateien fehlen =====" -ForegroundColor Red
Write-Log "===== Abbruch: Pflichtdateien fehlen ====="
Start-Sleep -Seconds 3
exit $overallExit
}
# --- PDF-Build ---------------------------------------------------------------
Write-Host ""
Write-Host "[1/3] PDF wird erzeugt (Pandoc + LuaLaTeX) ..." -ForegroundColor Yellow
Write-Log "--- Pandoc -> PDF (LuaLaTeX) ---"
$pdfArgs = @(
'--from=markdown+smart',
'--pdf-engine=lualatex',
"--template=$templateTex",
"--lua-filter=$luaFilter",
"--resource-path=$sourceDir",
"--output=$outputPdf",
$sourceFile
)
Write-Log ('Cmd: pandoc ' + ($pdfArgs -join ' '))
$pdfOutput = & pandoc @pdfArgs 2>&1
$pdfExit = $LASTEXITCODE
$pdfOutput | ForEach-Object { Write-Log ([string]$_) }
if ($pdfExit -eq 0 -and (Test-Path $outputPdf)) {
$sizeKB = [math]::Round((Get-Item $outputPdf).Length / 1KB, 1)
Write-Host " PDF OK ($sizeKB KB): $outputPdf" -ForegroundColor Green
Write-Log "PDF OK: $outputPdf ($sizeKB KB)"
} else {
Write-Host " PDF FEHLER (Exit $pdfExit) - Details siehe build.log" -ForegroundColor Red
Write-Log "PDF FEHLER (Exit $pdfExit)"
$overallExit = 1
Start-Sleep -Seconds 3
}
# --- DOCX-Build --------------------------------------------------------------
Write-Host ""
Write-Host "[2/3] DOCX wird erzeugt (Pandoc) ..." -ForegroundColor Yellow
Write-Log "--- Pandoc -> DOCX ---"
$docxArgs = @(
'--from=markdown+smart',
"--reference-doc=$referenceDoc",
"--lua-filter=$luaFilter",
"--resource-path=$sourceDir",
"--output=$outputDocx",
$sourceFile
)
Write-Log ('Cmd: pandoc ' + ($docxArgs -join ' '))
$docxOutput = & pandoc @docxArgs 2>&1
$docxExit = $LASTEXITCODE
$docxOutput | ForEach-Object { Write-Log ([string]$_) }
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
Start-Sleep -Seconds 3
}
$endTs = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
Write-Host ""
if ($overallExit -eq 0) {
Write-Host "===== Build beendet: $endTs, Exit-Code $overallExit (OK) =====" -ForegroundColor Cyan
} else {
Write-Host "===== Build beendet: $endTs, Exit-Code $overallExit (FEHLER) =====" -ForegroundColor Red
}
Write-Log "===== Build beendet: $endTs, Exit-Code $overallExit ====="
# Bei Fehler: auf Enter warten, damit Fenster nicht zumacht und User die
# rote Fehlermeldung im Scrollback in Ruhe lesen kann. Die Start-Sleep-3
# nach jedem fehlgeschlagenen Schritt allein reicht nicht, weil nachfolgende
# Schritte (DOCX, Post-Process) die rote Zeile aus dem sichtbaren Bereich
# scrollen koennen.
if ($overallExit -ne 0) {
# Bei Fehler: 3 Sekunden Pause, damit ein Mensch die rote Fehlermeldung
# im Scrollback noch lesen kann, bevor das PowerShell-Fenster zugeht.
# KEIN Read-Host: das Skript muss auch nicht-interaktiv von AI-Agents
# oder CI-Systemen ausfuehrbar sein. Die zusaetzliche Pause kommt OBEN-
# DRAUF zur Start-Sleep-3-pro-Fehler-Schritt-Pause weiter oben.
Write-Host ""
Write-Host "Build hatte Fehler. Details siehe build.log." -ForegroundColor Yellow
Write-Host "Fenster schliesst in 3 Sekunden ..." -ForegroundColor Yellow
Start-Sleep -Seconds 3
}
exit $overallExit

View File

@@ -0,0 +1,26 @@
-- header-image-wrap.lua
-- Wrappt das Header-Foto im PDF-Output mit \hfill\raisebox{-\height}[0pt][0pt]{...},
-- damit es in der rechten Tabellen-Zelle rechtsbuendig sitzt und die Bild-Top auf
-- der Cell-Top steht (statt durch parbox-[t]-Baseline-Logik nach oben aus der Zelle
-- herauszuragen).
--
-- Wirkt nur bei LaTeX-Output (FORMAT-Variable) und nur fuer Bilder mit "foto" im
-- Dateinamen. DOCX bleibt unberuehrt: Pandoc rendert das Image-Element normal,
-- und das DOCX-Post-Processing kuemmert sich um Rechtsbuendig + Spacing.
--
-- Wichtig: Das Image-Element MUSS im AST bleiben, damit Pandoc seine Resource-
-- Path-Resolution (Image-Datei finden, ggf. in den LaTeX-Build-Tempdir kopieren)
-- weiterhin durchfuehrt. Wenn das Image durch ein einzelnes RawInline mit
-- gebackenem Pfad ersetzt wuerde, wuerde LuaLaTeX die Datei nicht finden:
-- ! Package luatex.def Error: File `foto-...jpg' not found.
-- Deshalb geben wir eine Liste mit RawInline + Image + RawInline zurueck.
function Image(img)
if not FORMAT:match("latex") then return nil end
if not img.src:match("foto") then return nil end
return {
pandoc.RawInline("latex", "\\hfill\\raisebox{-\\height}[0pt][0pt]{"),
img,
pandoc.RawInline("latex", "}")
}
end

View File

@@ -31,6 +31,17 @@ abbilden koennen:
(left - hanging) = Bullet-Position; "Sondereinzug Haengend" = hanging.
Daher rechnen wir: left = (gewuenschter Einzug + gewuenschter Hanging) in dxa.
4. Header-Tabelle (S10) — Foto-Position und H1-Spacing:
- Erste Tabelle des Dokuments ist unsere Header-Tabelle (Name + Kontaktdaten
in linker Zelle, Foto in rechter Zelle).
- Im Heading1-Absatz der linken Zelle wird `<w:spacing w:before="0"/>`
gesetzt, damit die H1-Oberkante an der Spalten-Oberkante steht
(Pandoc/Word-Default: 18 pt vor H1).
- Im Foto-Absatz der rechten Zelle wird `<w:spacing w:before="100"/>`
(= 5 pt) gesetzt und das `<w:jc>` von `left` auf `right` geaendert,
damit das Foto an die rechte Spaltenkante rueckt und die Oberkante
mit der H1-Oberkante in einer Linie liegt.
Voraussetzungen: nur Python-Stdlib.
"""
@@ -209,6 +220,88 @@ def process_numbering_xml(xml):
new_xml = XML_DECL + ET.tostring(root, encoding="unicode")
return new_xml, stats
def modify_ppr(p_xml, spacing_before=None, jc=None):
"""In einem <w:p>...</w:p>-Block die Eigenschaften setzen.
spacing_before: str (Wert in 1/20 pt, z.B. "0" oder "100"), oder None
jc: str ("left", "right", "center", "both"), oder None
"""
ppr_re = re.compile(r"<w:pPr>(.*?)</w:pPr>", re.DOTALL)
m = ppr_re.search(p_xml)
if m is None:
# kein pPr -> einen leeren pPr direkt nach <w:p> hinzufuegen
# und rekursiv aufrufen
new_p = p_xml.replace("<w:p>", "<w:p><w:pPr></w:pPr>", 1)
return modify_ppr(new_p, spacing_before=spacing_before, jc=jc)
inner = m.group(1)
new_inner = inner
if jc is not None:
jc_re = re.compile(r'<w:jc\s+w:val="[^"]*"\s*/>')
if jc_re.search(new_inner):
new_inner = jc_re.sub(f'<w:jc w:val="{jc}"/>', new_inner)
else:
new_inner = new_inner + f'<w:jc w:val="{jc}"/>'
if spacing_before is not None:
sp_re = re.compile(r'<w:spacing\b[^/]*/>')
sm = sp_re.search(new_inner)
if sm:
sp_xml = sm.group()
if 'w:before=' in sp_xml:
new_sp = re.sub(r'w:before="[^"]*"',
f'w:before="{spacing_before}"', sp_xml)
else:
new_sp = sp_xml.replace('<w:spacing',
f'<w:spacing w:before="{spacing_before}"', 1)
new_inner = new_inner.replace(sp_xml, new_sp, 1)
else:
new_inner = new_inner + f'<w:spacing w:before="{spacing_before}"/>'
if new_inner == inner:
return p_xml
return p_xml.replace(f"<w:pPr>{inner}</w:pPr>",
f"<w:pPr>{new_inner}</w:pPr>", 1)
HEADING1_RE = re.compile(r'<w:pStyle\s+w:val="Heading1"\s*/?>')
def process_header_table(xml):
"""Modifiziert die ERSTE <w:tbl>...</w:tbl> im Dokument.
- Heading1-Paragraph der linken Zelle: spacing-before = 0 (statt 18 pt)
- Foto-Paragraph der rechten Zelle (enthaelt <w:drawing>):
spacing-before = 100 (= 5 pt) und jc = right (statt left).
"""
stats = {"h1_modified": 0, "foto_modified": 0}
tbl_re = re.compile(r'<w:tbl>.*?</w:tbl>', re.DOTALL)
tbl_m = tbl_re.search(xml)
if tbl_m is None:
return xml, stats
tbl = tbl_m.group()
new_tbl = tbl
p_re = re.compile(r'<w:p\b[^>]*>.*?</w:p>', re.DOTALL)
for pm in list(p_re.finditer(tbl)):
p_xml = pm.group()
if HEADING1_RE.search(p_xml):
new_p = modify_ppr(p_xml, spacing_before="0")
if new_p != p_xml:
new_tbl = new_tbl.replace(p_xml, new_p, 1)
stats["h1_modified"] += 1
elif '<w:drawing>' in p_xml:
new_p = modify_ppr(p_xml, spacing_before="100", jc="right")
if new_p != p_xml:
new_tbl = new_tbl.replace(p_xml, new_p, 1)
stats["foto_modified"] += 1
if new_tbl == tbl:
return xml, stats
return xml.replace(tbl, new_tbl, 1), stats
def main():
if not DOCX_FILE.exists():
sys.stderr.write(f"FEHLER: {DOCX_FILE} existiert nicht. "
@@ -220,6 +313,7 @@ def main():
members = {name: z.read(name) for name in z.namelist()}
doc_xml = members["word/document.xml"].decode("utf-8")
doc_xml, header_stats = process_header_table(doc_xml)
new_doc_xml, doc_stats = process_document_xml(doc_xml)
members["word/document.xml"] = new_doc_xml.encode("utf-8")
@@ -245,6 +339,8 @@ def main():
log(f" H2-Trenn-Absaetze eingefuegt: {doc_stats['separators_added']}")
log(f" numbering.xml abstractNum-Eintraege: {num_stats['abstractNums']}")
log(f" numbering.xml lvls modifiziert: {num_stats['lvls_modified']}")
log(f" Header-Tabelle H1 modifiziert: {header_stats['h1_modified']}")
log(f" Header-Tabelle Foto modifiziert: {header_stats['foto_modified']}")
log("Fertig.")
return 0

View File

@@ -1,24 +1,26 @@
===== Build gestartet: 2026-04-27 12:21:31 =====
===== Build gestartet: 2026-04-27 18:12:51 =====
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
Output-Dir: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output
--- Pandoc -> PDF (LuaLaTeX) ---
Cmd: pandoc --from=markdown+smart --pdf-engine=lualatex --template=Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\templates\template.tex --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.pdf Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\source\cv.md
PDF OK: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.pdf (71.2 KB)
Cmd: pandoc --from=markdown+smart --pdf-engine=lualatex --template=Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\templates\template.tex --lua-filter=Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\build\header-image-wrap.lua --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.pdf Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\source\cv.md
PDF OK: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.pdf (449.1 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 (22.6 KB)
Cmd: pandoc --from=markdown+smart --reference-doc=Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\templates\reference.docx --lua-filter=Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\build\header-image-wrap.lua --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 (379.7 KB)
--- 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: 26
[post-process-docx] Bullets in Listen: 180
[post-process-docx] keepNext gesetzt: 91
[post-process-docx] Bullets in Tabellen uebersprungen: 0
[post-process-docx] H2-Headings gefunden: 7
[post-process-docx] H2-Trenn-Absaetze eingefuegt: 7
[post-process-docx] Listen gefunden: 25
[post-process-docx] Bullets in Listen: 174
[post-process-docx] keepNext gesetzt: 87
[post-process-docx] Bullets in Tabellen uebersprungen: 6
[post-process-docx] H2-Headings gefunden: 6
[post-process-docx] H2-Trenn-Absaetze eingefuegt: 6
[post-process-docx] numbering.xml abstractNum-Eintraege: 2
[post-process-docx] numbering.xml lvls modifiziert: 18
[post-process-docx] Header-Tabelle H1 modifiziert: 1
[post-process-docx] Header-Tabelle Foto modifiziert: 1
[post-process-docx] Fertig.
===== Build beendet: 2026-04-27 12:21:37, Exit-Code 0 =====
===== Build beendet: 2026-04-27 18:12:57, Exit-Code 0 =====

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,15 @@
# Lebenslauf Dr.-Ing. Thomas Langer
## Kontaktdaten
- E-Mail: <Thomas.Langer@destengs.com>
- Telefon: [+49 89 413 27 59 20](tel:+4989413275920)
- Freelance.de: [[Link zum Profil]](https://www.freelance.de/Freelancer/301931-Consultant)
- Website: [[destengs.de]](https://destengs.de)
- LinkedIn: [[Link zum Profil]](https://www.linkedin.com/in/thomas-langer-b9742a2)
- Büroadresse: Biberger Straße 91, 82008 Unterhaching
+----------------------------------------------------------------------------------------------------------------+------------------------------------------------------------+
| # Lebenslauf Dr.-Ing. Thomas Langer | |
| | ![](foto-wrba_2026_6782_1.jpg){width=4.06cm height=4.06cm} |
| ## Kontaktdaten | |
| | |
| - E-Mail: <Thomas.Langer@destengs.com> | |
| - Telefon: [+49 89 413 27 59 20](tel:+4989413275920) | |
| - Freelance.de: [[Link zum Profil]](https://www.freelance.de/Freelancer/301931-Consultant) | |
| - Website: [[destengs.de]](https://destengs.de) | |
| - LinkedIn: [[Link zum Profil]](https://www.linkedin.com/in/thomas-langer-b9742a2) | |
| - Büroadresse: Biberger Straße 91, 82008 Unterhaching | |
+----------------------------------------------------------------------------------------------------------------+------------------------------------------------------------+
## Zusammenfassung

View File

@@ -61,6 +61,14 @@
pdftitle={Lebenslauf Dr.-Ing. Thomas Langer},
pdfcreator={Pandoc + LuaLaTeX},
}
%% Pandoc emittiert fuer href-Links, deren Display-Text wie eine URL aussieht
%% (z.B. eine E-Mail-Adresse als Display und mailto:Adresse als Ziel),
%% \nolinkurl{...} um den Text in Verbatim-Mode zu rendern. Verbatim-Mode
%% bricht in fragilen Kontexten wie `longtable`-Minipage mit
%% ! Paragraph ended before \@xverbatim was complete.
%% ab. Da wir im CV (Sans-Serif durchgehend) kein Verbatim-Mode-Rendering
%% fuer URLs brauchen, redefinieren wir \nolinkurl{} zu Plain-Text-Output.
\renewcommand{\nolinkurl}[1]{#1}
%% === Bilder =================================================================
\usepackage{graphicx}
@@ -137,7 +145,7 @@
{}
[\nopagebreak]
\titlespacing*{\section}{0pt}{1.4em}{0.5em}
\titlespacing*{\section}{0pt}{0pt}{0.5em}
\titlespacing*{\subsection}{0pt}{1.0em}{0.4em}
\titlespacing*{\subsubsection}{0pt}{0.7em}{0.2em}

View File

@@ -48,3 +48,4 @@ Chronologisches Log aller Entscheidungen und Prozessereignisse.
2026-04-26 20:35 | S08 | Teilgebiet 01 Iteration B4 fuer DOCX umgesetzt. Heading 1/2/3 in destengsblue (build/build-reference-docx.py Funktion set_heading_colors mit explizitem color val=0B5394, themeColor accent1 entfernt). Heading-Bottom-Borders direkt am Stil verworfen, weil Word die Border bei hanging-Indent linksbuendig statt zentriert rendert und der right-Indent sowohl Text als auch Border begrenzt. 21 Markdown-HRs aus cv.md entfernt - Quelle der wahrgenommenen Doppellinien war Pandocs DOCX-Konvertierung von --- Zeilen zu VML-rect mit o:hr=t (Embossed-Look). Tabellen-Strich-Zeilen blieben unangetastet. Zwischenfall: NTFS-Mount-Stale-Read der cv.md (20043 statt 20201 Bytes) haette fast die Live-Datei truncated, sofortige Wiederherstellung aus git show HEAD und HR-Removal erneut mit git-Version als Input. H2-Trennlinien via Post-Processing eingefuehrt (build/post-process-docx.py um Logik erweitert): nach jedem H2 wird ein leerer Trenn-Absatz mit linksbuendiger Bottom-Border eingefuegt, schwarz (000000), 8,6 cm Linienlaenge (right-Indent 4196 dxa), 1,25 pt Dicke (sz=10). Sandbox-Verifikation 7 H2 zu 7 Trenner. Visuelle Bestaetigung durch Thomas. teilgebiete/01-lebenslauf.md um Iteration-B4-Block ergaenzt (B4.1 Farben, B4.2 Heading-Border-Sackgasse, B4.3 HR-Removal inkl. Zwischenfall, B4.4 H2-Trennlinien) und Naechste-Schritte-Liste auf C/D verkuerzt.
2026-04-26 21:50 | S08 | Teilgebiet 01 Iterationen B5 (Trainings als Tabelle) und B6 (Bullet-Einzuege verkleinert) abgeschlossen. B5: Trainings-Bullet-Liste in cv.md durch Pandoc-Multiline-Tabelle ersetzt analog Ausbildung. B6: build/post-process-docx.py um dritte Modifikation erweitert die direkt die numbering.xml manipuliert weil Pandoc die Werte aus reference.docx ignoriert. Bullet-Einzuege auf E1 0,25/0,35 cm und E2 0,80/0,40 cm gesetzt. Word-Konvention dokumentiert: Einzug-links zeigt (left - hanging). teilgebiete/01-lebenslauf.md und agent-prompt.md fuer S09 fortgeschrieben.
2026-04-27 12:34 | S09 | Teilgebiet 01 Iteration Links umgesetzt: alle URLs in cv.md auf explizite Markdown-Links migriert, damit Pandoc echte w:hyperlink-Elemente in die DOCX emittiert (vorher Plain-Text-only, Word zeigte sie nicht als Links und kein Hover funktionierte; im PDF wurden sie ueber Words eigene URL-Erkennung beim PDF-Export trotzdem klickbar, was die Inkonsistenz erklaerte). E-Mail als Pandoc-Autolink-Form mit spitzen Klammern (mailto), Telefon als tel:-Link mit Display-Spaces und URL-ohne-Spaces gem RFC 3966, Web-Links als doppelte-Bracket-Markdown-Syntax mit sichtbaren aeusseren eckigen Klammern als einheitlicher Anzeigetext-Stil. Display-Texte: Link zum Profil fuer LinkedIn und Freelance.de, destengs.de fuer Website (bewusster Wechsel von .com auf .de stimmiger zur deutschen Primaersprache), Dissertation fuer Promotion, Link zum Zertifikat funktioniert nur im Browser fuer TUEV-Zertifikat. TUEV-Link-Problem in Word diagnostiziert: certif-id.com liegt hinter Cloudflare-Bot-Schutz und blockiert Words urlmon-Pre-Flight-Anfrage mit 403; Direkt-Klick aus Word schlaegt mit Die angeforderten Informationen koennen nicht heruntergeladen werden fehl trotz funktionierender URL im Browser. Optionen A (destengs.de-Redirect), B (LinkedIn-Safety-Redirect), C (kein Link) abgewogen und verworfen, Option D gewaehlt: direkter TUEV-Link beibehalten mit erklaerendem Display-Text der den Empfaenger ueber die Word-Einschraenkung informiert. Thomas hat zusaetzlich eine kuerzere TUEV-Direkt-URL besorgt (perscert-tuv.certif-id.com/expert/public/share/7MR0WDzG106JDCqV_RW7) statt der urspruenglichen 130-Zeichen-Hash-URL. Zwischenfall: zweite Edit-Tool-Truncation in dieser Session auf cv.md beim Edit der TUEV- und Promotion-Zeile, die Schluss-Zeile Dissertation fuenf Veroeffentlichungen ein Patent eine Erfindungsmeldung wurde mitten im Wort abgeschnitten. Reparatur identisch zum S08-Pattern: git HEAD-Version als Input, alle 7 Link-Replacements in einem Python-Script atomar via os.replace zurueckgeschrieben mit count==1-Check pro Replacement. Lehre fuer kommende Sessions: Edit-Tool fuer cv.md generell nicht mehr verwenden, Python-aus-git-Pattern bevorzugen. Build und visuelle Bestaetigung durch Thomas erfolgt fuer DOCX und PDF. teilgebiete/01-lebenslauf.md um Iteration-Links-Block ergaenzt.
2026-04-27 18:51 | S09 | Teilgebiet 01 Iteration C Foto-Einbindung umgesetzt. Header als 2-Spalten-Grid-Table in cv.md mit Strich-Verhaeltnis 112:60 (= 65,1%/34,9% Spaltenbreite, ca. 10,15/5,43 cm bei 16 cm Textbreite). Foto rechts oben, 4,06x4,06 cm, beide Dimensionen explizit im Markdown um Pandocs Default-Wrapper keepaspectratio mit height=textheight zu vermeiden, der die Layout-Box auf 24cm Hoehe streckt und die Tabellen-Zeile zerschiesst. DOCX-Header-Spacing per neuer 4. Modifikation in build/post-process-docx.py (process_header_table): findet erste Tabelle, setzt Heading1-spacing-before=0 und Foto-Paragraph spacing-before=100 (=5pt) plus jc=right. PDF-Layout via neuem Pandoc-Lua-Filter build/header-image-wrap.lua: wrappt das Header-Foto im LaTeX-Output mit hfill+raisebox(-height)[0pt][0pt]{...}, hfill schiebt rechtsbuendig in raggedright-p-Spalte, raisebox setzt Bild-Top auf Cell-Top und reportet null Hoehe an die Tabellen-Zeile. Filter prueft FORMAT=latex und Image-Pfad enthaelt foto, DOCX bleibt unberuehrt. Lua-Filter-Erste-Version (Image durch RawInline ersetzt mit gebackenem Pfad) hat Pandocs Image-Resource-Resolution gebrochen und LuaLaTeX scheiterte mit File foto.jpg not found, Fix: Filter gibt Lua-Liste zurueck mit Original-img-Element zwischen RawInline-Wrappern. Template-Hotfixes fuer PDF: renewcommand-nolinkurl-zu-Plaintext (verhindert at-xverbatim-Bruch in longtable-Minipage durch URL-Display-Text-Verbatim-Mode), titlespacing-section-before=0pt fuer H1-Top-Alignment. cv.md: Pipe-Alignment in Grid Table programmatisch via Python ljust und Pipe-Position-Eindeutigkeitscheck (Pandoc 3.x ist beim Grid-Table-Pipe-Alignment streng, Sandbox-Pandoc 2.9 ist toleranter und damit irrefuehrend). build.ps1 erweitert um lua-filter-Argument in PDF und DOCX, plus Read-Host-Wait-on-Error entfernt (blockiert AI-Agents und CI), durch Start-Sleep 3s am Ende ersetzt. header-image-wrap.lua als Pflichtdatei in Test-Path-Check aufgenommen. Vier weitere Edit-Tool-Truncation-Vorfaelle in S09 (cv.md, template.tex zweimal, build.ps1), Lehre verschaerft Edit-Tool fuer jede nicht-triviale Modifikation auf NTFS-Mount-Dateien meiden. Sandbox-NTFS-Stale-Read auf DOCX-Output (DOCX-Datei als not a zip file, Workaround DOCX im Sandbox neu generieren). Sandbox-NTFS-Mount kann auch Datei-Schreiben mit open(w) verweigern obwohl os.path.exists True liefert, Workaround tmp-Datei plus os.rename. Build und visuelle Bestaetigung durch Thomas erfolgt fuer DOCX und PDF. teilgebiete/01-lebenslauf.md um Iteration-C-Block ergaenzt und Naechste-Schritte-Liste auf S10-Plan umgestellt (1 DOCX-Heading-Farben pruefen, 2 Doublecheck der generierten Texte mindestens elektrischer-Gehaeuse ist sinnverkehrt, 3 Buzzword-Kompetenzen brainstormen mindestens Umgang mit quantisierten LLMs fehlt, 4 PDF-Spacings H1/H2/Kontaktdaten und hellgraue Trennlinien korrigieren, 5 Hyphenation-Feintuning, 6 Teilgebiet abschliessen). agent-prompt.md Aktueller-Stand-Block fuer S10 fortgeschrieben.

View File

@@ -253,11 +253,48 @@ Die in S04 mit docx-js erstellte Version hatte strukturelle typographische Mäng
**Build und visuelle Bestätigung durch Thomas (S09):** `build.ps1` ausgeführt; alle Links in DOCX und PDF wie gewünscht klickbar. TÜV-Klick zeigt erwartungsgemäß die Word-Fehlermeldung — der Display-Text warnt den Empfänger vorab, manuelles Copy-Paste in den Browser funktioniert.
## Iteration C (S09) — Foto-Einbindung via Grid Table
**Ziel:** Foto rechts oben auf Höhe Name + Kontaktdaten, 4,06 × 4,06 cm, eckig, nur Seite 1, einheitlich in DOCX und PDF.
**Layout-Pfad:** Grid Table im `cv.md` als 2-Spalten-Header. Linke Zelle: H1 (Name) + H2 (Kontaktdaten) + Bullet-Liste der Kontaktdaten. Rechte Zelle: Foto-Image. Spalten-Verhältnis 65,1% / 34,9% (Strich-Anzahl 112:60), entspricht ca. 10,15 cm linke / 5,43 cm rechte Spalte.
**C1 — Pipe-Alignment-Strenge in Pandoc 3.x:** Erste Grid-Table-Variante hatte inkonsistente Pipe-Positionen, weil ich die Cell-Inhalte nicht genau auf die Strich-Breiten gepaddet habe. Pandoc 2.9 (Sandbox) parst das tolerant als Tabelle, Pandoc 3.x (Thomas) erkennt das nicht als Grid Table und fällt auf Plain-Text-Rendering der Pipes zurück (DOCX-Output war reiner Text). Fix: Tabelle programmatisch in Python aufbauen mit `ljust(LEFT_W)`/`ljust(RIGHT_W)` und Eindeutigkeits-Check der Pipe-Positionen pro Zeile. Lehre: Pandoc 3.x Grid Tables verlangen exakt konsistente Pipe-Positionen in allen Zeilen.
**C2 — DOCX-Spacing für H1 und Foto via Post-Processing:** Pandoc emittiert für H1 Default-Spacing-before = 18 pt, das Foto landet ohne Spacing auf Cell-Top. Resultat: H1-Top liegt 0,7 cm unter Foto-Top. Thomas hat in Word experimentiert und gewünscht: H1-spacing-before = 0 pt, Foto-spacing-before = 5 pt, dazu Foto-Paragraph horizontal rechtsbündig (`<w:jc w:val="right"/>`). Da Pandoc das Image als „Mit Text in Zeile" einbettet (nicht als Floating Image), kann es nur über die umgebende Paragraph-Eigenschaft ausgerichtet werden. Umgesetzt als vierte Modifikation in `build/post-process-docx.py` (Funktion `process_header_table`): findet die erste Tabelle, modifiziert die `<w:pPr>` der Heading1- und der Drawing-tragenden Paragraphen.
**C3 — Foto-Größe 4,5 → 4,06 cm und Pandoc-Default-Image-Bug:** Bei der Größenänderung mit `{width=4.06cm}` allein emittierte Pandoc 3.x `\includegraphics[width=4.06cm,height=\textheight,keepaspectratio]`. Das `height=\textheight` ist Pandocs Default für Single-Width-Specs. Mit `keepaspectratio` rendert das Bild zwar visuell auf 4,06 cm × 4,06 cm, aber die Image-Box hat layoutmäßig `\textheight` (~24 cm) Höhe — und LaTeX zieht die Tabellen-Zeile auf 24 cm Höhe auf, was den ganzen Header-Layout zerschießt (Foto oben, Text unten — beobachtet von Thomas). Fix: beide Dimensionen explizit in der Markdown-Image-Syntax: `{width=4.06cm height=4.06cm}`. Pandoc emittiert dann `\includegraphics[width=4.06cm,height=4.06cm]` ohne textheight-Anteil — saubere Box-Höhe.
**C4 — PDF-Layout via Lua-Filter:** Selbst nach C3 saß das Foto im PDF in der falschen vertikalen Position (oben aus der Cell herausragend). Ursache: Pandoc 3.x emittiert für die rechte Cell mit nur einem Image-Element KEINEN `\begin{minipage}`-Wrapper (im Gegensatz zu Pandoc 2.9), das Image landet direkt in der `p{calc...}`-Spalte. In dieser p-Spalte wirkt eine implizite `\parbox[t]`-Logik: die Baseline des Images (= unterer Bildrand) wird auf die Cell-Top-Linie gesetzt, das Bild ragt also nach OBEN aus der Cell heraus. **Fix:** Pandoc-Lua-Filter `build/header-image-wrap.lua`, der das Header-Foto im LaTeX-Output mit `\hfill\raisebox{-\height}[0pt][0pt]{...}` umschließt: `\hfill` schiebt das Bild rechtsbündig in der `\raggedright`-p-Spalte, `\raisebox{-\height}[0pt][0pt]` setzt die Bild-Top auf die Baseline (= Cell-Top) und reportet null Höhe an die Tabellen-Zeile, damit die Zeilenhöhe von der linken Zelle bestimmt wird. Filter-Trigger: nur bei `FORMAT="latex"` und nur für `img.src` mit „foto" im Namen. DOCX bleibt unberührt; das DOCX-Post-Processing macht das Pendant per `<w:jc>` und `<w:spacing>`.
**C4a — Lua-Filter-Erste-Version (Image durch RawInline ersetzt) → Image-not-found:** Die erste Filter-Version hat das gesamte Image-Element durch ein einzelnes `RawInline` ersetzt, mit dem Image-Pfad gebacken in den raw-LaTeX-String. Folge: Pandoc sah kein Image-Element mehr im AST und triggerte seine Resource-Path-Resolution nicht. LuaLaTeX scheiterte mit `! Package luatex.def Error: File 'foto-wrba_2026_6782_1.jpg' not found: using draft setting.` Korrektur in der zweiten Version: Filter gibt eine Lua-**Liste** zurück, in der das Original-`img`-Element zwischen den beiden RawInline-Wrappern erhalten bleibt. So läuft Pandocs Image-Resource-Resolution weiterhin.
**C4b — `\nolinkurl{}` in `longtable`-Minipage → `\@xverbatim`-Fehler:** Pandoc emittiert für href-Links, deren Display-Text einer URL ähnelt (z.B. eine E-Mail-Adresse als Display und mailto:Adresse als Ziel), `\nolinkurl{...}` für Verbatim-Mode-Rendering. In einer `longtable`-Minipage bricht das mit `! Paragraph ended before \@xverbatim was complete.` ab. Fix: `\renewcommand{\nolinkurl}[1]{#1}` direkt nach `\hypersetup{}` im Template — URL-Display-Text wird normaler Text statt Verbatim-Mode. Im CV mit Sans-Schrift sowieso erwünscht (kein Monospace-Display für E-Mail).
**C4c — `\titlespacing*{\section}` für H1-Top-Alignment:** Default-`\titlespacing*{\section}{0pt}{1.4em}{0.5em}` (zweite Zahl = before-space) lässt H1 um 1,4 em unter der Cell-Top beginnen. Cv.md hat nur ein einziges H1 (Header-Name), daher unschädlich, das vor-Spacing global auf 0 zu setzen: `\titlespacing*{\section}{0pt}{0pt}{0.5em}`. H1 startet jetzt direkt am Cell-Top, parallel zum Foto-Top.
**C5 — Spaltenbreiten 112:60:** Strich-Verhältnis ergibt 65,12% / 34,88% ≈ 10,15 / 5,43 cm bei 16 cm Textbreite. Thomas-Wunsch war exakt 10,66 / 5,73 cm (= 65,04% / 34,96%); meine kompakteste Variante mit Image-Markdown-Mindestbreite 60 Zeichen liegt 0,5 cm linke Spalte zu schmal vs. Wunsch, ist aber funktional korrekt: H1 in einer Zeile, E-Mail in einer Zeile, Foto-Rechtsrand bündig mit Textbereich-Rand. Thomas hat das so akzeptiert.
**Build-System-Verbesserungen (S09):**
- `build/build.ps1` um `--lua-filter=$luaFilter` in PDF- und DOCX-Pandoc-Calls erweitert.
- `Read-Host`-Wait-on-Error aus `build.ps1` entfernt — das blockierte AI-Agents/CI-Aufrufe. Stattdessen `Start-Sleep -Seconds 3` am Ende bei Fehler, was menschliche Lesezeit ermöglicht und nicht blockt.
- `header-image-wrap.lua` als Pflichtdatei in den `Test-Path`-Check aufgenommen.
**Edit-Tool-Truncation-Vorfälle in S09 (vier weitere):** beim Initial-Edit der Grid Table in cv.md, bei der `\renewcommand{\nolinkurl}`-Insertion in template.tex, beim Einbau des Read-Host-Blocks in build.ps1, und nochmals bei der Dezimalpunkt-Korrektur. **Lehre verschärft: Edit-Tool für JEDE nicht-triviale Modifikation auf NTFS-Mount-Dateien meiden, generell Python-aus-git-HEAD- oder Python-aus-Disk-Pattern bevorzugen.**
**Sandbox-NTFS-Stale-Read auf DOCX-Output:** Beim Versuch, das von Thomas erzeugte DOCX im Sandbox zu inspizieren, lieferte der Sandbox-Read das DOCX als „File is not a zip file" zurück (End-of-central-directory fehlte). Workaround: DOCX in der Sandbox aus cv.md neu generieren statt das Live-File zu lesen.
**Build und visuelle Bestätigung durch Thomas (S09):** DOCX und PDF zeigen Foto rechts oben, korrekt ausgerichtet, korrekt bemessen. Layout aus Thomas' Sicht akzeptiert.
## Nächste Schritte
1. **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).
2. **Iteration D — Hyphenation-Feintuning für PDF:** Kurze Wortteile am Zeilenanfang mit höherer Penalty oder gezielten `\hyphenation`-Ausnahmen reduzieren. Iterativ.
3. Teilgebiet nach erfolgreichem Output und Freigabe durch Thomas abschließen (R2-OK von Thomas: Status auf „abgeschlossen" im zentral-index.md).
1. **DOCX-Mängel beheben:** Blau-Ton der Headings ist nicht DesTEngS-Blau (Soll-Wert `#0B5394`). Heading-Stile in `build/build-reference-docx.py` prüfen, ob die `set_heading_colors`-Funktion auf dem Stil greift oder ob Word den Theme-Color trotzdem als Aptos-Default rendert.
2. **Doublecheck der neu generierten Texte:** Mindestens „elektrischer Gehäuse" ist sinnverkehrt (vermutlich aus den V9/V10-Iterationen entstanden). cv.md komplett auf Sinn- und Sprachfehler durchgehen.
3. **Buzzword-Kompetenzen-Brainstorm:** Kenntnisse-Abschnitt erweitern. Mindestens „Umgang mit quantisierten LLMs" fehlt noch. Weitere KI-relevante Begriffe für das Agentur-Keyword-Matching identifizieren.
4. **PDF-Mängel beheben:** Abstände zwischen H1, H2 „Kontaktdaten" und der Kontaktdaten-Bullet-Liste stimmen nicht (Folge der `\titlespacing*{\section}{0pt}{0pt}{0.5em}`-Änderung). Hellgraue Trennlinien (rulegray, `#BFBFBF`) sind inakzeptabel — Farbe oder Linienführung überdenken.
5. **Iteration D — Hyphenation-Feintuning für PDF:** Kurze Wortteile am Zeilenanfang mit höherer Penalty oder gezielten `\hyphenation`-Ausnahmen reduzieren. Iterativ.
6. Teilgebiet nach erfolgreichem Output und Freigabe durch Thomas abschließen (R2-OK von Thomas: Status auf „abgeschlossen" im zentral-index.md).
## Artefakte