Files
fn_registry/python/functions/infra/pdf_create.py
T
egutierrez df424f2de0 feat: issue/0020 — generacion de PDFs en Python y Go
Añade 3 tipos Python (PDFDoc, PDFPage, PDFStyle) y 10 funciones Python
para construir PDFs con fpdf2 (builder fluent), fusionar PDFs con pypdf
y convertir HTML/Markdown a PDF via weasyprint (stub si no disponible).
Añade pdf_simple_report en Go como stub hasta que go-pdf/fpdf se integre.

- python/types/infra/: pdf_doc, pdf_page, pdf_style
- python/functions/infra/: pdf_create, pdf_add_page, pdf_add_text,
  pdf_add_table, pdf_add_image, pdf_add_header_footer, pdf_from_html,
  pdf_from_markdown, pdf_merge, pdf_save
- functions/infra/pdf_simple_report.go: stub Go con ReportSection/ReportTable
- 17 tests Python pasando (pytest)
- fpdf2 y pypdf añadidos via uv al venv Python

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 02:02:51 +02:00

95 lines
3.1 KiB
Python

"""pdf_create — inicializa un documento PDF nuevo con fpdf2."""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "python", "types"))
from fpdf import FPDF
# Import type from types directory
_types_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..", "python", "types", "infra")
sys.path.insert(0, _types_dir)
from pdf_doc import PDFDoc
class _PDFWithHeaderFooter(FPDF):
"""Subclase de FPDF con soporte para header/footer recurrente."""
def __init__(self, doc_ref: PDFDoc, *args, **kwargs):
super().__init__(*args, **kwargs)
self._doc_ref = doc_ref
def header(self):
if not self._doc_ref.header_text:
return
self.set_font(self._doc_ref.default_font, size=9)
self.set_text_color(100, 100, 100)
self.cell(0, 8, self._doc_ref.header_text, align="C", new_x="LMARGIN", new_y="NEXT")
self.ln(2)
def footer(self):
if not self._doc_ref.footer_text and not self._doc_ref.page_numbers:
return
self.set_y(-15)
self.set_font(self._doc_ref.default_font, size=9)
self.set_text_color(100, 100, 100)
text = self._doc_ref.footer_text
if self._doc_ref.page_numbers:
if "{n}" in text or "{total}" in text:
text = text.replace("{n}", str(self.page_no())).replace("{total}", "{nb}")
else:
text = f"Pagina {self.page_no()} de {{nb}}" if not text else text
self.cell(0, 10, text, align="C")
def pdf_create(
title: str = "",
author: str = "",
subject: str = "",
default_font: str = "Helvetica",
margin_left: float = 20.0,
margin_right: float = 20.0,
margin_top: float = 20.0,
margin_bottom: float = 20.0,
) -> PDFDoc:
"""Inicializa un documento PDF nuevo usando fpdf2.
Crea un objeto FPDF configurado con los margenes y metadatos especificados.
Retorna un PDFDoc listo para añadir paginas con pdf_add_page.
Args:
title: titulo del documento (aparece en metadata del PDF reader).
author: autor del documento (metadata).
subject: asunto del documento (metadata).
default_font: fuente por defecto. Built-ins: Helvetica, Times, Courier.
margin_left: margen izquierdo en mm. Por defecto 20.
margin_right: margen derecho en mm. Por defecto 20.
margin_top: margen superior en mm. Por defecto 20.
margin_bottom: margen inferior en mm. Por defecto 20.
Returns:
PDFDoc inicializado con el objeto fpdf2 configurado.
"""
doc = PDFDoc(
title=title,
author=author,
subject=subject,
default_font=default_font,
margin_left=margin_left,
margin_right=margin_right,
margin_top=margin_top,
margin_bottom=margin_bottom,
)
fpdf = _PDFWithHeaderFooter(doc_ref=doc)
fpdf.set_margins(margin_left, margin_top, margin_right)
fpdf.set_auto_page_break(auto=True, margin=margin_bottom)
fpdf.set_title(title)
fpdf.set_author(author)
fpdf.set_subject(subject)
fpdf.set_creator("fn-registry/pdf_create")
doc.fpdf = fpdf
return doc