Generar PDF con Claude Code: facturas y reportes con Playwright
Implementa PDF con Claude Code: Playwright, CSS de impresión, fuentes, facturas, reportes y pruebas visuales.
Un botón de “descargar PDF” parece una tarea pequeña hasta que el documento se usa con clientes reales. Una factura necesita importes correctos, impuestos, márgenes, datos fiscales, tablas legibles y un total que no quede cortado. Un reporte mensual necesita textos, capturas, gráficos y saltos de página razonables. Un certificado debe conservar nombre, fecha, identificador y marca visual aunque se imprima.
La forma más práctica para un equipo web es generar PDF desde HTML con un navegador real. Construyes el documento como HTML, aplicas CSS de impresión, renderizas con Chromium y exportas con Playwright o Puppeteer. Claude Code puede hacerlo bien, pero solo si le pides una superficie imprimible: tamaño de página, márgenes, fuentes, fondos, saltos, pruebas visuales y casos de error. Si el prompt es vago, puede devolver un ejemplo frágil basado en canvas o coordenadas manuales.
Usa fuentes oficiales para comprobar detalles: page.pdf de Playwright, PDF generation y PDFOptions de Puppeteer, @page y CSS de impresión en MDN, y la documentación de Claude Code. Para ampliar dentro de ClaudeCodeLab, revisa automatización de hojas de cálculo, testing con Playwright y estrategias de testing.
Por qué HTML-to-PDF es el punto de partida
Hay tres enfoques habituales. Con una librería de bajo nivel como jsPDF, dibujas texto y líneas con coordenadas. Sirve para etiquetas simples, pero una factura compleja se convierte pronto en ajustes de x e y. Con un enfoque de captura o canvas, creas una imagen y la colocas en el PDF. Puede verse correcto en una revisión rápida, pero el texto no se busca bien, pesa más, se ve borroso al ampliar y es difícil de comparar.
El enfoque HTML-to-PDF mantiene el documento como web imprimible. Las tablas siguen siendo tablas, los encabezados siguen siendo encabezados, y @page controla tamaño y margen. Además puedes abrir el HTML antes de exportar, hacer screenshots y detectar cambios visuales. Para Claude Code, esto también es mejor: edita HTML, CSS, scripts Node y tests, no una colección opaca de coordenadas.
flowchart TD
A["Datos de negocio"] --> B["Plantilla HTML"]
B --> C["CSS de impresión y @page"]
C --> D["Render de Chromium"]
D --> E["Archivo PDF"]
C --> F["Comparación de screenshot"]
F --> G["Evidencia de revisión"]
Casos de uso reales
El primer caso es la factura. Necesita vendedor, comprador, líneas, impuestos, total, vencimiento y número estable. Los fallos graves son importes mal redondeados, impuestos omitidos, fondo de tabla perdido o total desplazado a otra página.
El segundo caso es el reporte mensual. Marketing, SEO, ventas y SaaS suelen mezclar resumen, tablas, imágenes, gráficos y recomendaciones. Aquí importan los saltos de página: un título no debería quedar solo al final de una hoja, y un gráfico no debería separarse de su explicación.
El tercer caso es el certificado. Nombre, curso, fecha, ID, logo y QR suelen vivir en una sola página. Si conviertes todo en una imagen, pierdes búsqueda y aumentas tamaño. Mantén texto significativo como HTML y usa imágenes solo para logos, firmas o QR.
El cuarto caso es un reporte interno de auditoría. Cambios de permisos, despliegues, aprobaciones e incidentes deben poder rastrearse. Incluye fecha de generación, entorno, versión de la app e ID de datos en el pie.
Prompt útil para Claude Code
Implementa generación de PDF.
Dirección:
- Renderiza una plantilla HTML con Playwright Chromium y expórtala a PDF.
- No conviertas todo el documento en una sola imagen canvas.
- Usa A4 vertical, margen de 14mm, fondos visibles y CSS de impresión.
- Usa una pila de fuentes compatible con español, japonés e inglés.
- Deja una estructura reutilizable para facturas, reportes y certificados.
Implementación:
- Añade scripts/create-invoice-pdf.mjs.
- Genera out/invoice-ES-2026-0602.pdf desde datos de ejemplo.
- Formatea dinero con Intl.NumberFormat.
- Escapa texto de usuario antes de insertarlo en HTML.
- Usa printBackground y preferCSSPageSize en page.pdf.
Verificación:
- Documenta el comando de ejecución.
- Permite comparar por screenshot el HTML imprimible.
- Comprueba fuentes, saltos, fondos y total.
Código ejecutable
npm init -y
npm pkg set type=module
npm i -D playwright
npx playwright install chromium
mkdir scripts out
node scripts/create-invoice-pdf.mjs
import { chromium } from "playwright";
import { mkdir } from "node:fs/promises";
import { dirname, resolve } from "node:path";
const outputPath = resolve("out/invoice-ES-2026-0602.pdf");
const money = new Intl.NumberFormat("es-ES", { style: "currency", currency: "EUR" });
const invoice = {
number: "ES-2026-0602",
buyer: "Ejemplo Digital S.L.",
seller: "Masa Design Lab",
issuedAt: "2026-06-02",
items: [
{ name: "Diseño de plantilla PDF", quantity: 1, unitPrice: 800 },
{ name: "Script Playwright de generación", quantity: 1, unitPrice: 1200 },
{ name: "CSS de impresión y verificación de fuentes", quantity: 1, unitPrice: 600 },
],
};
function escapeHtml(value) {
return String(value).replace(/[&<>"']/g, (char) => ({
"&": "&", "<": "<", ">": ">", '"': """, "'": "'",
})[char]);
}
function renderHtml(data) {
const subtotal = data.items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);
const tax = Math.round(subtotal * 0.21);
const rows = data.items.map((item) => `<tr>
<td>${escapeHtml(item.name)}</td>
<td class="num">${item.quantity}</td>
<td class="num">${money.format(item.unitPrice)}</td>
<td class="num">${money.format(item.quantity * item.unitPrice)}</td>
</tr>`).join("");
return `<!doctype html><html lang="es"><head><meta charset="utf-8">
<style>
@page { size: A4; margin: 14mm; }
body { font-family: "Noto Sans", Arial, sans-serif; color: #202124; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
header { display: flex; justify-content: space-between; border-bottom: 3px solid #1f5eff; padding-bottom: 14px; }
h1 { margin: 0; font-size: 28px; }
table { width: 100%; border-collapse: collapse; margin-top: 24px; }
th { background: #eef3ff; text-align: left; }
th, td { border-bottom: 1px solid #d7dce5; padding: 10px 8px; }
.num { text-align: right; white-space: nowrap; }
.total { margin-left: auto; width: 260px; margin-top: 20px; font-size: 18px; font-weight: 700; }
.avoid-break { break-inside: avoid; page-break-inside: avoid; }
</style></head><body>
<header><h1>Factura</h1><div>Número: ${escapeHtml(data.number)}<br>Fecha: ${escapeHtml(data.issuedAt)}</div></header>
<p><strong>Cliente:</strong> ${escapeHtml(data.buyer)}</p>
<p><strong>Proveedor:</strong> ${escapeHtml(data.seller)}</p>
<table><thead><tr><th>Concepto</th><th class="num">Cantidad</th><th class="num">Precio</th><th class="num">Importe</th></tr></thead><tbody>${rows}</tbody></table>
<div class="total avoid-break">Total: ${money.format(subtotal + tax)}</div>
</body></html>`;
}
await mkdir(dirname(outputPath), { recursive: true });
const browser = await chromium.launch();
try {
const page = await browser.newPage();
await page.setContent(renderHtml(invoice), { waitUntil: "networkidle" });
await page.evaluate(() => document.fonts.ready);
await page.emulateMedia({ media: "print" });
await page.pdf({ path: outputPath, printBackground: true, preferCSSPageSize: true, margin: { top: "0", right: "0", bottom: "0", left: "0" } });
console.log(`Created ${outputPath}`);
} finally {
await browser.close();
}
Errores frecuentes y verificación
Los errores más comunes son convertir todo en imagen, olvidar printBackground, mezclar márgenes del navegador con @page, exportar antes de que carguen fuentes o logos, y meter nombres de cliente sin escapar. También es habitual probar solo una factura corta y descubrir demasiado tarde que una nota larga rompe el salto de página.
La verificación debe incluir más que “existe un PDF”. Prueba el cálculo del total con tests de unidad. Abre la ruta imprimible, aplica page.emulateMedia({ media: "print" }) y guarda un screenshot con Playwright para detectar cambios. Revisa una factura corta, una larga, una sin logo y una con texto multilingüe.
CTA y resultado probado
PDF es un tema monetizable porque conecta código con facturas, reportes, certificados, lead magnets y entregables de cliente. Empieza con la chuleta gratuita de Claude Code, usa productos y plantillas para flujos repetibles, y acude a formación o consultoría si tu equipo necesita integrar generación de PDF, revisión y Playwright en un repositorio real.
Para este artículo comprobé el flujo con Node.js local, Playwright Chromium y una factura de ejemplo. La parte más útil no fue llamar a page.pdf, sino tratar el HTML imprimible como una superficie verificable. En trabajos reales de Masa, las incidencias más frecuentes son fuentes que cambian el alto de línea, fondos que desaparecen y saltos de página que nadie miró antes de enviar el documento.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.