Fontloader + Ligaturen
Im letzten April habe ich einen Fontloader vorgestellt, der OpenType, TrueType und Type1 Fonts lädt und für TeX bereitstellt. Nun wurde ich gefragt, wie man den Fontloader um Ligaturen erweitern kann. Hier beschreibe ich die Antwort, den ursprünglichen Artikel erweitere ich entsprechend.
Um Ligaturen zu verwenden, musst du erst einmal wissen, für welche Sprache die Schriftart geladen werden soll. So gibt es im Niederländischen die ij-Ligatur und im Türkischen wird die fi-Ligatur vermutlich keine große Verbreitung haben, weil man dann nicht mehr richtig erkennt, ob das i ein i mit Punkt ist oder ohne: ı. Wenn das klar ist, können wir aus der gsub-Tabelle des Fonts alle notwendigen Feature-Namen auswählen. Ein Eintrag in der gsub-Tabelle sieht zum Beispiel so aus:
subtables = { { name = "ls_latn_l_11_s" } }, type = "gsub_ligature", flags = {}, name = "ls_latn_l_11", features = { { scripts = { { langs = { "NLD " }, script = "latn" } }, tag = "liga" } }
Das bedeutet: wenn das Schriftsystem (script) “latn
” und die Sprach “NLD
” gewünscht ist, dann müssen wir alle lookup-Tabellen mit dem Namen “ls_latn_l_11_s
” betrachten. Der Name steht in der Subtables-Tabelle. In der Fonttabelle gibt es bei den einzelnen Zeichen die lookups
-Tabelle. Ein Eintrag beim Zeichen »I_J« sieht zum Beispiel so aus:
ls_latn_l_11_s = { { type = "ligature", specification = { char = "I_J" components = "I J" } }
D.h. wenn wir Niederländisch ausgewählt haben, dann können wir diesen Eintrag in der lookups
-Tabelle beachten, da der Name mit dem aus der gsub
-Tabelle übereinstimmt. Mit dieser Information können wir dann die notwendige ligatures
-Tabelle für TeX erstellen:
ligatures = { [74] = { char = 306, type = 0 } }
Dieser Eintrag gehört dann zum Zeichen »I« (großes i). Damit wird TeX, wenn es auf die Folge IJ stößt, diese Zeichen durch die IJ-Ligatur ersetzen (IJ).
Im Code sieht das dann so aus:
-- Hier stehen die Schreibsysteme (scripts) und Sprachen, für die die Ligaturen gesucht werden sollen. local scripts_OK = { DFLT = true, latn = true } local langs_OK = { dflt = true } local lookups = {} if fontinfo.gsub then for i=1,#fontinfo.gsub do local gsub = fontinfo.gsub[i] for j=1,#gsub.features do local features = gsub.features[j] if features.tag =="liga" then for k=1,#features.scripts do local script_tbl = features.scripts[k] if scripts_OK[script_tbl.script] then -- die richtigen "scripts" haben wir gefunden, jetzt kommt es noch -- auf die richtige Sprache an. for l=1,#script_tbl.langs do local lang = script_tbl.langs[l] if langs_OK[lang] then lookups[gsub.subtables[1].name] = true end end end end end end end end
Die ersten beiden Tabellen beschreiben die gewünschte Sprache bzw. das Schreibsystem. Wichtig: die Bezeichner sind immer vier Zeichen lang, bei Niederländisch etwa wird mit Leerzeichen aufgefüllt: »NDL «. Dann müssen wir uns relativ weit nach unten »hangeln« um herauszufinden, ob es a) ein Ligatureintrag ist (feature="liga"
), ob das script übereinstimmt und ob die Sprache übereinstimmt. Erst dann können wir uns den Namen aus der subtable
merken. Nach Ende der Schleife kennen wir alle Namen für die lookup
-Tabelle.
Der zweite Teil besteht darin, für jedes Zeichen die lookup
-Tabelle durchzugehen, ob dort Einträge sind, die uns interessieren:
if glyph.lookups then for k,v in pairs(glyph.lookups) do if lookups[k] then for i,w in ipairs(v) do ligatures[#ligatures + 1] = w end end end end
Die Einträge können wir nicht direkt verarbeiten, weil die beteiligten Zeichen möglicherweise noch gar nicht verarbeitet wurden (= noch nicht im f.characters
-Array enthalten sind – siehe den vollständigen Code). Daher speichern wir die Ligatur-Einträge und wenn alle Zeichen verarbeitet wurden, werden die Ligaturen »angewendet«.
for _,v in ipairs(ligatures) do local spec = v.specification local result_cp = lookup_codepoint_by_name[spec.char] if result_cp > 0 then -- -1 == unencoded local components = string.explode(spec.components) -- z.B. "ff i" if #components == 2 then -- wir behandeln nur Ligaturen mit 2 Komponenten local char = f.characters[lookup_codepoint_by_name[components[1]]] char.ligatures = char.ligatures or {} char.ligatures[lookup_codepoint_by_name[components[2]]] = { char = result_cp } end end end
Dies erzeugt die ligatures
Einträge für die Zeichen, die als erstes in der Ligatur vorkommen. Bei der IJ-Ligatur also beim I. Den vollständigen Code findest du wie erwähnt im ursprünglichen Artikel.