Crear archivos PDF con Python es útil para automatizar facturas, certificados, informes, recibos, etiquetas y documentos personalizados. La biblioteca fpdf2 permite construir un PDF página por página, controlar fuentes, márgenes, imágenes, tablas, encabezados y saltos de página sin depender de una aplicación de escritorio.
En esta guía aprenderás a instalar la biblioteca, generar un primer documento, añadir párrafos, imágenes y tablas, trabajar con Unicode, reutilizar funciones y manejar errores de rutas y permisos. Para mantener las dependencias aisladas, comienza con la guía de entornos virtuales en Python.
Qué biblioteca instalar
Para proyectos nuevos instala fpdf2. El nombre del paquete y el nombre importado son diferentes:
python -m pip install fpdf2from fpdf import FPDF
print("FPDF está disponible")La documentación oficial de fpdf2 contiene tutoriales y la referencia completa. Aunque existen paquetes antiguos con nombres parecidos, evita instalar varias implementaciones en el mismo entorno.
Crear el primer PDF
from fpdf import FPDF
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=16)
pdf.cell(text="Hola desde Python")
pdf.output("primer-documento.pdf")La secuencia básica es siempre parecida: crear el objeto, añadir una página, elegir una fuente, insertar contenido y guardar el archivo. Sin una página o una fuente seleccionada, las llamadas de texto pueden fallar.
Elegir formato y orientación
pdf = FPDF(
orientation="P",
unit="mm",
format="A4",
)
pdf.add_page()P representa orientación vertical y L horizontal. Utilizar milímetros facilita trabajar con medidas de impresión. Configura los márgenes antes de añadir contenido:
pdf.set_margins(left=18, top=18, right=18)
pdf.set_auto_page_break(auto=True, margin=18)Títulos y párrafos
cell() funciona bien con textos cortos. multi_cell() envuelve párrafos automáticamente.
from fpdf import FPDF
pdf = FPDF()
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()
pdf.set_font("Helvetica", style="B", size=20)
pdf.cell(
0,
12,
"Informe mensual",
new_x="LMARGIN",
new_y="NEXT",
align="C",
)
pdf.ln(6)
pdf.set_font("Helvetica", size=11)
pdf.multi_cell(
0,
6,
"Este documento fue generado automáticamente con Python. "
"El texto largo se divide en varias líneas.",
)
pdf.output("informe.pdf")Los saltos automáticos evitan que el texto continúe fuera del área imprimible. No posiciones todos los elementos con coordenadas fijas cuando la longitud del contenido puede cambiar.
Encabezados y pies de página
Para repetir elementos en todas las páginas, crea una subclase:
from fpdf import FPDF
class InformePDF(FPDF):
def header(self):
self.set_font("Helvetica", style="B", size=10)
self.cell(0, 8, "Informe de ventas", align="C")
self.ln(12)
def footer(self):
self.set_y(-15)
self.set_font("Helvetica", style="I", size=9)
self.cell(0, 10, f"Página {self.page_no()}", align="C")FPDF ejecuta estos métodos automáticamente. Mantén el encabezado y el pie pequeños para no reducir demasiado el área útil.
Añadir imágenes
from pathlib import Path
from fpdf import FPDF
logo = Path("recursos") / "logo.png"
pdf = FPDF()
pdf.add_page()
if logo.exists():
pdf.image(str(logo), x=15, y=12, w=35)
pdf.set_y(55)
pdf.set_font("Helvetica", size=12)
pdf.cell(0, 8, "Documento con imagen")
pdf.output("documento-imagen.pdf")Comprueba la existencia del archivo antes de insertarlo. La guía de pathlib en Python explica rutas portables y directorios. La documentación oficial de pathlib sirve como referencia para sus métodos.
Crear una tabla sencilla
from fpdf import FPDF
filas = [
("Producto", "Cantidad", "Precio"),
("Teclado", "2", "90,00"),
("Ratón", "3", "60,00"),
("Monitor", "1", "240,00"),
]
anchos = [80, 40, 50]
pdf = FPDF()
pdf.add_page()
for indice, fila in enumerate(filas):
estilo = "B" if indice == 0 else ""
pdf.set_font("Helvetica", style=estilo, size=10)
for valor, ancho in zip(fila, anchos):
pdf.cell(ancho, 9, valor, border=1)
pdf.ln()
pdf.output("productos.pdf")Las tablas simples pueden construirse con celdas y anchos calculados. Cuando el contenido puede ocupar varias líneas, necesitarás medir alturas y controlar los saltos de página.
Generar un informe desde datos
ventas = [
{"cliente": "Ana", "total": 120.50},
{"cliente": "Luis", "total": 89.90},
{"cliente": "Sara", "total": 215.00},
]
pdf = FPDF()
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()
pdf.set_font("Helvetica", style="B", size=18)
pdf.cell(0, 12, "Resumen de ventas", new_x="LMARGIN", new_y="NEXT")
pdf.set_font("Helvetica", size=11)
for venta in ventas:
linea = f"Cliente: {venta['cliente']} | Total: {venta['total']:.2f}"
pdf.cell(0, 8, linea, new_x="LMARGIN", new_y="NEXT")
pdf.output("resumen-ventas.pdf")Los datos pueden proceder de un CSV, una base de datos, una API o un DataFrame. La guía de archivos CSV en Python y la de Pandas para principiantes muestran dos fuentes comunes.
Separar cálculos y presentación
No mezcles todas las reglas de negocio con el diseño del PDF. Calcula y valida los datos antes de renderizarlos.
def calcular_total(items):
return sum(
item["cantidad"] * item["precio_unitario"]
for item in items
)Una función pequeña es más fácil de probar. Consulta la guía de funciones en Python para organizar responsabilidades y retornos.
Crear una función reutilizable
from pathlib import Path
from fpdf import FPDF
def crear_informe(titulo, lineas, destino):
destino = Path(destino)
destino.parent.mkdir(parents=True, exist_ok=True)
pdf = FPDF()
pdf.set_auto_page_break(auto=True, margin=15)
pdf.add_page()
pdf.set_font("Helvetica", style="B", size=18)
pdf.cell(0, 12, titulo, new_x="LMARGIN", new_y="NEXT")
pdf.ln(4)
pdf.set_font("Helvetica", size=11)
for linea in lineas:
pdf.multi_cell(0, 6, linea)
pdf.ln(2)
pdf.output(str(destino))
return destinoUnicode y fuentes personalizadas
Las fuentes básicas del PDF no incluyen todos los caracteres. Para textos multilingües registra una fuente Unicode que tu proyecto tenga permiso para distribuir.
pdf = FPDF()
pdf.add_page()
pdf.add_font("DejaVu", fname="fuentes/DejaVuSans.ttf")
pdf.set_font("DejaVu", size=12)
pdf.multi_cell(0, 7, "Texto internacional: café, acción, español")
pdf.output("unicode.pdf")No supongas que una fuente instalada en tu ordenador existe en el servidor. Incluye el archivo dentro de una ruta controlada y revisa su licencia.
Manejar errores de escritura
from pathlib import Path
salida = Path("informes") / "resultado.pdf"
try:
salida.parent.mkdir(parents=True, exist_ok=True)
pdf.output(str(salida))
except PermissionError:
print("No hay permiso para escribir en esta carpeta")
except OSError as error:
print(f"No fue posible crear el PDF: {error}")Los fallos comunes incluyen imágenes inexistentes, fuentes ausentes, rutas inválidas, permisos y caracteres no cubiertos por la fuente.
Errores frecuentes
- Olvidar
add_page(). - Escribir texto antes de seleccionar una fuente.
- No activar los saltos de página automáticos.
- Usar coordenadas rígidas para contenido variable.
- Confiar en rutas relativas sin comprobar el directorio actual.
- Mezclar cálculos y diseño en una única función enorme.
- Sobrescribir un archivo existente sin validación.
- Usar una fuente sin los caracteres necesarios.
Probar la generación
Las pruebas no necesitan comprobar visualmente cada píxel. Puedes verificar que el archivo se crea, tiene tamaño mayor que cero y se escribe en el directorio esperado. Para cálculos de facturas, prueba los totales por separado con Pytest.
Conclusión
FPDF ofrece una forma directa de crear PDF con Python. Empieza con páginas, fuentes y párrafos, y después añade imágenes, tablas, encabezados, pies y fuentes Unicode. Mantén las rutas controladas, valida los recursos externos y activa saltos automáticos.
La solución será más fácil de mantener cuando los cálculos estén separados del diseño y la generación se concentre en funciones pequeñas. Así, un script inicial puede convertirse en un sistema reutilizable de facturas, certificados e informes.



