LaTeX3: Neue Mechanismen für die nächste Generation von Dokumenten

LaTeX3 bringt spannende Ansätze zur Programmierung für das codebasierte Textsatzsystem, die deutlich flexibler sind als die Erweiterung durch Skriptsprachen.

In Pocket speichern vorlesen Druckansicht 22 Kommentare lesen
LaTeX3: Neue Mechanismen für die nächste Generation von Dokumenten
Lesezeit: 17 Min.
Von
  • Marei Peischl
Inhaltsverzeichnis

Wer sich als LaTeX-Nutzer outet, hört oft Sätze wie "Das gibt es immer noch?". Die meisten haben es im Rahmen ihres Studiums irgendwann verwendet und häufig später verworfen. Die Gründe dafür sind vielfältig, aber es ist schade, dass viele dadurch nie gesehen haben, wie LaTeX als codebasiertes Textsatzsysteme die Effizienz und Qualität in der Dokumentenerstellung steigern kann. Ein weiterer Faktor ist, dass die LaTeX-Dokumente, wie sie gegenwärtig die meisten Hochschulen verwenden, immer noch so aussehen wie vor über 20 Jahren. LaTeX verfügt mittlerweile über flexiblere Darstellungsmöglichkeiten, die einfachere Mittel zur Grafikgenerierung (wie TikZ/pgf) und die Unterstützung für OTF-Fonts in XeLaTeX und LuaLateX.

Zusätzlich zu diesen Erweiterungen, die stetig mehr Verbreitung finden, erleichtern die neuen Entwicklungen auch die Nutzung von Programmiermechanismen in LaTeX. Neben der Einbettung der Skriptsprache Lua innerhalb von LuaTeX entwickelt das LaTeX3-Projekt-Team eine andere Möglichkeit. Viele der Anforderungen für die Entwicklungen an LaTeX3 sind durch die Ausbreitung digitaler Dokumente sowohl in SGML als auch PDF mit Hypertext-Elementen inspiriert. Dadurch lässt es sich auch nutzen, um andere Plain-Text-Formate als regulären TeX-Code in expl3 einzulesen und entsprechend zu parsen. Die genauen Hintergründe der Entwicklungen zu expl3 finden sich in ausführlicher Form auch in der Beschreibung und auf der Projektseite.

Übersicht der gängigen LaTeX-Engines (Abb. 1)

Dieser Artikel behandelt die bisher verwendbaren LaTeX3-Funktionen in L3in2e (LaTeX3 in LaTeX2e) und vergleicht die Vorgehensweise mit der Verwendung von Lua in LuaLaTeX.

LaTeX3 befindet sich nach wie vor in der Entwicklung, ist jedoch bereits lange als Paket zu LaTeX2e nutzbar. Der Unterschied zu LuaLaTeX ist hauptsächlich, dass LaTeX3 in verschiedenen Ebenen ansetzt, die an die Arbeitsweise von LaTeX angepasst sind. Sie beschäftigen sich mit der Programmierung, dem Textsatz, dem Design und der abschließenden Darstellung der Dokumente in der PDF-Datei. Die Ebene, die am stärksten von der LaTeX2e-Nutzung abweicht, ist die der Programmierung, die das LaTeX2e-Paket expl3 durch folgenden Befehl zur Verfügung stellt:

\usepackage{expl3}

Um die Programmierebene entkoppelt zu halten, nutzt expl3 eine eigene Syntax, die den Eingriff der Nutzer in die Expansion von Makros und deren Argumente bietet. Dass die Umsetzung der neuen Syntax nicht ganz einfach ist, fasste der LaTeX-Schöpfer Leslie Lamport bereits im Vorfeld äußerst treffend zusammen:

"I presume this is one of several dozen bugs that would arise over the years if anyone were foolish enough to try allowing '_' in command names."

Typische expl3-Befehle haben in der Tat eine gewöhnungsbedürftige Form:

  • \ExplSyntaxOn: Syntax einschalten,
  • \int_new:N \g_MyModule_item_int: Ganzzahl-Variable anlegen,
  • \int_gset:Nn \g_MyModule_item_int { 42 }: Variable einen Wert zuweisen,
  • \int_use:N \g_MyModule_item_int: Variable ausgeben und
  • \ExplSyntaxOff: Syntax wieder ausschalten.

Die Syntaxumschaltung ist aufgrund der Funktion der Unterstriche in Standard-LaTeX als Subscript- Zeichen notwendig. Außerdem bewirkt das Umschalten, dass das System Leerzeichen innerhalb dieser Syntax ignoriert. Dadurch ist es möglich, komplexe Codepassagen durch zusätzliche Umbrüche und Leerzeichen lesbarer zu gestalten. Nutzer bewirken die Syntax-Änderung durch die Änderung der Category Codes (CatCodes). Zeichentypen wie Buchstaben oder Kommentarzeichen haben eine Kategorie, die über eine Zahl kodiert ist. Je nach CatCode wird das Zeichen anders interpretiert. Setzen Entwickler den CatCode für das Dollarzeichen (üblicherweise CatCode3 als Mathe-Modus-Umschalter) auf 11 (Buchstabe), ändert es seine Funktion, sodass $ ohne Maskierung im Text nutzbar ist.

Außer der Ignoranz gegenüber Leerzeichen ändert \ExplSyntaxOn die Bedeutung des Unterstrichs in der Form, dass sie innerhalb von Makronamen vorkommen dürfen. Der Doppelpunkt erhält ebenfalls eine neue Rolle und dient dazu, Argumentspezifikationen vom Namen zu trennen. LaTeX3 ermöglicht über diese Syntax, ein Makro mit unterschiedlichen Expansionsstadien für dessen Argumente zu deklarieren. Dies ermöglicht universellere Handhabung der Funktionen.

exp13 vereinfacht das Manipulieren der Expansion. Bei "plain" TeX ist die Expansionssteuerung gewöhnungsbedürftig und schwer durchschaubar. LaTeX3 ermöglicht es nicht nur, gezielt zu expandieren, sondern auch ein besseres Verständnis für den dahinter steckenden Mechanismus von TeX zu erhalten. Hierfür existieren die Parameter N/n (No manipulation), c (csname), V/v (value), o (once), x (exhaustive) und f (full). Die Funktionsweise lässt sich anhand eines Beispiels erklären. Zunächst erfolgt die Deklaration einiger Makros:

\newcommand\eins{eins}
\newcommand\zwei{\eins, zwei}
\newcommand\drei{\eins,\zwei,drei}

Jedes Makro ist nun eine Art Kiste, deren Inhalt für LaTeX bis zur Expansion verborgen bleibt. (s. Abb. 1, links) Wenn \eins im Textmodus aufgerufen wird, wird die Kiste "ausgepackt" (s. Abb. 1, rechts). Da dieses Makro nur Text enthält, ist keine weitere Expansion möglich.

Die Bedeutung des Makros ist in einer Box verpackt (links). LaTeX erkennt die Bedeutung erst bei der Expansion (rechts) (Abb. 2).

Bei Kompilierungsprozessen ist die Ausgabe in diese Boxen nicht realisierbar. Hierfür eignet sich das expl3-Makro \tl_show:. \show existiert bereits in "plain" TeX, gibt die Bedeutung des nachfolgenden Tokens auf der Konsole aus und schreibt diese Informationen ins Log. Egal ob in TeX oder LaTeX3 sollte für die Nutzung der Debugging-Ausgaben über die Kommandozeile kompiliert werden: \tl_show:n stoppt den Compiler an der entsprechenden Stelle und zeigt den Wert seines Arguments an. Der Vorgang lässt sich anschließend mit Enter fortsetzen und mit X abbrechen.

\tl_show:n {\drei}

gibt durch den Parameter n den Inhalt der nachfolgenden Gruppe ohne Expansion aus.

Ausgabe von \tl_show:n {\drei}. Enter[ setzt den Kompiliervorgang fort (Abb. 3).

Da \tl_show:n nur als nicht expandierende Variante existiert, müssen zunächst Varianten für die Expansion mit den Parametern o, f und x erzeugt werden. Alle Varianten verhalten sich nun identisch bis auf die Handhabung des Arguments.

\ExplSyntaxOn
\cs_generate_variant:Nn \tl_show:n {o,f,x}

\tl_show:n {\drei}
\tl_show:o {\drei}
\tl_show:f {\drei}
\tl_show:x {\drei}
\ExplSyntaxOff

\cs_generate_variant erzeugt dabei zunächst Varianten des Makros \tl_show:n, sodass es auch die anderen gibt. Alle vier Varianten verhalten sich nun identisch bis auf die Handhabung des Arguments.

Die Wirkung lässt sich direkt über die Konsole beobachten: \tl_show:o erzeugt

\eins ,\zwei ,drei

Das ist bis auf das für bessere Lesbarkeit eingefügte Leerzeichen identisch zur Definition des Makros. Bei einfacher Expansion ersetzt der Compiler somit das Makro durch den Text der Deklaration. Das entspricht dem Öffnen der äußersten Box:

Das Makro \drei mit der zugehörigen ersten Expansion (Abb. 4)

\tl_show:f ergibt

eins,\zwei ,drei.

Das entspricht einer zweifachen Expansion. Allerdings ist der begrenzende Faktor dabei, dass das Makro \eins lediglich ein Mal expandierbar ist, es enthält keine weitere geschlossene Box, sondern ein freiliegendes Text-Element, das nicht weiter expandiert.

Darstellung der vollständigen Expansion (Abb. 5)

Eine vollständige Expansion wird somit beendet, wenn eines der Makros keine weitere Expansionsstufe mehr hat. In diesem Fall entspricht das Vorgehen einer zweifachen Expansion.

\tl_show:x führt hingegen eine gründliche Expansion durch. Es hat keine Begrenzung und expandiert die weiter expandierbaren Teile unabhängig vom Rest weiter. Somit ergibt es:

eins,eins,zwei,drei

Darstellung der exhaustive_exp3 (Abb. 6)

Da für die Ausgabe als letzter Schritt ohnehin eine gründliche Expansion notwendig ist, wirkt die Kontrolle darüber zunächst unnötig. Allerdings ist Expansionssteuerung für die automatisierte Verarbeitung von Daten unerlässlich: Betrachtet man die Anzahl der Kommata, fällt auf, dass \tl_show:n mit "\drei" kein einziges Komma enthält, wohingegen "eins,eins,zwei, drei" über drei verfügt. Würde man diese Token-Liste (tl) also als Komma-Liste (clist) speichern, hätte die erste Variante nur ein Element und die letzte vier.

Expansionssteuerung ist immer dann nötig, wenn der Inhalt eines Arguments möglichst keinen Einschränkungen unterliegen soll. In diesem Fall darf das einzelne Element Kommata enthalten, ohne dass es zerrissen wird.

Neben den Spezifikationsparametern für die Expansion von Makros existieren noch vier weitere Varianten, die spezielleren Mechanismen für die Arbeit mit Variablen dienen. Diese wurden mit Beispielen im zweiten Teil des LaTeX-Tutorials der iX erläutert.

Ein weiteres Feature in expl3 sind die längeren Makronamen. Das wirkt zunächst ineffizient, gestaltet den Code jedoch deutlich lesbarer und ermöglicht es, die Zuordnung von Makros durch eine feste Namensstruktur zu vereinfachen. Dadurch ist die neue Syntax nach erster Einarbeitung deutlich intuitiver und liefert zusätzlich ein einheitliches Interface für die bisherige Mischung aus TeX- und LaTeX-Befehlen.

Makro- und Variablennamen sollten immer über einen Namensteil verfügen, der dem Modul entspricht. Das Modul ist sozusagen die Ursprungsbezeichnung und erleichtert das Auffinden der Dokumentation des Makros. Das verhindert nicht nur Namenskonflikte, sondern vereinfacht zudem das Identifizieren von Datentypen bei Variablen. Das Benennen der Module erfolgt üblicherweise nach dem zugehörigen Paket oder Bundle. Bei den primitiven Makros ist es immer der Programmname. Alle durch LuaTeX eingeführten Makros beginnen beispielsweise mit \luatex_. Darüber hinaus gehören die einzelnen Datentypen zusätzlichen Modulen an. Somit setzt das Makro \int_set:Nn aus dem Integer-Modul eine Ganzzahl, wohingegen \str_set:Nn für Strings zum Einsatz kommt. Da der Variablennamen ebenfalls eine Typbezeichnung erhält, verringern sich die Fehler aufgrund von Verwechslung.

Allgemein sollten Entwickler die folgende Namensstruktur verwenden:

  • Funktionen: \<Modul>_<Funktionsname>:<Argumentenspezifikation>
  • Variablen: \<Gültigkeit>_<Modul>_<Variablenname>_<Typ>

Detaillierter sind die Namensstruktur und die Bedeutung der Argumentenspezifikation in Teil 2 des iX-Tutorials zu LaTeX geschildert.

Zur Datenspeicherung führt expl3 diverse Datentypen ein, die für Entwickler zunächst merkwürdig erscheinen mögen. Zwar existieren Datentypen, die sie aus anderen Programmiersprachen kennen: Boolean (bool), Ganzzahlen (int), Gleitkommazahlen (fp), Folgen/Stacks (seq), Strings (str), Dictionaries (prop für "property list"), aber sie unterscheiden sich aufgrund TeX-spezifischer Eigenheiten in der Handhabung. Beispielsweise existiert neben den Strings noch der Datentyp der Token-Liste (tl). Das System verarbeitet Strings auf die Weise, dass es sie direkt wieder ausgegeben kann, da die Sonderzeichen ihre Funktion verlieren. Ein Dollarzeichen innerhalb eines Strings leitet somit nicht den Mathe-Modus ein, sondern wird einfach ausgegeben. Innerhalb einer Token-Liste behält das Zeichen dagegen seine Bedeutung.

Ähnlich zu dieser Spezialisierung gibt es noch rein TeX-spezifische Typen wie Boxen oder Längen. Diese Konstrukte existieren bereits in reinem TeX, sind darin jedoch mit einer einheitlichen Syntax belegt. Beispielsweise haben in LaTeX2e Zähler immer nur einen Namen, Längen werden jedoch wie ein Makro erzeugt:

\newlength{\mylength}
\newcounter{mycounter}

In expl3 entsteht daraus Folgendes:

\dim_new:N \l_mylength_dim
\int_new:N \l_mycounter_int

Die Struktur ist insofern vereinheitlicht, dass die Modulbezeichnung den einzigen Unterschied darstellt. Es existieren jedoch zusätzlich auf den Datentyp abgestimmte Funktionen, sodass es beispielsweise kein Gegenstück zu \int_add:Nn im Boxen-Modul gibt.

Eine Übersicht der Datentypen und Funktionen ist online verfügbar. Die jeweils richtige Version für die installierte TeX-Live-Version dieser Datei lässt sich über das Programm texdoc abrufen. Es ist Bestandteil der Standard-TeX-Live-Installation, leider jedoch in vielen Linux-Paket-Systemen nicht enthalten. Entwickler sollten vor der Online-Suche nach der Anleitung prüfen, welche Version sie installiert haben.

expl3 liefert neben speichernden Datentypen den Datentyp Stream mit zwei Varianten: ior zum Einlesen von Dateien und iow zum Schreiben. Das ermöglicht es, zusätzlich zur Ausgabe in PDF andere Dateien zu schreiben wie XMP-Metadaten, die beispielsweise das Paket pdfx für das Erstellen von PDF/A-Dateien benötigt, oder für die Ausgabe im XML-Format. Das hat den Vorteil, dass sich damit weitere codebasierte Formate aus einer TeX-Datei generieren lassen. Allerdings ist diese Variante momentan noch relativ aufwendig, da Nutzer das Parsen für Ergänzungspakete anpassen müssen. Ein Beispiel stellt das Paket lwarp dar, das über diese Vorgehensweise eine HTML-Ausgabe für.tex-Dateien ermöglicht. Sie lässt sich zudem als Hilfsmittel zum Generieren von Epub-Dateien nutzen. Allerdings besteht dort noch Verbesserungsbedarf, um das Format umfassender zu unterstützen.

Neben der zusätzlichen Ausgabe von Dateien lassen sich die Input-Streams analog für das Einlesen externer Daten nutzen. expl3 bietet einfache Mechanismen, um Dokumente zeilenweise einzulesen und anschließend zu verarbeiten.

Ein Beispiel hierfür findet sich in den Codebeispielen zum dritten Teil des LaTeX-Tutorials der iX und auf der Website der Autorin.

Ein wesentlicher Nachteil von TeX sind die relativ ungenauen Methoden für Berechnungen. Da TeX nur auf die Ausgabe abzielt und die Ungenauigkeit mit 1 sp (Scaled Point, kleinste von TeX nutzbare Längeneinheit), was 1/65536 pt entspricht, weit unter dem sichtbaren Bereich liegt, war das früher kein ernstes Problem. Um flexiblere Dokumente automatisiert zu erstellen, sind jedoch Methoden zu Gleitkommazahlberechnungen nötig.

expl3 liefert im Modul l3fp die Möglichkeit, numerische Berechnungen bis zu doppelter Genauigkeit durchzuführen. Bisher sind arithmetische Operationen, Vergleiche, Boolsche Logik, Exponentialausdrücke sowie trigonometrische Funktionen und deren Umkehrfunktionen implementiert. Diese Mechanismen erlauben es beispielsweise, Längeneinheiten umzurechnen und die Werte von gespeicherten Variablen für Berechnungen zu nutzen.

Zwar lassen sich seit der Entwicklung von pgf und TikZ durch Till Tantau ebenso innerhalb dieser Umgebung Berechnungen durchführen, aber die Syntax, die expl4 zur Verfügung stellt, ist deutlich leichter nachvollziehbar und auch ohne pgf nutzbar. Zusätzlich lässt sich die Formatierung einfacher an LaTeX anpassen.

Die Mechanismen ermöglichen es, sämtliche Berechnungen zu vereinheitlichen und nicht immer auf den Vorgang des Parsens aus dem Paket angewiesen zu sein. Das vereinfacht beispielsweise das Generieren von Datendiagrammen oder die Extraktion von Daten aus Tabellen durch deren Umsetzung in das gewünschte Format vor der Ausgabe.

Bei der Arbeit mit expl3 stellt sich immer auch die Frage, wieso man nicht entweder eine Skriptsprache in TeX einbettet, wie es mit Lua für LuaTeX der Fall ist, oder mit einer beliebigen Sprache TeX-Code generiert. Gerade hinsichtlich der Verbreitung anderer Skriptsprachen und der dadurch wegfallenden Einarbeitung ist der Einwand durchaus berechtigt. Allerdings birgt die Vermischung verschiedener Sprachen Risiken aufgrund der abweichenden Syntax von TeX zu gewöhnlichen Programmiersprachen. Es erfordert häufig mehrfaches Escapen und das stetige Bewusstsein dafür, in welcher Sprache ein Codesegment verfasst ist.

Die Vorteile, die sich scheinbar durch die Vorkenntnisse ergeben, können aufgrund der deutlich abweichenden Syntax zum Nachteil werden. Unter anderem sind die unterschiedlichen Kommentarzeichen eine typische Fehlerquelle in LuaLaTeX. Beispielsweise würde

\directlua{
--test
tex.print("Hier steht Text")
}

keine Ausgabe erzeugen. Das liegt an der Reihenfolge beim Einlesen. Zunächst liest TeX und portiert die Zeilenumbrüche in Leerzeichen. Lua erhält somit die Zeile

--test tex.print("Hier steht Text")

und erkennt sie als reine Kommentarzeile. Das Problem lässt sich durch das Erzeugen von Skriptdateien und das Verkürzen der Lua-Aufrufe innerhalb von LuaTeX zwar größtenteils umgehen, bleibt aber ein Risikofaktor.

Darüber hinaus ist die Vorgehensweise von Lua grundverschieden gegenüber der LaTeX3-Programmierung. Beide Ansätze haben somit ihre Berechtigung und ermöglichen gar Kombinationen, die durchaus ein reizvolles Zusammenspiel ergeben. Die Verwendung von Lua unterliegt jedoch der Einschränkung durch den Compiler, und da TeX bereits Turing-vollständig ist, scheint es naheliegend, Kontrollstrukturen direkt in TeX zu nutzen. Letztlich hängt die richtige Wahl von der Problemstellung und den persönlichen Vorlieben ab.

LaTeX3 befindet sich erkennbar auf einem guten Weg. Die bisher nutzbaren Module bieten Funktionen, die dem Textsatzsystem das Image entstauben können. Das LaTeX3-Projektteam leistet eine hervorragende Arbeit, ein altes Programm weiter am Laufen zu halten, und es ist noch viel Entwicklung in Sicht.

Das Projekt verdient Aufmerksamkeit, denn LaTeX kann bedeutend mehr als hübsche mathematische Ausdrücke erzeugen. Gerade weil die Layoutmöglichkeiten stetig zunehmen, sollte nicht untergehen, dass auch der Inhalt sich entwickeln kann. Im Fall von expl3 kann man neben der ungeschlagenen Qualität von Knuths Satzalgorithmen die neuen Mechanismen nutzen, um sich die Arbeit zu erleichtern und somit die nächste Generation von Dokumenten zu erzeugen, die vielleicht nicht immer auf eine Print-Ausgabe abzielen müssen.

LaTeX3 wird durch andere Entwicklungen wie LuaLaTeX keinesfalls überflüssig, sondern kann sein Potenzial noch umfassender entfalten. Auch für diejenigen, die mit Skriptsprachen vertraut sind, lohnt sich die Auseinandersetzung mit der ungewöhnlichen Syntax, um schließlich Dokumente mit bisher ungekannter Flexibilität zu erzeugen.

Marei Peischl
arbeitet selbstständig als TeXnischer Consultant. Sie ist spezialisiert auf die Entwicklung flexibler Templates und hält individuelle Schulungen innerhalb und außerhalb des wissenschaftlichen Umfeldes.
(ane)