Editorial Craft Bible
Recetas de generacion desde HTML
Inicio Herramientas y anti-patterns Recetas de generacion desde HTML
Herramientas y anti-patterns

Recetas de generacion desde HTML

11 reglas Chrome for Developers · Chromium Headless docspagedjs.org · W3C CSS Paged Media L3weasyprint.org · W3C CSS Paged Mediawkhtmltopdf.org · project archive notice
29

Recetas de generacion desde HTML

11 reglas
310

Genera PDFs con headless Chrome y las banderas correctas

Headless Chrome renderiza tu HTML con el mismo motor del navegador, asi que lo que previsualizas es lo que obtienes. Usa --headless=new con --print-to-pdf, agrega --no-pdf-header-footer para matar la franja de fecha y URL por default, y --virtual-time-budget para dar tiempo a que fuentes, imagenes y layout se asienten antes de la captura. Apuntalo a una URL file:// o a un servidor local; la salida respeta tu CSS de @page y @media print.

# macOS: el binario vive dentro del bundle de la app
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"

"$CHROME" --headless=new \
  --print-to-pdf=out.pdf \
  --no-pdf-header-footer \
  --virtual-time-budget=10000 \
  --run-all-compositor-stages-before-draw \
  file:///abs/path/doc.html
Correcto
--no-pdf-header-footer
Pagina limpia: sin fecha, URL ni franja de conteo impuesta
Evitar
--print-to-pdf(solo)
7/17/26, 10:04file:///doc.html
El header/footer default estampa fecha y URL en cada pagina
Chrome for Developers · Chromium Headless docs
311

Suma Paged.js para desbloquear Paged Media real

Chrome ignora la mayor parte de la spec CSS Paged Media: sin headers corrientes, sin counter(pages), sin cajas de margen de @page, sin bleed/marks. Paged.js es un polyfill que trocea tu flujo en paginas reales en el DOM e implementa esas features, asi que titulos, folios y marcas de corte funcionan. Carga el script en el navegador para previsualizar, o corre el CLI para renderizar directo a PDF.

# Preview en navegador: pagina el DOM en vivo
<script src="https://unpkg.com/pagedjs/dist/paged.polyfill.js"></script>

# CLI: entra HTML, sale PDF paginado (usa Puppeteer/Chromium)
npx pagedjs-cli doc.html -o out.pdf
npx pagedjs-cli doc.html --output out.pdf --page-size Letter
pagedjs.org · W3C CSS Paged Media L3
312

Usa WeasyPrint para reportes server-side

WeasyPrint es una libreria de Python que convierte HTML y CSS en PDF con buen soporte nativo de Paged Media: headers corrientes, counter(page), cajas de margen, notas al pie y guionado, sin navegador ni Chromium. No ejecuta JavaScript, asi que es ideal para documentos deterministas basados en plantilla (facturas, estados de cuenta, reportes en lote) generados en un servidor. Dale una URL, un archivo, o un string HTML mas una hoja de estilos.

# CLI
weasyprint doc.html out.pdf
weasyprint https://example.com/report out.pdf

# Python
from weasyprint import HTML
HTML("doc.html").write_pdf("out.pdf")
weasyprint.org · W3C CSS Paged Media
313

Evita wkhtmltopdf para CSS moderno

wkhtmltopdf renderiza con un WebKit congelado de hace anos (Qt WebKit alrededor de 2012) y su proyecto esta archivado y sin mantenimiento. Flexbox, CSS grid, propiedades break-* modernas, fuentes variables y funciones de color actuales salen mal o no salen. Usalo solo para pipelines legados simples que no puedas cambiar; para algo nuevo, usa headless Chrome, Paged.js o WeasyPrint. Si no hay remedio, fuerza estilos de impresion con --print-media-type.

# Solo legado, documentos simples
wkhtmltopdf --print-media-type --enable-local-file-access \
  doc.html out.pdf

# Estado: proyecto archivado, WebKit congelado ≈ 2012
wkhtmltopdf.org · project archive notice
314

Usa Prince o DocRaptor cuando Paged Media deba ser perfecto

Prince (y DocRaptor, su API hospedada) es el motor comercial con el soporte mas profundo de CSS Paged Media: notas al pie, referencias cruzadas con target-counter(), salida PDF/X y PDF/A, y control preciso que las herramientas abiertas no tienen. Es de pago (gratis para uso no comercial, con un logo estampado en salidas sin licencia). Elige por necesidad: la tabla de abajo mapea cada motor a su profundidad de Paged Media, licencia y mejor uso.

# Prince CLI
prince doc.html -o out.pdf
prince doc.html --pdf-profile="PDF/X-4" -o print.pdf
Comparativa de motores
Motor Paged Media Licencia Mejor para
Headless ChromeParcial (necesita Paged.js)GratisJS pesado, fidelidad exacta de navegador
Paged.jsBueno (polyfill)Gratis / MITLibros, headers, marcas
WeasyPrintBueno (nativo, sin JS)Gratis / BSDReportes server, plantillas
wkhtmltopdfDebil (WebKit viejo)Gratis / archivadoLegado simple
Prince / DocRaptorEl mejor (spec completa)ComercialNotas al pie, PDF/X, cross-refs
Empareja el motor a la tarea: gratis cubre casi todo, comercial gana en profundidad
princexml.com · docraptor.com · W3C CSS Paged Media
315

Haz el documento autocontenido con data URI

Un pipeline de PDF se rompe cuando una imagen o fuente da 404 a media renderizada o carga muy lento. Inserta los assets criticos como URIs data: (base64) para que el HTML lleve todo lo que necesita y nada dependa de la red. Embebe fuentes igual dentro de @font-face, y declara siempre fallbacks de sistema en el stack por si una tipografia falla al decodificar. Base64 suma cerca de 33% de peso, asi que reservalo para los assets que deben estar presentes al capturar.

/* Imagen inline */
<img src="data:image/png;base64,iVBORw0KGgo...">

/* Fuente inline con fallback de sistema */
@font-face { font-family:'Inter';
  src:url(data:font/woff2;base64,d09GMgAB...) format('woff2'); }
body { font-family:'Inter', -apple-system, Georgia, serif; }

# Codifica un archivo a base64 (macOS/Linux)
base64 -i logo.png | pbcopy
Autocontenido
data:
base64
El asset viaja dentro del HTML; sin red al capturar
Evitar
/img/logo.png
La ruta remota da 404 o carga tarde; la imagen falta en el PDF
RFC 2397 (data URI) · MDN · W3C CSS Fonts
316

Separa los estilos de impresion con @media print

Envuelve todo lo que solo pertenece al PDF dentro de @media print: dimensionado fisico de @page, reglas de salto de pagina, ajuste exacto de color, y ocultar el chrome de pantalla como navs, botones y banners de cookies con display:none. Deja el layout de pantalla intacto para que el mismo HTML previsualice normal en el navegador e imprima limpio. Aqui cambias de pensar en viewport (px, scroll) a pensar en hoja (mm, paginas).

@media print {
  @page { size: Letter; margin: 18mm 16mm; }
  nav, .toolbar, .cookie-banner { display: none !important; }
  a { color: inherit; text-decoration: none; }
  h2, h3 { break-after: avoid; }
  figure, tr { break-inside: avoid; }
}
Vista de impresion
Nav y botones ocultos; solo se imprime el contenido
Evitar
El nav y toolbar de pantalla se cuelan en la pagina impresa
W3C CSS Paged Media · MDN @media print
317

Espera fuentes e imagenes antes de capturar

El PDF roto mas comun es una captura disparada antes de que las web fonts terminaran de cargar, asi que el texto cae a Times y el layout se corre. Condiciona la impresion a document.fonts.ready (y a promesas decode() de imagenes) cuando manejas con Puppeteer/Playwright, o dale al CLI un --virtual-time-budget generoso para que Chrome adelante timers hasta que la pagina se asiente. Nunca dependas de un sleep fijo a ciegas; espera la senal real de readiness.

// Puppeteer: espera la senal real de readiness
await page.goto(url, { waitUntil: 'networkidle0' });
await page.evaluate(() => document.fonts.ready);
await page.pdf({ path:'out.pdf', printBackground:true, format:'Letter' });

# Equivalente CLI: da tiempo a que corran los timers
--virtual-time-budget=10000
Correcto
Aa Editorial 66
Capturado tras document.fonts.ready: sale la tipografia buscada
Evitar
Aa Editorial 66
Capturado muy pronto: el texto salta a un fallback tipo Times
MDN document.fonts.ready · Puppeteer docs
318

Postprocesa con Ghostscript para CMYK y PDF/X

Los navegadores solo emiten PDFs RGB, asi que un flujo HTML a PDF que va a prensa necesita un segundo paso. Ghostscript convierte a DeviceCMYK, estampa un perfil PDF/X con Output Intent, hace downsample de imagenes y mergea archivos, todo desde la linea de comandos. Usa -dPDFX con un perfil ICC de salida y una definicion PDFX_def.ps para PDF/X-1a o X-3 real; para un convert CMYK simple usa -sColorConversionStrategy=CMYK -dProcessColorModel=/DeviceCMYK.

# Convert RGB -> CMYK simple
gs -o print.pdf -sDEVICE=pdfwrite \
  -sColorConversionStrategy=CMYK \
  -dProcessColorModel=/DeviceCMYK \
  -dOverrideICC -sOutputICCProfile=FOGRA39.icc web.pdf

# PDF/X-3 con Output Intent
gs -dPDFX -dBATCH -dNOPAUSE -sDEVICE=pdfwrite \
  -sColorConversionStrategy=CMYK -dProcessColorModel=/DeviceCMYK \
  -o printx.pdf PDFX_def.ps web.pdf
Ghostscript docs · ISO 15930 (PDF/X) · Adobe
319

Verifica leyendo las paginas reales del PDF

Un PDF solo revela sus fallas al paginarse, nunca en el scroll continuo del preview del navegador. Despues de cada export, abre el archivo y leelo pagina por pagina: el fondo llega a borde sin filos blancos, los saltos son limpios sin titulos huerfanos ni figuras partidas, se embebieron las fuentes buscadas, los margenes son parejos. Convierte paginas a imagenes para revisarlas rapido, y verifica fuentes embebidas en la linea de comandos.

# Confirma que cada fuente esta embebida (subset)
pdffonts out.pdf   # la columna 'emb' debe decir 'yes'

# Renderiza paginas a PNG para revisar saltos y sangrado
pdftoppm -png -r 150 out.pdf page

# Numero de paginas y tamano
pdfinfo out.pdf
Poppler utils (pdffonts, pdftoppm) · PDF Association
320

Receta dark full-bleed: margen cero mas color exacto

Un PDF dark o de color a borde necesita tres cosas juntas o falla. Pon @page { margin: 0 } para que Chrome no deje margenes de papel blanco alrededor de tu body oscuro. Agrega print-color-adjust: exact para que el motor conserve fondos e imagenes en vez de descartarlos. Y coloca el fondo en el elemento que llena la hoja (html, body, .doc), empujando la zona segura del texto como padding de un contenedor interno. Ve la seccion fondos-dark para el tratamiento completo.

@page { margin: 0; }
html {
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}
html, body, .doc { background: #14181F; color: #F4F6FA; }
.doc { padding: 15mm 16mm; }  /* zona segura del texto */
.cover { min-height: 245mm; break-after: page; }  /* Carta, pad 15mm */
Correcto
margin:0 + exact: el dark llena toda la hoja hasta el borde
Evitar
El margen @page default deja papel blanco enmarcando el body dark
Chromium print-color-adjust · W3C CSS Color Adjust · ECB fondos-dark