from __future__ import annotations import logging from reportlab.lib.pagesizes import A4 from reportlab.pdfgen import canvas logger = logging.getLogger(__name__) _MARGIN = 72 _FONT_SIZE = 10 _LINE_HEIGHT = 14 def generate_text_pdf(text: str, output_path: str) -> None: """Generate a single-page PDF containing the given plain text. Text is rendered with automatic wrapping and page breaks for long chunks. Raises on I/O or reportlab errors — caller should handle gracefully. """ c = canvas.Canvas(output_path, pagesize=A4) width, height = A4 usable_width = width - 2 * _MARGIN y = height - _MARGIN for paragraph in text.split("\n"): if not paragraph.strip(): y -= _LINE_HEIGHT if y < _MARGIN: c.showPage() y = height - _MARGIN continue lines = _wrap_text(paragraph, usable_width, c) for line in lines: if y < _MARGIN: c.showPage() y = height - _MARGIN c.drawString(_MARGIN, y, line) y -= _LINE_HEIGHT c.save() def _wrap_text(text: str, max_width: float, canvas_obj: canvas.Canvas) -> list[str]: """Wrap text to fit within max_width using the canvas's stringWidth.""" words = text.split() lines: list[str] = [] current_line = "" for word in words: test_line = f"{current_line} {word}".strip() if current_line else word if canvas_obj.stringWidth(test_line, "Helvetica", _FONT_SIZE) <= max_width: current_line = test_line else: if current_line: lines.append(current_line) current_line = word if current_line: lines.append(current_line) return lines