Version 5.9.3

# Lua-Callbacks

Einige Teile des Publishing-Laufs lassen sich mit Lua-Funktionen anpassen: die Dateisuche und die Konvertierung von Bildern mit externen Programmen. Dazu wird eine Lua-Datei über die Konfigurationseinstellung `luafile` (oder auf der Kommandozeile mit `--luafile`) angegeben, zum Beispiel:

    luafile = hooks.lua

Die Datei wird zu Beginn des Publishing-Laufs geladen, noch bevor Layout- und Datendatei geöffnet werden. Sie registriert Funktionen für feste Callback-Namen mit `register_callback()`:

    register_callback("lookup_file", function(name)
        -- ...
    end)

Die Einstellung wird mit Absicht nur aus der Konfigurationsdatei und von der Kommandozeile gelesen. Sie kann nicht im Layout gesetzt werden, damit ein Layout niemals Code in den Publishing-Prozess einschleusen kann (Stichwort Servermodus).

Verfügbar seit Version 5.9.2. Die Callbacks lösen auf lange Sicht die Konfigurationseinstellungen `pathrewrite`, `imagehandler` und `resizehandler` ab; diese Einstellungen funktionieren weiterhin wie bisher und werden immer dann verwendet, wenn kein Callback registriert ist oder der Callback `nil` zurückgibt.

## Die Umgebung der Callback-Datei

Die Callback-Datei sieht die Interna des Publishers nicht. Sie läuft in einer eingeschränkten Umgebung, die nur die offizielle Schnittstelle bereitstellt:

`register_callback(name, fn)`
  Registriert die Funktion `fn` für einen der unten beschriebenen Callback-Namen.
`log(level, meldung, schlüssel, wert, ...)`
  Schreibt eine Meldung in die Protokolldatei. Der Level ist `debug`, `info`, `warn` oder `error`.
`api.version`
  Die Version der Callback-Schnittstelle. Zurzeit `1`.
Standardbibliothek
  `string`, `table`, `math`, `tonumber`, `tostring`, `error`, `assert`, `pcall`, `pairs`, `ipairs`, `next`, `type`, `select`, `io.open`, `io.lines` und `os.getenv`.

Über die Grenze zwischen Publisher und Callbacks gehen nur einfache Werte (Zeichenketten, Zahlen und Tabellen daraus). Dadurch bleibt die Callback-Datei unabhängig von den Interna des Publishers.

## lookup_file

Der Callback bekommt den Namen jeder angeforderten Datei (Bilder, Schriften, Layout- und Daten-XML, URLs), bevor die eingebaute Dateisuche läuft. Er kann einen neuen Namen zurückgeben oder `nil`, um den Namen unverändert zu lassen. Die reguläre Suche läuft anschließend mit dem Ergebnis.

    register_callback("lookup_file", function(name)
        if string.match(name, "^D:\\MEDIA\\") then
            name = string.gsub(name, "^D:\\MEDIA\\", "bilder/")
            return string.gsub(name, "\\", "/")
        end
    end)

Das ist der mächtigere Ersatz für die Einstellung `pathrewrite`: der Callback kann mit Mustern arbeiten, Groß- und Kleinschreibung ignorieren oder eine Zuordnungstabelle aus einer externen Datei einlesen. Da der Callback bei jeder Dateisuche aufgerufen wird (auch für Schriften und interne Dateien), sollte er schnell zurückkehren. Das Ergebnis wird pro Namen für den Lauf zwischengespeichert, der Callback muss für denselben Namen also immer dasselbe Ergebnis liefern.

## image_handler

Der Callback entscheidet, wie eine Bilddatei in ein Format konvertiert wird, das der Publisher einbinden kann (PDF, PNG oder JPEG). Er bekommt eine Tabelle mit diesen Einträgen:

`input`
  Der vollständige Pfad der Bilddatei.
`extension`
  Die kleingeschriebene Dateiendung, zum Beispiel `tif`. Leer bei eingebetteten Bildinhalten.
`imagetype`
  Nur bei eingebetteten Bildinhalten (`<Image>` mit Kindelementen): der Wert des Attributs `imagetype`.
`outputbase`
  Ein Pfad im Bildcache (ohne Dateiendung), der für die Ausgabedatei dieser Konvertierung reserviert ist.

Der Callback gibt eine der folgenden Möglichkeiten zurück:

- `nil`: der Callback behandelt das Bild nicht. Die Einstellung `imagehandler` und die eingebauten Handler greifen wie bisher.
- Eine Tabelle mit `command` und `output`: der Publisher führt das Kommando aus (eine Liste mit dem Programm und einem Eintrag pro Argument) und bindet die Datei `output` ein.
- Eine Tabelle nur mit `output`: die Datei wird unverändert eingebunden, ohne ein Programm auszuführen.

    register_callback("image_handler", function(job)
        if job.extension == "tif" then
            local out = job.outputbase .. ".pdf"
            return {
                command = { "magick", job.input, out },
                output = out,
            }
        end
    end)

Da das Kommando eine Liste ist, sind keine Anführungszeichen oder Escaping nötig, auch nicht bei Dateinamen mit Leerzeichen. Die Ausgabedatei bleibt im Bildcache: existiert sie schon, wird das Kommando nicht erneut ausgeführt. Der Cachename in `outputbase` enthält einen Hash über den Eingabepfad und die Callback-Datei; eine geänderte Callback-Datei macht den Cache also automatisch ungültig. Schlägt das Kommando fehl, landet seine vollständige Ausgabe in der Protokolldatei.

## resize_handler

Pro-Funktion: ist das Attribut `dpi` bei `<PDFOptions>` gesetzt, werden Bilder mit höherer Auflösung verkleinert. Der Callback `resize_handler` kann diese Konvertierung übernehmen. Er bekommt dieselbe Tabelle wie `image_handler` mit diesen zusätzlichen Einträgen:

`width`, `height`
  Die gewünschte Größe des Ausgabebilds in Pixeln.
`imagetype`
  Der Typ des Bildes, `png` oder `jpg`.

Der Rückgabewert funktioniert genau wie bei `image_handler`:

    register_callback("resize_handler", function(job)
        local out = job.outputbase .. ".png"
        return {
            command = { "magick", job.input, "-resize", job.width .. "x" .. job.height, out },
            output = out,
        }
    end)

#### Inhalt