From 2247c42e81b9ae40359562463146952ba8582d05 Mon Sep 17 00:00:00 2001 From: Rob Date: Tue, 30 Jun 2026 09:43:25 +0200 Subject: [PATCH] use static font size over all document texts --- app.py | 1 + coldrun.py | 1 + config/settings.ini | 4 ++++ src/config.py | 2 ++ src/models.py | 1 + src/writer.py | 43 +++++++------------------------------------ 6 files changed, 16 insertions(+), 36 deletions(-) diff --git a/app.py b/app.py index f1b6001..2a41803 100644 --- a/app.py +++ b/app.py @@ -289,6 +289,7 @@ if generate_clicked: mapping=app_config.output_mapping, output_dir=output_dir, filename_pattern=app_config.filename_pattern, + fontsize=app_config.fontsize, ) st.session_state.generated_files = generated except Exception as e: diff --git a/coldrun.py b/coldrun.py index 0918c1e..3c3c35d 100644 --- a/coldrun.py +++ b/coldrun.py @@ -113,6 +113,7 @@ def main(): mapping=config.output_mapping, output_dir=output_dir, filename_pattern=config.filename_pattern, + fontsize=config.fontsize, ) output_file = generated[0] diff --git a/config/settings.ini b/config/settings.ini index 9fdc870..e735c6d 100644 --- a/config/settings.ini +++ b/config/settings.ini @@ -6,6 +6,10 @@ output_dir = output [output] filename_pattern = {last_name}_{first_name}_Tätigkeitsnachweis.pdf +[font] +# Statische Schriftgröße (in Punkt) für alle Felder im PDF +fontsize = 9 + [parsing] # Sheets whose names match these patterns are treated as monthly data sheets # Use * as wildcard. Sheets not matching are ignored (e.g. "Alle Rollen"). diff --git a/src/config.py b/src/config.py index 0bfebb9..b9064ad 100644 --- a/src/config.py +++ b/src/config.py @@ -21,6 +21,7 @@ def load_settings(config_dir: Path, project_root: Path) -> AppConfig: paths = parser["paths"] if "paths" in parser else {} output = parser["output"] if "output" in parser else {} parsing = parser["parsing"] if "parsing" in parser else {} + font = parser["font"] if "font" in parser else {} return AppConfig( input_dir=str(project_root / paths.get("input_dir", "input")), @@ -30,6 +31,7 @@ def load_settings(config_dir: Path, project_root: Path) -> AppConfig: month_sheet_pattern=parsing.get("month_sheet_pattern", "* 20??"), skip_empty_name=parsing.getboolean("skip_empty_name", True), skip_empty_antragsnummer=parsing.getboolean("skip_empty_antragsnummer", True), + fontsize=font.getfloat("fontsize", 11.0), ) diff --git a/src/models.py b/src/models.py index 2cbbddb..eece087 100644 --- a/src/models.py +++ b/src/models.py @@ -63,6 +63,7 @@ class AppConfig: month_sheet_pattern: str = "* 20??" skip_empty_name: bool = True skip_empty_antragsnummer: bool = True + fontsize: float = 11.0 @dataclass diff --git a/src/writer.py b/src/writer.py index a46231c..bbc2053 100644 --- a/src/writer.py +++ b/src/writer.py @@ -101,6 +101,7 @@ def _get_field_value(record: EmployeeRecord, employee_name: str, source_field: s def _scan_template_fields( template_path: Path, mapping: list[OutputMapping], + fontsize: float, ) -> dict[str, dict]: """ Scannt ALLE Seiten des PDF-Templates nach _Field-Platzhaltern. @@ -117,7 +118,7 @@ def _scan_template_fields( field_info: dict[str, dict] = {} - # 1. Per search_for die Rechtecke aller PDF-Feld-Marker finden (alle Seiten) + # Per search_for die Rechtecke aller PDF-Feld-Marker finden (alle Seiten) for pdf_field, source_field in pdf_to_source.items(): found_any = False for page_num in range(doc.page_count): @@ -128,7 +129,7 @@ def _scan_template_fields( "source_field": source_field, "page_num": page_num, "rect": found[0], - "fontsize": 11, # Fallback + "fontsize": fontsize, "fontname": "helv", } found_any = True @@ -136,23 +137,6 @@ def _scan_template_fields( if not found_any: field_info[pdf_field] = None # nicht gefunden - # 2. Schriftgrößen aus den Text-Details extrahieren (alle Seiten) - for page_num in range(doc.page_count): - page = doc[page_num] - text_dict = page.get_text("dict") - for block in text_dict.get("blocks", []): - if "lines" not in block: - continue - for line in block["lines"]: - for span in line["spans"]: - text = span["text"].strip() - if text.endswith("_Field") and text in field_info: - info = field_info[text] - if info is not None: - info["fontsize"] = span["size"] - info["fontname"] = span.get("font", "helv") - info["rect"] = fitz.Rect(span["bbox"]) - doc.close() return field_info @@ -167,17 +151,13 @@ def _fill_pdf_pages( employee_name: str, mapping: list[OutputMapping], field_info: dict[str, dict], + fontsize: float, ) -> None: """Befüllt alle Template-Seiten: redacted _Field-Platzhalter und fügt echte Daten ein.""" # Mehrzeilige Felder, die als Textbox (mit Zeilenumbruch) behandelt werden MULTILINE_FIELDS = {"Tätigkeiten_Field", "Dienstreisen_Field"} TEXTBOX_WIDTH_PT = 400 # Breite der Textbox in Punkt - # Felder mit automatischer Schriftgrößen-Anpassung bei langem Text - AUTOSCALE_FIELDS = {"Qualifikation_Field", "Funktion_Field"} - AUTOSCALE_MAX_CHARS = 35 # ab dieser Länge wird verkleinert - AUTOSCALE_MIN_FACTOR = 0.75 # minimale Schriftgröße relativ zum Original - for m in mapping: if m.pdf_field is None: continue @@ -192,7 +172,6 @@ def _fill_pdf_pages( page = pages[page_num] rect = info["rect"] - fontsize = info.get("fontsize", 11) value = _get_field_value(record, employee_name, m.source_field) formatted = _format_value(value, m.format) @@ -204,15 +183,6 @@ def _fill_pdf_pages( if not formatted: continue - # Automatische Schriftgrößen-Anpassung für lange Texte - if m.pdf_field in AUTOSCALE_FIELDS and len(formatted) > AUTOSCALE_MAX_CHARS: - text_width = fitz.get_text_length(formatted, fontname="helv", fontsize=fontsize) - available_width = rect.width - if text_width > available_width: - factor = available_width / text_width - factor = max(factor, AUTOSCALE_MIN_FACTOR) - fontsize = fontsize * factor - # Mehrzeilige Felder: Zeilenweise per insert_text schreiben if m.pdf_field in MULTILINE_FIELDS: # Doppelte Leerzeilen entfernen @@ -269,6 +239,7 @@ def generate_activity_reports( mapping: list[OutputMapping], output_dir: Path, filename_pattern: str, + fontsize: float = 11.0, ) -> list[Path]: """ Erzeugt für jeden Mitarbeiter ein PDF. @@ -278,7 +249,7 @@ def generate_activity_reports( generated_files: list[Path] = [] # Pre-Scan: Feld-Positionen auf allen Template-Seiten ermitteln - field_info = _scan_template_fields(template_path, mapping) + field_info = _scan_template_fields(template_path, mapping, fontsize) for employee_name, records in employee_records.items(): if not records: @@ -301,7 +272,7 @@ def generate_activity_reports( temp_doc = fitz.open(template_path) pages = [temp_doc[i] for i in range(temp_doc.page_count)] - _fill_pdf_pages(pages, record, employee_name, mapping, field_info) + _fill_pdf_pages(pages, record, employee_name, mapping, field_info, fontsize) # Alle modifizierten Seiten ins Ergebnis-PDF einfügen result_doc.insert_pdf(temp_doc)