Skip to content

← Zurück zu den Artikeln

Einen Pandoc-Lua-Filter schreiben: Überschriften aus Word eigene Markierungen hinzufügen

Wenn ein Word-Dokument zu Markdown wird, übertragen sich die Formatvorlagen als Struktur: Absätze mit der Formatvorlage „Überschrift 1“ werden zu Headern der obersten Ebene, „Überschrift 2“ zu Headern der zweiten Ebene und so weiter. Manchmal reicht das nicht. Vielleicht möchten Sie, dass jede Überschrift aus dem Word-Import eine Markierung trägt, die Ihr Website-Template oder Ihre Build-Pipeline erkennt – und Hunderte Überschriften von Hand zu bearbeiten ist kein Plan.

Pandoc hat eine eingebaute Antwort: Lua-Filter, kleine Skripte, die das Dokument während der Konvertierung verändern. Diese Anleitung erklärt, wie sie funktionieren, baut Schritt für Schritt einen Filter genau für diese Markierungsaufgabe und zeigt, wie Sie ihn ausführen und debuggen.

Was ein Lua-Filter ist und warum sich Pandoc für Lua entschieden hat

Pandoc parst jede Eingabe in einen gemeinsamen abstrakten Syntaxbaum, eine interne Darstellung von Elementen wie Headern, Absätzen und Tabellen, bevor es irgendeine Ausgabe schreibt. Ein Filter ist Code, der diesen Baum durchläuft und umschreibt. Da der Filter auf dem normalisierten Baum arbeitet, verhält er sich gleich, egal ob die Eingabe aus docx, HTML oder Markdown stammt.

Pandoc bindet einen Lua-Interpreter ein, aktuell Lua 5.4, sodass Filter direkt im Pandoc-Prozess laufen – ohne separate Laufzeitumgebung und ohne zu kompilierende Erweiterung. Sie schreiben eine Textdatei, übergeben sie Pandoc per Kommandozeilenflag, und die Umwandlung geschieht mitten in der Konvertierung. Filter dieser Art vermeiden außerdem den JSON-Umweg, den externe Filter in Kauf nehmen müssen, was den Overhead klein hält.

Der Aufbau eines Filters

Eine Filterdatei definiert einfache Funktionen, die nach AST-Elementtypen benannt sind. Eine Funktion namens Header läuft einmal pro Überschrift im Dokument, eine namens Para einmal pro Absatz. Gibt die Datei keine explizite Filtertabelle zurück, sammelt Pandoc diese Funktionen auf oberster Ebene automatisch nach Namen ein.

Der Rückgabewert entscheidet, was das Element im Baum ersetzt. nil zurückzugeben – dazu gehört auch, schlicht nichts zurückzugeben – lässt das Element unverändert, eine vergessliche Funktion ist also harmlos. Das Element selbst zurückzugeben, ob verändert oder nicht, ersetzt es durch die neue Version. Eine Liste zurückzugeben fügt deren Elemente an Stelle des Originals ein, und eine leere Liste ist die Art, wie Sie ein Element aus dem Dokument löschen.

Das Header-Element und seine Attribute

Jeder Header im AST von Pandoc trägt drei Dinge: eine Ebene als Zahl, eine Liste von Inline-Inhalten und einen Attributsatz. Der Attributsatz besteht aus drei Teilen: einem Bezeichner für Anker, einer Liste von Klassennamen und beliebigen Schlüssel-Wert-Attributen. Es ist dasselbe Trio, das in der Ausgabe als id und class auf einem HTML-Überschrift-Tag auftaucht.

Wo diese Attribute landen, hängt vom Ausgabeformat ab. Pandoc-Markdown kann sie als Annotationen in geschweiften Klammern hinter dem Überschriftentext darstellen, und die HTML-Ausgabe schreibt sie direkt auf das Tag. Ein reiner GFM-Export hat keine Syntax für Attribute, Klassen würden dort also verworfen – gut zu wissen, bevor Sie ein Zielformat wählen.

Die Aufgabe: Jede Word-Überschrift markieren

Das Ziel ist ein Filter, der jeden Header prüft und bei passenden Kriterien eine eigene Klasse anhängt – zum Beispiel bei jeder Überschrift oder nur bei solchen der zweiten Ebene. Die Logik liest das level-Feld, entscheidet und hängt einen String an die classes-Liste an, entweder mit der insert-Methode der Liste oder mit einem einfachen table.insert. Alles andere bleibt unverändert.

Bedingungen machen ihn praxistauglich. Sie können Überschriften der obersten Ebene eine Klasse geben und tieferen eine andere, das Markieren auf Überschriften beschränken, deren Text mit einem bestimmten Wort beginnt, oder Überschriften überspringen, die die Klasse bereits tragen – so bleibt der Filter idempotent und kann zweimal laufen, ohne Markierungen zu doppeln.

Die Codelogik Schritt für Schritt

Ausgeschrieben ist der Filter vier kurze Schritte: Definieren Sie eine Funktion namens Header, damit Pandoc jede Überschrift an sie weiterleitet. Prüfen Sie darin das level-Feld gegen den Schwellenwert, der Sie interessiert. Bei Übereinstimmung hängen Sie den Klassennamen an die classes-Liste an und setzen optional ein Schlüssel-Wert-Attribut. Geben Sie am Ende das Element zurück.

Dieses letzte return verdient Aufmerksamkeit: Das Element an Ort und Stelle zu verändern und zurückzugeben ist das dokumentierte Muster, aber ein fehlendes return bedeutet, dass Pandoc das unveränderte Original behält – die Änderung wird berechnet und dann still verworfen. Mit dem return kommen die Überschriften mit der neuen Klasse heraus, sichtbar auf HTML-Tags oder in den geschweiften Klammern von Pandoc-Markdown.

Den Filter ausführen

Auf der Kommandozeile akzeptiert Pandoc ein --lua-filter-Flag, das auf Ihre Datei zeigt; das Flag lässt sich wiederholen, um mehrere Filter der Reihe nach zu verketten. Filter laufen zwischen dem Lesen und dem Schreiben, die Umwandlung gilt also unabhängig vom gewählten Ausgabeformat.

Auf dieser Website bieten die Pro-Optionen einen Lua-Filter-Upload: Geben Sie dieselbe .lua-Datei zusammen mit Ihrem Word-Dokument ab, und der Browser-Worker reicht sie an dieselbe Pandoc-Engine weiter. Die Klassen landen genau so in der Ausgabe wie lokal – ohne dass irgendetwas an einen Server gesendet wird.

Debugging-Techniken

Das schnellste Werkzeug ist die print-Funktion: Ausgaben aus einem Filter gehen nach stderr, verunreinigen also nie das konvertierte Dokument, und Sie können Ebene, Klassen oder Text einer Überschrift ausgeben, um zu sehen, was der Filter tatsächlich erhalten hat. Pandoc bringt außerdem ein Logging-Modul mit, pandoc.log, dessen info- und warning-Funktionen Meldungen mit einem Schweregrad versehen.

Eine gute Gewohnheit ist es, an einer winzigen Datei zu entwickeln. Bauen Sie ein docx mit je einer Überschrift pro Ebene, lassen Sie den Filter darauf laufen und prüfen Sie die Ausgabe, bevor Sie ihn auf echte Archive loslassen. Die meisten Fehler sind Tippfehler in Feldnamen, und der Fehlerkanal deckt sie sofort auf.

Erweiterungen und Performance

Dasselbe Muster reicht weit über Überschriften hinaus: Absätze ohne Text entfernen, aus Überschriftentext Bezeichner für stabile Anker erzeugen oder Tabellen in ein Div hüllen, damit ein Website-Template sie als Gruppe stylen kann. All das ist jeweils eine Funktion pro Elementtyp in derselben Datei.

Performance ist selten ein Problem. Filter laufen pro Element innerhalb des Konvertierungsprozesses, und der Durchlauf ist selbst bei sehr großen Dokumenten schnell. Das Einzige, was Sie vermeiden sollten, ist schwere, pro Element wiederholte Arbeit – etwa das Lesen einer Datei innerhalb der Funktion; das gehört an den Anfang des Skripts, wo es nur einmal läuft.

Weiterlesen

Testen Sie es mit Ihrem docx