diff --git a/.checkpoint-pending.txt b/.checkpoint-pending.txt index 306fc51..e6ee86d 100644 --- a/.checkpoint-pending.txt +++ b/.checkpoint-pending.txt @@ -1,2 +1,2 @@ -S07 -Iteration B3 und B3.5 fuer Teilgebiet 01 abgeschlossen. B3 in build/build-reference-docx.py ergaenzt: DocDefault widowControl plus keepNext und keepLines auf Heading 1/2/3 und FirstParagraph (Pandoc-Stil fuer ersten Absatz nach einem Heading, deckt die fett formatierten Kenntnisse-Subsection-Labels KI Software-Design Methodik IT etc ab). Erster Versuch Compact-Stil mit keepNext hat Listen komplett unteilbar gemacht (Job-Stationen begannen jedes Mal auf einer neuen Seite, ungenutzte Seitenenden) und wurde verworfen. Auf Wunsch von Thomas auf 3-3-Regel umgestellt: bei Listen mit mindestens 6 Bullets duerfen Trennungen passieren, aber mindestens 3 Bullets bleiben jeweils zusammen vor und nach dem Umbruch. Bei kuerzeren Listen alles zusammen. Da das stilbasiert nicht abbildbar ist (alle Bullets haben pStyle Compact), neues Post-Processing-Skript build/post-process-docx.py: scannt das fertige DOCX, findet Sequenzen aufeinanderfolgender Bullets mit numPr-Eigenschaft ausserhalb von Tabellen-Zellen, setzt keepNext auf den ersten 2 und den N-3 N-2 Bullets jeder Liste mit n groesser gleich 6 (bei n kleiner 6 alle keepNext). build.ps1 erweitert auf 3 Schritte und ruft das Post-Processing-Skript automatisch nach erfolgreichem DOCX-Build auf, mit Console-Output und Log-Statistiken (Anzahl Listen Bullets keepNext-Markierungen). Sandbox-Verifikation 26 Listen 184 Bullets 93 keepNext, Pattern fuer 11-Bullet-Liste KK......KK.. Auf Thomas System visuell bestaetigt: Listen werden an guten Stellen getrennt, keine ungenutzten Seitenenden, keine einzelnen Bullets allein am Seitenrand. teilgebiete/01-lebenslauf.md um B3- und B3.5-Bloecke ergaenzt sowie Naechste-Schritte-Liste auf B4 C D umstrukturiert. agent-prompt.md Aktueller-Stand-Abschnitt fortgeschrieben mit B3 und B3.5, Hinweis auf 3-stufige DOCX-Pipeline und Edit-Tool-Truncation an build.ps1 ergaenzt. Naechste Session startet mit B4 (Heading-Farben oder Trennlinien analog PDF). +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. diff --git a/artefakte/01-lebenslauf/build/__pycache__/build-reference-docx.cpython-310.pyc b/artefakte/01-lebenslauf/build/__pycache__/build-reference-docx.cpython-310.pyc new file mode 100644 index 0000000..5a5c7c6 Binary files /dev/null and b/artefakte/01-lebenslauf/build/__pycache__/build-reference-docx.cpython-310.pyc differ diff --git a/artefakte/01-lebenslauf/build/__pycache__/post-process-docx.cpython-310.pyc b/artefakte/01-lebenslauf/build/__pycache__/post-process-docx.cpython-310.pyc new file mode 100644 index 0000000..26f7bf2 Binary files /dev/null and b/artefakte/01-lebenslauf/build/__pycache__/post-process-docx.cpython-310.pyc differ diff --git a/artefakte/01-lebenslauf/build/build-reference-docx.py b/artefakte/01-lebenslauf/build/build-reference-docx.py index df7494d..99babe2 100644 --- a/artefakte/01-lebenslauf/build/build-reference-docx.py +++ b/artefakte/01-lebenslauf/build/build-reference-docx.py @@ -6,7 +6,7 @@ build-reference-docx.py Baut die templates/reference.docx fuer die Pandoc-DOCX-Pipeline aus der Pandoc-Default-Reference, mit gezielten Anpassungen. -Iteration B1 + B1.5 + B2 + B3 (aktuell): +Iteration B1 + B1.5 + B2 + B3 + B4 (aktuell): B1 - Theme-Schriften (majorFont und minorFont) beide auf Calibri. B1 - Direkte Schriftnamen-Referenzen in styles.xml auf Calibri (Code-Schriften wie Consolas bleiben). @@ -18,14 +18,20 @@ Iteration B1 + B1.5 + B2 + B3 (aktuell): analog PDF (top/bottom 2.2 cm, left/right 2.5 cm). B3 - DocDefault widowControl. Heading 1/2/3 mit keepNext + keepLines. Zusaetzlich 'FirstParagraph' (Pandoc-Stil fuer den ersten Absatz - nach einem Heading) — deckt die fett formatierten Kenntnisse- + nach einem Heading) - deckt die fett formatierten Kenntnisse- Subsection-Labels ab. Hinweis: Listen-Bullet-Schutz (3-3-Regel) passiert nicht hier, sondern im Post-Processing (build/post-process-docx.py), das auf das fertige DOCX angewendet - wird — ein Stil kann keine Per-Bullet-Logik abbilden. + wird - ein Stil kann keine Per-Bullet-Logik abbilden. + B4 - Heading 1/2/3 in destengsblue (0B5394) gefaerbt (themeColor + entfernt, damit die Farbe nicht aus dem Word-Theme kommt). + Hinweis (S08): die zwischenzeitlich eingebauten Heading- + Trennlinien (Bottom-Border + Indent-Trick) wurden zurueck- + gerollt, weil sie in Word linksbuendig statt zentriert + gerendert wurden (Word-Border folgt bei hanging-Indent der + visuellen Absatz-Position, nicht den Indent-Werten). Geplant in Folge-Iterationen: - B4 - optional Heading-Farben auf DesTEngS-Blau analog PDF C - Foto-Einbindung D - Hyphenation-Feintuning fuer PDF """ @@ -70,10 +76,15 @@ SIZE_HEADING3 = 24 HEADING_SIZES = {"Heading1": SIZE_HEADING1, "Heading2": SIZE_HEADING2, "Heading3": SIZE_HEADING3} -# Compact NICHT mehr in dieser Liste — Listen-Bullet-Schutz uebernimmt das +# Compact NICHT mehr in dieser Liste - Listen-Bullet-Schutz uebernimmt das # Post-Processing-Skript pro-Bullet. KEEP_STYLES = ("Heading1", "Heading2", "Heading3", "FirstParagraph") +# B4 - Heading-Farben (Trennlinien wurden in S08 zurueckgerollt, siehe +# Modul-Docstring). Bleibt: Heading 1/2/3 in destengsblue, themeColor entfernt. +HEADING_COLOR = "0B5394" # destengsblue (analog template.tex) +HEADING_COLOR_STYLES = ("Heading1", "Heading2", "Heading3") + PAGE_W = 11906 PAGE_H = 16838 MARGIN_TOP = 1247 @@ -87,19 +98,19 @@ HEADER_RIGHT_TAB = PAGE_W - MARGIN_LEFT - MARGIN_RIGHT HEADER_LEFT = "Dr.-Ing. Thomas Langer" HEADER_RIGHT = "Lebenslauf" -def log(msg: str) -> None: +def log(msg): print(f"[build-reference-docx] {msg}", flush=True) XML_DECL = b'\n' -def write_xml(tree: ET.ElementTree, dest: Path) -> None: +def write_xml(tree, dest): body = ET.tostring(tree.getroot(), encoding="utf-8") dest.write_bytes(XML_DECL + body) -def write_xml_bytes(content: bytes, dest: Path) -> None: +def write_xml_bytes(content, dest): dest.write_bytes(XML_DECL + content) -def fetch_pandoc_default(dest: Path) -> None: +def fetch_pandoc_default(dest): log("Pandoc-Default-Reference extrahieren ...") result = subprocess.run( ["pandoc", "--print-default-data-file", "reference.docx"], @@ -111,11 +122,11 @@ def fetch_pandoc_default(dest: Path) -> None: dest.write_bytes(result.stdout) log(f" -> {dest} ({dest.stat().st_size} Bytes)") -def unpack_docx(src: Path, dest_dir: Path) -> None: +def unpack_docx(src, dest_dir): with zipfile.ZipFile(src, "r") as z: z.extractall(dest_dir) -def repack_docx(src_dir: Path, dest: Path) -> None: +def repack_docx(src_dir, dest): files = [] for path in src_dir.rglob("*"): if path.is_file(): @@ -126,12 +137,12 @@ def repack_docx(src_dir: Path, dest: Path) -> None: for path, arcname in files: z.write(path, arcname) -def is_code_font(name: str) -> bool: +def is_code_font(name): return (name or "").strip().lower() in CODE_FONTS # --- B1: Schriften --------------------------------------------------------- -def set_theme_fonts_to_calibri(theme_xml: Path) -> None: +def set_theme_fonts_to_calibri(theme_xml): tree = ET.parse(theme_xml) root = tree.getroot() for kind in ("majorFont", "minorFont"): @@ -146,7 +157,7 @@ def set_theme_fonts_to_calibri(theme_xml: Path) -> None: 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: +def replace_direct_fonts_in_styles(styles_xml): tree = ET.parse(styles_xml) root = tree.getroot() changed = 0 @@ -166,14 +177,16 @@ def replace_direct_fonts_in_styles(styles_xml: Path) -> None: f" gesetzt (Code-Fonts unangetastet: {skipped})") write_xml(tree, styles_xml) -def set_table_borders_none(styles_xml: Path) -> None: +def set_table_borders_none(styles_xml): 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") + tbl_pr = style.find(f"{W}tblPr") + if tbl_pr is None: + tbl_pr = ET.SubElement(style, f"{W}tblPr") existing = tbl_pr.find(f"{W}tblBorders") if existing is not None: tbl_pr.remove(existing) @@ -187,19 +200,27 @@ def set_table_borders_none(styles_xml: Path) -> None: log(" Style 'Table': tblBorders=none auf allen Sides") write_xml(tree, styles_xml) -def set_default_body_size(styles_xml: Path) -> None: +def set_default_body_size(styles_xml): 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") + docDefaults = root.find(f"{W}docDefaults") + if docDefaults is None: + docDefaults = ET.SubElement(root, f"{W}docDefaults") + rPrDefault = docDefaults.find(f"{W}rPrDefault") + if rPrDefault is None: + rPrDefault = ET.SubElement(docDefaults, f"{W}rPrDefault") + rPr = rPrDefault.find(f"{W}rPr") + if rPr is None: + rPr = 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 = rPr.find(tag) + if elem is None: + elem = 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: +def set_heading_sizes(styles_xml): tree = ET.parse(styles_xml) root = tree.getroot() for style in root.findall(f"{W}style"): @@ -207,25 +228,35 @@ def set_heading_sizes(styles_xml: Path) -> None: if sid not in HEADING_SIZES: continue target = HEADING_SIZES[sid] - rPr = style.find(f"{W}rPr") or ET.SubElement(style, f"{W}rPr") + rPr = style.find(f"{W}rPr") + if rPr is None: + rPr = 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 = rPr.find(tag) + if elem is None: + elem = 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) -def set_widow_control_default(styles_xml: Path) -> None: +def set_widow_control_default(styles_xml): tree = ET.parse(styles_xml) root = tree.getroot() - docDefaults = root.find(f"{W}docDefaults") or ET.SubElement(root, f"{W}docDefaults") - pPrDefault = docDefaults.find(f"{W}pPrDefault") or ET.SubElement(docDefaults, f"{W}pPrDefault") - pPr = pPrDefault.find(f"{W}pPr") or ET.SubElement(pPrDefault, f"{W}pPr") + docDefaults = root.find(f"{W}docDefaults") + if docDefaults is None: + docDefaults = ET.SubElement(root, f"{W}docDefaults") + pPrDefault = docDefaults.find(f"{W}pPrDefault") + if pPrDefault is None: + pPrDefault = ET.SubElement(docDefaults, f"{W}pPrDefault") + pPr = pPrDefault.find(f"{W}pPr") + if pPr is None: + pPr = ET.SubElement(pPrDefault, f"{W}pPr") if pPr.find(f"{W}widowControl") is None: ET.SubElement(pPr, f"{W}widowControl") log(" pPrDefault: widowControl aktiviert") write_xml(tree, styles_xml) -def set_keep_next_styles(styles_xml: Path) -> None: +def set_keep_next_styles(styles_xml): tree = ET.parse(styles_xml) root = tree.getroot() seen = set() @@ -233,7 +264,9 @@ def set_keep_next_styles(styles_xml: Path) -> None: sid = style.get(f"{W}styleId") if sid not in KEEP_STYLES: continue - pPr = style.find(f"{W}pPr") or ET.SubElement(style, f"{W}pPr") + pPr = style.find(f"{W}pPr") + if pPr is None: + pPr = ET.SubElement(style, f"{W}pPr") for tag in (f"{W}keepNext", f"{W}keepLines"): if pPr.find(tag) is None: ET.SubElement(pPr, tag) @@ -244,7 +277,31 @@ def set_keep_next_styles(styles_xml: Path) -> None: log(f" Hinweis: Stil(e) {sorted(missing)!r} nicht gefunden, uebersprungen") write_xml(tree, styles_xml) -def header_default_xml() -> bytes: +# --- B4: Heading-Farben ---------------------------------------------------- + +def set_heading_colors(styles_xml): + 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_COLOR_STYLES: + continue + rPr = style.find(f"{W}rPr") + if rPr is None: + rPr = ET.SubElement(style, f"{W}rPr") + color = rPr.find(f"{W}color") + if color is None: + color = ET.SubElement(rPr, f"{W}color") + # Theme-Color-Attribute entfernen, damit die Farbe nicht aus dem + # Word-Theme abgeleitet wird (Pandoc-Default: themeColor accent1). + for attr in (f"{W}themeColor", f"{W}themeTint", f"{W}themeShade"): + if attr in color.attrib: + del color.attrib[attr] + color.set(f"{W}val", HEADING_COLOR) + log(f" Stil {sid!r}: color={HEADING_COLOR} (themeColor entfernt)") + write_xml(tree, styles_xml) + +def header_default_xml(): return ( b'\n' b' \n' @@ -259,14 +316,14 @@ def header_default_xml() -> bytes: b'\n' ) -def header_first_blank_xml() -> bytes: +def header_first_blank_xml(): return ( b'\n' b' \n' b'\n' ) -def footer_default_xml() -> bytes: +def footer_default_xml(): return ( b'\n' b' \n' @@ -292,12 +349,12 @@ REL_FOOTER = "http://schemas.openxmlformats.org/officeDocument/2006/relationship 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: +def next_free_rel_id(rels_xml): 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: +def add_relationship(rels_xml, rid, rtype, target): text = rels_xml.read_text(encoding="utf-8") new_rel = f'' if new_rel in text: @@ -305,7 +362,7 @@ def add_relationship(rels_xml: Path, rid: str, rtype: str, target: str) -> None: text = text.replace("", new_rel + "") rels_xml.write_text(text, encoding="utf-8") -def add_content_type_override(ct_xml: Path, part_name: str, ct: str) -> None: +def add_content_type_override(ct_xml, part_name, ct): text = ct_xml.read_text(encoding="utf-8") new_override = f'' if part_name in text: @@ -313,10 +370,7 @@ def add_content_type_override(ct_xml: Path, part_name: str, ct: str) -> None: text = text.replace("", new_override + "") 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: +def update_sectpr_with_headers(document_xml, header_default_rid, header_first_rid, footer_default_rid): text = document_xml.read_text(encoding="utf-8") new_sectpr = ( f'' @@ -341,7 +395,7 @@ def update_sectpr_with_headers(document_xml: Path, 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: +def add_header_footer(unpacked): word_dir = unpacked / "word" rels_xml = word_dir / "_rels" / "document.xml.rels" ct_xml = unpacked / "[Content_Types].xml" @@ -368,7 +422,7 @@ def add_header_footer(unpacked: Path) -> None: update_sectpr_with_headers(doc_xml, rid_h_def, rid_h_first, rid_f_def) -def main() -> int: +def main(): log(f"Ziel: {OUTPUT_FILE}") TEMPLATES_DIR.mkdir(parents=True, exist_ok=True) @@ -398,6 +452,8 @@ def main() -> int: set_widow_control_default(styles_xml) log("Anpassung: keepNext + keepLines auf Heading 1/2/3 + FirstParagraph (B3)") set_keep_next_styles(styles_xml) + log("Anpassung: Heading 1/2/3 in destengsblue (B4)") + set_heading_colors(styles_xml) log("Anpassung: Header und Footer einbauen (B2)") add_header_footer(unpacked) diff --git a/artefakte/01-lebenslauf/build/post-process-docx.py b/artefakte/01-lebenslauf/build/post-process-docx.py index eb4b848..b3e6193 100644 --- a/artefakte/01-lebenslauf/build/post-process-docx.py +++ b/artefakte/01-lebenslauf/build/post-process-docx.py @@ -3,23 +3,33 @@ post-process-docx.py ==================== -Wird auf das von Pandoc erzeugte DOCX angewendet, NACH `build.ps1`. Setzt -Per-Bullet-keepNext-Markierungen, die ein Stil nicht abbilden kann: +Wird auf das von Pandoc erzeugte DOCX angewendet, NACH `build.ps1`. Macht +zwei XML-Modifikationen, die ein Stil oder die `reference.docx` nicht +abbilden koennen: -3-3-Regel fuer Listen-Bullets: - - Eine Liste ist eine Sequenz aufeinanderfolgender Absaetze mit - -Eigenschaft im Body (nicht innerhalb von Tabellen-Zellen). - - Bei einer Liste mit weniger als 6 Bullets: alle Bullets bekommen - (Liste bleibt unteilbar — bei <6 ist die 3-3-Regel - sowieso nur durch Zusammenhalten aller erfuellbar). - - Bei einer Liste mit 6 oder mehr Bullets: die ersten 2 und die - drittletzten und vorletzten Bullets bekommen . - Damit gilt: nach Bullet 1 darf nicht getrennt werden (1+2+3 zusammen), - und nach Bullet N-3 darf nicht getrennt werden (N-2+N-1+N zusammen). - Trennen ist erlaubt zwischen den Bullets in der Mitte. +1. 3-3-Regel fuer Listen-Bullets (B3.5): + - Eine Liste ist eine Sequenz aufeinanderfolgender Absaetze mit + -Eigenschaft im Body (nicht innerhalb von Tabellen-Zellen). + - Bei einer Liste mit weniger als 6 Bullets: alle Bullets bekommen + (Liste bleibt unteilbar - bei <6 ist die 3-3-Regel + sowieso nur durch Zusammenhalten aller erfuellbar). + - Bei einer Liste mit 6 oder mehr Bullets: die ersten 2 und die + drittletzten und vorletzten Bullets bekommen . + Damit gilt: nach Bullet 1 darf nicht getrennt werden (1+2+3 zusammen), + und nach Bullet N-3 darf nicht getrennt werden (N-2+N-1+N zusammen). + Trennen ist erlaubt zwischen den Bullets in der Mitte. + Bullets in Tabellen-Zellen werden uebersprungen. -Bullets in Tabellen-Zellen werden uebersprungen — Compact wird auch fuer -Tabellen-Zellen-Inhalte verwendet, dort wollen wir kein keepNext. +2. H2-Trennlinie (S08): + - Nach jedem H2-Absatz wird ein leerer Trenn-Absatz eingefuegt. + - Trenn-Absatz: linksbuendige Bottom-Border, schwarz (000000), + 1,25 pt (sz=10), 8,6 cm Linienlaenge (right-Indent 4196 dxa bei + 9072 dxa Textbreite). + - Run-Properties auf sz=2 (1 pt), damit der Absatz selbst minimale + Hoehe hat. + - Ein schmaler-als-Heading-Border ist ueber den Heading-Stil selbst + nicht moeglich, weil Words right-Indent sowohl Text als auch + Border begrenzt. Deshalb separater Trenn-Absatz. Voraussetzungen: nur Python-Stdlib. """ @@ -37,52 +47,64 @@ DOCX_FILE = BASE_DIR / "output" / "Lebenslauf_Dr-Ing_Thomas_Langer.docx" W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main" -def log(msg: str) -> None: +# H2-Trenn-Absatz: linksbuendige Bottom-Border, schwarz, 8,6 cm lang, 1,25 pt dick. +# Textbreite = PAGE_W - MARGIN_LEFT - MARGIN_RIGHT = 11906 - 1417 - 1417 = 9072 dxa +# 8,6 cm = 8,6 * 567 dxa/cm = 4876 dxa +# right-Indent = 9072 - 4876 = 4196 dxa +# Border sz ist in 1/8 pt: 1,25 pt * 8 = 10 +H2_SEP_XML = ( + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' +) + +H2_STYLE_RE = re.compile(r'') + +def log(msg): print(f"[post-process-docx] {msg}", flush=True) -def is_bullet_paragraph(p_xml: str) -> bool: - """True wenn Absatz-XML eine numPr-Eigenschaft hat (= Listen-Bullet).""" +def is_bullet_paragraph(p_xml): return " bool: +def is_h2_paragraph(p_xml): + return bool(H2_STYLE_RE.search(p_xml)) + +def has_keep_next(p_xml): return " str: +def add_keep_next(p_xml): """Fuegt in das pPr-Element ein. Falls kein pPr existiert, - wird es angelegt. Idempotent (wenn schon vorhanden, unveraendert).""" + wird es angelegt. Idempotent.""" if has_keep_next(p_xml): return p_xml if "" in p_xml: return p_xml.replace("", "", 1) if "" in p_xml: return p_xml.replace("", "", 1) - # kein pPr: vor oder vor new_ppr = "" - if "", "" + new_ppr, 1) \ - if p_xml.startswith("") else p_xml + if ""): + return p_xml.replace("", "" + new_ppr, 1) return p_xml.replace("", new_ppr + "", 1) -# Regex: ein ..., optional gefolgt vom oeffnenden Marker fuer -# Tabelle () oder schliessenden Body (). Wir splitten nicht, -# sondern iterieren paragraphenweise und tracken Tabellen-Schachtelung. - -P_RE = re.compile(r"]*>.*?", re.DOTALL) +P_RE = re.compile(r"]*>.*?", re.DOTALL) TBL_OPEN = "" TBL_CLOSE = "" -def process_document_xml(xml: str) -> tuple[str, dict]: - """Findet Listen-Sequenzen ausserhalb von Tabellen, wendet 3-3-Regel an. - Gibt das modifizierte XML und Statistiken zurueck.""" - # Tokenize: ...-Bereiche markieren, damit wir sie ueberspringen. - # Ansatz: wir gehen durch das XML und tracken aktuelle Tabellen-Tiefe. - # Wenn Tiefe > 0: Bullets in Tabellen-Zellen ueberspringen. +def process_document_xml(xml): + """Tokenisiert den Body, wendet 3-3-Regel auf Bullet-Listen an und + fuegt nach jedem H2-Heading einen Trenn-Absatz ein.""" out = [] - pos = 0 + bullet_run = [] table_depth = 0 - bullet_run: list[tuple[int, str]] = [] # (out_idx, p_xml) Indizes in out stats = {"lists": 0, "bullets_in_lists": 0, "bullets_keepnext": 0, - "skipped_in_tables": 0} + "skipped_in_tables": 0, "h2_headings": 0, "separators_added": 0} def flush_run(): if not bullet_run: @@ -102,9 +124,6 @@ def process_document_xml(xml: str) -> tuple[str, dict]: stats["bullets_keepnext"] += 1 bullet_run.clear() - # Wir scannen das XML linear nach ..., , - # und sammeln Bullet-Sequenzen ausserhalb von Tabellen. - # Dafuer iterieren wir mit einem regex der ALLE drei Token findet. token_re = re.compile( r"(?P" + re.escape(TBL_OPEN) + r")" r"|(?P" + re.escape(TBL_CLOSE) + r")" @@ -113,49 +132,46 @@ def process_document_xml(xml: str) -> tuple[str, dict]: ) last_end = 0 for m in token_re.finditer(xml): - # nicht-tokenisierten Text dazwischen anhaengen if m.start() > last_end: out.append(xml[last_end:m.start()]) last_end = m.end() if m.group("tblopen"): - flush_run() # Listen vor Tabelle abschliessen + flush_run() table_depth += 1 out.append(m.group()) elif m.group("tblclose"): - flush_run() # innerhalb-Tabellen-Listen wir flushen, aber haben - # sie eh nicht angesammelt + flush_run() table_depth -= 1 out.append(m.group()) else: p_xml = m.group("para") out.append(p_xml) if table_depth > 0: - # Bullets in Tabellen-Zellen ignorieren if is_bullet_paragraph(p_xml): stats["skipped_in_tables"] += 1 - # nicht-bullet-paragraph in tabelle: kein effekt continue if is_bullet_paragraph(p_xml): bullet_run.append((len(out) - 1, p_xml)) - else: - # Sequenz-Ende: 3-3-Regel anwenden - flush_run() + continue + flush_run() + if is_h2_paragraph(p_xml): + out.append(H2_SEP_XML) + stats["h2_headings"] += 1 + stats["separators_added"] += 1 - # Rest hinten dranhaengen if last_end < len(xml): out.append(xml[last_end:]) - flush_run() # falls Liste am Body-Ende + flush_run() return "".join(out), stats -def main() -> int: +def main(): if not DOCX_FILE.exists(): sys.stderr.write(f"FEHLER: {DOCX_FILE} existiert nicht. " f"Erst build.ps1 laufen lassen.\n") return 1 log(f"Verarbeite: {DOCX_FILE}") - # DOCX in memory einlesen with zipfile.ZipFile(DOCX_FILE, "r") as z: members = {name: z.read(name) for name in z.namelist()} @@ -163,12 +179,10 @@ def main() -> int: new_xml, stats = process_document_xml(doc_xml) if new_xml == doc_xml: - log(" keine Aenderung — keine bullet-Listen gefunden oder bereits gesetzt") + log(" keine Aenderung") members["word/document.xml"] = new_xml.encode("utf-8") - # DOCX zurueckschreiben (mode='w' truncatet) with zipfile.ZipFile(DOCX_FILE, "w", zipfile.ZIP_DEFLATED) as z: - # [Content_Types].xml zuerst order = sorted(members.keys(), key=lambda n: (0 if n == "[Content_Types].xml" else 1, n)) for name in order: @@ -178,6 +192,8 @@ def main() -> int: log(f" Bullets in Listen: {stats['bullets_in_lists']}") log(f" keepNext gesetzt: {stats['bullets_keepnext']}") log(f" Bullets in Tabellen uebersprungen: {stats['skipped_in_tables']}") + log(f" H2-Headings gefunden: {stats['h2_headings']}") + log(f" H2-Trenn-Absaetze eingefuegt: {stats['separators_added']}") log("Fertig.") return 0 diff --git a/artefakte/01-lebenslauf/output/Lebenslauf_Dr-Ing_Thomas_Langer.docx b/artefakte/01-lebenslauf/output/Lebenslauf_Dr-Ing_Thomas_Langer.docx index d4cf75f..30769ce 100644 Binary files a/artefakte/01-lebenslauf/output/Lebenslauf_Dr-Ing_Thomas_Langer.docx and b/artefakte/01-lebenslauf/output/Lebenslauf_Dr-Ing_Thomas_Langer.docx differ diff --git a/artefakte/01-lebenslauf/output/Lebenslauf_Dr-Ing_Thomas_Langer.pdf b/artefakte/01-lebenslauf/output/Lebenslauf_Dr-Ing_Thomas_Langer.pdf index 6cf210f..3eacf59 100644 --- a/artefakte/01-lebenslauf/output/Lebenslauf_Dr-Ing_Thomas_Langer.pdf +++ b/artefakte/01-lebenslauf/output/Lebenslauf_Dr-Ing_Thomas_Langer.pdf @@ -179,18 +179,20 @@ endobj << /S /GoTo /D [ 82 0 R /Fit ] >> endobj 85 0 obj -<< /Filter /FlateDecode /Length 2849 >> +<< /Filter /FlateDecode /Length 3132 >> stream -x[͎# S"Ram[-A9$~$Q(U]=ӝ] j%şu?=v] -?i:%8w-^oߞ~591[~;kt_?(YhMLW$OI_n:,84+m.TBAb/︺힫PM$?)O5dͽ.wl4ĭ^Ir]5MH[M'+Iz4n6b\}>a {IVr42\))@ڴNPflȗ!̖,?"G^#)sL}Z&] ?An1 lxZ*$$jSuTTIO!>>jWƛ󘮰-*ug8^;Oj7NK!ey}ejF $ɚ,?|MS= 1ʎAˢICe_pl8XzIVSB,e&X駱P!b [JqE58abJ{B{p 6 F'LQmx86*@ EۮK2dzuJpIԴzr-B}̸2N}ܢ VLB#X9juR kGqf\&LsG Z䘁8 -i, [hC[UI)$Pbr4gI5j2Z1#NRjTr -e?N 50v'_D'7[uRu+K]SUKGтPЫw$u2q|aݔ`oKk[)X Q98)13n%Stg2.ڲBbT$UnE"NH[F4JK3.3 -˿8l͍9X%(/I5qLs@ETw-R%f Q4XJp"1C62Vp "q8%t,q>jnYkL&@І#a2hz ?"BTq#y@kWK%Qx pKm30 .^|7 ظ;O㑃YT8خm{ Kom^ 2 njؤԶo NY~/҅!X>Lx"MV#L} 'zm<Ehʝ,z WGu#ϯy噁,m*d x~å1ތ`ڄ8xM8rr#Ή^MhW[}"yh2 1#H@e`'GwVd=pLe3wM1t7< ]ta5j̝8ѥ=AːX ^鯿/z[<arz4vN=5Cp8ΧjqUdGXAy`:8eLuq=eQem?aQQ qwN>|Lbz俿 '.1\sE3m-#竐_ъÉ9=ۼl# ᐒTRE䖸wlS1iFW +x[͎# S"_am[- {K^?HQ\vLgQv#V?ˋj%ؤaýG_/{Z;oϟ_s?׃ufq<} +<e K;Ee"NeQ˟n&.z}ܼ_Rp/_*_W)oS| Wm|QS,[)5TÔTpLӫ=]iB5eC1-cj&5 %’Ek#k1;Sv!]]Th)ɩ+9>KtjI* D}N[hVbF½Cw)zn _;YI֢6 7Җ z [l2ač8fp]=wkd+rnCĽds +ks=܉'qXc,zmztD?A"ah<|<YMmU[zk2}Zi%SD4ATtjcMٌ!SccIJjx QU?|%'fj-0<guySt5ÇM] n8yM  v1]Ic˳-ѰӀ&lweԕԀ)/50S w".VnHi;c5ơ[CX~ac0xe$ΨDsS(:RfeIG +vs8C13dW @XmPRLJ;<h^ A޴4 )@b9YI^u(aiiA"Wn tLVNxRz/Ifc뚊/#ʡHci=5zhtA=̯pFEalVh„:nj-$&" ?b =ScC8F!&q3։ P2Eu=5٩yv7Uucȓ}vҙfEf83$2ZY4b)~*@I:CiT]HfqWzE 8ab47k_ B%L^mnJU(:2UId)Z 0 wD,2il[#4㖊sxK.q #[E>T'#`fsdj1 q=4i yk/kϛ3:s/1pBMZWvКl`AS:! 8KAnkkU|$RcO"異ǻ&ϯH;װ'>Y/-`>zDUPۦi5 ]Q~'* +zDq6A/9ƗBZGaujC(3'P œmbJ+/Ve} >a|4UXU"rE[f4>sk>xk͊W7Φ@7? wcz<і, !t*uSo9 DOdnxnq܇d']L +4@~M\ɡ(Y7\r1?xpwYRZsM+q=ETS8Y'$+|E}ǥIf +/DA`Aght`kvbgܺW@`v⺴N(,yLZz5b"YHCotNc2~M"':OR #0ql?iwx3QH<07 Zk&PYWbp֟X:.ws!:5gB?%_BbY(ޭ!MJ%y_С_paU/h5c +EerT S=Q& J&)jPSPAO-V2\q^Baz KY9JTkKnUʷ׋&'P\ضؑ2!!Zg3CMq4np oBlOzc1Zc( ٬2ܥ33(͟=KvMMwLf[5]6us0u;զE0.kIF=tZ҇|$d7! o?/I9kOBx$$P6-uwN`S<ի8UC[l^Izgu~J晩WPmt X ucFWW!h>^lj#1 +r/}3w+>Er2 x)$~?}ť5% F#,ǵH~[x%6N / +&AAꂽ*t,w͒k5& baU\o&A|xr|nؔbb"L~d!?.n" 0,uc E h~{)b{ bs"=lb}&X J ԏ2x0uuFgڥӴF,j4x~#jXNؤz[U1a8opzezgd $!Vlmmz=Tg1 +JQwdLo}OYPOT.w(md>-6yS> >> +<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.8) >> >> endobj 86 0 obj << /D [ 82 0 R /XYZ 69.866 813.476 null ] >> @@ -209,27 +211,30 @@ endobj << /D [ 82 0 R /XYZ 70.866 779.528 null ] >> endobj 6 0 obj -<< /D [ 82 0 R /XYZ 70.866 744.1 null ] >> +<< /D [ 82 0 R /XYZ 70.866 743.931 null ] >> endobj 16 0 obj -<< /D [ 82 0 R /XYZ 70.866 344.932 null ] >> +<< /D [ 82 0 R /XYZ 70.866 403.899 null ] >> endobj 84 0 obj << /Font << /F39 87 0 R /F27 88 0 R >> /ProcSet [ /PDF /Text ] >> endobj 95 0 obj -<< /Filter /FlateDecode /Length 3493 >> +<< /Filter /FlateDecode /Length 3940 >> stream -xڭ[IoWN!e%m0K!KY(Y?D!1Eˡ wL;xd l) -F+sm/Q|} rX$2,Me:,}ц#C[~jZM yv:(,tJTk|lF9-AsePhL[1aw!aɲM I5YՌ!"Ib\4oL dYI]-PoL;[g5Yu2:Yjefk"m?^]NOTLAjCJ %2w2 َ^Hdulo/ s)%V0hL,R2VȹvKR1  m8p7? $͖7XE(:IjCrzL6Ŷ1~wA"xH5$&J,J2FN(1G*tQ!loQb6E[/$+ ETpuCqGP[WŽ.L$z-Y}NR3HnX"~1OxV!tZpI,ąt+#F.r ܠQu"@;k_^gFV֠ *B@L1]:;Zf]h'~x$EF zfǒ תʃ&+Q!X@@~E L֧ppq%:I=: 3hDSuao}0T8Z}kR*`кR~;\B:tE :1+Ս`=nZkM2b L񓇚v kjpbILemXX_Q\7hF?Ƙ#w&9Ha?A)$RM@}k*{\h b/P~{/"Bc"ڨp)/W2fOPȱ{b.ފWкdb~N.|K]XIѸ0L~Q6a* 8;MgȉLԜ?-)9FG&-")(ZBmFZ6ʫ lzN.uJ;)]U%қ"ih,YH2{)>/mb[h:kj<|?sZ_8C V y"7ͣcڐG*Z$gSTĀ'326nI">IU=|4)g&Jс>r4GChL`P6 @o2ۢNږCR]+-4"&DNNCe#Ӭ;՛Tt2 -(#jCwŨ@Y)ԋddR j+\yxR -ZѠ5Dff.a]`RڄN跺8,]OaNBEI qecZ}S oҨUI1paTÌF*7&/,o!]cAFa#u ˝+kvshNtV }/U0 P5s"tagxi5dPvX6]@T7I#ް'@Aw`4 ǒAxH<20TTZLt0A5Y?iHYQe;9i`4`ya^2ݧtE)O,X6̍Qq+c}RFEj5kdi2DO*iʾ|Y$Fx=C#ݶN=xřgY<"RtA&R?Ot|nSm攠9E } &)բ\Hݍ<]M HuYJ&3u3GO >߱N5Ò(57;kc[ jqHa"X ujrWOXj^&ZsZydGKP_dF}ށt0e4 ٘*9]J&B5|={m'}4e./[rxK=(-]+,R6k+T'a<8k;n zk4!LRUE9vZ|jP݁=?49^0aQޟh?N- Mt?tΞ¨a -م+B_告la,k>*lWGwbʼbJ -:hYl8 CV-3a -2b#7r tFQL$rZ/L/l-/-ABW]e.n'-\Փp%Qo';HygiOҡ{Fw߆ U7H))4ðй&SWoƕgko[|,=:}XZSݍhfU[zIK w.پ'3~'7Tm9pg ('<+O8k-WF! ;k'91pDSxr')5jEYy.ߏe,Z(}ܩ5@RlJMz&0y)7wVEt~-u60Mdh[  +xڵ\ɎW$T2lm0'`_&dTʙ6ٙ-E-._7ߟ~z*Ktʙ˷tC\ŋ%8w B,V˷_B()kH[t=a}nZK˟J3%#^)%:0dxYh kO)۷_M2~ޭ>k{_K7>I:azF/#XH: +Eu.,EpcrA/qdH[@- u2e1N\3U2S9t5s.3%6X[0Vso=vyhM'&zy{r(& a8Yk`3.AOMD29C*\F e2O2 +yo@fQ͂b&s!p?ĥى^RK9|X.X/Qi4`4>Hdc$l`MAaJ/"͒5͑6IkZ31WV&šʌ^rUnWf9] *Tzjt(a-z 0\g…#YVz2@fDh:  (U3K4c;,^ދ 6G <?psݺ1RY[St)v:tpN.`Ux~cFiO~IP i"&(Xք!Fa+ 8 64g!#ZzD|c>6ɫa:':픮jS2}jlt-;)a)IMajA玌M0DDıq SyhD(p"Uu$pϦM3C l͓C,P- %0ŎrPl6i'M!%XF [ Qd2rS';:ɋ&(ٌE;5Rt"*UЩv@\5#a:bTxoa8( ߑ.(t  +Ѩ(+OG+b, &2 ts1S5P`8 ;pF53>8%2Aj?egh)|iFs8G#&cʘžuz8h&er4'mx/>_U@$ΦO`'bAerΟDT1gT偭KnȊ[xD$%Y"'Ixf-79N 4hHX5JB:*6`Ce;Lf< _g቙ 'zI(26aiID۝2=kgknGb C^/(h9^xHAO%Ěr!6IɚȤzIZ^"3Zρ0a*8l2)mB=\hfP,OY&t(4Kn5R;ejfUC][i"3ET +EESYЦ,ηIJIHEnfh#Lӑs/ D/eL|?쐉1W.R:3T ]\~^(4Y4"0p28M?Ww`!,n&&;Jۜio"Y y2 (Fw1fDʵIYq(g8nq{v?+jT.bj2F +4Hv7R;8ѣ$rοS#n&_"vZdh-[@فoxzX2;vnw{4ThLkZ *T2Wkِ*⒩慇V/!(abWelm'M\lNSrp#Ѥplcw4gh0vݏ!-5픳DJM.d'yv\JX|[qܫڶZ쳵7%Y>y.4sH\K+ K?ZoRkhXnTvRW֢AC B..PU.9A#ʂ,und?ɷOúrCsK=?Lx769[IdfYV5Atll{rzc"ך[rXi!Qeܴ-B0mSJ.ʶTuNQ.N"kY#3) +mO[Io!b+}t9j٦nݿx @@k*c2l|#1GVq4YV1C9LuՐV d6ڬO8kvӨʼnx1yebAFuɼ +7)!8 VV9'ޛׄ,3?}ɻC>}d`r(q&.vM0 +_'c'È=}:4>vڃIlb֡mԼ^ t387@μv1ݱ»U%Anc]PBZ'%47# 3n-籊 015ֿҎōVg]r#h++f쩼-^MPJm:߭9I EXL<_qǡe9 `DVS$FL4XhlGN4Zba~Ofm)z&L7x5- s{uf +4XJ"']ӣkZد_g[D/Rp%ys~P: Gr1BlB^z `4+[w{JLS1·mw^AL?+r_[..d!!9ɼi5L3w]? C endstream endobj 94 0 obj @@ -239,7 +244,7 @@ endobj [ 92 0 R ] endobj 92 0 obj -<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.9) >> >> +<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.8) >> >> endobj 96 0 obj << /D [ 94 0 R /XYZ 69.866 813.476 null ] >> @@ -248,26 +253,33 @@ endobj << /Font << /F27 88 0 R /F39 87 0 R >> /ProcSet [ /PDF /Text ] >> endobj 101 0 obj -<< /Filter /FlateDecode /Length 3441 >> +<< /Filter /FlateDecode /Length 3596 >> stream -xڭ[K#ϯdB#ȀoIf d%?$HVVk0vYϯ?.˛oo{(Y)g.& q/%X%/߾_~ -nA#~ԦwN]r{o:=*~?w] xqC gc -7x -ue?~yguk{~/?Iz /ϷWbYERxYƅźP:. t<^ -xo J!=|D{$(,-53D6isP̰y<ܤ(>mƶ7%(믙I_ -J_ϒg5\mYjj[VH -O&rkѹ!}Ie=@mtMJzb =3\pޭl^ŗ_/k;ѪI!hW%xڪQ[L C -qأ,Y)x.0 YΙ%v)?lK4vGOQtA8s1 vCofYOvNɆ? :S.ȋ]vG`v!2.q.W vcƐW -z4ѼdC!٢0FI9$1"1eF䑘ߍA<|F%Y֭f EfFb"Nhz dȴb^U1@t#'iC haW xKE%ȹ(9 @:-qO r:CAL-p_Ho7Gz - OQܲ@ }IeHclQ/lH< ,*t2a, ^6B4p!W *BJׂ<=2!2ݦD6_IZNODqm:s9\ WGk2*bā_]q>#BBql ('PX5r;8rV.A7%xR~bd;aj:b13{ E&*lfeO1$V4dDcmY̜.I:7cN,;u9{b57ͦJn=S4pSp`j B3N.ޮ46*RUUʾ$? r l#RR4+?c pڧst\U6p3-M$ΕۄU}~ՠbbm4Ăd35x[h mi~RqaQ"< e12E[CZcrQUTmɭ0ze0]7:RMbdþ"̏xgS[G+5̺#`YiU;RMVtnQjU%-$vP*.V 5-ԑbѲbJZ-l=y93!iС P9El9 -_k)78Fr(!7 3,wIJGybL՝l,F%^#c_ݗRwğ}kΰ$0]I;ӝ CnXHIkNSqhH婾{h}0SUPYu[ -\ $y#ժUxvfrTh88AT`*!ç\$4 8e͡$kT؁ɤj([Щ??igh;'$1ء}/(U:7Gɧ16!!YC,[nsĬ ?huNhP -O^Pi/P1n+mh_എvxux;fzfډI - m޳$/f{O틾8'#}Oqbpj^>bVuH/2jXNR]5Y]}wir0\UF)&ߕ:$N4R -j+yWr!6&nDG%ՀSZgQYtzvQ)uX6fsxWtduvS/,MkEc3)[OV&਻#RCSSwdj-l]FgSdPCY%9Nkuߟ"lo:].]XRImC:!1fS=rTE 1W4 +x\K-0m[- {K~HV,R, x-Yϯju.?)˺|uWKpZ,_ߕBP +5~^u.^c*\΃oL8]viWhq\~7LmDdWWĐI(=X7K}LٝxTB_8Si +IݠB>c̓>@፤?02ĩ“ +;KBIuߐt r!bV15V7{GڈuF|ƿ.ψIBZR\7΂"^0QȆn><6GGP8FgTYPϣJ M XK5qx#qB=MLKςSgS ~ +;cA|i# +MFIŌ,1rNWkާCə3|: ˖6=RǬEXaS k䠔d~k3Xl;;z4r q<Ԣ~=^P~sdMHXd/ƞO)[-zakjtĭ X(7]4(m@Ҭ _ui"(,##jAk lC6䛚*"m&Wr%7 +DvRqH7}&HF^Mi5Sƹ vj#|b1'6nqn}4~n`5%x,98S[4ӜgAmAh#y\k+2B ܻKǒG:m,2 +C` @H5ڰ +"Gl1J!OE^g 縱 Lʛ;.kNDIa> Y㡰8zT0j]l҉.o.{ZPDep¯}Zʖ p{,Ȍ>iʃ|6(Pcz@|ټܽ0"ZAޕ6 .Ts뀋x ~ϐB_dcΛ`7sc;/ZG5ދs't}3(3$7zhS-EJƻp5G jDzlގ)1:ĹmAxyYvCIS4mv9ikuWdy\F)ݿ!v(41SḬxTò__ݶ"lToKL[g+4K<;j:8ů?GIk9SP[^B˽t~E!';--ϵ[v4_݁v͌XP4l-.L؆p ݫNSRsMx#V _z:Za`ɳAXn.6 Ȗ*XA{iloQMuo{Z!jy%! մX%k#_lFإ. Dwҁ:Fg6x^5. +o^\Cȸ" &`+/=𵌍^ЛN*[5EFW7X +Q][NjYxٺ}Ba>fR5UWߪ6=1您)$qj%z(#G/×MǮ7``fC:,x= +ګ,ܫ,o@) Txe۸#km|7`,~ O}!t կN@<;^%_$MGWxg% ԫvY<ҹ|nuAm%fA8Ɉ~CQ_=;-8{ > >> +<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.8) >> >> endobj 102 0 obj << /D [ 100 0 R /XYZ 69.866 813.476 null ] >> @@ -286,23 +298,17 @@ endobj << /Font << /F27 88 0 R /F39 87 0 R >> /ProcSet [ /PDF /Text ] >> endobj 107 0 obj -<< /Filter /FlateDecode /Length 3477 >> +<< /Filter /FlateDecode /Length 3841 >> stream -x[K ޟ_1z gdբ.M~%(YI"|#G~;O -^??4LK0> uҧ9Ӭ>}~?rVh_gK&靚o.}56链~36 >)#`o6͍0d]Hߟ8;Sϟ?ݢ̩N{_ho?~5{&e>~?9o'l_?.'ES޳!̓ ^V7]OM#?}&"2In^rHB A -k曎3:T@BF*y[\5˘@͝U}}@*& 3<$ -gYėK"$V0v)Z7ܛ4xc?8SF+Rb}ˤM}LoQT !U-pYjR\4l)oüQZ!r΀@ϓ -LR/XN$݁(fH-T)ܠ4R {+dG;+ ipB+Cw6Y`(k>FVoeߔye*.DryT5+]B!hXIq{+.hyV>i(2I]3Ɵ?#' yeX1E9SU( KD6,z심;*t3i#AX#9s2ЛO`GҒH[ k]zt@;{+^TUakZDz]:)P8k"{1y@7O$8ƲiUT;Qh -|C{z7/E*nwNK[2-҅=Mt^ HOG,hroU]"clxc 76Tr2^r]:2O>E8Ψ)G)P?.yTbWR˳Szw^Q Mu^c<"#+P"K10=QH Æa(B yp~&Ul{8eR|8UFj0p(>-E~3VUꦪrXMح f#TB\&$0h(#kdq)T͜ (alRFRKZcCAo5G&%4Vb1^cQ 5D6&%`*ƽ'L;[I6W3KeF}ܖP>7#Ls5qސcx J%xHX&Nƚ e݄`X;n:@ Qx D"<0xV[2N"/w%:WH@t# /'35{C]kD787sa& x' sMlsjO?!N/̮fSFC&=))HZ3n5}HCh}j "T&.%E(W)4vl\'\cBr!>{Df>H'z81 bclZ?j-72f(!|AHs ӸN̚ /e nzC[ rj]B;bs68=KJk w% ݨ[Vw^sGZo/d2Yh_.˸&UdD?DR AEl>^f.fNrDn!ciel[עB6NjȿX -_׭ @ :(|y5QKjJ~lڱ#{hOE9(N3~왉oNTw0|0~YLZk+R_mZTX6(xߥ (T.Rq~d@try7Q٭TKVj`kE$r#!yR֩IbM -CvcT=gPŖD}LJ\3 -ImaJV|`9-eijف!| $Rk-C6 q$Z.pb.m/SX|v,v6#;sle2c\2"elES2܋f^;a/Ζ/eV;SYF9DiޛnM>\nI8T|ӟ79eZP:C wxcڝmwU%PaG;Ұ*- -(3U7{^1b#۠Q~>vRlp1m\SuL@]un8n>!Z속;˲!D]0eNsdؠzӿno#ws)A -ϲWp[/U6尛-ǃlg.&ƚk<ۿ"'Dד -R8\Wޚ03s 1\k΅!#>A ;f7kKp([VM 7uYl>͑sb.qҖ*#W;>.މ ਰ`:QٓaG3$=Pi|{ۘ6M,1&"bږ}7iy8Hu`a7)׶8 Vdެ^` M-w{2zHm\) 7@ڏ mY8]߶>^K=rʟ%Cu3~딩uLK%D[w;6싁ӰWxumɷ}oS}dL0˺\>QuZ^|:]CAk87lܣ.M "CO33A{ l C6\ 7-%mƎSpj ^>ލZaD˧ FٯB{O9~A_@uHZZ_qnG'gջH[ӏRdY8p44%A%#!׈{&$ ^-W~ٙ%9]pz}Ε Jw8fU-MwzlÁ\;*%^- oEIV'?Lx"wo-9ݨd}s M k'{2,e6īXPnNU [ד4?ysmn{0X]H1vz ~ -^iLWlrw~9:v&c^&$MyzL֩dNI i<^fѱE=,s=X; E#.Q0TAFdXt\vy(mQtم9?8-r%(g4}C1mYJ JLjjPH}GRGqu" +mZ q1Y8M5znzd销Q|嚴KT|bޛ YUqhm?SrqnchSl"%K+hX B@#raU'iLQ5O*Xxޜ(E ea/68r$$S&HaL?#2%!. 2$6=l64 3˴OH åo$gd4z.ASFnlpܥlKK=ܮ }hOqx7M,6\HOa&/u2*Rl#,T"`y6(]XРxrUnH6+[;m YǛg ȋ!٫G"]Mb&hŌ%2`  .c@C>څw8F{yRi]phg:]e"d3ZڍL|) 6?$)F`gE&!z ccŸ-HtH +~880 LnV[.HΣ% +ֹ U(ij18&7#]e 0ਹ5ZM6G1um=TݯC">$A& P-*ٰzJQ&%X @<9 ̕AhAUXݚԴi:%_DSr[2fʊ(=KgѦX aƙEea6\O(1TVƲ#l׋'7Oך[2V%lG(@ȚF1`'['Ҟr\  \l@ޗM##Nʏx ?Mmu+ Llm$ "%vrP-dO1/H@g$ 1J"dpIRú,G%7vIY5?*ׇ{DeLӥߙ| ߕYMI$z1-?FEfPjtT1?.yzޣz'= zH8`fvr}k*`(@1,jvSʆ'8 /R3jvȯd vm=ۦ-cbprr[׫\Vcϑ;2PzȌTg>Iex_s7[¹4R zFj!EBx&l|r?Rf?yRhs%IdQo^DB܋|KIK0>r_zW9(5iߏīLݏ1%b_o]QiH,Xh:Ԉ^ ĀՍnp;{(XI0R$m5WYyrS$|ѬR^gS`ջE;eOʪU-ѦJheͩNtp@b, kɗ b3sdnRV+(Z:;\K78&8Ċ%K冖ŏ$?DF-T'| +>ifHV 趩PW{ҪX.튵m_Jtնc1>rg-4Hkm|}wOE˱4ʹ sBgÛsӳOOد6NT +Ѿ#Rj8eB~JdVKwQ5/`ȱ596*J5|"UId.9Q8PC\Iyn7cT2U_JYaqqW+fM*-*T U y6rԤ(33 7Fs%UosQǘ a3N+CWjExZ^z9Նrm`Ϫ#뺆|+4m9铎Fi +n'5E\f_i1FoONiw-%4~;EIy7ԩe,s7S7]\[o-xO;ᚬaU<$Ɓliv}1d2Cɮi'|F;nsu~%tjo@;~Cù4.1^ڇDDvIY ,VL^AEdUm]VxQ:ES9 \bG~xWױYYMa*z7\]Q.;%TJ0u8Pٛ2z)t}mb쏽'8A[Һj߄+ OxrgzDZV..*ޔv0s1R`hg|"1: E#TNlnvp굍a:$iy}|pkx.$s$5 ״4M*]ފ;J-Jqj'Mb,FQ/*9_<-~N=vbIȆ¶Eh{x^In8}wӵ$g8b]wjn0J' T,^;z?eGd*>i"Ar[L5] +s㕢6==k'%Vi|ym 8B{͛KTߵ&vval`@Sv/Cp,Q;oYv7*ŭ2l!jt8g #7Ƭ"<$dC5?^]'p;x[σul&j#Jaؘp\z z{4y͞HhP4ݾ?F\ endstream endobj 106 0 obj @@ -312,28 +318,41 @@ endobj [ 104 0 R ] endobj 104 0 obj -<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.9) >> >> +<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.8) >> >> endobj 108 0 obj << /D [ 106 0 R /XYZ 69.866 813.476 null ] >> endobj +50 0 obj +<< /D [ 106 0 R /XYZ 70.866 293.162 null ] >> +endobj 105 0 obj << /Font << /F27 88 0 R /F39 87 0 R >> /ProcSet [ /PDF /Text ] >> endobj 113 0 obj -<< /Filter /FlateDecode /Length 3190 >> +<< /Filter /FlateDecode /Length 3599 >> stream -xڵ[Ɏ$W4} - tgu lXA-UC(UM&X^ >_/"qqyk/sf|~ʹ yJ;WU = JeSPyWd 1Wu#2~ gN./_\^Ƿ?ǭ`S/_3~~T/(f}ߨ`U-$gƆ&YWx?zOxj\NPU(Dj@nT=I ]q{JUuwt&8{iŁ'h ?ͧmE,-e(Y-x17I4cV&4 )8mqI#bW" ,#ʂeQF`Ad?z=/JDoW6Ԕ̍-9(TY뾎nyMiti0¤0±Ial0G=t' Sc*^1jh^Uk% <2dj"t_RD7CM `-Q֧{ŬlZ`FkPλV[0t^ɫ T75찡-`[h[T幛tMAoQjoEMnǛ|l*Ȋ$͐7:iڡ-1GGxj rfPOw̙,fˋ͂u bqV `RѶߐ n3+1b2,FRMW]cKcL6@1%t\5.`!ۂ& hBВv%2,7>1 L ׭k-ўku6 IHg[̺KE&jP -0g-= -jNP\[Н=A.t8g/%*M'aTt X&uĄc!k.(H"u\U -&Y]e@\ -<fىSE83HB qCVQF}ʤeuĔDFAȂV$MC{l(oQ oZD?}Qc7< QWEQ"֠\GbOB*>1δ-iE Gm|J!ܭO.MWI"ڋ)a&G)oyqTU,W5L.xtu%bfb&9:;:D[r8Seq}Qnd4DX#}QMrBw`boY휂?`)C =@Ⱥچq0_TB!\Rn~\Xˤ'D -I͕ڔa\!+EAHh},:IIrS!i.IXD'*úUћf ^]%[$amf >llCn.ɞcaJŖ`cCI5Н\V;mY{jδۋQec sΌ V[y~F ۙ$<*=1^bE2^֥?TL @2֝6p3~orVnxӊ̛wmQZ粋 И0H j&YwsTk&DLxx05u?]nhuVWfc>mbR󽣺 GKꓞXUZyV,mוEP1!C)#!575idt:.3,ta>C") -GU?}j*ݡ_EJA$6|4ӭV\ʼO5g$8[%&59,r M6CNvHY%pMIPW2w(O  f`>AtGZ c*7Y PY#32/yV\Ǽ,N.9*Gν4n 819P2gwhE7qhӗL\RRXm(%4^̡Jh,_3 ++lrL16 *콫xbum=GYU(}B>^9~;K4h/n][{QJݴ:حM +xڽ\ˮ$ ߯ࢁۏk$3JoEG(T{g/Ɔ1ֹ^RbKM/+'.Tן]V4ѭvy1-Zoo?Rn|~*lNBN;[~`gkeUζ\+Ä}hZ<; +74A+ +Jw!򇩟cZo-]`eS^ +Klsƞ:qRiK,?YUlET//b(6ȵ-߅Xt$-%T +rꍘnI7~BG+{%dÜ\|$6c҅iR,V _E3. *1O*5tM!J9OV +Ɯp +ڭݦ9ESLhçsKHJr{͒.Ć8 +/}\Rޢ~rehmzqv1IՉX<(".3E#B[,>&4Uf)u˩~0}u_6DĽzf) eCOqRݔd*t4l;o,a.uطɍ6 0*}̀Wr>ha ) ÀzHm1m)E;i$"Cqnl>[x?B`|;M8vK%ɢX>lsxં\9J%$a:<#lʁ3)#  + #ÿ:4́i PMyU6#78loO5?2n{XdxuDSnL5l[:]ô;. `/SԹkyD M՗YŒxbL<'#W&>_+f-ᐷpzDŌpomWPA1cBsv@US5%#j,KFa.Hu чS~N>1z,\U"ʼ ӞFJKYI,34 +'ǹWggqyY;m7[v/h ֆU=FVo +FrKw/<螬oA"+=ž4P=-3E(=,Yi˝HK1E&4)!x\wfgZyuOQKT &%g~g +lO{wX]HY%.=h-y -y9 GcfݟSM#=Y2 s;Se?K.=9|8z$15' ٟ +{L|t<^O24<@90&{ܖ><&g_,wR%t!R 5"|a.*~aE=b60%䱐7(Zk/IT@|[]ԛU #rΫ-ȿd[aqd'_@ily!Ӊ<)3fs^vrhg|~,pj͈la9?vaJ4(6PbLPݿOAxFo^n-g݇#RrE'y"K8\}؛]>:އQFsdV5!X‰~-G[fc NM38#SUTrI3efr,׼ /31l.~jMh/OtzxG|Ò};M]!DBF<_W}w'j )(a]ⷅ|bkt5nLINjBUge-ItZFjD$BcK5'B4dՍ:E}>p &`-rG93i6X!N MS/՝:Hiq̢_11R[ +a?8̲2Փ#RfV_///Ti}u'hE׵`fyPP,>t;;1?>|sYS*ɄtPߖyJEâF])_ETћ6Ө4Tݟƾ;ujS`EEDt6DZscIYs]F*j.pkv65dÌW lE^\:`@AM/iLeOo/;vR_Esd7zt싡yIVvS䤨2NDȍ\K*|%<(qG'޴Ԫ=WY,tx +::LݜHo endstream endobj 112 0 obj @@ -343,35 +362,30 @@ endobj [ 110 0 R ] endobj 110 0 obj -<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.9) >> >> +<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.8) >> >> endobj 114 0 obj << /D [ 112 0 R /XYZ 69.866 813.476 null ] >> endobj -50 0 obj -<< /D [ 112 0 R /XYZ 70.866 678.213 null ] >> -endobj 111 0 obj -<< /Font << /F27 88 0 R /F39 87 0 R /F44 115 0 R >> /ProcSet [ /PDF /Text ] >> +<< /Font << /F27 88 0 R /F44 115 0 R /F39 87 0 R >> /ProcSet [ /PDF /Text ] >> endobj 120 0 obj -<< /Filter /FlateDecode /Length 2930 >> +<< /Filter /FlateDecode /Length 3048 >> stream -xڵ[Ɏ$WL4 -ʪ :ٰ.} F̥jFTe3".]7o4Lpz僺KTK -ᒔZї.+eR_*.ǹsOz,[f:*0Ny1m׽wպf`5O0Db,&ڨe-ƇZ%F?d "uM iڠ644"cB,Ԭ);(ԫ/V>~$a C#vI<\ V4k;gl#O*^>;9F \eO5"J6YݜWCIT6LG;@*؉hִ0ewCt00B䤥dDXe%(Bm3y_[(x `9Bw3[b95xL#Ԑ؍Vgf-94+B/,!\c,`>!%l-ǰШ*> XƎ̗.U*|WD3tĺu^枥JA%f*P{QJwHg@N|Bd7 'Ƅb/ 3Pۼd{W(35g1 -YS mp?+Y%*iOfax FL34܂9Mh^3qMx]L@08"+B/U5㫥I`o>SKH5Yͳt $ELY #,Ol{n"3簥nyipppm VWVdߢYFpZ&.< h󔆒lʏvexNNݷ>G: [P#*aFXB'(K$ܣWE3(IWWX?X&ڏ}j7p1oB8\ vrc|4-N|xAP'OJU -zs"M8^^S\C?YD:l8O?Nc~ -ܟ2cK?ª!64ԨN ;_\wnLtTAKf4L$)*]pBdVh5H`'65#U)_g/ -/pL4)Ꮣ{6*r;6OSa@zF6ŦȢ -m< { f9ɇI*JCHs]!o0E8f{bZUr"PR$ v zI61|TBB=i0lt\?nUؼlCBM ZI;ƞ*S%&\P MY"x޷]g̋(F0!Dieim-. ԞǂU0[Q," G"ۋQgik⢻g3m[L)2Z Qƌa ,MҬ`{U pUT*I*d\}+/=27-PYAh?J<#kitw ,B٣ɩUf%bk꽣d8ī[/D`F,L3 -e% rNx΋ODk㱋ߐ{ -1^̾iY-;a4zAtPPa|H n, 1'W6w%rbtGMn<"g2̒cz4`'y8N pBQun=|ԇZUjpoTVE\ŝJ :?3:傲sOm;h'i0E٘ K'wyK?tQa(\}r;T Si7-%kߩÂZ/-cí6MSxi쨄E\V!/~'52B.:;~8#&ZR2||s{CoN=1Mw%7_$kuXWTONff> +xڽ[Ɏ$ WT=BY dsqZHT, j2;B+E>>Ju._^~}ef| %*59 .)A)pרwwKjwN+jo&?~OuK___OjH2ǫI}Cir2?wZ +?`g/ח[q|{:39k_&QYgi|ImBpck}U7BsM"Fiv=y@Tjt W9 n5xz2ky!i,ڹ)'hQY4pC }T͸ 6cWKL#~rV*M5p+}-̮B} >Bۤ)mSI38ʮ6dSSGam t&u5I&rɓPtgk У~UeHZ)̞y׊-HQuYd憡h#SE @ mtpmw܆Oft}ht$'xkiLhg_6׵ F}v/~xmIy\+GU" H<֕56W=BAt7iIMʆራtlW42 z뷐@5 )}C gw`݀d@Q34畈͐1qU"߇SpLנ&]oEs0o^ߛmr1:r '<ӊ^?A= +P5'p7vJ_g-n;fW]g^5WoEc;B{&a} \G1.THi1F-]8L3g 94H:8! Rh%.I؆" 3IiTXcQ)|m&"Yn£_CqeQwSM \sco(S0rb{e\j# tW$9-$>]LQseۯ~@ +|\65B:3$2 ȦĴ>y}4dhQJM2EFM,H{%;OJ}Tr P 0?L`Y4M:/`KXR֤ǁ +StfHKK,- m =tۤxhmHȞP}]`4`Rh,4.|/n0:GV3Z$D+ $>`$ 5 cnqUPax>C*Eq?63.m hz(lj8mTX7Oiԣe0еos3`gw-&hN'Q%qUwun]4f|^R<3ޤ5 +b؂AR[TС$ڷڇf +'<%9ܺpwGtGl],eB=]ϝk\K,ŵBވ`FO&Γ[88}ԝrg3Iaıw!i\Y[XOCYÎj:Y4-WB +͠s9%h7P+xNǕ=^}:0xX;xܤᮓ쿬ԯ + 4QÓiEDS-= > >> +<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.8) >> >> endobj 121 0 obj << /D [ 119 0 R /XYZ 69.866 813.476 null ] >> endobj +75 0 obj +<< /D [ 119 0 R /XYZ 70.866 510.077 null ] >> +endobj 118 0 obj << /Font << /F27 88 0 R /F39 87 0 R >> /ProcSet [ /PDF /Text ] >> endobj 126 0 obj -<< /Filter /FlateDecode /Length 2654 >> -stream -xZɎ$7WF - TeVm<}3|`|>/FR,hIA=B_~O}ˢ^ }K*zZ+pZFkk~zLyx}6`ӕɷcLtKS'[ڗw<47!ץ֦=.kG˗ -vxeO?<y߼x_҃ߏiY2:5=s,j鿌l\pzͻ+3Wp D,5gnn=]{!yfgi=f-Ah 8po i[=->-:m{Q]{QvhFє.Sڒ(\tAX`2"doR4Tkk#/qbX kTQwu$e9KfL/a%N+( ܆Uo&VW hDcUG.a6 F-.̃+Y~ԭ5iH:A&a/M J\ۊ]~\R2~5 /0pYl.Q^uy]/$GV_zΎlfl5j ߁x Z.N[eb+1S@f-ƙL'01CǤ:a'ӸŶb˸O4ex>twTj. S01qaӴ88<{ M -2;2r^GĶHb^E`]镂9CzjLtnz -qt/F@1ղj4jGޫZ x jlA_GҡYT4E"6lYvA?iV >;߉3fh=v/ɞÒn̞FiMlr8㧸VDtr..tJccn?I䄐T l0 4@/f ,i72W!$Lvj_@zC?lr|LMyR4䤄yTe1i%S+Vg4;]I6ꂚ";h[TtfFʮ">7-&`+X?v"n dO!TWl$"n?dvmv9yfӕJ58QJ%A*&=2ns%/#4'C -ʦRr&Hֶc1+hCpɸ`H6 Db:W:/n+7"Qfpg**dV"EQǧn'*m54_݄S{ jS 0U -!m逷ĜZ+uJY7S:DP(;KI&wopodqoiX?׾R'>\Ɉ7& - |DU9Mt_2StQUs;`ƅ( S WapkK?һe2r2~'6DޣqNc jTԻuSB |VS-3ޭTU -S{KjY$4zͭw,8n+a$qIe 6J !v;;;Pa|K{ΗDžs7p]U֡A2NMHD9)Bc!}|2(±.-DեAYH`?e/fC/0OU g?kO mV>ϥ8jj;M=Rw§V/O"J|[|%6 pN%| fCFlc<99+(2abW9Einoi"qЁ|>bѳw޴E4KX-Lv9+rD2Sl  Ccye4` Rz?GmqY(omxlކjڈ:;Ko|'ؔ.'q\83MLi ,k5F a>΍ELu2r ʞ "r(ڶq!8lXNw0n#~q8;; B@0J ZP&tr(p  -k;J>k|hTRFǧ/<E)i\_Yi67*EZP|k}cINfHnv@@$j=$ N,'%Oҷ~ISN -9kjX$a7tC$>};Qi<4_g\7:Ma0^W_^֋x.>5iQ>ᮍU6]k SSS%8c/:NzkXq}ۧcoØHnxo1}Ϙ+ wPE#q~}3$ʲHs|uHqۜ8%nchC.X>"L]²6tYK-giKȝ;>8Xr"LDZ#Fo-]l kmRw??fO*D0tx0|Y%js^k~U+͢Chͥ!(/yp}eK#ƥG=kJ'6X{`;xǖU#[YXϯ KN~%4).r -#9m *kpf*>{ -endstream -endobj -125 0 obj -<< /Type /Page /Contents 126 0 R /Resources 124 0 R /MediaBox [ 0 0 595.276 841.89 ] /Parent 89 0 R /Annots 128 0 R >> -endobj -128 0 obj -[ 123 0 R ] -endobj -123 0 obj -<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.9) >> >> -endobj -127 0 obj -<< /D [ 125 0 R /XYZ 69.866 813.476 null ] >> -endobj -75 0 obj -<< /D [ 125 0 R /XYZ 70.866 570.185 null ] >> -endobj -124 0 obj -<< /Font << /F27 88 0 R /F39 87 0 R >> /ProcSet [ /PDF /Text ] >> -endobj -132 0 obj << /Filter /FlateDecode /Length 2923 >> stream xڭ[Ɏ$7W] @@ -435,58 +418,58 @@ xڭ[Ɏ$7 ( u.,r#6FԚMCX0#cw oު٬y )I+հr%C䅳Ыx$\I2ƂbX|Y-Ff %,H6bUq t 709Yo]wVޯ$H~b;ۚ7鋷j$N!o|Ek5jHs M'ļF6s$&u;#`4 Vm e nijTd/rR `6Y 9Nj/jGP5*ؤ 1Ҹ #!TPvD9/BVƑ]0JiB LrCklz\dz[AҼ`cO ݌,ŔF{a7ք[o_`'Eg+m CN$'R/wK蓜DII~{jn<ǭPB$u]j{qȍrݦ].x6V^Ydg NR"0^E +'ckYIB Jz4zbq(v^"\L"y5)苦<ˎ/2k[Ig$ܪ*|ͩ;3tƚCTDEd1h7V!e,_׈=AxR㗂iq* ņ~A{51Qd@u !Sɖk=K#wʖX4PE:4lal;.  5/I/J f~ vԉ9e5)o'fޘ'gG[Ri&a>5*ؤ 1Ҹ #!TPvD9/BVƑ]0JiB LrCklz\dz[AҼ`cO ݌,ŔF{a7ք[o_`'Eg+m CN$'R/wK蓜DII~{jn<ǭPB$u]j{qȍrݦ].x6V^Y*58IQF4?8܄ endstream endobj -131 0 obj -<< /Type /Page /Contents 132 0 R /Resources 130 0 R /MediaBox [ 0 0 595.276 841.89 ] /Parent 89 0 R /Annots 134 0 R >> +125 0 obj +<< /Type /Page /Contents 126 0 R /Resources 124 0 R /MediaBox [ 0 0 595.276 841.89 ] /Parent 89 0 R /Annots 128 0 R >> endobj -134 0 obj -[ 129 0 R ] +128 0 obj +[ 123 0 R ] endobj -129 0 obj -<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.9) >> >> +123 0 obj +<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.8) >> >> endobj -133 0 obj -<< /D [ 131 0 R /XYZ 69.866 813.476 null ] >> +127 0 obj +<< /D [ 125 0 R /XYZ 69.866 813.476 null ] >> endobj -130 0 obj +124 0 obj << /Font << /F27 88 0 R /F39 87 0 R >> /ProcSet [ /PDF /Text ] >> endobj -138 0 obj +132 0 obj << /Filter /FlateDecode /Length 847 >> stream xڥVn0 )QGI]m[nEOz.{Q"eKv ՂHJɏdxJmp0'ÞO8)8X YeXY;p?5Tax_YM8wv`d𯉰:|~"7-Ь!l)<5EVqqxMHy1 $͹p*3pr' s]q-1#m}Ğ=H>l;|= E7|vZpXqF݆(ʪ)mW}n (^&=aV5|{f f4qݾp>w7;3ߩU|$LDR|Slpٯ#uɗ2Wew!w]_Ҁ;"4Sɬ(CƆ~bAjmZ]*cNW2Q'iP:BZ+S`$wlLay}U] d qN -ؕaw۹KesS!ۼG c>=Q?ϛIY~0CK}VE) hdىqaⲾlhF {M#H4Bq#N/_P :ו483'aAG鬹6koT; +ؕaw۹KesS!ۼG c>=Q?ϛIY~0CK}VE) hdىqaⲾlhF {M#H4Bq#N/_P :ו483'aAG鬹6koݗT9 endstream endobj -137 0 obj -<< /Type /Page /Contents 138 0 R /Resources 136 0 R /MediaBox [ 0 0 595.276 841.89 ] /Parent 89 0 R /Annots 139 0 R >> +131 0 obj +<< /Type /Page /Contents 132 0 R /Resources 130 0 R /MediaBox [ 0 0 595.276 841.89 ] /Parent 89 0 R /Annots 133 0 R >> endobj -139 0 obj -[ 135 0 R ] +133 0 obj +[ 129 0 R ] endobj -135 0 obj -<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.9) >> >> +129 0 obj +<< /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [ 517.436 29.433 525.406 43.381 ]/A << /S /GoTo /D (page.8) >> >> endobj 91 0 obj -<< /D [ 137 0 R /XYZ 69.866 813.476 null ] >> +<< /D [ 131 0 R /XYZ 69.866 813.476 null ] >> endobj -136 0 obj +130 0 obj << /Font << /F27 88 0 R /F39 87 0 R >> /ProcSet [ /PDF /Text ] >> endobj -140 0 obj +134 0 obj [ 1 [ 583 ] 3 [ 572 488 ] 6 [ 511 307 508 ] 11 [ 565 264 ] 14 [ 519 264 848 565 550 572 ] 21 [ 364 ] 23 [ 320 ] 30 [ 623 ] 34 [ 565 ] 42 [ 784 ] 44 [ 679 588 ] 48 [ 577 557 ] 56 [ 600 ] 60 [ 600 ] 62 [ 600 ] 67 [ 600 ] 90 [ 273 ] 92 [ 293 ] 112 [ 328 328 ] ] endobj -142 0 obj +136 0 obj << /Filter /FlateDecode /Length 23 >> stream xڋIA@*_| endstream endobj -143 0 obj +137 0 obj << /Subtype /CIDFontType0C /Filter /FlateDecode /Length 2729 >> stream xڥW PSpiKKFPkT\qC+U`  H#Z,.VEťEOVkf:әwgns_rXΎaYV<}n@IfIa^ ap\]ȑ*~؟RFz*8Ny^Y*31+)0B'g3`;IWHJ14g.^mܣUyɪdU!,.42A5jxM @@ -499,10 +482,10 @@ B* (:TxAe+ATx2|pz$\[ZVVDVHfmT+g1'i2#e[Gv"w0qsFRSw-e3!<~$.$b)&Pp4q6Tl*Eo?mGg/[C5XKͬzW"&[9|8o'Z3XPm|gI:m]9CYⷻ!|QZYpKKb&&9NX28{TYg\J5L& t>55(H~ޞg)ln^o^c2ǥC 8H%M\uAMeSŞe+qk sj++2RE!\ARyÿM>q߉n!zI+,Ƞjb_y ϸLQa+۽^;:YL,A]/B~nr:zl%Cde+Sފ#`Ձn^@#؎}E^:Ŕ'M0 |0wnꭌtCOc}ROK_[9, endstream endobj -141 0 obj -<< /Type /FontDescriptor /FontName /SSHRCC+IBMPlexSans-Italic /Flags 4 /FontBBox [ -245 -245 1180 1120 ] /Ascent 1025 /CapHeight 698 /Descent -275 /ItalicAngle -12 /StemV 91 /XHeight 516 /FontFile3 143 0 R /CIDSet 142 0 R >> +135 0 obj +<< /Type /FontDescriptor /FontName /SSHRCC+IBMPlexSans-Italic /Flags 4 /FontBBox [ -245 -245 1180 1120 ] /Ascent 1025 /CapHeight 698 /Descent -275 /ItalicAngle -12 /StemV 91 /XHeight 516 /FontFile3 137 0 R /CIDSet 136 0 R >> endobj -144 0 obj +138 0 obj << /Filter /FlateDecode /Length 496 >> stream xڅы0WkڴEawq5%p䫻p\&d:)ùjGζ5#)ߪ6ID-ڽidI=Ųu`Ņߢk%quxG?òXmNt}ue]ޮOV?)qv7Q?tu_Y9ʛ &W6?Z0;A3U~ g;0\^΅>4t&[zsɇQdj}d.퉜#ƃٌ):ޫ3Bl񕵻bLFQVLL M_q1We> +<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /BaseFont /SSHRCC+IBMPlexSans-Italic /DescendantFonts [ 139 0 R ] /ToUnicode 138 0 R >> endobj -145 0 obj -<< /Type /Font /Subtype /CIDFontType0 /BaseFont /SSHRCC+IBMPlexSans-Italic /FontDescriptor 141 0 R /W 140 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >> +139 0 obj +<< /Type /Font /Subtype /CIDFontType0 /BaseFont /SSHRCC+IBMPlexSans-Italic /FontDescriptor 135 0 R /W 134 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >> endobj -146 0 obj +140 0 obj [ 1 [ 534 ] 3 [ 580 503 580 549 324 528 ] 11 [ 568 250 250 527 272 873 568 560 580 580 367 487 351 568 492 768 507 499 464 641 653 621 671 583 559 695 707 400 510 634 501 812 707 708 606 708 640 581 572 678 609 891 613 593 580 600 ] 59 [ 600 600 600 600 600 600 600 600 600 ] 80 [ 694 891 399 ] 86 [ 588 780 ] 89 [ 565 272 ] 92 [ 292 272 292 ] 101 [ 474 ] 103 [ 475 ] 112 [ 335 335 ] 118 [ 383 ] 122 [ 927 ] 142 [ 600 ] 157 [ 396 ] 194 [ 567 ] 200 [ 534 ] 314 [ 560 ] 344 [ 640 ] 355 [ 568 ] 476 [ 708 ] 515 [ 678 ] ] endobj -148 0 obj +142 0 obj << /Filter /FlateDecode /Length 44 >> stream xڋ`xpH71pis / endstream endobj -149 0 obj +143 0 obj << /Subtype /CIDFontType0C /Filter /FlateDecode /Length 5351 >> stream xڭY \SW1T @@ -556,10 +539,10 @@ x K+K}gfo]w endstream endobj -147 0 obj -<< /Type /FontDescriptor /FontName /KKCYEW+IBMPlexSans /Flags 4 /FontBBox [ -260 -245 1241 1119 ] /Ascent 1025 /CapHeight 698 /Descent -275 /ItalicAngle 0 /StemV 91 /XHeight 516 /FontFile3 149 0 R /CIDSet 148 0 R >> +141 0 obj +<< /Type /FontDescriptor /FontName /KKCYEW+IBMPlexSans /Flags 4 /FontBBox [ -260 -245 1241 1119 ] /Ascent 1025 /CapHeight 698 /Descent -275 /ItalicAngle 0 /StemV 91 /XHeight 516 /FontFile3 143 0 R /CIDSet 142 0 R >> endobj -150 0 obj +144 0 obj << /Filter /FlateDecode /Length 736 >> stream xu]k@+f/ @@ -572,22 +555,22 @@ J endstream endobj 88 0 obj -<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /BaseFont /KKCYEW+IBMPlexSans /DescendantFonts [ 151 0 R ] /ToUnicode 150 0 R >> +<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /BaseFont /KKCYEW+IBMPlexSans /DescendantFonts [ 145 0 R ] /ToUnicode 144 0 R >> endobj -151 0 obj -<< /Type /Font /Subtype /CIDFontType0 /BaseFont /KKCYEW+IBMPlexSans /FontDescriptor 147 0 R /W 146 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >> +145 0 obj +<< /Type /Font /Subtype /CIDFontType0 /BaseFont /KKCYEW+IBMPlexSans /FontDescriptor 141 0 R /W 140 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >> endobj -152 0 obj +146 0 obj [ 1 [ 569 ] 3 [ 608 517 608 562 361 552 ] 11 [ 596 286 286 577 303 894 596 564 608 608 404 504 383 596 538 841 ] 28 [ 534 518 685 667 651 697 607 585 719 724 432 559 696 530 819 724 714 656 ] 47 [ 674 624 584 694 650 973 ] 55 [ 607 600 ] 59 [ 600 600 600 600 600 600 ] 66 [ 600 600 ] 80 [ 721 ] 82 [ 403 ] 86 [ 588 780 ] 90 [ 310 ] 92 [ 330 310 ] 101 [ 535 ] 103 [ 539 ] 112 [ 338 338 ] 118 [ 460 ] 194 [ 647 ] 200 [ 569 ] 314 [ 564 ] 344 [ 692 ] 355 [ 596 ] 515 [ 694 ] ] endobj -154 0 obj +148 0 obj << /Filter /FlateDecode /Length 39 >> stream xڋ?o`Xp70`2E ' endstream endobj -155 0 obj +149 0 obj << /Subtype /CIDFontType0C /Filter /FlateDecode /Length 4888 >> stream xڝ9 TW4U)[6U ;ŀj ʾ:PGKF%n b0q4&5.xPx8?9Ι8Mzw @@ -614,10 +597,10 @@ ZQG + endstream endobj -153 0 obj -<< /Type /FontDescriptor /FontName /OANFAB+IBMPlexSans-Bold /Flags 4 /FontBBox [ -307 -275 1332 1150 ] /Ascent 1025 /CapHeight 698 /Descent -275 /ItalicAngle 0 /StemV 103 /XHeight 525 /FontFile3 155 0 R /CIDSet 154 0 R >> +147 0 obj +<< /Type /FontDescriptor /FontName /OANFAB+IBMPlexSans-Bold /Flags 4 /FontBBox [ -307 -275 1332 1150 ] /Ascent 1025 /CapHeight 698 /Descent -275 /ItalicAngle 0 /StemV 103 /XHeight 525 /FontFile3 149 0 R /CIDSet 148 0 R >> endobj -156 0 obj +150 0 obj << /Filter /FlateDecode /Length 690 >> stream x}]kPE+<kW4aR06v$'+0LGPsr1ǫo"D߆ŵթ$U]~9msSe[׉dڔEwopݥEK^%q~Nb9,?l'O¡} E*82?\U m2O@ѥ^|2oFm0H%"F?cм=;n}fb]soS(n#`>3z?-W9|k'Yeٕ.!s1K> +<< /Type /Font /Subtype /Type0 /Encoding /Identity-H /BaseFont /OANFAB+IBMPlexSans-Bold /DescendantFonts [ 151 0 R ] /ToUnicode 150 0 R >> endobj -157 0 obj -<< /Type /Font /Subtype /CIDFontType0 /BaseFont /OANFAB+IBMPlexSans-Bold /FontDescriptor 153 0 R /W 152 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >> +151 0 obj +<< /Type /Font /Subtype /CIDFontType0 /BaseFont /OANFAB+IBMPlexSans-Bold /FontDescriptor 147 0 R /W 146 0 R /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >> endobj 89 0 obj -<< /Type /Pages /Count 9 /Kids [ 82 0 R 94 0 R 100 0 R 106 0 R 112 0 R 119 0 R 125 0 R 131 0 R 137 0 R ] >> +<< /Type /Pages /Count 8 /Kids [ 82 0 R 94 0 R 100 0 R 106 0 R 112 0 R 119 0 R 125 0 R 131 0 R ] >> endobj -158 0 obj +152 0 obj << /Type /Outlines /First 3 0 R /Last 79 0 R /Count 1 >> endobj 79 0 obj @@ -715,187 +698,181 @@ endobj << /Title 8 0 R /A 5 0 R /Parent 3 0 R /First 10 0 R /Last 10 0 R /Count -1 >> endobj 3 0 obj -<< /Title 4 0 R /A 1 0 R /Parent 158 0 R /First 7 0 R /Last 7 0 R /Count -1 >> +<< /Title 4 0 R /A 1 0 R /Parent 152 0 R /First 7 0 R /Last 7 0 R /Count -1 >> endobj -159 0 obj -<< /Names [ (Doc-Start) 2 0 R (none.1) 75 0 R (page.1) 86 0 R (page.2) 96 0 R (page.3) 102 0 R (page.4) 108 0 R (page.5) 114 0 R (page.6) 121 0 R (page.7) 127 0 R (page.8) 133 0 R (page.9) 91 0 R (section*.1) 6 0 R (section*.2) 16 0 R (section*.3) 50 0 R ] /Limits [ (Doc-Start) (section*.3) ] >> +153 0 obj +<< /Names [ (Doc-Start) 2 0 R (none.1) 75 0 R (page.1) 86 0 R (page.2) 96 0 R (page.3) 102 0 R (page.4) 108 0 R (page.5) 114 0 R (page.6) 121 0 R (page.7) 127 0 R (page.8) 91 0 R (section*.1) 6 0 R (section*.2) 16 0 R (section*.3) 50 0 R ] /Limits [ (Doc-Start) (section*.3) ] >> endobj -160 0 obj -<< /Dests 159 0 R >> +154 0 obj +<< /Dests 153 0 R >> endobj -161 0 obj -<< /Type /Catalog /Pages 89 0 R /Outlines 158 0 R /Names 160 0 R /PageMode/UseOutlines /OpenAction 81 0 R >> +155 0 obj +<< /Type /Catalog /Pages 89 0 R /Outlines 152 0 R /Names 154 0 R /PageMode/UseOutlines /OpenAction 81 0 R >> endobj -162 0 obj -<< /Author(\376\377\000D\000r\000.\000-\000I\000n\000g\000.\000\040\000T\000h\000o\000m\000a\000s\000\040\000L\000a\000n\000g\000e\000r)/Title(\376\377\000L\000e\000b\000e\000n\000s\000l\000a\000u\000f\000\040\000D\000r\000.\000-\000I\000n\000g\000.\000\040\000T\000h\000o\000m\000a\000s\000\040\000L\000a\000n\000g\000e\000r)/Subject()/Creator(\376\377\000P\000a\000n\000d\000o\000c\000\040\000+\000\040\000L\000u\000a\000L\000a\000T\000e\000X)/Keywords() /Producer (LuaTeX-1.24.0) /CreationDate (D:20260426162906+02'00') /ModDate (D:20260426162906+02'00') /Trapped /False /PTEX.FullBanner (This is LuaHBTeX, Version 1.24.0 (MiKTeX 26.2)) >> +156 0 obj +<< /Author(\376\377\000D\000r\000.\000-\000I\000n\000g\000.\000\040\000T\000h\000o\000m\000a\000s\000\040\000L\000a\000n\000g\000e\000r)/Title(\376\377\000L\000e\000b\000e\000n\000s\000l\000a\000u\000f\000\040\000D\000r\000.\000-\000I\000n\000g\000.\000\040\000T\000h\000o\000m\000a\000s\000\040\000L\000a\000n\000g\000e\000r)/Subject()/Creator(\376\377\000P\000a\000n\000d\000o\000c\000\040\000+\000\040\000L\000u\000a\000L\000a\000T\000e\000X)/Keywords() /Producer (LuaTeX-1.24.0) /CreationDate (D:20260426202917+02'00') /ModDate (D:20260426202917+02'00') /Trapped /False /PTEX.FullBanner (This is LuaHBTeX, Version 1.24.0 (MiKTeX 26.2)) >> endobj xref -0 163 +0 157 0000000000 65535 f 0000000020 00000 n -0000016167 00000 n -0000065136 00000 n +0000016450 00000 n +0000063710 00000 n 0000000065 00000 n 0000000266 00000 n -0000016227 00000 n -0000065042 00000 n +0000016510 00000 n +0000063616 00000 n 0000000312 00000 n 0000000399 00000 n -0000064946 00000 n +0000063520 00000 n 0000000445 00000 n 0000000548 00000 n -0000064848 00000 n +0000063422 00000 n 0000000595 00000 n 0000000827 00000 n -0000016285 00000 n -0000064750 00000 n +0000016570 00000 n +0000063324 00000 n 0000000874 00000 n 0000001316 00000 n -0000064652 00000 n +0000063226 00000 n 0000001363 00000 n 0000002095 00000 n -0000064554 00000 n +0000063128 00000 n 0000002142 00000 n 0000002669 00000 n -0000064456 00000 n +0000063030 00000 n 0000002716 00000 n 0000003178 00000 n -0000064358 00000 n +0000062932 00000 n 0000003225 00000 n 0000003952 00000 n -0000064260 00000 n +0000062834 00000 n 0000003999 00000 n 0000004558 00000 n -0000064162 00000 n +0000062736 00000 n 0000004605 00000 n 0000005132 00000 n -0000064064 00000 n +0000062638 00000 n 0000005179 00000 n 0000005636 00000 n -0000063966 00000 n +0000062540 00000 n 0000005683 00000 n 0000006155 00000 n -0000063868 00000 n +0000062442 00000 n 0000006202 00000 n 0000006688 00000 n -0000063757 00000 n +0000062331 00000 n 0000006735 00000 n 0000006998 00000 n -0000032099 00000 n -0000063683 00000 n +0000029618 00000 n +0000062257 00000 n 0000007045 00000 n 0000007969 00000 n -0000063596 00000 n +0000062170 00000 n 0000008016 00000 n 0000008794 00000 n -0000063509 00000 n +0000062083 00000 n 0000008841 00000 n 0000009856 00000 n -0000063422 00000 n +0000061996 00000 n 0000009903 00000 n 0000010557 00000 n -0000063335 00000 n +0000061909 00000 n 0000010604 00000 n 0000011265 00000 n -0000063248 00000 n +0000061822 00000 n 0000011312 00000 n 0000011938 00000 n -0000063174 00000 n +0000061748 00000 n 0000011985 00000 n 0000012457 00000 n -0000063087 00000 n +0000061661 00000 n 0000012504 00000 n 0000012582 00000 n -0000038842 00000 n -0000063000 00000 n +0000037424 00000 n +0000061574 00000 n 0000012625 00000 n 0000012698 00000 n -0000062926 00000 n +0000061500 00000 n 0000012741 00000 n 0000012819 00000 n -0000015799 00000 n -0000015958 00000 n -0000016346 00000 n +0000016082 00000 n +0000016241 00000 n +0000016631 00000 n 0000012869 00000 n -0000016106 00000 n -0000062370 00000 n -0000055407 00000 n -0000062727 00000 n -0000015931 00000 n -0000043695 00000 n -0000020161 00000 n -0000020370 00000 n -0000020002 00000 n -0000016428 00000 n -0000020309 00000 n -0000020134 00000 n -0000024138 00000 n -0000024349 00000 n -0000023975 00000 n -0000020452 00000 n -0000024286 00000 n -0000024110 00000 n -0000028155 00000 n -0000028367 00000 n -0000027990 00000 n -0000024431 00000 n -0000028304 00000 n -0000028126 00000 n -0000031887 00000 n -0000032161 00000 n -0000031722 00000 n -0000028450 00000 n -0000032036 00000 n -0000047876 00000 n -0000031858 00000 n -0000035434 00000 n -0000035646 00000 n -0000035269 00000 n -0000032257 00000 n -0000035583 00000 n -0000035405 00000 n -0000038630 00000 n -0000038904 00000 n -0000038465 00000 n -0000035729 00000 n -0000038779 00000 n -0000038601 00000 n -0000042157 00000 n -0000042369 00000 n -0000041992 00000 n -0000038987 00000 n -0000042306 00000 n +0000016389 00000 n +0000060952 00000 n +0000053989 00000 n +0000061309 00000 n +0000016214 00000 n +0000042277 00000 n +0000020893 00000 n +0000021102 00000 n +0000020734 00000 n +0000016713 00000 n +0000021041 00000 n +0000020866 00000 n +0000025025 00000 n +0000025236 00000 n +0000024862 00000 n +0000021184 00000 n +0000025173 00000 n +0000024997 00000 n +0000029406 00000 n +0000029680 00000 n +0000029241 00000 n +0000025318 00000 n +0000029555 00000 n +0000029377 00000 n +0000033609 00000 n +0000033821 00000 n +0000033444 00000 n +0000029763 00000 n +0000033758 00000 n +0000046458 00000 n +0000033580 00000 n +0000037212 00000 n +0000037486 00000 n +0000037047 00000 n +0000033917 00000 n +0000037361 00000 n +0000037183 00000 n +0000040739 00000 n +0000040951 00000 n +0000040574 00000 n +0000037569 00000 n +0000040888 00000 n +0000040710 00000 n 0000042128 00000 n -0000043546 00000 n -0000043757 00000 n -0000043381 00000 n -0000042452 00000 n -0000043517 00000 n -0000043840 00000 n -0000047056 00000 n -0000044116 00000 n -0000044221 00000 n -0000047298 00000 n -0000048034 00000 n -0000048238 00000 n -0000054356 00000 n -0000048773 00000 n -0000048899 00000 n -0000054589 00000 n -0000055557 00000 n -0000055754 00000 n -0000061359 00000 n -0000056244 00000 n -0000056365 00000 n -0000061598 00000 n -0000062525 00000 n -0000062852 00000 n -0000065230 00000 n -0000065544 00000 n -0000065582 00000 n -0000065708 00000 n +0000042339 00000 n +0000041963 00000 n +0000041034 00000 n +0000042099 00000 n +0000042422 00000 n +0000045638 00000 n +0000042698 00000 n +0000042803 00000 n +0000045880 00000 n +0000046616 00000 n +0000046820 00000 n +0000052938 00000 n +0000047355 00000 n +0000047481 00000 n +0000053171 00000 n +0000054139 00000 n +0000054336 00000 n +0000059941 00000 n +0000054826 00000 n +0000054947 00000 n +0000060180 00000 n +0000061107 00000 n +0000061426 00000 n +0000063804 00000 n +0000064101 00000 n +0000064139 00000 n +0000064265 00000 n trailer -<< /Size 163 /Root 161 0 R /Info 162 0 R /ID [ ] >> +<< /Size 157 /Root 155 0 R /Info 156 0 R /ID [ ] >> startxref -66368 +64925 %%EOF diff --git a/artefakte/01-lebenslauf/output/build.log b/artefakte/01-lebenslauf/output/build.log index e63b9c3..ef5aea3 100644 --- a/artefakte/01-lebenslauf/output/build.log +++ b/artefakte/01-lebenslauf/output/build.log @@ -1,4 +1,4 @@ -===== Build gestartet: 2026-04-26 16:29:03 ===== +===== Build gestartet: 2026-04-26 20:29:14 ===== 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 @@ -6,15 +6,17 @@ Output-Dir: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslau --- 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 (68.2 KB) +PDF OK: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.pdf (66.6 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 KB) +DOCX OK: Q:\DesTEngS\Pro\Git\marketing\claude_cowork\artefakte\01-lebenslauf\output\Lebenslauf_Dr-Ing_Thomas_Langer.docx (21.9 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: 27 [post-process-docx] Bullets in Listen: 186 [post-process-docx] keepNext gesetzt: 95 [post-process-docx] Bullets in Tabellen uebersprungen: 0 +[post-process-docx] H2-Headings gefunden: 7 +[post-process-docx] H2-Trenn-Absaetze eingefuegt: 7 [post-process-docx] Fertig. -===== Build beendet: 2026-04-26 16:29:09, Exit-Code 0 ===== +===== Build beendet: 2026-04-26 20:29:20, Exit-Code 0 ===== diff --git a/artefakte/01-lebenslauf/source/cv.md b/artefakte/01-lebenslauf/source/cv.md index 73ca30a..602acd0 100644 --- a/artefakte/01-lebenslauf/source/cv.md +++ b/artefakte/01-lebenslauf/source/cv.md @@ -9,8 +9,6 @@ - LinkedIn: https://www.linkedin.com/in/thomas-langer-b9742a2 - Büroadresse: Biberger Straße 91, 82008 Unterhaching ---- - ## Zusammenfassung - TÜV-zertifizierter AI Consultant und promovierter Ingenieur mit über 30 Jahren Erfahrung in Entwicklung, Test und System Integration von Elektronik @@ -21,8 +19,6 @@ - Kommunikations- und Präsentationsstärke auf allen Ebenen, von Fachteams bis Geschäftsführung - Flexibler Arbeitsstil als Gruppenleiter (10 Mitarbeiter), Team-Mitglied und autonomer Experte ---- - ## Projekte als freiberuflicher Consultant ### Seit Juli 2011 — Inhaber von DesTEngS Dr.-Ing. Thomas Langer, nahe München: @@ -34,8 +30,6 @@ - Anwendung von KI bei Konzepten, System Engineering, Software Design, Automatisierungen, System Integration und Tests - KI-gestützte Dokumentationen und Illustrationen ---- - ### Aug. 2024 – Feb. 2026 — Consultant bei ASMPT (Industrielle Bestückungsmaschinen), System Integration Abteilung R&D 38, München: - KI-Workshop @@ -50,8 +44,6 @@ - Ermittlung von System Integration Standard Use-Cases und Erstellung der zugehörigen Vector CANalyzer Konfigurationen zur Steigerung der Effizienz durch Vereinheitlichung der Testplatz-Umgebungen - Entwicklung eines Python-Tools mit PyShark zur Konvertierung spezieller Bestückungsmaschinen Trace-Daten für die Nutzung in Vector CANalyzer ---- - ### Sep. 2025 – Feb. 2026 — Projekt „Kischdle", Geschäftskonzept von 2 potenziellen Gründern: - On-Premise KI-System mit Consumer-GPU und Retrieval Augmented Generation (RAG) @@ -60,8 +52,6 @@ - Erweiterung durch PyTorch-Umgebung für multimodale KI-Modelle (Bild und Text), Evaluierungen des Inference-Modells Qwen3-VL-8B und des Embedding-Modells tomoro-colqwen3-embed-4b - Evaluierung von Langflow für Workflow-Automatisierungen mit AI-Agents ---- - ### Jan. – Feb. 2026 — Consultant bei Lumiz (Marketing-Dienstleister), Taufkirchen: - KI-gestützte Automatisierung der Einkäufe auf einer Druckerei-Website mit UI.Vision @@ -69,8 +59,6 @@ - Hochladen der Druckdaten aus der Lumiz-Cloud - Protokollierung der Vorgänge ---- - ### Nov. 2020 – Mai 2024 — Consultant bei ASMPT (Industrielle Bestückungsmaschinen), System Integration Abteilung R&D 38, München: - Maßgebliche Mitwirkung bei Konzepterstellung und Einführung eines neuen Gigabit Ethernet Feldbus für performantere Steuerung von ASMPTs industriellen Bestückungsmaschinen @@ -92,8 +80,6 @@ - Entdeckung sporadischer Zeitabweichungen der Ethernet-Interface Hardware, Idee und Aufbau einer hochpräzisen Zeit-Referenz mit einem preiswerten GPS-Modul, Feststellung einer signifikanten Spec-Verletzung der Vector Hardware Zeitbasis - Evaluierung GL Communications PacketExpert Gigabit Ethernet Tester ---- - ### Aug. 2018 – Juli 2020 — Consultant bei Magna Electronics Europe (Automobil-Zulieferer), München: - LIDAR Compute Module @@ -108,8 +94,6 @@ - xDiagnostics ermöglichte eine Design-Validierung in einem frühen Entwicklungsstadium - Elektromagnetische Feldsimulationen mit CST für den GMSL Pfad eines Kameramoduls zur Verifikation der Signalintegrität ---- - ### Nov. 2014 – Juli 2018 — Consultant bei Infineon, Abteilung DES TCP PCB, Großraum München: - Signal Integrity und Power Integrity Simulationen von IC Packages und PCBs @@ -121,15 +105,11 @@ - EM Feldsimulationen zur Modell Extraktion von IC Packages und PCBs - Erstellung von Matlab, Python und IronPython Programmen ---- - ### Apr. – Aug. 2015 — Consultant bei Kathrein.net.tech (Ubidyne-Nachfolger), Ulm: - Inbetriebnahme, Evaluierung und Optimierung von Transceiver Modulen - Automatisierung von HF-Tests mit Matlab und Ruby ---- - ### Sep. 2011 – Juli 2014 — Consultant bei Alcatel-Lucent, Abteilung MS/E, Stuttgart: - Projekt Light Radio AAA: Aktives Antennen Array für 2.5 GHz LTE @@ -145,12 +125,8 @@ - Erstellung von Test Routinen mit Matlab und embedded Linux Programmierung des WiFi SoC, Aufbau einer Messumgebung für automatisierte Tests - Agile Design mit Scrum ---- - ### Juli 2011 — Gründung von DesTEngS Dr.-Ing. Thomas Langer (Ingenieurbüro), nahe Ulm ---- - ## Berufliche Stationen vor der Selbständigkeit ### Juli 2006 – Juni 2011 — Head of RF Integration bei Ubidyne (Startup, ca. 60 Mitarbeiter, aktive Antennen für Mobilfunk-Basisstationen), Abteilung Engineering, Ulm: @@ -171,8 +147,6 @@ - Antennenhersteller Kathrein, Andrew und Huber & Suhner - _(Ab Okt. 2009: Principal Member of Technical Staff)_ ---- - ### Jan. 2003 – Juni 2006 — Entwicklungsingenieur bei Toshiba Electronics Europe, European LSI Development and Engineering Centre, Düsseldorf: - Mitarbeit in den Normierungsgremien OIF und MIPI @@ -183,8 +157,6 @@ - Detaillierte Analysen elektrischer IC-Gehäuse (bis zu 11 Gb/s, bis zu 1444 Balls) - _(Ab April 2004: Senior Engineer)_ ---- - ### Dez. 2000 – Dez. 2002 — Entwicklungsingenieur bei Multilink Technology (Startup mit bis zu 360 Mitarbeitern weltweit, ICs und Module für faseroptische Übertragungssysteme), Berlin: - Entzerrer-ICs für Backplane-Systeme bis 12,5 Gb/s: @@ -195,16 +167,12 @@ - Systemsimulationen zur Bestimmung der Anforderungen für elektrische Entzerrer-ICs - 3,1 Gb/s 8:32 CMOS Demultiplexer-IC: BGA Gehäuse-Entwurf, Teststrategie, Leiterplatten-Entwicklung ---- - ### Nov. 1998 – Nov. 2000 — Entwicklungsingenieur bei Siemens, Bereich Information and Communication Networks, München: - Verantwortlich für ein 1,8 GHz RX Frontend Modul für GSM Mobilfunk-Basisstationen: Projektkoordination, Definition der Architektur, Systemsimulation, Schaltungsentwurf, Layout, Evaluierung von Labormustern und Prototypen, Fertigungseinführung - Qualifizierung und Fertigungseinführung eines 10 GHz VCOs - Layout und Aufbautechnik von 40 Gb/s High Speed Digital Modulen für Faseroptische Systeme ---- - ### Okt. 1994 – Okt. 1998 — Wissenschaftlicher Mitarbeiter am Ferdinand-Braun-Institut für Höchstfrequenztechnik, Berlin: - Erzeugung elektrischer Transienten im Picosekundenbereich mit einer nichtlinearen Diodenleitung @@ -216,20 +184,14 @@ - Elektromagnetische Feldsimulationen koplanarer Leitungsstrukturen bis 1 THz - Entwicklung eines breitbandigen Low-Power Transimpedanzverstärker GaAs-MMICs für 100 MHz - 6 GHz ---- - ### Sep. 1992 – Aug. 1993 — Studentische Hilfskraft am Ferdinand-Braun-Institut für Höchstfrequenztechnik, Berlin: - Modellierung passiver Elemente, Dioden und Transistoren auf GaAs Wafern ---- - ### Jan. 1990 – März 1992 — Studentische Hilfskraft am Hahn-Meitner-Institut, Berlin: - Entwurf und Aufbau von ultra-breitbandigen Verstärkern 10 KHz – 16 GHz inklusive Entwicklung und Implementierung eines neuen Verfahrens zur Temperaturkompensation ---- - ## Ausbildung ---------- ---------------------------------------------------------------------- @@ -242,8 +204,6 @@ Okt. 1994 **Dipl.-Ing. Elektrotechnik** an der TU Berlin, Vertiefungsfach Hoch Juni 1986 **Abitur** am Oberstufenzentrum Elektrotechnik in Berlin ---------- ---------------------------------------------------------------------- ---- - ## Trainings - Dez. 2016 — Ansys SIwave Training @@ -253,8 +213,6 @@ Juni 1986 **Abitur** am Oberstufenzentrum Elektrotechnik in Berlin - Apr. 2006 — Die Akademie, „Gedächtnis- und Konzentrationstraining" - Mai 2000 — Seminar „Persönlichkeitsentwicklung im Team" ---- - ## Kenntnisse **KI:** diff --git a/artefakte/01-lebenslauf/templates/reference.docx b/artefakte/01-lebenslauf/templates/reference.docx index c896380..4e9d51a 100644 Binary files a/artefakte/01-lebenslauf/templates/reference.docx and b/artefakte/01-lebenslauf/templates/reference.docx differ diff --git a/changelog.md b/changelog.md index d66121f..711a473 100644 --- a/changelog.md +++ b/changelog.md @@ -45,3 +45,4 @@ Chronologisches Log aller Entscheidungen und Prozessereignisse. 2026-04-26 10:58 | S07 | PDF-Build-Fehler endgueltig behoben. S06-Hotfix (array, calc, providecommand real) loeste das Problem nicht; nach Rebuild kam unveraendert "! LaTeX Error: No counter 'none' defined." Eigentliche Ursache: Pandoc 3.x emittiert fuer unnummerierte Tabellen direkt vor begin{longtable} die Zeile def LTcaptype none, ohne den Counter none zu definieren. Pandocs eigene Default-Vorlage definiert ihn (commit d835461 in Pandoc 3.8.2.1), Custom-Templates muessen das selbst tun (siehe Pandoc-Issue 11201). Fix: eine Zeile newcounter none direkt nach providecommand real im Tabellen-Block des Templates. Sandbox-Reproduktion exakter Fehlertext ohne Fix, sauberes PDF mit Fix. Auf Thomas' System: PDF wird erzeugt, Ausbildungs-Layout im PDF visuell bestaetigt. Iteration A fuer Teilgebiet 01 damit inhaltlich abgeschlossen. Build-UX-Fix: build.ps1 ergaenzt um Start-Sleep -Seconds 3 nach jedem fehlschlagenden Build-Schritt (Pflichtdatei-Check, PDF-Build, DOCX-Build), damit die rote Fehlerzeile lesbar bleibt bevor das PowerShell-Fenster zugeht. teilgebiete/01-lebenslauf.md um beide Fixes ergaenzt (zweistufige Hotfix-Geschichte, PDF-Bestaetigung, UX-Fix). agent-prompt.md Aktueller-Stand-Abschnitt fuer S08 fortgeschrieben mit verbleibender Iterationsreihenfolge B-C-D, Hinweise auf Pandoc-Versionsunterschied praeziser formuliert. 2026-04-26 13:29 | S07 | Teilgebiet 01 Iteration B (Iterationen B1, B1.5, B2) durchgezogen. Neue Datei build/build-reference-docx.py baut templates/reference.docx programmatisch aus Pandocs Default-Reference (Python-Stdlib only, kein pip; pandoc --print-default-data-file zur Laufzeit, ZIP entpacken, ElementTree-XML-Anpassungen, repacken). B1: Theme major+minor und alle direkten Schrift-Refs in styles.xml auf Calibri umgestellt (Code-Schriften wie Consolas bleiben), Tabellen-Default-Stil mit tblBorders=none auf allen Sides. B1.5: Body-DocDefault 11 pt, Heading 1/2/3 auf 15/13/12 pt analog PDF. B2: header1.xml (Default ab Seite 2 mit Name links und Lebenslauf rechts), header2.xml (leer fuer Seite 1 via titlePg), footer1.xml (rechts Seite n / m mit PAGE/NUMPAGES-Feldern, doppelt referenziert als default und first damit Seite 1 trotz titlePg den Footer hat). Page-Setup explizit in sectPr: A4 mit 2.2 cm oben/unten und 2.5 cm links/rechts analog PDF, Tab-Stop am rechten Textrand 9072 dxa. Beziehungen mit dynamisch naechster freier rId in document.xml.rels, Content-Types-Overrides in [Content_Types].xml, sectPr regex-ersetzt idempotent. Sandbox-End-to-End mit Pandoc 2.9 verifiziert (sectPr und Header/Footer im generierten DOCX vorhanden). Auf Thomas System: DOCX visuell bestaetigt. teilgebiete/01-lebenslauf.md um vollstaendigen Iteration-B-Block ergaenzt, Naechste-Schritte-Liste auf B3, B4, C, D umstrukturiert. agent-prompt.md Aktueller-Stand-Abschnitt fortgeschrieben mit Hinweisen zur reference-docx-Pipeline (manuell vor build.ps1 aufrufen, nicht von Hand in Word editieren) und zur Edit-Tool-Truncation auf dem NTFS-Mount. Build-UX-Fix in build.ps1 mit 3-Sekunden-Pause pro fehlgeschlagenem Schritt war ebenfalls Teil dieser Session. 2026-04-26 16:40 | S07 | Iteration B3 und B3.5 fuer Teilgebiet 01 abgeschlossen. B3 in build/build-reference-docx.py ergaenzt: DocDefault widowControl plus keepNext und keepLines auf Heading 1/2/3 und FirstParagraph (Pandoc-Stil fuer ersten Absatz nach einem Heading, deckt die fett formatierten Kenntnisse-Subsection-Labels KI Software-Design Methodik IT etc ab). Erster Versuch Compact-Stil mit keepNext hat Listen komplett unteilbar gemacht (Job-Stationen begannen jedes Mal auf einer neuen Seite, ungenutzte Seitenenden) und wurde verworfen. Auf Wunsch von Thomas auf 3-3-Regel umgestellt: bei Listen mit mindestens 6 Bullets duerfen Trennungen passieren, aber mindestens 3 Bullets bleiben jeweils zusammen vor und nach dem Umbruch. Bei kuerzeren Listen alles zusammen. Da das stilbasiert nicht abbildbar ist (alle Bullets haben pStyle Compact), neues Post-Processing-Skript build/post-process-docx.py: scannt das fertige DOCX, findet Sequenzen aufeinanderfolgender Bullets mit numPr-Eigenschaft ausserhalb von Tabellen-Zellen, setzt keepNext auf den ersten 2 und den N-3 N-2 Bullets jeder Liste mit n groesser gleich 6 (bei n kleiner 6 alle keepNext). build.ps1 erweitert auf 3 Schritte und ruft das Post-Processing-Skript automatisch nach erfolgreichem DOCX-Build auf, mit Console-Output und Log-Statistiken (Anzahl Listen Bullets keepNext-Markierungen). Sandbox-Verifikation 26 Listen 184 Bullets 93 keepNext, Pattern fuer 11-Bullet-Liste KK......KK.. Auf Thomas System visuell bestaetigt: Listen werden an guten Stellen getrennt, keine ungenutzten Seitenenden, keine einzelnen Bullets allein am Seitenrand. teilgebiete/01-lebenslauf.md um B3- und B3.5-Bloecke ergaenzt sowie Naechste-Schritte-Liste auf B4 C D umstrukturiert. agent-prompt.md Aktueller-Stand-Abschnitt fortgeschrieben mit B3 und B3.5, Hinweis auf 3-stufige DOCX-Pipeline und Edit-Tool-Truncation an build.ps1 ergaenzt. Naechste Session startet mit B4 (Heading-Farben oder Trennlinien analog PDF). +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. diff --git a/teilgebiete/01-lebenslauf.md b/teilgebiete/01-lebenslauf.md index 0759e82..aa379be 100644 --- a/teilgebiete/01-lebenslauf.md +++ b/teilgebiete/01-lebenslauf.md @@ -184,12 +184,25 @@ Die in S04 mit docx-js erstellte Version hatte strukturelle typographische Mäng - `build.ps1` ruft das Skript automatisch nach erfolgreichem DOCX-Build auf (Schritt [3/3]), Console-Output und Log enthalten Statistiken (Anzahl Listen, Bullets, gesetzte keepNext-Markierungen). - Sandbox-Verifikation: 26 Listen, 184 Bullets, 93 keepNext-Markierungen, Pattern für Listen mit n ≥ 6 z.B. `KK......KK.` (Liste mit 11 Bullets: erste 2 + Bullets 9 und 10 keepNext). Auf Thomas' System visuell bestätigt: Stationen-Listen werden jetzt sauber an guter Stelle getrennt, keine ungenutzten Seitenenden mehr, kein einzelner Bullet alleine am Seitenrand. +## Iteration B4 (S08) — Heading-Farben und H2-Trennlinien + +**B4.1 — Heading-Farben in destengsblue:** Heading 1, 2 und 3 werden in `build/build-reference-docx.py` per Funktion `set_heading_colors` auf `` gesetzt; das `themeColor`-Attribut (Pandoc-Default: `accent1`) wird entfernt, damit die Farbe nicht aus dem Word-Theme kommt. Visuelle Bestätigung im DOCX: alle drei Heading-Levels erscheinen in destengsblue. + +**B4.2 — Heading-Trennlinien (Sackgasse):** Erster Versuch war eine Bottom-Border direkt auf den Heading 1/2-Stilen, mit symmetrischem Indent (`left=2268`, `right=2268`, `hanging=2268`) für eine zentrierte Halblinie ca. 50 % der Textbreite. Word hat den `hanging`-Indent jedoch so interpretiert, dass die Border bei der visuellen Position der ersten Zeile (= 0 dxa) beginnt — die Linien erschienen linksbündig statt zentriert. **Verworfen.** Lehre: Words `right`-Indent begrenzt sowohl Text als auch Border, deshalb ist eine Border *schmaler als der Heading-Text* über den Heading-Stil selbst nicht erreichbar. Die Heading-Border-Logik wurde aus dem Skript wieder entfernt; nur die Heading-Farben (B4.1) sind geblieben. + +**B4.3 — Markdown-HRs aus cv.md entfernt:** Beim Build der ersten B4-Variante fielen Thomas „Doppellinien über die gesamte Zeilenbreite" auf, die als anklickbare Word-Horizontal-Lines erschienen. Quelle: 21 alleinstehende `---`-Zeilen in `cv.md`. Pandoc rendert Markdown-HRs im PDF als `\begin{center}\rule{0.5\linewidth}{0.5pt}\end{center}` (saubere zentrierte Halblinie 0.5 pt) und im DOCX als VML-`` (Embossed-Doppellinien-Look). Auf Thomas' Wunsch wurden alle 21 HR-Zeilen aus `cv.md` entfernt — PDF verliert die Trennlinien zwischen Stationen, DOCX verliert die Doppellinien. Die Tabellen-Strich-Zeilen der Multiline-Tabelle für Ausbildung blieben unangetastet (anderes Pattern: `^---------- -----...$`). Sandbox-Verifikation: 21 → 0 `o:hr="t"` Vorkommen im DOCX. + +**B4.3-Vorfall — Datei-Verlust und Wiederherstellung:** Beim ersten HR-Removal hat die Sandbox die `cv.md` durch einen NTFS-Mount-Stale-Read truncated gelesen (20043 statt 20201 Bytes — die letzten 5 Zeilen mit „Englisch", „Veröffentlichungen", „Dissertation, fünf Veröffentlichungen, ein Patent, eine Erfindungsmeldung" fehlten). Das Python-Skript hat die HRs aus dieser truncated Version entfernt und die Datei zurückgeschrieben — die echte `cv.md` auf Thomas' System wäre damit ohne den Schluss-Block gewesen. Sofortige Wiederherstellung aus `git show HEAD:artefakte/01-lebenslauf/source/cv.md` (= S06-Commit be4f695, der letzte mit cv.md-Änderung); HR-Removal anschließend erneut auf der git-Version als Input (kein zweiter Sandbox-Read), Output direkt zurückgeschrieben. Live-Datei nach erfolgreichem zweiten Versuch: 20096 Bytes / 288 Zeilen, korrektes Ende. **Lehre für die Pipeline:** Bei jedem Sandbox-write auf NTFS-Mount-Datei mit grösserem Volumen erst die git-Version verifizieren, nicht blind dem Sandbox-Read trauen. + +**B4.4 — H2-Trennlinien via Post-Processing:** Thomas wünschte stattdessen Trennlinien unter H2 (für visuelle Trennung der Hauptabschnitte und um H3 wieder klarer abzuheben). Nach einem Demo-Vergleich (linksbündiger Trennstrich vs. Underline-auf-Heading-Text in Text-Breite) Entscheidung für Trennstrich. Finale Parameter: schwarz (`000000`), 8,6 cm Linienlänge (= 4876 dxa, `right`-Indent 4196 dxa bei 9072 dxa Textbreite), 1,25 pt dick (`` — sz ist in 1/8 pt). Umgesetzt in `build/post-process-docx.py`: Funktion `process_document_xml` ergänzt um eine zweite Logik, die nach jedem H2-Absatz einen leeren Trenn-Absatz mit Bottom-Border einfügt. Trenn-Absatz: ``, ``, ``, `` (1 pt) für minimale Absatzhöhe. Sandbox-Verifikation: 7 H2 → 7 Trenner. Visuelle Bestätigung durch Thomas: Trennlinien sehen gut aus, Hierarchie zwischen H2 und H3 wieder klar. + +**Warum kein Heading-Stil-Border:** Words `right`-Indent gilt sowohl für Text als auch für Border. Eine Border *schmaler als der Heading-Text* ist über den Stil selbst nicht abbildbar, weil Indent den Text mitkürzt. Lösung: separater Trenn-Absatz nach dem Heading. Die Underline-Alternative (Linie genau in Heading-Text-Breite) wurde verworfen, weil sie wie ein unterstrichener Text wirkt und nicht wie ein Trenner. + ## Nächste Schritte -1. **Iteration B4 — Heading-Farben auf DesTEngS-Blau und/oder Trennlinien analog PDF.** Eher Kosmetik bei Vorlage für Consulting-Agenturen, aber für eigene Direktverwendung des DOCX (Website-Download, persönliche Bewerbungen) sinnvoll. -2. **Iteration C — Foto-Einbindung:** Portraitfoto in `source/cv.md` einbetten (Pandoc-Image-Syntax), Position und Größe im Template absichern (z.B. oben rechts neben Name, ca. 3 cm). -3. **Iteration D — Hyphenation-Feintuning für PDF:** Kurze Wortteile am Zeilenanfang mit höherer Penalty oder gezielten `\hyphenation`-Ausnahmen reduzieren. Iterativ. -4. Teilgebiet nach erfolgreichem Output und Freigabe durch Thomas abschließen (R2-OK von Thomas: Status auf „abgeschlossen" im zentral-index.md). +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). ## Artefakte @@ -199,8 +212,8 @@ Die in S04 mit docx-js erstellte Version hatte strukturelle typographische Mäng - `artefakte/01-lebenslauf/source/foto-wrba_2026_6782_1.jpg` — Portraitfoto (umbenannt, noch nicht in cv.md eingebunden). - `artefakte/01-lebenslauf/templates/template.tex` — Pandoc-LaTeX-Template für LuaLaTeX (Iteration A inkl. Pandoc-3.x-Hotfix `\newcounter{none}`). - `artefakte/01-lebenslauf/templates/reference.docx` — Pandoc-Reference-Doc, **automatisch erzeugt** durch `build/build-reference-docx.py`. Nicht von Hand editieren — Änderungen würden beim nächsten Skript-Lauf überschrieben. -- `artefakte/01-lebenslauf/build/build-reference-docx.py` — Python-Skript zum Bauen der `reference.docx` (Iterationen B1, B1.5, B2, B3). Manuell aufrufen, wenn Stile geändert werden sollen, danach normalen `build.ps1` laufen. -- `artefakte/01-lebenslauf/build/post-process-docx.py` — Python-Skript für DOCX-Post-Processing (B3.5 Listen-Bullet-Schutz). Wird automatisch von `build.ps1` als Schritt [3/3] aufgerufen. +- `artefakte/01-lebenslauf/build/build-reference-docx.py` — Python-Skript zum Bauen der `reference.docx` (Iterationen B1, B1.5, B2, B3, B4.1 Heading-Farben). Manuell aufrufen, wenn Stile geändert werden sollen, danach normalen `build.ps1` laufen. +- `artefakte/01-lebenslauf/build/post-process-docx.py` — Python-Skript für DOCX-Post-Processing (B3.5 Listen-Bullet-Schutz und B4.4 H2-Trennlinien). Wird automatisch von `build.ps1` als Schritt [3/3] aufgerufen. - `artefakte/01-lebenslauf/build/build.ps1` — PowerShell-Build-Skript (PDF + DOCX + Post-Process) mit 3-Sekunden-Pause bei Fehler. - `artefakte/01-lebenslauf/output/` — erzeugte Ausgaben plus `build.log`.