Wie definiere ich eigene Kommandos?
Bitte nicht vor der Länge des Beitrages erschrecken 😉
LaTeX stellt sehr viele Anweisungen zur Verfügung, die einem die Formatierung des Dokumentes erleichtern. Diese Vielfalt wird durch eine große Zahl von Paketen erweitert. Ob LaTeX oder Pakete, es ist nicht möglich alle Nutzerwünsche abzudecken. So kommt es sehr häufig vor, dass eigene Anweisungen definiert werden sollen.
In TeX selbst konnte das mittels [cce lang=”latex” inline=”true”]\def[/cce] realisiert werden. Diese Anweisung ist in LaTeX2e durch [cce lang=”latex” inline=”true”]\newcommand[/cce] bzw. [cce lang=”latex” inline=”true”]\renewcommand[/cce] erweitert bzw. ersetzt worden. Aber auch diese Anweisungen sind in ihrer Funktionalität sehr stark beschränkt. Das “neue” Paket [cce lang=”latex” inline=”true”]xparse[/cce] soll in diesem Post aufzeigen, wie einfach neue Definitionen mit verschiedensten Einstellungen möglich sind.
Bevor ich auf die neuen Möglichkeiten zu sprechen komme, möchte ich ein kurzes Resümee über die genannten Anweisungen geben. Allerdings sind zu Beginn einige Grundlagen zu legen.
Mathematische Grundoperationen
Leere Umgebungen ergeben noch keine Formel. Um die im vorangegangenen Teil erklärten Umgebungen zu füllen [1], zeigt dieses Tutorial den Umgang mit den einfachsten und essentiellsten Formelelementen. Anschließend sind wir in der Lage Summen, Wurzeln, Polynome, Integrale und Brüche darzustellen.
Über Fußnoten
Als ersten Beitrag möchte ich eine Übersetzung eines Posts bringen, den ich auf meinem Blog schon vor ein paar Tagen gebracht habe.
Ihr alle wisst sicher, wie einfach man in LaTeX Fußnoten bekommt. Die meiste Zeit ist es so einfach wie [cce lang=”latex” inline=”true”]\footnote{Das ist eine Fu\ss note}[/cce]. Das Ergebnis ist allerdings typographisch oft nicht sonderlich überzeugend. Lasst uns ein Beispiel ansehen:
LyX im Google Summer of Code 2013
Das LyX-Projekt nimmt am Google Summer of Code (GSoC) 2013 teil. Jeden Sommer seit 2005 vergibt Google Stipendien an Studenten, die erfolgreich ein freies Open-Source-Projekt während des Sommers entwickeln – daher der Name, vom 1967 Summer of Love herrührend.
Hallo TeXwelt!
Dieser Blog entsteht von TeX-Freunden für TeX-Freunde und Interessierte. Wir schreiben hier über Neuigkeiten aus der TeX- und LaTeX-Welt, erzählen unsere Ideen und geben Tipps. Es gibt keine festgelegten Themen, abgesehen von „TeX“. Jeder, der Lust hat, zu diesem Blog beizutragen, ist eingeladen mitzumachen! Wir freuen uns über jede und jeden!
Bald sieht man hier mehr.
Wir lesen uns!
Versal – SZ mit LuaTeX
Gerade habe ich eine Frage in meinem Mail-Postfach gefunden: Wie kann ich das Versal-ß unter LuaTeX benutzen? Die Antwort ist einfach: so wie es Unicode vorschlägt. Nur musst du eine Schriftart haben, wo dieses Ungetüm enthalten ist:
\documentclass{article}
\usepackage[german]{babel}
\usepackage{fontspec}
\setmainfont{Linux Libertine O}
\begin{document}
Das Mädchen ging über die Brücke nach draußen.
DAS MÄDCHEN GING ÜBER DIE BRÜCKE NACH DRAUẞEN.
\end{document}
Gibt bei mir (TeXlive 2010) ein ordentliches PDF, das das Versal-sz auch per Copy/Paste überträgt.
lua-visual-debug – Das Unsichtbare sichtbar machen
Das LuaTeX-Paket lua-visual-debug gibt dem Textautor Hilfen bei Positionierung von Elementen auf der Seite, in dem es die normalerweise unsichtbaren Elemente sichtbar macht. Der Artikel schlägt zwei Fliegen mit einer Klappe: Neben der Beschreibung des Pakets wird die Programmierung in Lua erklärt.
Benutzung und Erklärung
Wer kennt die Situation nicht: die minipage-Umgebung ragt über den rechten Rand hinaus, aber warum? Weshalb ist das Bild nicht genau unter der Tabelle? Wieso erzeugen alle Überschriften eine overfull box? Manchmal wäres es wünschenswert, genau zu sehen, wie LaTeX Elemente auf der Seite platziert und wo überall dehnbarer Zwischenraum eingefügt wird, wo Penalties gesetzt werden und ob ein einer Stelle eine Strut-Box erscheint.
Auf CTAN und in der TeXlive-Distribution ist seit Februar 2012 das Paket lua-visual-debug verfügbar, das (nur) mit LuaTeX funktioniert, aber sowohl von plain-TeX als auch mit LaTeX 2ε benutzbar ist. Die Anwendung ist einfach, ein
\usepackage{lua-visual-debug}
bzw. für plain TEX
\input lua-visual-debug.sty
reicht aus, um den Text zu „dekorieren“. Das könnte wie in dieser Abbildung aussehen:
Folgede Elemente werden von dem Paket dargestellt:
- Kästen (vbox,hbox)
- Struts (0pt breite Linien)
- Trennstellen
- Dehnbare Längen (glue)
- Unterschneidungen (kern) sowie
- Strafpunkte (penalty)
Diese Abbildung zeigt Beispiele für die verschiedenen Elemente:
- Vertikale Längen werden in schwarzen gestrichelten Linien dargestellt, deren Anfang und Ende mit einem Strich links markiert sind. Somit kann man aufeinanderfolgende Längen auseinanderhalten.
- Horizontale Längen werden in drei unterschiedlichen Farben dargestellt: Grau für Längen in ihrer natürlichen Breite, Magenta für gestauchte Längen und Blau für gedehnte Längen. So wird die am Absatzende übliche Länge
\parfillskipin Blau dargestellt. - Negative Kerns (Unterschneidungen) werden als rotes Kästchen angezeigt, positive als gelbes.
- Mögliche Trennstellen markiert das Paket mit einem kleinen blauen Strich unterhalb der Trennstelle
- Horizontale Kästen (hbox) werden mit einem mittlerem Grau umrahmt, vertikale Kästen mit einem dunklem Grau.
- Penalties >= 10 000 sind mit einem nicht gefülltem Quadrat gezeichnet, ansonsten werden die Quadrate gefüllt dargestellt.
Da das Paket noch relativ jung ist, könnte die Darstellung in Zukunft noch anders aussehen.
Die zweite Fliege – Implementierung
Das Paket ist gut für eine Einführung in die Programmierung von LuaTeX geeignet, da der grundlegende Aufbau einem einfachen Schema folgt. TeX hat zu dem Zeitpunkt, an dem eine Seite ausgegeben wird, alle benötigten Elemente in einer Box gespeichert. Mithilfe des Pakets atbegshi von Heiko Oberdiek kann einfach auf diese Box zugegriffen werden. Da die dargestellten Elemente auf der Seite nicht verschoben werden sollen, bietet es sich an, mit PDF-Anweisungen die Markierungen einzufügen. Diese PDF-Anweisungen haben selbst keine Breite und damit keinen Einfluss auf nachfolgende Elemente.
Bevor TeX die Seiteninhalte in PDF ausgibt, wird ein Zwischenformat erzeugt, bestehend aus sogenannten Nodes. Diese Nodes sind elementare Einheiten wie zum Beispiel ein Zeichen, ein Penalty oder eine horizontale Box. Die Idee ist nun, dieses Zwischenformat zu untersuchen, um die PDF Anweisungen für die Markierungen einzufügen.
Die Nodes sind in einer linearen Liste miteinander per „next“ und „prev“-Zeiger miteinander verbunden. Manche Nodes haben selbst wieder Inhalte. Das sind beispielsweise Boxen oder Ligaturvorschläge. Diese Inhalte sind dann über spezielle Felder erreichbar. Damit ist die grundlegende Schleife wie folgt:
aktueller_knoten = Zeiger auf ersten Knoten solage wie aktueller_knoten nicht "nil" ist untersuche Knoten und füge ggf. die PDF-Anweisung ein gehe zum nächsten Knoten: aktueller_knoten = aktueller_knoten.next ende
bzw. in Lua:
untersuche_box = function(box)
head = box.list
while head do
-- Untersuchen und PDF Anweisungen einfügen
head = head.next
end
end
Damit wird die Knotenliste (nodelist) Element für Element durchgegangen und untersucht. Der letzte Knoten hat einen next-Zeiger “nil” und damit ergibt die Bedingung in der while-Anweisung “falsch” und die Funktion untersuche_box() wird verlassen.
Wie können wir nun die einzelnen Nodes voneinander unterscheiden? Jeder Knoten hat eine “ID”-Nummer, die man im Referenzhandbuch nachschlagen kann. Beispielsweise haben Zeichen die ID-Nummer 37, horizontale Boxen die 0 und vertikale Boxen die 1. Nun wollen wir nicht nur die äußerste Box untersuchen, sonder alle innenliegenden. Die äußerste Box ist nämlich quasi nur die Umrandung der Seite. Daher muss das Programm, wenn es auf eine Box mit der ID-Nummer 0 oder 1 trifft, diese wieder darstellen. Das ist einfach, denn es kann einfach die Funktion untersuche_box() rekursiv aufrufen:
untersuche_box = function(box)
head = box.list
while head do
if head.id == 0 or head.id == 1 then
untersuche_box(head.list)
end
head = head.next
end
end
In head.list ist der Inhalt der aktuellen Box gespeichert. Damit wenden denselben Mechanismus auf den Inhalt an, der gerade angewendet wird (also den Inhalt der Box untersuchen, solange es noch Elemente in der Liste gibt).
Anschließend muss die Box noch umrahmt werden (das ist ja die Funktion des Pakets). Dazu wird ein Node vom Typ “PDF-Anweisung” erzeugt:
local rechteck = node.new("whatsit","pdf_literal")
Die PDF-Anweisung sieht vereinfacht so aus:
rechteck.data = " 0 0 <Breite> <Höhe> re s"
Das sind Befehle für das PDF Anzeigeprogramm um ein Rechteck zu zeichnen, das „links unten“ anfängt und die angegebene Höhe und Breite hat. In der Praxis müssen wir noch zwischen der horizontalen und der vertikalen Box unterscheiden, da in der hbox der erste Punkt links unten ist und in der vbox links oben. Außerdem verändern wir mit der Anweisung 0.5 G noch die Farbe der Linie und mit 0.1 w noch die Linienbreite. Das muss noch in q..Q geklammert werden, damit sich die Linienstärke und Farbe nicht auf die nächsten Zeichenoperationen auswirkt. Somit ist die PDF Anweisung in etwa:
rechteck.data = "q 0.5 G 0.1 w 0 0 <Breite> <Höhe> re s Q"
Der erste Teil der Schleife ist schon funktionstüchtig:
untersuche_box = function(box)
head = box.list
while head do
if head.id == 0 or head.id == 1 then
untersuche_box(head.list)
local wd = head.width / 65782
local ht = (head.height + head.depth) / 65782
local dp = head.depth / 65782
local rechteck = node.new("whatsit","pdf_literal")
if head.id == 0 then -- hbox
rechteck.data = string.format("q 0.5 G 0.1 w 0 %g %g %g re s Q",-dp, wd, ht)
else
rechteck.data = string.format("q 0.1 G 0.1 w 0 0 %g %g re s Q", 0, wd, -ht)
end
head.list = node.insert_before(head.list,head.list,rechteck)
end
head = head.next
end
end
Mit node.insert_before() wird der neu erzeugte Knoten vor dem ersten Element aus der aktuellen Box eingefügt. Dadurch muss der Zeiger auf den Inhalt in der übergeordneten Liste (head.list) angepasst werden, da ja der neu erzeugte Knoten das erste Element ist. Ohne die Anpassung würde der Zeiger auf den Inhalt auf dem alten ersten Element stehen und das Rechteck würde nicht angezeigt. node.insert_before() setzt auch die notwendigen prev und next Zeiger richtig.
Die Zahl 65782 kommt von der Umwandlung von scaled Points, TeXs interner Längeneinheit und dem PDF-Punkt von 1/72 Zoll. D.h. in einem PDF Punkt (big point) sind 65782 scaled points.
Nach diesem Prinzip werden alle genannten Elemente behandelt. Die Details kannst du im Quellcode nachschauen.
LuaLaTeX: Zeige die Trennstellen
Weil ich gerade selbst an Silbentrennung arbeite, gibt’s hier ein schickes LuaLaTeX-Beispiel. Es zeigt alle Trennstellen im Dokument an. Was passiert ist eigentlich recht einfach. Es wird nach dem Absatzumbruch die Nodeliste durchgegangen, und alle disc-Nodes werden um eine PDF-Anweisung ergänzt, die einen kleinen Strich zeichnet.
[cce lang=”latex”]\documentclass[12pt,a4paper]{scrartcl}
\usepackage[ngerman]{babel}
\usepackage{blindtext}
\usepackage{fontspec}
\directlua{
show_hyph = function(head)
while head do
if head.id == 0 or head.id == 1 then % hlist, vlist
show_hyph(head.list) % sollte head.head in LuaTeX > 0.65 sein
elseif head.id == 7 then % disc
local n = node.new(“whatsit”,”pdf_literal”)
n.mode = 0
n.data = “q 0.3 w 0 2 m 0 7 l S Q”
n.next = head.next
n.prev = head
head.next = n
head = n
end
head = head.next
end
return true
end
luatexbase.add_to_callback(“post_linebreak_filter”,show_hyph,”show_hyph”)
}
\begin{document}
\blindtext
\end{document}[/cce]
Und hier ein Beispiel, wie das aussehen könnte:
Inzwischen ist diese Funktionalität als eigenständiges Paket verfügbar. Siehe die Paketbeschreibung auf CTAN. Einfach benutzen mit \usepackage{showhyphens}.
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.
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.



