use static font size over all document texts

This commit is contained in:
Rob committed 2026-06-30 09:43:25 +02:00
1 parent 23cb3c35b3
commit 2247c42e81
6 files changed
+16 -36

No files matched your search

+1
View File
@@ -289,6 +289,7 @@ if generate_clicked:
mapping=app_config.output_mapping, mapping=app_config.output_mapping,
output_dir=output_dir, output_dir=output_dir,
filename_pattern=app_config.filename_pattern, filename_pattern=app_config.filename_pattern,
fontsize=app_config.fontsize,
) )
st.session_state.generated_files = generated st.session_state.generated_files = generated
except Exception as e: except Exception as e:
+1
View File
@@ -113,6 +113,7 @@ def main():
mapping=config.output_mapping, mapping=config.output_mapping,
output_dir=output_dir, output_dir=output_dir,
filename_pattern=config.filename_pattern, filename_pattern=config.filename_pattern,
fontsize=config.fontsize,
) )
output_file = generated[0] output_file = generated[0]
+4
View File
@@ -6,6 +6,10 @@ output_dir = output
[output] [output]
filename_pattern = {last_name}_{first_name}_Tätigkeitsnachweis.pdf filename_pattern = {last_name}_{first_name}_Tätigkeitsnachweis.pdf
[font]
# Statische Schriftgröße (in Punkt) für alle Felder im PDF
fontsize = 9
[parsing] [parsing]
# Sheets whose names match these patterns are treated as monthly data sheets # Sheets whose names match these patterns are treated as monthly data sheets
# Use * as wildcard. Sheets not matching are ignored (e.g. "Alle Rollen"). # Use * as wildcard. Sheets not matching are ignored (e.g. "Alle Rollen").
+2
View File
@@ -21,6 +21,7 @@ def load_settings(config_dir: Path, project_root: Path) -> AppConfig:
paths = parser["paths"] if "paths" in parser else {} paths = parser["paths"] if "paths" in parser else {}
output = parser["output"] if "output" in parser else {} output = parser["output"] if "output" in parser else {}
parsing = parser["parsing"] if "parsing" in parser else {} parsing = parser["parsing"] if "parsing" in parser else {}
font = parser["font"] if "font" in parser else {}
return AppConfig( return AppConfig(
input_dir=str(project_root / paths.get("input_dir", "input")), 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??"), month_sheet_pattern=parsing.get("month_sheet_pattern", "* 20??"),
skip_empty_name=parsing.getboolean("skip_empty_name", True), skip_empty_name=parsing.getboolean("skip_empty_name", True),
skip_empty_antragsnummer=parsing.getboolean("skip_empty_antragsnummer", True), skip_empty_antragsnummer=parsing.getboolean("skip_empty_antragsnummer", True),
fontsize=font.getfloat("fontsize", 11.0),
) )
+1
View File
@@ -63,6 +63,7 @@ class AppConfig:
month_sheet_pattern: str = "* 20??" month_sheet_pattern: str = "* 20??"
skip_empty_name: bool = True skip_empty_name: bool = True
skip_empty_antragsnummer: bool = True skip_empty_antragsnummer: bool = True
fontsize: float = 11.0
@dataclass @dataclass
+7 -36
View File
@@ -101,6 +101,7 @@ def _get_field_value(record: EmployeeRecord, employee_name: str, source_field: s
def _scan_template_fields( def _scan_template_fields(
template_path: Path, template_path: Path,
mapping: list[OutputMapping], mapping: list[OutputMapping],
fontsize: float,
) -> dict[str, dict]: ) -> dict[str, dict]:
""" """
Scannt ALLE Seiten des PDF-Templates nach _Field-Platzhaltern. Scannt ALLE Seiten des PDF-Templates nach _Field-Platzhaltern.
@@ -117,7 +118,7 @@ def _scan_template_fields(
field_info: dict[str, dict] = {} 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(): for pdf_field, source_field in pdf_to_source.items():
found_any = False found_any = False
for page_num in range(doc.page_count): for page_num in range(doc.page_count):
@@ -128,7 +129,7 @@ def _scan_template_fields(
"source_field": source_field, "source_field": source_field,
"page_num": page_num, "page_num": page_num,
"rect": found[0], "rect": found[0],
"fontsize": 11, # Fallback "fontsize": fontsize,
"fontname": "helv", "fontname": "helv",
} }
found_any = True found_any = True
@@ -136,23 +137,6 @@ def _scan_template_fields(
if not found_any: if not found_any:
field_info[pdf_field] = None # nicht gefunden 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() doc.close()
return field_info return field_info
@@ -167,17 +151,13 @@ def _fill_pdf_pages(
employee_name: str, employee_name: str,
mapping: list[OutputMapping], mapping: list[OutputMapping],
field_info: dict[str, dict], field_info: dict[str, dict],
fontsize: float,
) -> None: ) -> None:
"""Befüllt alle Template-Seiten: redacted _Field-Platzhalter und fügt echte Daten ein.""" """Befüllt alle Template-Seiten: redacted _Field-Platzhalter und fügt echte Daten ein."""
# Mehrzeilige Felder, die als Textbox (mit Zeilenumbruch) behandelt werden # Mehrzeilige Felder, die als Textbox (mit Zeilenumbruch) behandelt werden
MULTILINE_FIELDS = {"Tätigkeiten_Field", "Dienstreisen_Field"} MULTILINE_FIELDS = {"Tätigkeiten_Field", "Dienstreisen_Field"}
TEXTBOX_WIDTH_PT = 400 # Breite der Textbox in Punkt 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: for m in mapping:
if m.pdf_field is None: if m.pdf_field is None:
continue continue
@@ -192,7 +172,6 @@ def _fill_pdf_pages(
page = pages[page_num] page = pages[page_num]
rect = info["rect"] rect = info["rect"]
fontsize = info.get("fontsize", 11)
value = _get_field_value(record, employee_name, m.source_field) value = _get_field_value(record, employee_name, m.source_field)
formatted = _format_value(value, m.format) formatted = _format_value(value, m.format)
@@ -204,15 +183,6 @@ def _fill_pdf_pages(
if not formatted: if not formatted:
continue 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 # Mehrzeilige Felder: Zeilenweise per insert_text schreiben
if m.pdf_field in MULTILINE_FIELDS: if m.pdf_field in MULTILINE_FIELDS:
# Doppelte Leerzeilen entfernen # Doppelte Leerzeilen entfernen
@@ -269,6 +239,7 @@ def generate_activity_reports(
mapping: list[OutputMapping], mapping: list[OutputMapping],
output_dir: Path, output_dir: Path,
filename_pattern: str, filename_pattern: str,
fontsize: float = 11.0,
) -> list[Path]: ) -> list[Path]:
""" """
Erzeugt für jeden Mitarbeiter ein PDF. Erzeugt für jeden Mitarbeiter ein PDF.
@@ -278,7 +249,7 @@ def generate_activity_reports(
generated_files: list[Path] = [] generated_files: list[Path] = []
# Pre-Scan: Feld-Positionen auf allen Template-Seiten ermitteln # 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(): for employee_name, records in employee_records.items():
if not records: if not records:
@@ -301,7 +272,7 @@ def generate_activity_reports(
temp_doc = fitz.open(template_path) temp_doc = fitz.open(template_path)
pages = [temp_doc[i] for i in range(temp_doc.page_count)] 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 # Alle modifizierten Seiten ins Ergebnis-PDF einfügen
result_doc.insert_pdf(temp_doc) result_doc.insert_pdf(temp_doc)