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,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