diff --git a/app.py b/app.py index ed7134f..7e16cdb 100644 --- a/app.py +++ b/app.py @@ -262,11 +262,18 @@ def _split_thinking(text: str) -> tuple[str, str]: def _md_to_pdf(title: str, content_md: str) -> bytes: """Render Markdown content to a PDF byte string.""" + + def _l1(text: str) -> str: + """Sanitize text to Latin-1; fpdf2 built-in fonts require it.""" + return text.encode("latin-1", errors="replace").decode("latin-1") + + _title = _l1(title[:100]) + class PDF(fpdflib.FPDF): def header(self): self.set_font("Helvetica", "B", 9) self.set_text_color(100, 100, 100) - self.cell(0, 6, title, align="L") + self.cell(0, 6, _title, align="L") self.ln(2) self.set_draw_color(200, 200, 200) self.line(self.l_margin, self.get_y(), self.w - self.r_margin, self.get_y()) @@ -282,41 +289,37 @@ def _md_to_pdf(title: str, content_md: str) -> bytes: pdf.set_auto_page_break(auto=True, margin=15) pdf.add_page() - # Render markdown line by line with minimal formatting + def _cell(text: str, size: int = 10, bold: bool = False, indent: float = 0, h: float = 5): + try: + pdf.set_font("Helvetica", "B" if bold else "", size) + pdf.set_text_color(20 if bold else 30, 20 if bold else 30, 20 if bold else 30) + if indent: + pdf.set_x(pdf.l_margin + indent) + pdf.multi_cell(pdf.w - pdf.l_margin - pdf.r_margin - indent, h, _l1(text)) + except Exception: + pass + for raw_line in content_md.split("\n"): line = raw_line.rstrip() if line.startswith("# "): - pdf.set_font("Helvetica", "B", 14) - pdf.set_text_color(20, 20, 20) - pdf.multi_cell(0, 7, line[2:]) + _cell(line[2:], size=14, bold=True, h=7) pdf.ln(1) elif line.startswith("## "): - pdf.set_font("Helvetica", "B", 12) - pdf.set_text_color(40, 40, 40) - pdf.multi_cell(0, 6, line[3:]) + _cell(line[3:], size=12, bold=True, h=6) pdf.ln(1) elif line.startswith("### "): - pdf.set_font("Helvetica", "B", 11) - pdf.set_text_color(60, 60, 60) - pdf.multi_cell(0, 6, line[4:]) + _cell(line[4:], size=11, bold=True, h=6) elif line.startswith(("- ", "* ", "+ ")): - pdf.set_font("Helvetica", "", 10) - pdf.set_text_color(30, 30, 30) - pdf.multi_cell(0, 5, " • " + line[2:]) + _cell("• " + line[2:], indent=5) elif re.match(r"^\d+\. ", line): - pdf.set_font("Helvetica", "", 10) - pdf.set_text_color(30, 30, 30) - pdf.multi_cell(0, 5, " " + line) - elif line == "" or line == "---": + _cell(line, indent=5) + elif line in ("", "---"): pdf.ln(3) else: - # Strip inline markdown markers for bold/italic clean = re.sub(r"\*\*(.+?)\*\*", r"\1", line) clean = re.sub(r"\*(.+?)\*", r"\1", clean) clean = re.sub(r"`(.+?)`", r"\1", clean) - pdf.set_font("Helvetica", "", 10) - pdf.set_text_color(30, 30, 30) - pdf.multi_cell(0, 5, clean) + _cell(clean) return bytes(pdf.output()) @@ -775,8 +778,9 @@ def _process_analysis_job(job_id: int): try: pdf_bytes = _md_to_pdf(step_title, content) attachments.append((f"{fname_base}.pdf", pdf_bytes, "application", "pdf")) - except Exception: - pass + except Exception as pdf_err: + import logging + logging.warning(f"PDF-Generierung fehlgeschlagen für '{step_title}': {pdf_err}") _send_email(email_to, subject, answer, attachments) except Exception as mail_err: _job_set(job_id, error=f"[E-Mail-Fehler] {mail_err}")