diff --git a/src/writer.py b/src/writer.py index e0aae1e..9c576e1 100644 --- a/src/writer.py +++ b/src/writer.py @@ -102,10 +102,10 @@ def _scan_template_fields( template_path: Path, mapping: list[OutputMapping], fontsize: float, -) -> dict[str, dict]: +) -> dict[str, list[dict]]: """ Scannt ALLE Seiten des PDF-Templates nach _Field-Platzhaltern. - Rückgabe: dict[pdf_field] → {source_field, page_num, rect, fontsize, fontname, ...} + Rückgabe: dict[pdf_field] → Liste von {source_field, page_num, rect, fontsize, fontname, ...} Felder mit pdf_field=None werden übersprungen. """ doc = fitz.open(template_path) @@ -116,26 +116,23 @@ def _scan_template_fields( if m.pdf_field is not None: pdf_to_source[m.pdf_field] = m.source_field - field_info: dict[str, dict] = {} + field_info: dict[str, list[dict]] = {} - # Per search_for die Rechtecke aller PDF-Feld-Marker finden (alle Seiten) + # Alle Vorkommen aller PDF-Feld-Marker finden (alle Seiten) for pdf_field, source_field in pdf_to_source.items(): - found_any = False + occurrences: list[dict] = [] for page_num in range(doc.page_count): page = doc[page_num] found = page.search_for(pdf_field) - if found: - field_info[pdf_field] = { + for rect in found: + occurrences.append({ "source_field": source_field, "page_num": page_num, - "rect": found[0], + "rect": rect, "fontsize": fontsize, "fontname": "helv", - } - found_any = True - break # erstes Vorkommen (seitenübergreifend) - if not found_any: - field_info[pdf_field] = None # nicht gefunden + }) + field_info[pdf_field] = occurrences doc.close() return field_info @@ -150,7 +147,7 @@ def _fill_pdf_pages( record: EmployeeRecord, employee_name: str, mapping: list[OutputMapping], - field_info: dict[str, dict], + field_info: dict[str, list[dict]], fontsize: float, ) -> None: """Befüllt alle Template-Seiten: redacted _Field-Platzhalter und fügt echte Daten ein.""" @@ -162,76 +159,74 @@ def _fill_pdf_pages( if m.pdf_field is None: continue - info = field_info.get(m.pdf_field) - if info is None: + occurrences = field_info.get(m.pdf_field, []) + if not occurrences: continue # Feld nicht im Template gefunden - page_num = info["page_num"] - if page_num >= len(pages): - continue - - page = pages[page_num] - rect = info["rect"] - value = _get_field_value(record, employee_name, m.source_field) formatted = _format_value(value, m.format) - # Platzhalter immer redacten, auch wenn kein Wert zum Einsetzen da ist - page.add_redact_annot(rect) - page.apply_redactions() - - if not formatted: - continue - - # Mehrzeilige Felder: Zeilenweise per insert_text schreiben - if m.pdf_field in MULTILINE_FIELDS: - # Doppelte Leerzeilen entfernen - clean_text = "\n".join( - line for line in formatted.split("\n") if line.strip() - ) - if not clean_text: + # Jedes Vorkommen des Feldes im PDF befüllen + for info in occurrences: + page_num = info["page_num"] + if page_num >= len(pages): continue - # Zeilenweise mit automatischem Umbruch schreiben - lines = clean_text.split("\n") - line_spacing = fontsize * 1.2 # Zeilenabstand + page = pages[page_num] + rect = info["rect"] - # Zuerst alle Ausgabezeilen sammeln, um Gesamthöhe zu berechnen - output_lines: list[str] = [] - for line in lines: - max_width = TEXTBOX_WIDTH_PT - words = line.split(" ") - current_line = "" - for word in words: - test_line = current_line + (" " if current_line else "") + word - text_width = fitz.get_text_length(test_line, fontname="helv", fontsize=fontsize) - if text_width <= max_width or not current_line: - current_line = test_line - else: - output_lines.append(current_line) - current_line = word - if current_line: - output_lines.append(current_line) + # Platzhalter redacten + page.add_redact_annot(rect) + page.apply_redactions() - # Textblock oben beginnend schreiben - y = rect.y0 + fontsize + if not formatted: + continue - for current_line in output_lines: - page.insert_text( - (rect.x0, y), current_line, - fontname="helv", fontsize=fontsize, + # Mehrzeilige Felder: Zeilenweise per insert_text schreiben + if m.pdf_field in MULTILINE_FIELDS: + # ... (unverändert) + clean_text = "\n".join( + line for line in formatted.split("\n") if line.strip() + ) + if not clean_text: + continue + + lines = clean_text.split("\n") + line_spacing = fontsize * 1.2 + + output_lines: list[str] = [] + for line in lines: + max_width = TEXTBOX_WIDTH_PT + words = line.split(" ") + current_line = "" + for word in words: + test_line = current_line + (" " if current_line else "") + word + text_width = fitz.get_text_length(test_line, fontname="helv", fontsize=fontsize) + if text_width <= max_width or not current_line: + current_line = test_line + else: + output_lines.append(current_line) + current_line = word + if current_line: + output_lines.append(current_line) + + y = rect.y0 + fontsize + for current_line in output_lines: + page.insert_text( + (rect.x0, y), current_line, + fontname="helv", fontsize=fontsize, + ) + y += line_spacing + else: + # Einzeilige Felder: vertikal zentriert platzieren + mid_y = (rect.y0 + rect.y1) / 2 + baseline_y = mid_y + fontsize * 0.35 + page.insert_text( + (rect.x0, baseline_y), + formatted, + fontname="helv", + fontsize=fontsize, ) - y += line_spacing - else: - # Einzeilige Felder: vertikal zentriert platzieren - mid_y = (rect.y0 + rect.y1) / 2 - baseline_y = mid_y + fontsize * 0.35 - page.insert_text( - (rect.x0, baseline_y), - formatted, - fontname="helv", - fontsize=fontsize, - ) # ───────────────────────────────────────────── diff --git a/template/Tätigkeitsnachweise Stellenförderung.odg b/template/Tätigkeitsnachweise Stellenförderung.odg index 564a1c9..556f50e 100644 Binary files a/template/Tätigkeitsnachweise Stellenförderung.odg and b/template/Tätigkeitsnachweise Stellenförderung.odg differ diff --git a/template/Tätigkeitsnachweise Stellenförderung.pdf b/template/Tätigkeitsnachweise Stellenförderung.pdf index 58f9e8e..fa4d1f4 100644 Binary files a/template/Tätigkeitsnachweise Stellenförderung.pdf and b/template/Tätigkeitsnachweise Stellenförderung.pdf differ