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.

01. Januar 2011 von Patrick Gundlach
Kategorien: LuaTeX | Schlagwörter: , , , | Schreibe einen Kommentar

Schreibe einen Kommentar