La sustitución contextual OpenType en LuaTeX
(y un ejemplo con la escritura griega)
Una de las características más atractivas que ofrece la tecnología OpenType es aquella que consiste en sustituir unas letras por otras dependiendo de un determinado contexto. Puede usarse para resolver ciertas necesidades de tipografía pragmática, pero también habrá escenarios para satisfacer otras de índole más bien estilística o histórica, casi siempre sepultadas en el olvido tras la llegada de los ordenadores al mundo de la producción editorial. A este segundo grupo pertenecería la sustitución en mitad de palabra de la letra griega beta minúscula β por su variante estilística curvada (o beta sin descendente) ϐ, procedimiento muy habitual en la tradición impresora francesa del pasado siglo y que, aplicado en los textos y en las condiciones adecuadas, puede crear un efecto muy grato al lector (o al menos al lector que soy yo).
Tenemos (si somos consecuentes con la tesis Unicode que distingue caracteres de glifos) un único carácter para la letra
beta, idea platónica que puede concretarse textualmente en dos posibles glifos: el de la beta «normal» con descendente y
el de la beta curvada, que el estándar Unicode sitúa bajo el código U+03D0
y que denomina «Greek beta symbol» (fig.
1)
Lo que viene a aportar OpenType es el añadir a la fuente tipográfica una propiedad —digamos— tridimensional, que
permite que el carácter opte por un glifo u otro según una serie de instrucciones que la fuente pueda incluir. Basta con
activar la etiqueta adecuada (que casi siempre suele ser calt
, de «contextual alternates»), por ejemplo en nuestro
LuaLATEX con el paquete Fontspec. A los efectos de transmisión textual —y esto es lo esencial, insistimos— ambos
glifos siguen siendo el mismo carácter beta, como podemos comprobar si copiamos cualquier pasaje de un PDF donde se haya
operado la sustitución contextual y lo pegamos en un editor de texto.
Figura 1: Descripción del carácter Unicode GREEK BETA SYMBOL
Ahora bien, OpenType se limita a ofrecer los protocolos necesarios. Otro cantar es hasta qué punto los implementan los
diseñadores de fuentes. Por supuesto, si nuestra fuente no dispone de esa característica, como sucede en la gran mayoría
de fuentes que incluyen soporte para la escritura griega, siempre podremos añadirla nosotros mismos mediante el editor
de fuentes Fontforge. Pero hoy toca hablar aquí de otra manera mucho más simple, si cabe, de hacerlo, que es dentro de
LuaTEX y echando mano de la utilísima función Lua fonts.handlers.otf.addfeature
, que nos permite definir en el
pre-procesado del material textual ciertas características OpenType «al vuelo», gracias a la habilidad que LuaTEX
tiene de «injertar», como su propio nombre anuncia, scripts y código Lua en el proceso de compilación. LuaTEX
incluye en sus engranajes un intérprete de Lua, lo que supone que podemos puentear las primitivas de TEX mediante
este versátil y ligero lenguaje. En lo que atañe a nuestra deseada sustitución, la ventaja de hacerlo así es evidente,
pues añadiremos la propiedad OpenType a cualquier fuente que se nos antoje, sin necesidad de editarla y de crear
versiones modificadas de ésta. Siempre y cuándo, claro, la fuente disponga de los glifos necesarios con que jugar, pues
fonts.handlers.otf.addfeature
no crea letras de la nada, sino que opera sobre la posición y el contexto de los glifos
de que ya dispone la fuente.
Veamos una prueba, por ejemplo, con la fuente Linux Libertine, que sí dispone de esos glifos. Añadimos lo siguiente a
nuestro archivo fuente de LATEX, convenientemente encerrado en la primitiva \directlua
, necesaria para poder
insertar código Lua.
Empezamos por definir una variable y asignarle una tabla de todos los caracteres griegos que deben ir antes del glifo a sustituir:
letras_griegas = { "α", "ἀ", "ἁ", "ἂ", "ἃ", "ἄ", "ἅ", "ἆ", "ἇ", "Ἀ", "Ἁ", "Ἂ", "Ἃ", "Ἄ", "Ἅ", "Ἆ", "Ἇ", "ε", "ἐ", "ἑ", "ἒ", "ἓ", "ἔ", "ἕ", "Ἐ", "Ἑ", "Ἒ", "Ἓ", "Ἔ", "Ἕ", "η", "ἠ", "ἡ", "ἢ", "ἣ", "ἤ", "ἥ", "ἦ", "ἧ", "Ἠ", "Ἡ", "Ἢ", "Ἣ", "Ἤ", "Ἥ", "Ἦ", "Ἧ", "ι", "ἰ", "ἱ", "ἲ", "ἳ", "ἴ", "ἵ", "ἶ", "ἷ", "Ἰ", "Ἱ", "Ἲ", "Ἳ", "Ἴ", "Ἵ", "Ἶ", "Ἷ", "ο", "ὀ", "ὁ", "ὂ", "ὃ", "ὄ", "ὅ", "Ὀ", "Ὁ", "Ὂ", "Ὃ", "Ὄ", "Ὅ", "υ", "ὐ", "ὑ", "ὒ", "ὓ", "ὔ", "ὕ", "ὖ", "ὗ", "Ὑ", "Ὓ", "Ὕ", "Ὗ", "ω", "ὠ", "ὡ", "ὢ", "ὣ", "ὤ", "ὥ", "ὦ", "ὧ", "Ὠ", "Ὡ", "Ὢ", "Ὣ", "Ὤ", "Ὥ", "Ὦ", "Ὧ", "ὰ","ά","ὲ","έ","ὴ","ή","ὶ","ί","ὸ","ό"," ὺ","ύ","ὼ","ώ","ᾀ","ᾁ","ᾂ","ᾃ","ᾄ","ᾅ","ᾆ","ᾇ", "ἈΙ","ᾉ", "ἊΙ","ᾋ", "ἌΙ","ᾍ", "ἎΙ","ᾏ", "ᾐ","ᾑ","ᾒ","ᾓ", "ᾔ","ᾕ","ᾖ","ᾗ","ἨΙ","ᾙ", "ἪΙ","ᾛ", "ἬΙ","ᾝ", "ἮΙ","ᾟ", "ᾠ","ᾡ","ᾢ","ᾣ","ᾤ","ᾥ","ᾦ","ᾧ", "ὨΙ","ᾩ", "ὪΙ", "ᾫ", "ὬΙ","ᾭ", "ὮΙ","ᾯ", "ᾰ","ᾱ","ᾲ","ᾳ","ᾴ","ᾶ","ᾷ","Ᾰ","Ᾱ","ῆ","ῇ", "ῖ","ῗ","Ῐ","Ῑ","ῠ","ῡ","ῢ","ΰ","ῤ","ῥ","ῦ","ῧ","Ῠ","Ῡ","Ῥ", "ῲ","ῳ","ῴ","ῶ","ῷ", "Ά","Έ","Ή","Ί","Ό","Ύ","Ώ","ΐ", "Α","Β","Γ","Δ","Ε","Ζ","Η","Θ","Ι","Κ", "Λ","Μ","Ν","Ξ","Ο","Π","Ρ","Σ","Τ","Υ","Φ","Χ","Ψ","Ω","Ϊ","Ϋ", "ά", "έ","ή","ί","ΰ","α","β","γ","δ","ε","ζ","η","θ", "ι","κ","λ","μ","ν","ξ","ο","π","ρ","ς","σ","τ","υ", "φ","χ","ψ","ω","ϓ","ϔ","ϕ","ϖ","ϗ", "Ϙ", "ϙ","Ϛ","ϛ","Ϝ","ϝ", "ϐ" }
Y a continuación, la nueva propiedad OpenType:
fonts.handlers.otf.addfeature{ name = "contextualtest", type = "chainsustitution", lookups = { { type = "sustitution", data = { ["β"] = "ϐ", }, }, }, data = { rules = { { before = { letras_griegas }, current = { { "β" } }, lookups = { 1 }, }, }, }, }
Explicándolo un poco a trazo grueso. Hemos creado una propiedad OpenType que llamamos contextualtest
, y declaramos que
es de tipo chainsustitution
, es decir, queremos definir una cadena de sustitución contextual. Le incluimos un
lookup que indica en qué consiste la sustitución («β» a «ϐ»), y más abajo añadimos las reglas. Las cuales consisten en
que todos los caracteres encerrados bajo la variable before
propiciarán la sustitución de beta simple a curvada
sólo si ellos se sitúan inmediatamente antes de la letra.
Bien, ya casi lo tenemos. Esto nos soluciona el 99 % de los contextos para sustituir la beta en mitad de palabra. Pero la queremos, precisamente, así, en mitad de palabra y no al final, lo que no evitaría el código anterior. Naturalmente, son muy raros los casos en que aparezca una beta en esa posición, si pensamos en la escritura griega real. Pero algún que otro contexto puede surgir, así que sumamos al anterior este otro código, que evitará que nuestra beta se sustituya por la variante curvada al final de una palabra:
fonts.handlers.otf.addfeature{ name = "contextualtest2", type = "chainsustitution", lookups = { { type = "sustitution", data = { ["ϐ"] = "β", }, }, }, data = { rules = { { after = { { " ", 0xFFFC, ",", ".", "´", "·"} }, current = { { "ϐ" } }, lookups = { 1 }, }, }, }, }
Y pasamos a probar nuestro código, cargando las dos nuevas propiedades OpenType con fontspec
. Definimos entonces, para
la Linux Libertine, dos familias, con y sin sustitución (resultado de la compilación en la fig. 2):
\documentclass{article} \usepackage{fontspec} % Aquí iría todo el código Lua... \setmainfont{Linux Libertine} \usepackage[greek.ancient]{babel} \newfontfamily\nobetasub{Linux Libertine} \newfontfamily\sibetasub[RawFeature={+contextualtest,+contextualtest2}]{Linux Libertine} \begin{document} \nobetasub βαρβάρων - ἐβούλετο - ιβ´ \sibetasub βαρβάρων - ἐβούλετο - ιβ´ \end{document}
Figura 2: Resultado de la compilación
∞
Publicado: 31/08/19
Última actualización: 21/01/22
Esta obra está bajo una licencia de Creative Commons Reconocimiento-NoComercial 4.0 Internacional.