Saltar al contenido

Las fuentes en un SVG exportado

Por qué un SVG servido como imagen no carga fuentes externas, las cuatro salidas reales con sus costes, y cómo decidir entre conservar el texto y convertirlo a contornos.

El modo seguro

Cuando un SVG se carga a través de img, background-image, border-image, content o mask, el navegador lo renderiza en un modo restringido. En ese modo:

  • No se ejecuta JavaScript.
  • No se cargan recursos externos: ni hojas de estilo, ni imágenes, ni ficheros de fuente.
  • El documento no tiene acceso al documento anfitrión ni al revés.
  • No hay interacción: ni eventos, ni enlaces, ni hover.

Esas restricciones existen porque un SVG es un documento con capacidad de ejecutar código y de hacer peticiones. Permitir que cualquier img de la web arrastrase eso convertiría a la etiqueta de imagen en un vector de ataque. La decisión es correcta y no va a cambiar.

Lo que significa para las fuentes: un @font-face con src: url(...) dentro del SVG no carga el fichero. Y la fuente de la página anfitriona tampoco está disponible, porque el SVG es un documento independiente. El motor cae a la fuente que resuelva la familia declarada dentro del propio SVG, o a la de reserva del sistema.

El mismo bloqueo aplica cuando dibujas un SVG en un lienzo pasando por un img, que es la vía estándar para exportar a PNG. Volveremos sobre ello en el nivel 26.

Las cuatro salidas

Uno: usar solo fuentes del sistema. Si el SVG declara font-family: system-ui, sans-serif o una pila de fuentes que existen en el sistema operativo, el resultado es razonable en todas partes. Se pierde la identidad tipográfica y se gana peso cero y cero complicaciones.

Es la opción correcta para gráficos generados, diagramas técnicos e ilustraciones donde el texto es informativo y no de marca. Y es la que casi nadie considera porque parece un renuncia; en un gráfico de datos, no lo es.

Dos: incrustar la fuente como data URI. Un @font-face dentro del SVG con la fuente codificada en base64:

HTML
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 100">  <defs>    <style>      @font-face {        font-family: 'MiFuente';        src: url('data:font/woff2;base64,d09GMgABAAAAA...') format('woff2');      }      text { font-family: 'MiFuente', sans-serif; font-size: 24px; }    </style>  </defs>  <text x="20" y="50" fill="#cdd6f4">Texto con fuente incrustada</text></svg>

Funciona: la data URI no es un recurso externo, así que el modo seguro la permite. El coste es el tamaño. Una fuente WOFF2 completa ronda los 20 a 40 kB, y la codificación en base64 añade un 33 por ciento. Un SVG de 3 kB pasa a pesar 45.

La mitigación obligatoria es subconjuntar: generar una versión de la fuente con solo los glifos que el SVG usa. Con las herramientas de subconjuntado habituales, un texto de veinte caracteres distintos produce una fuente de 2 a 4 kB. Ahí sí compensa.

Tres: convertir el texto a contornos. La herramienta de diseño convierte cada glifo en un path. El resultado se ve idéntico en todas partes, sin fuentes ni dependencias.

Lo que se pierde es considerable y hay que decirlo entero: el texto deja de ser texto. No se puede seleccionar, no se puede buscar, no lo lee un lector de pantalla, no se puede traducir, no se puede cambiar sin volver a la herramienta, y el peso sube porque un glifo como contorno son cientos de bytes frente a un carácter.

Cuatro: no meter el texto en el SVG. Si el SVG se usa en línea en una página, el texto puede ir en HTML superpuesto y todo el problema desaparece. Si el SVG tiene que ser autónomo, esta opción no existe.

La tabla de decisión

Destino del SVG Estrategia
En línea en tu propia página Cualquiera; la fuente de la página funciona
img en tu propia página Fuente del sistema, o incrustada y subconjuntada
Descarga que el usuario abrirá en otro programa Contornos, o incrustada
Correo electrónico Contornos, y aun así comprueba el cliente
Favicon o icono de aplicación Contornos, sin duda
Gráfico generado en servidor para un informe Fuente del sistema o incrustada subconjuntada
Logotipo de marca en una biblioteca de componentes Contornos

El logotipo merece un comentario: es el caso donde los contornos son claramente correctos. Un logotipo es una forma, no un texto. Que no sea seleccionable es lo apropiado, y el nombre accesible se pone con title y aria-labelledby, que es de lo que trata el nivel siguiente.

Subconjuntar la fuente

Cuando eliges incrustar, el subconjuntado no es opcional. El procedimiento:

  1. Recoge los caracteres únicos que aparecen en el SVG.
  2. Genera la fuente con solo esos glifos.
  3. Codifica en base64 y sustituye el src.
JS
// Los caracteres unicos que hay que conservarfunction caracteresDe(svgTexto) {  const textos = svgTexto.match(/<text[\s\S]*?<\/text>/g) ?? [];  const soloTexto = textos.join('').replace(/<[^>]+>/g, '');  return [...new Set(soloTexto)].sort().join('');}

Con esa cadena se alimenta la herramienta de subconjuntado que uses. Conviene añadir siempre los dígitos completos y los signos habituales aunque no aparezcan, porque un gráfico con datos dinámicos generará caracteres que no estaban en el momento de compilar.

Y una advertencia de licencia: incrustar una fuente en un SVG es redistribuirla. Muchas licencias de fuentes comerciales permiten el uso web con @font-face desde tu dominio y no permiten la incrustación en un fichero que se descarga. Antes de meter una fuente en base64 en un SVG que los usuarios van a descargar, lee la licencia. Las fuentes con licencia libre no tienen este problema.