Saltar al contenido

Los rangos Unicode mienten: pregúntale a la tipografía, no al rango

Publicado el 30 de septiembre de 2026

El generador que compone la tarjeta que se publica en redes cuando sale una entrada del blog pintaba cuadraditos. No siempre, pero lo suficiente como para tener que mirar cada imagen antes de aprobarla. El commit 6995d6c se llama "Preguntar a la tipografía, no a un rango, si sabe dibujar un carácter" y resume bastante bien el arreglo. Te cuento el porqué, que es lo que sirve.

El problema: el cuadradito

Cuando una tipografía no tiene glifo para un carácter, el motor de render no falla: dibuja .notdef, que en la mayoría de fuentes es un rectángulo vacío. En inglés lo llaman tofu. En una tarjeta de 1080 píxeles que se va a Facebook e Instagram con el título de la entrada encima, un tofu en mitad de una palabra canta muchísimo.

El título lo escribe un modelo y lo reviso yo, así que entra de todo: comillas tipográficas, guiones largos, puntos suspensivos de un solo carácter, algún símbolo de moneda, una flecha suelta. Cosas legítimas en un texto en español, pero que no todas las tipografías cubren.

La primera solución, que era la mala

Lo primero que hice fue una lista blanca por rangos Unicode. ASCII imprimible, el suplemento Latin-1 para las vocales acentuadas y la eñe, un puñado de signos de puntuación general. Todo lo que cayera fuera, se limpiaba antes de pintar.

Funciona el 90 % de las veces y falla en las dos direcciones a la vez:

  • Falsos positivos. Que un carácter esté en un rango "permitido" no significa que esa tipografía lo tenga. Latin-1 incluye la Þ islandesa y el símbolo de la libra; muchas fuentes recortadas para web no traen todo el bloque. El rango decía que sí, la fuente decía tofu.
  • Falsos negativos. Al revés: caracteres perfectamente dibujables se caían de la tarjeta porque yo no había metido su rango en la lista. El resultado es peor que el cuadradito, porque es silencioso: la palabra sale mal escrita y no te enteras.

Y además la lista era código que había que mantener. Cada vez que aparecía un carácter nuevo tocaba abrir la tabla de Unicode y añadir un rango a mano. Eso ya es señal de que el enfoque está mal.

La solución: preguntar a la fuente

Un fichero de tipografía lleva dentro una tabla, la cmap, que es exactamente el mapa de puntos de código a glifos. Está ahí para esto. La pregunta "¿sabes dibujar este carácter?" tiene respuesta directa: buscas el punto de código en la cmap y, si el identificador de glifo que te devuelve es 0 —el .notdef—, no lo sabe dibujar.

Así que la lista blanca desapareció y en su lugar quedó una función que recorre el texto y pregunta carácter a carácter a la fuente que se va a usar de verdad para pintar. Ni rangos ni suposiciones. La fuente es la única que tiene la información correcta, porque es la que va a hacer el trabajo.

Dos detalles que costaron un rato:

  1. Hay que recorrer puntos de código, no unidades de UTF-16. En JavaScript, iterar una cadena con un bucle clásico sobre índices te parte por la mitad cualquier carácter fuera del plano básico —emojis, sobre todo— y acabas preguntando por dos mitades que no existen. Con for...of o Array.from la cadena se recorre por puntos de código y el problema no aparece.
  2. La cmap responde por carácter, no por secuencia. Un emoji con selector de variación, o una familia unida con zero-width joiner, son varios puntos de código que sólo significan algo juntos. La fuente puede tener los trozos y no saber componerlos.

Qué hago cuando la respuesta es que no

Quitar el carácter a secas es tirar información. El orden que sigue el generador es:

  • Si hay equivalente razonable, se sustituye: comillas curvas por rectas, guion largo por guion, puntos suspensivos de un carácter por tres puntos.
  • Si no lo hay, se elimina — mejor un hueco que un rectángulo negro.
  • En los dos casos queda anotado, y esa anotación llega al correo de aprobación que reviso antes de que la tarjeta salga a ningún sitio. Si un título pierde un símbolo importante, me entero antes de publicar, no después.

Lo que esto no arregla

Para que no te pille:

  • No pinta emojis. Saber que la fuente no tiene el glifo no te da el glifo. Para emojis en color hace falta otra fuente y un motor que soporte glifos en color; eso no está montado y ahora mismo no lo necesito.
  • No hace shaping. En árabe, devanagari o cualquier escritura con formas contextuales, tener todos los glifos no basta: hay que decidir qué forma toma cada letra según sus vecinas, y de eso se encarga una librería como HarfBuzz. Comprobar la cmap no sustituye eso ni de lejos.
  • No mide. Que un carácter se pueda dibujar no significa que el título quepa. El desbordamiento del texto del cartel fue otro arreglo distinto, el commit de al lado.
  • No juzga la calidad. Una fuente puede tener un glifo horrendo para un símbolo raro. La cmap te dice que existe, no que sea presentable.

La regla que me llevo

Cuando quieras saber si algo funciona, pregúntaselo a la cosa que lo va a hacer, no a una tabla que describe cómo debería ser el mundo. Es la misma idea que la detección de características en el navegador en vez de mirar el user agent, o que llamar a la API y ver qué contesta en vez de fiarte de la documentación. Las listas de rangos, los mapas de compatibilidad y las tablas de "esto lo soporta X" envejecen mal y mienten sin avisar. La fuente, la API o el navegador, no.

Cualquier duda me dices y lo vemos.

Un saludo, Vicente.

¿Te ha surgido una duda leyendo esto?

Pregúntanos. Respondemos aunque no acabes siendo cliente.