Eigenen Fontloader erstellen

Wieder etwas für die Hacker unter uns.

Hier ein etwas längerer Artikel (zumindest was den Quellcode betrifft). Anfangs hatte ich versprochen mal zu schreiben, wie man denn in LuaTeX selbst Schriftarten lädt. Wenn unser gutes, altes TeX eine Schriftart über das \font-Kommando lädt, dann wird intern eine Tabelle ausgefüllt mit den Eigenschaften der .tfm-Datei. Dazu gehört der Name der Schriftart, die Einstellungen für die Leerräume zwischen den Wörtern und natürlich Informationen über die Zeichen selbst (Breite, Tiefe und Höhe, Kerning, Ligaturen). Die Idee ist nun, selbst die Tabelle auszufüllen und dann mit font.define() und tex.definefont() TeX verfügbar zu machen. Doch wie komme ich an die Informationen aus den Schriftdateien, die auf der Festplatte liegen? Ganz einfach, dazu beinhaltet LuaTeX FontForge als Bibliothek, in der Form von der fontloader-Lua Bibliothek. Das Referenzhandbuch dazu schreibt:

function load_font (filename)
  local metrics = nil
  local font = fontloader.open(filename)
  if font then
    metrics = fontloader.to_table(font)
  end
  fontloader.close(font)
  return metrics
end

metrics dürfte die Sache nicht ganz richtig treffen, da noch andere Informationen in dieser Tabelle enthalten sind. Als erstes hier die TeX-Datei (mit plain LuaTeX aufrufen)

 

Die Fontdateien müssen entweder im TeXlive-Baum zu finden sein oder im aktuellen Verzeichnis liegen. Und hier folgt nun der eigentliche Kern der Fontdefinition:

 

fontinfo ist die Datenstruktur, die die fontloader-Bibliothek erzeugt, f ist die Tabelle, die später TeX übergeben wird (mit tex.definefont()). Die Idee ist, erst einmal herauszufinden, an welche Unicode-Positionen die einzelnen Zeichen gehören (das ist bei OpenType und bei Type1 Schriftarten unterschiedlich). Dann werden die einzelnen Zeichen in die f.characters-Tabelle eingefügt mit der Unicode-Position als Index. Die restlichen Informationen für die Schrifttabelle können direkt aus der fontinfo Tabelle übernommen werden.

Dieser Fontloader ist noch relativ einfach gestrickt, ich habe hier zum Beispiel auf OpenType Features verzichtet. Sobald man viele verschiedene OpenType-Features unterstützen möchte, wird es viel Programmierarbeit. Vom Prinzip her bleibt es aber immer so, wie hier beschrieben.

Ein kleiner Nachtrag: die Idee für den Fontloader stammt zum größten Teil aus dem LuaTeX-Wiki.

Und noch ein Nachtrag (9. Januar 2011): Den Fontloader habe ich heute erweitert, so dass er auch Ligaturen verarbeiten kann. Die Erklärung dazu findest du in einem eigenen Artikel.

03. April 2010 von Patrick Gundlach
Kategorien: LuaTeX | Schreibe einen Kommentar

selene unicode Bibliothek

Auf der LuaTeX Mailingliste wurde gefragt, welche Funktionalität die in LuaTeX eingebundene Bibliothek selunicode hat. Die Bibliothek bietet einige Ersatzfunktionen für die eingebaute string-Bibliothek. Speziell handelt es sich um:

  • byte(str, start [,end=-1])
  • char(i [,j...])
  • len(str)
  • lower(str)
  • reverse(str)
  • sub(str, start [,end=-1])
  • upper(str)

sowie veränderte Zeichenklassen für

  • find
  • match
  • gmatch
  • gsub

Ein kleines Beispiel soll zeigen, warum die spezielle Handhabung von UTF-8 Strings notwendig ist:

print(string.format("string-len=%d, unicode.utf8-len=%d",
                    string.len("Löss"), unicode.utf8.len("Löss")))

ergibt

string-len=5, unicode.utf8-len=4

Und das Ergebnis 4 hätte ich auch erwartet. Der Hintergrund ist der, dass das ö als zwei Zeichen in UTF-8 kodiert ist, und zwar als 195 und 182 (0xC3B6). Da die original Lua-String-Funktionen nicht mit UTF-8 umgehen können, sehen sie dieses zusammengesetzte Zeichen als einzelne Zeichen. Dadurch ergibt sich die höhere Ausgabe der string.len()-Funktion.

Die Zeichenklassen für find, match, gmatch und gsub sind folgende:

%a L* (Lu+Ll+Lt+Lm+Lo)
%c Cc
%d 0-9
%l Ll
%n N* (Nd+Nl+No, neu)
%p P* (Pc+Pd+Ps+Pe+Pi+Pf+Po)
%s Z* (Zs+Zl+Zp) und die Controllzeichen 9-13 (HT,LF,VT,FF,CR)
%u Lu (und Lt ?)
%w %a+%n+Pc (z.B. '_')
%x 0-9A-Za-z
%z das Null-byte (\0)

Die Erklärung der Symbole Nd, Nl, No, … findest du unter http://www.unicode.org/Public/4.0-Update1/UCD-4.0.1.html#General_Category_Values.

Hier noch ein Beispiel für die Verwendung der Zeichenklassen:

local str = "...Α-Ω..."

print(string.find(str,"%u"))          --  nil
print(unicode.utf8.find(str,"%u"))    --  4   5

print(string.match(str,"%u"))         --  nil
print(unicode.utf8.match(str,"%u"))   --  A

for w in string.gmatch(str, "%u") do  -- keine Treffer
  -- (wird nicht erreicht)
end

for w in unicode.utf8.gmatch(str, "%u") do
  print(w)                            -- Α Ω
end

Vielen Dank an den Fragesteller für die ergänzenden Hinweise zu den Zeichenklassen!

15. Februar 2010 von Patrick Gundlach
Kategorien: LuaTeX | Schlagwörter: , | Schreibe einen Kommentar

\directlua{…} und directtex(…)

Bei LuaTeX wird der  Befehl \directlua{...} sehr häufig benutzt. Er macht nichts anderes, als den TeX-Interpreter „anzuhalten“ und in den Lua-Modus zu springen. Jetzt kann man beliebige Lua-Befehle ausführen und wenn TeX wieder zurück springt (wenn keine Lua-Befehle mehr abgearbeitet werden sollen), dann ist TeX wieder im normalen Modus und verarbeitet wieder normale TeX-Befehle. Gibt es eigentlich einen äquivalenten Lua-Befehl, der in den TeX-Modus springt?

Man könnte sagen: ja, es gibt doch zum Beispiel tex.sprint(). Doch folgendes Beispiel zeigt ein kleines Problem damit:

dokument.tex:

\directlua{ dofile("luacode.lua")  }
\bye

luacode.lua:

tex.sprint("\\setbox1\\hbox{Hallo Welt}")
texio.write_nl("Die Breite der Box 1 ist ".. tostring(tex.wd[1]))

(Warum der Inhalt von luacode.lua nicht im \directlua{..}-Block steht dürfte dir klar werden, wenn du es ausprobierst.)

Was ist die Ausgabe von dem Programm? Ich hätte erwartet, dass die Breite der Box (in scaled points) ausgegeben wird. Doch gefehlt! Es wird eine 0 ausgegeben. Das liegt daran, dass TeX noch gar nicht dazu gekommen ist, die \hbox zu setzen und der Box 1 zuzuordnen. Die Inhalte von tex.sprint() werden gesammelt und erst dann ausgeführt, wenn der \directlua{...}-Block verlassen wird. Nun, wie kann ich dann TeX zwischenzeitlich dazu veranlassen, den Code auszuführen? Die erste Lösung liegt nahe:

dokument.tex:

\directlua{ dofile("luacode.lua") }
\directlua{
 a()
}
\directlua{
 b()
}
\bye

luacode.lua:

function a(  )
  tex.sprint("\\setbox1\\hbox{Hallo Welt}")
end

function b(  )
  texio.write_nl("Die Breite der Box 1 ist ".. tostring(tex.wd[1]))
end

Es funktioniert und die Ausgabe ist 3076557. Doch schön ist die Lösung nicht, da der Kontrollfluss immer noch auf der TeX-Seite ist. Du musst im TeX-Code immer die passenden Lua-Routinen aufrufen. Für manche Problemstellungen mag das machbar sein, doch sobald du dich hauptsächlich auf der Lua-Seite bewegen möchtest (es macht einfach Spaß in Lua zu programmieren), ist das eine Sackgasse. Eine Lösung für das Problem sind Coroutinen. Mithilfe von Coroutinen kannst du zwischen zwei Programmteilen hin- und herspringen, der Lua-Interpreter merkt sich die Stelle, an der du den einen Teil des Programms verlassen hast. An dieser Stelle gibt es keine Einführung in Coroutinen in Lua, das würde den Rahmen wirklich sprengen. Ich musste mich schon ein klein wenig damit beschäftigen, bis ich auf folgende Lösung für das Problem gekommen bin:

dokument.tex:

\directlua{ dofile("luacode.lua") }

\newif\ifcontinue
\continuetrue

\directlua { co = coroutine.create(main_loop) }
\loop \directlua{  ok,b=coroutine.resume(co)  tex.sprint(b) }\ifcontinue  \repeat

\bye

luacode.lua:

function directtex(str)
  coroutine.yield(str)
end

function main_loop()
  directtex("\\setbox1\\hbox{Hallo Welt}")
  texio.write_nl("Die Breite der Box 1 ist ".. tostring(tex.wd[1]))
  directtex("\\continuefalse")
end

Der Kern besteht aus der Schleife \loop ... \repeat, die mit directlua{...} und coroutine.resume() in den Lua-Modus springt, bis dort ein coroutine.yield() aufgerufen wird, das Argument von coroutine.yield() an TeX übergibt, in den TeX-Modus springt und das wieder von vorne, bis die Bedingung \ifcontinue false ergibt. Naheliegend, nicht wahr?  😉

Eine wirklich praktikable Lösung, wie ich finde, die auch nicht allzu komplex ist. Zumindest wenn man sich mal mit Coroutinen beschäftigt hat. Du wirst aber ein kleines Problem erkennen, wenn du dieses Schema benutzt: die Fehlerhandhabung ist ziemlich schlecht geworden, manchmal reagiert TeX nicht mehr, manchmal gibt es kryptische Fehlermeldungen – in den seltensten Fällen gibt es einen echten Hinweis auf die Fehlerursache. Da hilft die Lua-Funktion pcall(), die ruft die angegebene Funktion im geschützten Modus auf, dadurch hast du eine Chance noch echte Fehlermeldungen auszugeben, in etwa so:

function call(...)
  local ret = { pcall(...) }
  if ret[1]==false then
    texio.write_nl("Fehler:" .. tostring(ret[2]))--  .. debug.traceback())
    directtex("\\continuefalse")
  end
  return unpack(ret,2)
end

call(funktion,argument1,argument2)

So, viel Spaß beim Verstehen und Ausprobieren – ich freue mich über Kommentare!

29. Januar 2010 von Patrick Gundlach
Kategorien: LuaTeX | Schlagwörter: | Schreibe einen Kommentar

Neuere Artikel →