Lua-Filter / Datenvorverarbeitung
Manchmal möchte man vor der eigentlichen PDF-Erstellung die Daten in ein anderes Format überführen oder auf Korrektheit überprüfen. Dafür gibt es die Möglichkeit (seit Version 3.1.9), ein Lua-Skript vor dem eigentlichen Publishing-Lauf auszuführen. Lua ist eine einfache, aber mächtige Programmiersprache, die dafür vorgesehen ist, in andere Programme als Skriptsprache einzubauen.
Im Beispiele-Repository sind drei Anwendungsfälle zu finden.
Aufruf des Lua-Skripts
Gestartet wird der Filter entweder über die Kommandozeile
sp --filter myfile.lua
oder über die Konfigurationsdatei, die folgenden Eintrag enthalten muss:
filter=myfile.lua
Das angegebene Lua-Skript wird ausgeführt, bevor die Erzeugung der PDF-Datei beginnt. Das Skript muss im Publisher-Suchpfad liegen. Daher ist die Hauptanwendung dieses Pre-Processing die Transformation von Daten in ein Format, das für den speedata Publisher geeignet ist. So können CSV oder Excel-Dateien nach XML konvertiert und anschließend die PDF-Erzeugung gestartet werden. Daneben ist es auch möglich, Daten zu validieren.
Im Folgenden werden die Anwendungsmöglichkeiten beschrieben, ganz zum Schluss gibt es nochmals eine Übersicht über die eingebauten Funktionen und Methoden.
Validieren von Eingabedaten
Um RELAX NG-Dateien zu validieren gibt es mehrere Möglichkeiten. Neben einen Variante, direkt im XML Editor das Schema zu validieren (siehe den entsprechenden Abschnitt im Handbuch) gibt es die Möglichkeit, ein externes Programm dafür zu nutzen. Eines davon ist Jing. Es wird mit dem Publisher mitgeliefert.
In der Lua-Vorverarbeitung gibt es eine Funktion, die die Validierung übernimmt:
runtime = require("runtime")
runtime.validate_relaxng(‹xmlfile›,‹schemafile›)
Die Funktion liefert im Fehlerfall false
und die Fehlermeldung zurück. Beispiel:
-- adjust the paths, of course
runtime = require("runtime")
ok, msg = runtime.validate_relaxng("layout.xml","../schema/layoutschema-en.rng")
if not ok then
print(msg)
os.exit(-1)
end
Das speichert man in einer Datei, z.B. validate.lua
und ruft dann den Publisher mit
sp --filter validate.lua
auf. Vor jedem Lauf wird nun überprüft, ob die Layoutdatei dem Schema entspricht und nur dann wird mit der Verarbeitung fortgefahren.
Man kann nicht nur die Layoutdatei auf Korrektheit überprüfen, sondern auch alle anderen XML-Dateien. Hierzu muss man sich jedoch ein eigenes RELAX NG-Schema erstellen. Eine Anleitung dazu ist unter https://speedata.github.io/relaxngtutorial-de/ verfügbar. Je nach Datendatei ist das auch recht einfach. Insbesondere wenn man immer wieder Daten aus fremden Quellen geliefert bekommt, kann man sich so sicher sein, dass die gewünschte Struktur eingehalten wird. |
Ausführen einer Transformation
Eine XSLT-Transformation verarbeitet eine XML-Datei mit einem XSLT-Skript und erzeugt eine Ausgabedatei. XSLT ist eine Programmiersprache, die für die Verarbeitung von XML-Daten entworfen wurde. Häufig sind Daten aus PIM-Systemen oder Datenbanken nicht in der Form, die für den Publisher optimal sind. Mit XSLT kann man solche Ausgangsdaten verarbeiten und verändern.
Das Programm saxon
(mit dem speedata Publisher mitgeliefert) kann ein XSLT-Skript ausführen.
Der Aufruf ist folgender:
runtime = require("runtime")
runtime.run_saxon(‹XSL›,‹source›,‹outfile›,‹parameter›)
Es kann nur ein Parameter angegeben werden (key=value
).
Für mehrere Parameter muss die alternative Version über benannte Parameter benutzt werden.
runtime = require("runtime")
ok, msg = runtime.run_saxon("transformation.xsl","sourcefile.xml","data.xml")
-- quit the publishing process if the transformation fails
if not ok then
print(msg)
os.exit(-1)
end
Alternativ kann der Aufruf von Saxon auch wie folgt geschehen:
runtime = require("runtime")
ok, msg = runtime.run_saxon({ key = value, key = value, ... })
Erlaubte key/value-Paare sind:
Schlüssel | Wert |
---|---|
source |
Quelldatei (XML) |
stylesheet |
Stylesheetdatei (XSL) |
out |
Resultat |
initialtemplate |
Name des Template, das aufgerufen werden soll |
params |
Tabelle mit Parametern für das Stylesheet |
Beispiel:
runtime = require("runtime")
ok, msg = runtime.run_saxon({stylesheet = 'json2xml.xsl',
out = 'data.xml',
initialtemplate = 'main',
params = { ["key"] = "value" }})
Siehe dazu auch das Beispiel im Beispiele-Repository.
Erstellen von XML-Dateien
Man kann die zu verarbeitende Datendatei auch mit dem Lua-Skript erstellen.
Dazu gibt es im Modul xml
die Funktion encode_table()
, die aus einer Lua-Tabelle eine XML-Datei erstellt.
Das Skript
xml = require("xml")
tbl = {
["_type"] = "element",
["_name"] = "data",
{
["_type"] = "element",
["_name"] = "child",
"Hello, world",
}
}
ok, msg = xml.encode_table(tbl)
if not ok then
print(msg)
os.exit(-1)
end
erzeugt die XML-Datei
<data><child>Hello, world</child></data>
die für den folgenden Publisher-Lauf zur Verfügung steht. Das ist besonders dann von Nutzen, wenn die Datenquelle nicht in XML vorliegt.
Verarbeiten von Excel-Dateien
Ein häufig anzutreffender Anwendungsfall ist, dass die Daten für die Verarbeitung aus Excel-Dateien gelesen werden sollen.
Dazu gibt es im Modul xlsx
die Funktion open()
, die eine vorhandene Datei öffnet:
xlsx = require("xlsx")
spreadsheet, err = xlsx.open("myfile.xlsx")
if not spreadsheet then
print(err)
os.exit(-1)
end
Das Objekt spreadsheet
beinhaltet die einzelnen Arbeitsblätter (Worksheets).
Die Anzahl der Arbeitsblätter lässt sich über den length-Operator feststellen und die einzelnen Arbeitsblätter per Index (1 ist das erste Arbeitsblatt).
numWorksheets = #spreadsheet
ws = spreadsheet[1]
Mit dem Objekt ws
kann direkt auf die Zelleninhalte zugegriffen werden.
Dazu wird es als Funktion aufgerufen und liefert eine Zeichenkette zurück.
Die erste Zelle oben links hat die Koordinaten 1,1, die erste Zelle in der zweiten Zeile 1,2 und so weiter.
cell1 = ws(1,1)
cell2 = ws(1,2)
Den Namen des Arbeitsblattes kann man über den Wert name
ermitteln:
name = ws.name
Lesen von CSV-Dateien
Ähnlich wie bei Excel-Dateien kann man auch CSV-Dateien direkt einlesen. Die Struktur ist jedoch einfacher, da es nur ein »Arbeitsblatt« gibt.
csv = require("csv")
csvtab, msg = csv.decode("myfile.csv",{columns = {1,2,3}})
if not csvtab then
print(msg)
os.exit(-1)
end
Der zweite Parameter bei csv.decode()
ist optional.
In diesem Beispiel werden nur die Spalten 1, 2 und 3 ausgegeben.
Das Ergebnis ist eine Tabelle aus Zeilen.
Jede Zeile ist wiederum eine Tabelle, die die einzelnen Werte der Zeile enthält.
Im Beispiele-Repository wird gezeigt, wie man aus der CSV-Datei eine XML-Datei erstellen kann.
Funktionsreferenz
runtime
In diesem Modul werden alle Funktionen und Einstellungen gesammelt, die eher allgemeiner Natur sind.
projectdir
-
Eine Zeichenkette, die das aktuelle Projektverzeichnis enthält (das Verzeichnis mit der
layout.xml
bzw.publisher.cfg
-Datei) variables
-
Eine Tabelle mit alle Variablen, die per
-v
auf der Kommandozeile oder in der Konfigurationsdatei mitvars=…
angegeben wurden. finalizer
-
Ist dieser Variablen eine Funktion zugewiesen, so wird sie nach der PDF-Erzeugung aufgerufen (callback). Die Funktion hat keine Parameter und keinen Rückgabewert.
runtime = require("runtime") function finished() print("PDF is finished now.") end runtime.finalizer = finished
options
-
Tabelle mit Konfigurationswerten (siehe Konfiguration des Publishers). Kann sowohl zum Lesen der Werte als auch zum Setzen benutzt werden.
validate_relaxng(‹xmldatei›,‹schemadatei›)
-
Diese Funktion validiert die angegebene XML-Datei mit dem im zweiten Parameter angegebenen RELAX NG (XML-Syntax) Schema. Die Rückgabe ist ein boolean-Wert, der true ist, wenn der Befehl fehlerfrei ausgeführt wurde. Ansonsten wird ein zweiter Rückgabewert (string) zurück gegeben, der die Fehlermeldung enthält.
run_saxon(‹XSL›,‹Quelldatei›,‹Ausgabedatei›,‹Parameter›)
-
Diese Funktion ruft das zum Publisher mitgelieferte Programm
saxon
auf. Sie erwartet drei string-Argumente (das Stylesheet, die Eingabe- und die Ausgabedatei) und ein optionales Argument das als Parameter an saxon übergeben wird. Die Rückgabe ist ein boolean-Wert, der true ist, wenn der Befehl fehlerfrei ausgeführt wurde. Ansonsten wird ein zweiter Rückgabewert (string) zurück gegeben, der die Fehlermeldung enthält. Der Parameter hat die Formkeyword=value
. run_saxon(‹tabelle›)
-
Diese Funktion ruft das zum Publisher mitgelieferte Programm
saxon
auf. Sie erwartet eine Tabelle als Argument mit den Schlüsselwörtern, die unten aufgelistet sind. Die Rückgabe ist ein boolean-Wert, der true ist, wenn der Befehl fehlerfrei ausgeführt wurde. Ansonsten wird ein zweiter Rückgabewert (string) zurück gegeben, der die Fehlermeldung enthält.Schlüssel Wert source
Quelldatei (XML) stylesheet
Stylesheetdatei (XSL) out
Resultat initialtemplate
Name des Template, das aufgerufen werden soll params
Tabelle mit Parametern für das Stylesheet execute(‹Tabelle mit Programmname und Argumenten›)
-
Führt ein Programm aus und gibt dessen Ausgabe in die Konsole aus. Um den speedata Publisher z.B. selbst zu starten, funktioniert folgende Syntax:
runtime.execute({"sp","--runs","2"})
If necessary, the first parameter (the program name) must be specified with absolute path of the program. On Windows, forward slashes (
/
) also work as separators instead of backward slashes (\
). find_file(‹Dateiname oder URL›)
-
Findet die angegebene Datei im Publisher-Suchpfad und gibt den absoluten Pfad zurück. Bei nicht gefunden: nil bzw. false und eine Fehlermeldung.
xml
Mit dem XML-Modul werden XML-Dateien erzeugt bzw. gelesen.
encode_table(‹tabelle›,[dateiname])
-
Erzeugt eine XML-Datei (
data.xml
bzw. der optional gegebene Dateiname) der übergebenen Tabelle. Rückgabewert 1 ist ein bool (success), Wert 2 ist die Fehlermeldung, wenn der erste Wertfalse
ist. Die Tabelle hat folgende Struktur:element = { ["_type"] = "element", ["_name"] = "elementname", attribute1 = "value1", attribute2 = "value2", child1, child2, child3, ... }
child1
,…
sind entweder Zeichenketten, Elemente oder Kommentare. Kommentare haben folgende Form:comment = { _type = "comment", _value = " Das ist ein Kommentar! " }
decode_xml(‹dateiname›)
-
Liest eine XML-Datei ein und gibt eine Tabelle in der Struktur wie unter
encode_table()
beschrieben zurück. Die Rückgabewerte der Funktion sind ein Boolean-Wert und im Falle von true, eine Tabelle wie oben und im Falle von false, eine Fehlermeldung.
CSV
CSV-Dateien
decode(‹dateiname›,‹parameter›)
-
Liest eine CSV-Datei ein. Der Rückgabewert ist eine Tabelle bzw. im Fehlerfall
false
und eine Fehlermeldung.Die
parameter
werden in einer Tabelle kodiert:charset
-
Wenn die CSV-Datei Latin-1 kodiert ist, muss dieser Wert auf ISO-8859-1 stehen. Andere Kodierungen auf Anfrage. Die Voreinstellung (ohne Angabe von charset) ist UTF-8.
separator
-
Entweder ein Komma (Voreinstellung), ein Semikolon oder das entsprechend genutzte Trennzeichen.
columns
-
Eine Tabelle, die die gewünschten Spalten in ihrer Reihenfolge enthält. Z.B.
{3,2,1}
für die ersten drei Spalten in umgekehrter Reihenfolge.
xlsx
Liest eine Excel-Datei ein.
open(‹dateiname›)
-
Öffnet die angegebene Datei. Der Rückgabewert ist ein
spreadsheet
-Objekt bzw. im Fehlerfallfalse
und eine Fehlermeldung.Das
spreadsheet
-Objekt beinhaltet die einzelnen Arbeitsblätter. Die Anzahl der Arbeitsblätter kann mit dem#
-Operator ermittelt werden. Auf die einzelnen Arbeitsblätter kann man mit dem Index-Operator[]
zugreifen, wobei das erste Arbeitsblatt den Index 1 hat.Die einzelnen Arbeitsblätter können als Funktion mit zwei Parametern benutzt werden (siehe Beispiel oben). Die Parameter sind die x und y Koordinaten der auszulesenden Zelle, die erste Zelle oben links hat die Koordinate 1,1. Die Ausmaße des Inhalts kann über die Parameter
minrow
,maxrow
,mincol
und undmaxcol
ermittelt werden. Der Name ist im Parametername
enthalten. string_to_date(‹string›)
-
Wandelt eine Zahl (kodiert als Zeichenkette) in ein Datum um. Rückgabe ist eine Tabelle mit den Schlüsseln
day
,month
,year
,hour
,minute
undsecond
. Beispiel:xlsx.string_to_date("43458")
ergibt{ ["day"] = "24" ["month"] = "12" ["year"] = "2018" ["hour"] = "0" ["minute"] = "0" ["second"] = "0" }
http
Die HTTP-Bibiliothek ist unter https://github.com/cjoudrey/gluahttp beschrieben.