Cuaderno de GNUtas

Una función para comprobar si una fuente incuye determidado glifo

Ante de meternos en harina, refresquemos un poco la teoría. Es sabido que el estándar Unicode distingue entra caracteres y glifos. Los primeros vienen a ser como la «idea platónica de una letra»; los segundos, su representación tipográfica concreta. Lo que Unicode somete a su estandarización, reamente, son los caracteres, y a cada uno le asigna un código hexadecimal inamovible y lo incluye en un rango o familia de letras (latín básico, latín extendido, griego, cirílico, y un larguísimo etcétera). En lo que hace a los glifos que representan los caracteres, su inclusión en una fuente, su variedad y su prodigalidad quedan al albur de los diseñadores. Hay fuentes más completas, por ejemplo, en ciertos rangos, otras que sólo son aptas para representar caracteres latinos básicos y algunas que guardan en sus chibaletes digitales cientos y cientos de letras y signos de todo pelaje y condición, por supuesto bien ordenados por sus respectivos rangos.

Si usamos mucho una determinada fuente, nos compete el deber de conocerla razonablemente bien, y esto incluye el estar al tanto de qué caracteres es capaz o no de representar, y cómo o con qué arte. Pero puede suceder que, bien porque no estemos demasiado habituados a una fuente, o bien porque nos surge una duda repentina, deseemos saber si nuestra fuente incluye algún glifo en concreto, o grupo de glifos. Así nos ahorramos la sorpresa de encontrarnos con esos delatores cuadraditos o aspas, que suele ser el carácter de reemplazo con que la fuente rellena la ausencia de nuestra deseada letra.

Bien, hay varias maneras de comprobar si tal o cual glifo está presente en nuestra familia de letras. Lo más obvio y sencillo es usar cualquier editor de texto, activar esa fuente e incluir algún texto que contenga ese glifo, o insertarlo nosotros directamente. Otra forma, más sistemática, es abrir algún mapa de caracteres, o incluso algún editor de fuentes como Fontforge, y buscar por los casilleros si la fuente tiene a bien dibujar o no la letra que buscamos. Por último, podemos ejecutar en una terminal el siempre utilísimo programa otfinfo, con la opción -g (de glifo), que nos listará en tropel los códigos de todos los caracteres que dibuja la fuente, o con la opción -u (de unicode), que nos hará una lista de rangos. Si, por poner un ejemplo, lo que estamos buscando el la letra griega sampi mayúscula (Ϡ, U+03E0+), que pertenece al rango de griego básico, y nuestra fuente lleva ese rango, pues es probable que sea capaz de representar ese carácter. Pero, como no es normativo que los diseñadores de tipos tengan que incluir los rangos completos (si bien es la práctica deseable), puede que nos llevemos una sorpresa.

Espejito, espejito

Todos estos procedimientos, en fin, funcionan y nos sacan de la siempre corrosiva duda. El problema es que uno se vuelve perezoso, y tal vez prefiriese algo como «espejito, espejito, ¿tendrá la fuente xxxx el glifo de la (por seguir con el ejemplo) sampi mayúscula griega?». Y como uno también se ha acostumbrado a hacer casi todo en GNU Emacs y a no salir de esa zona de confort más de lo imprescindible, pues se me ocurrió ensayar una función que me hiciera el apaño. Una función bien sencilla, cuyo mérito realmente está en LuaLATEX, que corre en segundo plano (al igual que en esta otra función nuestra para extraer un espécimen rápido de una fuente). En este caso, la idea es utilizar la clase standalone y compilar directamente a un PNG. La función solicitará escribir en el minibúfer el nombre de la fuente (el mismo que usamos para llamar a una fuente con el paquete fontspec), como se muestra en la fig. 1 y a continuación poner el glifo o los glifos demandados, directamente (fig. 2), aunque se puede probar otra variante que pregunte por código o nombre de carácter. Y en un periquete (fig. 3) se nos abrirá una ventana temporal contigua con la imagen del glifo (si existe en la fuente) o el carácter de reemplazo, en caso de que la letra ni esté ni se la espere.

Nuestra función, en un primer borrador resultón (y, sí, abusa un pelín de la shell), quedaría tal que así:

(defun glifo-en-fuente ()
  "Muestra un glifo o glifos contenido(s) en una fuente, en caso
de que estén incluidos, compilando de fondo LuaLaTeX"
  (interactive)
  (let*
      ((fuente (read-from-minibuffer "Nombre fuente: "))
       (glifos (read-from-minibuffer "Glifo(s): "))
       (doc (concat "\\documentclass[convert={true},varwidth]{standalone}"
		    "\n\\pagestyle{empty}"
		    "\n\\usepackage{fontspec}"
		    "\n\\usepackage{xcolor}"
		    "\n\\setmainfont{"
		    fuente
		    "}"
		    "\n\\begin{document}"
		    "\n\\pagecolor[RGB]{255,255,254}\n"
		    "\\fontsize{120pt}{120pt}\\selectfont\n"
		    glifos
		    "\n\\end{document}")))
    (save-window-excursion
      (shell-command "touch glifo-temp.tex")
      (write-region doc nil "glifo-temp.tex")
      (shell-command "lualatex -shell-escape glifo-temp.tex"))
    (when (get-buffer "glifo")
      (kill-buffer "glifo"))
    (get-buffer-create "glifo")
    (set-buffer "glifo")
    (insert "#+ATTR_ORG: :width 400\n[[./glifo-temp.png]]")
    (org-mode)
    (org-toggle-inline-images)
    (temp-buffer-window-show "glifo")
    (run-with-timer 1 nil (lambda ()
			    (shell-command "rm glifo-temp*")))))

Como se ve, lo más cómodo me ha resultado representar la imagen del glifo en una ventana temporal con un archivo de Org Mode, teniendo activado org-toggle-inline-images. Por último, le concedo un segundo (run-with-timer) para que la imagen pueda aparecer, y a continuación llamo a un comando de shell que elimina de una tacada todos los archivos temporales generados (glifo-temp*). Poco elegante, no demasiado idiomático para Elisp y hasta traicionero y tramposillo pero, bueno, funciona y te saca del apuro.

fuenteglifo1.png

Figura 1: ¿…incluirá la fuente Old Standard…

fuenteglifo2.png

Figura 2: …este glifo?

fuenteglifo3.png

Figura 3: Pues parece que sí lo incluye

Y una variante para Dired

Esta otra variante de la función actúa sobre un archivo de fuente en Dired que tengamos marcado, o en el que esté posicionado el cursor, y es útil para los que tenemos enormes colecciones de fuentes almacenadas sin instalar en el sistema. Aprovecha la capacidad que tiene LuaTEX para usar fuentes no instaladas, simplemente con indicar la ruta en las especificaciones de fontspec.

Definimos antes estas dos funciones:

(defun nombre-fuente-sola ()
  (interactive)
  (let
      ((nombre (dired-get-file-for-visit)))
    (replace-regexp-in-string ".+/" "" nombre)))

(defun ruta-fuente ()
  (interactive)
  (let
      ((nombre (dired-get-file-for-visit)))
    (replace-regexp-in-string "\\(.+/\\).+" "\\1" nombre)))

Y nuestra otra versión de la función indagadora y espejil:

(defun glifo-en-dired-fuente ()
  "Muestra un glifo o glifos contenido(s) en una fuente marcada
en Dired, en caso de que estén incluidos, compilando de fondo
LuaLaTeX"
  (interactive)
  (let*
      ((nombre-fuente (nombre-fuente-sola))
       (ruta (ruta-fuente))
       (glifos (read-from-minibuffer "Glifo(s): "))
       (doc (concat "\\documentclass[convert={true},varwidth]{standalone}"
		    "\n\\pagestyle{empty}"
		    "\n\\usepackage{fontspec}"
		    "\n\\usepackage{xcolor}"
		    "\n\\setmainfont{"
		    nombre-fuente
		    "}[Path="
		    ruta
		    "]"
		    "\n\\begin{document}"
		    "\n\\pagecolor[RGB]{255,255,254}\n"
		    "\\fontsize{120pt}{120pt}\\selectfont\n"
		    glifos
		    "\n\\end{document}")))
    (save-window-excursion
      (shell-command "touch glifo-temp.tex")
      (write-region doc nil "glifo-temp.tex")
      (shell-command "lualatex -shell-escape glifo-temp.tex"))
    (when (get-buffer "glifo")
      (kill-buffer "glifo"))
    (get-buffer-create "glifo")
    (set-buffer "glifo")
    (insert "#+ATTR_ORG: :width 400\n[[./glifo-temp.png]]")
    (org-mode)
    (org-toggle-inline-images)
    (temp-buffer-window-show "glifo")
    (run-with-timer 1 nil (lambda ()
			    (shell-command "rm glifo-temp*")))))

Actualización de 28/06/20

Trasteando un poco más, podemos mejorar algo las funciones. Por ejemplo, en el caso de la primera función, glifo-en-fuente, no estaría mal modificar la manera como se aplica valor a las variables locales fuente y glifos. En el caso de la primera, tendría su gracia definir una lista Ido para navegar por los nombres de las fuentes del sistema por medio de la función font-family-list. Así pues, definimos antes:

(defun lista-fuentes-sistema (argumento)
  "Devuelve el nombre de la fuente escogida a partir de una lista
con las fuentes del sistema"
  (interactive  (list (ido-completing-read "Fuentes en el sistema: " (font-family-list) nil t)))
  (setq ido-fuente-escogida argumento))

Y una vez definido nuestro Ido, esta otra función que lo llamará interactivamente:

(defun fuente-escogida ()
  (interactive)
  (call-interactively 'lista-fuentes-sistema)
  (format "%s" ido-fuente-escogida))

Y aplicamos esta función a la variable local fuente. De esta forma nos ahorramos el tener que escribir el nombre de la fuente, y delegamos tal tarea en el siempre confortable Ido:

(let*
    ((fuente (fuente-escogida))
     ;; [...]
     ;; etc.
     ;; [...]
     ))

En cuanto a la variable glifos, aquí la idea de mejora consiste en poder escoger entre introducir el glifo directamente en el minibúfer o, en lugar de aquel, su código hexadecimal.

Primero, una variable con una lista de dos opciones, carácter y código. Ambas devuelven como resultado una cadena de texto, pero la segunda formateada con «^^^^» al inicio para que LuaTeX lo entienda como un código de carácter.

(setq  lista-glifo-en-fuente '((?1 "Carácter" (lambda ()
						  (interactive)
						  (format "%s" (read-from-minibuffer "Insertar carácter: "))))
				 (?2 "Código" (lambda ()
						(interactive)
						(format "^^^^%s" (read-from-minibuffer "Insertar código: U+ "))))))

La función siguiente ofrece la elección en el minibúfer.

(defun escoger-glifo-en-fuente ()
  (interactive)
  (let ((opcion (read-char-choice (concat "Buscar por carácter o código >>\n"
					  (mapconcat (lambda (item) (format "%c: %s" (car item) (nth 1 item))) lista-glifo-en-fuente " -- "))
				  (mapcar #'car lista-glifo-en-fuente))))
    (funcall (nth 2 (assoc opcion lista-glifo-en-fuente)))))

Y ya sólo nos queda añadir esta última función a nuestra variable local, tal que así:

(let*
    ((fuente (fuente-escogida))
     (glifos (escoger-glifo-en-fuente))
     ;; [...]
     ;; etc.
     ;; [...]
     ))

Publicado: 28/04/20

Última actualización: 28/06/20


Índice general

Acerca de...

Esta obra está bajo una licencia de Creative Commons Reconocimiento-NoComercial 4.0 Internacional.

© Juan Manuel Macías
Creado con esmero en
GNU Emacs