legco_ai_assistant/backend/app/utils/text_to_pdf.py

63 lines
1.7 KiB
Python

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