¿Cómo funciona la lógica de CSS print al convertir HTML a PDF?
9 min de lectura
Cuando conviertes una página web a PDF obtienes un resultado distinto del que ves en pantalla, y no es un error: es la consecuencia natural del paso entre dos modelos de maquetación. En este artículo explicamos cómo se reparten las páginas web en páginas, las reglas de impresión de CSS y por qué el contenido dinámico da problemas.
Dos modelos de maquetación
Una página web es un medio continuo. No existe el concepto de página; el contenido fluye de arriba abajo como una columna infinita. El ancho cambia según la ventana del navegador y la maquetación se adapta.
El PDF es un medio paginado. Hay páginas de tamaño fijo y el contenido se reparte entre ellas.
CSS separa ambos mediante el concepto de "tipo de medio" y permite aplicar reglas distintas a cada uno.
@media print: dos conjuntos de estilos distintos
Las páginas web pueden definir estilos distintos para pantalla y para impresión:
/* Para todos */
body { font-family: sans-serif; }
/* Solo en pantalla */
@media screen {
nav { position: fixed; background: #222; }
}
/* Solo al imprimir */
@media print {
nav, .sidebar, .anuncio, .comentarios { display: none; }
body { font-size: 11pt; color: #000; background: #fff; }
a { text-decoration: underline; }
}
La generación del PDF usa las reglas de print. En los sitios bien diseñados, eso limpia notablemente la salida:
- Se ocultan los menús de navegación, las barras laterales y los anuncios.
- Los colores se vuelven simples y legibles.
- Los tamaños de letra se ajustan en puntos.
Pero también genera dos tipos de problema:
Ocultación excesiva. Algunos sitios ocultan en su estilo de impresión también cuadros de información importantes.
Ausencia total de estilo. Los sitios que no han definido estilos de impresión usan la maquetación de pantalla tal cual, y el resultado suele ser malo: los menús de posición fija pueden repetirse en cada página y los fondos oscuros pueden dar problemas.
El equivalente físico de las unidades CSS
En el contexto de impresión, las unidades CSS tienen valores físicos precisos:
| Unidad | Equivalente físico | |---|---| | 1in (pulgada) | 2,54 cm | | 1cm | 1 cm | | 1mm | 1 mm | | 1pt (punto) | 1/72 de pulgada | | 1pc (pica) | 12 puntos | | 1px (píxel CSS) | 1/96 de pulgada |
La última fila es crítica: 96 píxeles CSS son exactamente 1 pulgada.
La consecuencia de esa constante: una página A4 (210 mm = 8,27 pulgadas de ancho) mide, antes de descontar los márgenes, unos 794 píxeles CSS de ancho.
Y eso tiene un efecto importante en los diseños adaptables. Muchos sitios usan puntos de ruptura como estos:
@media (max-width: 768px) { /* maquetación de tableta y móvil */ }
@media (max-width: 1024px) { /* maquetación de pantalla pequeña */ }
794 píxeles está por debajo de la segunda regla y, en algunos sitios, cerca también de la primera. Resultado: en el PDF la página adopta una maquetación distinta de la que ves en el escritorio: las columnas se apilan, el menú se convierte en un icono de hamburguesa y las imágenes ocupan todo el ancho.
Esa es la respuesta a la pregunta "¿por qué en el PDF la página se ve como en el móvil?".
Vías de solución: usar orientación horizontal (un A4 horizontal son unos 1123 píxeles), reducir los márgenes o aplicar un escalado.
@page: definir la página en sí
En CSS, la regla @page define la caja de página:
@page {
size: A4;
margin: 2cm 1.5cm;
}
Para size se pueden usar tamaños predefinidos (A4, A3, Letter, Legal) o medidas propias (size: 210mm 297mm). La palabra clave landscape cambia la orientación.
Con las pseudoclases se pueden aplicar reglas distintas a páginas distintas:
@page :first {
margin-top: 5cm; /* espacio superior en la portada */
}
@page :left {
margin-left: 3cm; /* margen de encuadernación */
}
@page :right {
margin-right: 3cm;
}
Esto se usa para dejar margen de encuadernación en salidas a doble cara estilo libro.
El algoritmo de salto de página
El flujo de contenido se corta en trozos del alto de la página. Cuando el punto de corte cae en medio de un elemento, el navegador tiene que tomar una decisión.
CSS ofrece propiedades para guiar esa decisión:
/* Que este elemento no se parta en dos */
table, figure, .tarjeta { page-break-inside: avoid; }
/* Que empiece una página nueva antes de este elemento */
h1, .capitulo { page-break-before: always; }
/* Que no haya salto justo después de este elemento */
h2, h3 { page-break-after: avoid; }
La tercera regla es especialmente útil: evita que un título se quede solo al final de la página con su texto en la página siguiente.
Control de líneas huérfanas y viudas:
p {
orphans: 3; /* que queden al menos 3 líneas del párrafo al pie de página */
widows: 3; /* que haya al menos 3 líneas al principio de la página */
}
Son reglas clásicas de tipografía: que una sola línea de un párrafo se quede sola al final o al principio de una página queda mal.
Comportamiento del corte según el tipo de elemento:
| Elemento | Comportamiento al cortar | |---|---| | Bloque de texto | Se corta por los límites de línea | | Tabla | Se corta por los límites de fila | | Imagen | No se puede partir; se desplaza o queda cortada | | Elemento posicionado | Resultados imprevisibles | | Flexbox / Grid | El soporte varía según el navegador |
Las dos últimas filas son importantes: el posicionamiento absoluto y los sistemas de maquetación modernos no siempre se comportan bien en el contexto de impresión. Una maquetación compleja con grid puede romperse de forma inesperada en el límite de página.
Por qué no se imprimen los gráficos de fondo
Los motores de impresión, por defecto, no imprimen los colores ni las imágenes de fondo. Es un comportamiento integrado para ahorrar tinta.
En CSS hay una forma de forzarlo:
* {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
En el diálogo de impresión del navegador también existe la opción "Gráficos de fondo".
Con ese ajuste desactivado, los diseños de fondo oscuro se convierten en un desastre: el texto blanco sobre fondo negro, al no imprimirse el fondo, queda blanco sobre blanco y no se ve nada.
Si preparas tú el HTML, invertir los colores en el estilo de impresión es un enfoque más seguro:
@media print {
.seccion-oscura { background: #fff; color: #000; }
}
Enlaces e interacción
Los hipervínculos suelen convertirse en anotaciones de enlace del PDF y siguen siendo clicables. Eso depende de la capacidad del conversor.
En un documento impreso no se ve adónde lleva un enlace. Con CSS es posible añadir la dirección al texto:
@media print {
a[href^="http"]::after {
content: " (" attr(href) ")";
font-size: 0.8em;
color: #555;
}
}
Los campos de formulario suelen convertirse en su aspecto estático. Algunos conversores pueden transformar formularios HTML en campos de formulario PDF, pero no es una función habitual.
Las interacciones de JavaScript se pierden por completo: menús desplegables, pestañas, acordeones. El contenido que está cerrado no aparece en el PDF: si una sección de preguntas frecuentes está en formato acordeón, en el PDF solo se verán las preguntas, no las respuestas.
Para esos casos hay que abrir el contenido oculto en el estilo de impresión:
@media print {
.contenido-acordeon { display: block !important; }
}
Contenido dinámico y el problema de la sincronización
La mayoría de las páginas web modernas traen buena parte del contenido después de cargar la página: llamadas a API, imágenes con carga diferida, desplazamiento infinito.
El conversor da la página por "lista" en cierto momento y arranca la generación del PDF. Para determinar ese momento se usan distintas estrategias:
- Evento de carga (load event): cuando bajan los primeros recursos de la página. Suele ser insuficiente.
- Red inactiva (network idle): cuando no llega ninguna petición nueva durante cierto tiempo. Es una señal mejor.
- Espera fija: por ejemplo, esperar 3 segundos. Es tosco pero simple.
- Espera de un elemento: esperar hasta que aparezca un selector CSS concreto. Es el método más preciso, pero exige configuración.
En las páginas con desplazamiento infinito ninguna estrategia consigue el contenido completo: el contenido solo se carga a medida que el usuario baja, y el conversor no se desplaza.
Las imágenes con carga diferida (lazy loading) sufren el mismo problema: las que no entran en el área visible no se cargan nunca y quedan en blanco en el PDF.
Carga de tipografías
Las tipografías web se descargan de fuentes externas:
@font-face {
font-family: 'Especial';
src: url('https://fonts.example.com/especial.woff2');
}
Si el entorno de conversión no puede acceder a ese recurso, la tipografía no se carga y el navegador recurre a una de reserva. Como cambian los anchos de las letras, la maquetación se desplaza.
Además hay un problema de sincronización: si el PDF se genera antes de que cargue la tipografía, la página se dibuja con la de reserva.
Soluciones en documentos críticos:
- Incrustar la tipografía en el CSS como base64.
- Usar tipografías del sistema.
- Añadir un control que espere a que la tipografía cargue.
En resumen
La conversión de HTML a PDF es el paso de un medio continuo a un medio paginado, y CSS gestiona ese paso con @media print, @page y las propiedades de salto de página. Como el píxel CSS está definido como 1/96 de pulgada, la página A4 mide unos 794 píxeles de ancho y eso hace que los diseños adaptables pasen a su versión móvil. Los gráficos de fondo no se imprimen por defecto, lo que es un problema serio en los diseños de fondo oscuro. Las decisiones de salto de página se pueden guiar con propiedades como page-break-inside, orphans y widows, pero esas reglas tienen que estar definidas en el propio código de la página. Y el contenido dinámico puede llegar incompleto según cuándo decida el conversor que la página está "lista": en páginas con desplazamiento infinito y carga diferida, obtener el contenido completo suele ser imposible.
Preguntas frecuentes
¿A qué medida física corresponde un píxel en CSS?
En el contexto de impresión, el píxel CSS está definido como 1/96 de pulgada. Es decir, 96 píxeles equivalen exactamente a 1 pulgada, unos 2,54 centímetros. Gracias a esa constante, una página A4 (210 mm de ancho) mide unos 794 píxeles CSS de ancho. Esa es la razón por la que los diseños adaptables pasan a su versión móvil en el PDF: 794 píxeles está por debajo del punto de ruptura de tableta o móvil en muchos sitios.
¿Qué hace la regla @page?
Define la página en sí: tamaño, orientación y márgenes. Por ejemplo, la regla @page { size: A4 landscape; margin: 1.5cm; } hace que la salida sea A4 horizontal y deja 1,5 cm de margen en cada lado. Además, con las pseudoclases :first, :left y :right se pueden aplicar reglas distintas a la primera página o a las páginas pares e impares.
¿Cómo decide el algoritmo de salto de página?
El flujo de contenido se corta en trozos del alto de la página. Si el punto de corte cae en medio de un elemento, el navegador comprueba primero las propiedades de salto de página (como page-break-inside); si no hay ninguna restricción, parte el elemento en dos. Los bloques de texto se cortan por los límites de línea, las tablas por los límites de fila y las imágenes, que no se pueden partir, o se desplazan enteras a la página siguiente o quedan cortadas.
¿Por qué a veces el contenido generado con JavaScript no llega al PDF?
Porque el conversor da la página por 'lista' en cierto momento y arranca la generación del PDF. Si el contenido llega mediante peticiones de red posteriores a la carga de la página y el conversor no espera lo suficiente, la salida se produce antes de que esas peticiones terminen. Algunas herramientas esperan a que se detenga el tráfico de red, pero en páginas con desplazamiento infinito o actualización periódica esa situación no llega a producirse nunca.
Pruébalo ahora mismo con HTML/URL → PDF.
Probar HTML/URL → PDF