Qué realmente sobrevive de HTML a PDF, medido
Doce características de CSS de impresión renderizadas a través de Chromium sin interfaz gráfica y luego leídas de vuelta desde el PDF — saltos de página, tamaños @page, encabezados de tabla repetidos, enlaces y fuentes. Dos de ellos no funcionan, y una trampa común es culpa de la API, no del CSS.
Novedades aquí: Una matriz de soporte por característica para CSS de impresión de HTML a PDF, medida en Chromium 148 renderizando cada caso e inspeccionando el recuento de páginas, dimensiones de página, anotaciones de enlaces y texto por página del PDF resultante — no leyendo documentación. Cada veredicto es reproducible desde el arnés publicado y el conjunto de datos, y se muestra que dos técnicas ampliamente recomendadas no funcionan.
Todo servicio HTML a PDF dice que su salida es fiel. Ninguno de ellos publica qué significa eso. Así que lo medimos: doce características de CSS de impresión, cada una renderizada a través de Chromium headless — el motor detrás de MostlyRender y detrás de la mayoría de servicios HTML a PDF — y luego leerlas nuevamente de la PDF resultante.
Nada aquí está citado de la documentación. Cada veredicto proviene de inspeccionar el archivo: cuántas páginas tiene, qué tamaño tienen, qué texto llegó a qué página, si existe una anotación de enlace, si se incustró una fuente.
# La matriz
Medido en Chromium 148.0.7778.96, primero el 1 de septiembre de 2026 y nuevamente el 27 de septiembre de 2026. Ambas ejecuciones produjeron los mismos doce veredictos.
| Característica | Funciona | Lo que observamos |
|---|---|---|
break-after: page |
Sí | 2 páginas, como se solicitó |
page-break-after: always (heredado) |
Sí | 2 páginas — la ortografía antigua sigue siendo válida |
break-inside: avoid |
Sí | El bloque se movió completamente a la página 2 en lugar de dividirse |
@page { size: A5 } |
Sí | 420×595pt — exactamente A5 |
@page { size: A4 landscape } |
Sí | 842×595pt — horizontal |
Repetición de <thead> entre páginas |
Sí | 3 páginas, encabezado presente en la página 2 |
position: fixed como encabezado fijo |
Sí | Presente en 2 de 2 páginas |
@page cajas de margen + counter(page) |
No | Sin número de página en ningún lugar de la salida |
<a href> como enlace interactivo |
Sí | Anotación de enlace presente |
Enlaces internos #anchor |
Sí | Destino presente |
| Incustración de fuentes | Sí | DejaVuSansMono incustrada y subconjunto |
| Cuadrícula CSS entre saltos de página | No | Los 8 elementos en la página 1; página 2 vacía |
Diez de doce funcionan. Los dos que no funcionan merecen ser conocidos antes de que diseñes alrededor de ellos.
# Los dos que fallan
Las cajas de margen de CSS Paged Media no se representan. La especificación te da @page { @bottom-center { content: "Page " counter(page) } }, está en cada tutorial sobre impresión desde la web, y Chromium lo ignora. No encontramos ningún número de página en la salida en absoluto. Si necesitas números de página, deben ser agregados por lo que produce el PDF — por eso las herramientas que ofrecen numeración de páginas lo hacen como una característica en lugar de decirte que escribas CSS.
La cuadrícula CSS no se fragmenta. Diseñamos ocho elementos en una cuadrícula de dos columnas lo suficientemente alta para necesitar dos páginas. Los ocho se renderizaron en la página uno, y la página dos salió en blanco. La cuadrícula funciona bien para contenido que cabe en una sola página; para cualquier cosa que pagine, usa flujo de bloque o una tabla.
# La trampa que no es CSS en absoluto
Mientras construíamos el arnés, registramos @page { size: A5 } como no compatible — y fue nuestro propio error. El renderizador recibía la instrucción de usar A4 por la llamada API, y un formato de papel pasado por el llamador silenciosamente anula el tamaño @page del documento. Con el formato omitido y el tamaño de página CSS preferido, A5 y horizontal resultaron exactamente correctos.
Esto merece ser declarado claramente porque casi con certeza es la razón más común por la que las personas concluyen que @page está roto: el CSS está bien, y algo anterior silenciosamente lo está superando. Solo lo descubrimos porque un resultado que estaba en desacuerdo con la especificación nos hizo releer el arnés en lugar de publicar el número.
# Reproducir esto
El arnés y el conjunto de datos detrás de la tabla están ambos publicados:
- bench-htmltopdf.js inicia Chromium, renderiza cada caso, e inspecciona la salida con pdf-lib y pdf.js.
- html-to-pdf.json es el archivo de salida del que escribimos esta tabla, marcado con la versión de Chromium que midió.
Para ejecutarlo tú mismo, guarda el arnés en una carpeta vacía y ejecuta:
npm i playwright pdf-lib pdfjs-dist@3
npx playwright install chromium
node bench-htmltopdf.js
Un límite que merece ser declarado: la detección de fuentes escanea los bytes de PDF sin procesar para buscar nombres de fuentes, por lo que un resultado positivo es sólido pero uno vacío solo significa “no visible para este método”. El arnés reporta ese caso como no concluyente en lugar de como un fracaso, porque publicar “las fuentes no están incustradas” sobre la base de un escaneo que no puede verlas sería exactamente el tipo de afirmación que este artículo existe para evitar.
# Por qué lo medimos
Construimos una herramienta y API HTML a PDF en este motor. Publicar qué hace y qué no hace es más útil que afirmar que es fiel — y si un número aquí está equivocado, el arnés que lo produjo está vinculado arriba para ser ejecutado.
Preguntas frecuentes
¿Sigue funcionando page-break-after, o necesito break-after?
Ambos funcionan. Medimos break-after: page moderno y page-break-after: always heredado por separado y cada uno produjo el documento de dos páginas esperado en Chromium 148. Si tienes plantillas antiguas que emiten la ortografía heredada, no son la razón por la que tu paginación es incorrecta.
¿Por qué se ignora mi tamaño de @page?
Casi siempre porque el llamador lo anuló. Si la API o biblioteca que impulsa el renderizado pasa su propio formato de papel, eso prevalece y tu @page { size: … } se descarta silenciosamente. Con el formato omitido y tamaño de página CSS preferido, A5 y landscape salieron exactamente como se pidió. Este es el informe falso más común de "@page size does not work" — nos atrapó mientras construíamos el arnés para este artículo.
¿Puedo poner números de página en el margen con CSS?
No en Chromium. @page { @bottom-center { content: counter(page) } } es parte de CSS Paged Media y está ampliamente documentado, pero la caja de margen simplemente no se renderiza — lo medimos y no encontramos número de página en ningún lugar del resultado. Los números de página tienen que provenir de la herramienta que realiza la conversión, no de tu hoja de estilos.
¿Se dividen correctamente los diseños de cuadrícula CSS en páginas?
No. En nuestra prueba, los ocho elementos de la cuadrícula se renderizaron en la primera página y la segunda página salió vacía — la cuadrícula no se fragmentó. Si tu documento es lo suficientemente largo para paginar, coloca la parte repetida con flujo de bloque normal o una tabla y mantén la cuadrícula para cosas que caben en una página.
¿Se conservan los hipervínculos en el PDF?
Sí. Tanto un <a href> externo como un #anchor interno produjeron anotaciones de enlace reales en el archivo de salida, por lo que los enlaces permanecen interactivos y una tabla de contenidos generada permanece navegable.