Mit Skriptsprachen wie Perl oder Python verheiraten Sie Datenbanken und LaTeX zu einem Dream-Team beim Seriendruck.
Zur strukturierten und effizienten Verwalten von Daten stehen leistungsfähige Datenbanksysteme (DBS) wie PostgreSQL [1], Apache CouchDB [2], SQLite [3] oder BaseX [4] bereit. Die Programme sind ausschließlich auf diesen Einsatzzweck hin optimiert. Bei Dokumenten mit perfektem Druckbild gilt dagegen das Textsatzsystem LaTeX als Maß der Dinge. Dieser Beitrag zeigt, wie Sie die beiden Spezialisten zu einem Dream-Team vereinen.
Die Abfrage der Datenbank – also der Zugriff auf die gespeicherten Daten – erfolgt über standardisierte Schnittstellen und Abfragesprachen, wie SQL oder XQuery. Inzwischen verfügen fast alle Programmiersprachen über entsprechende Module, um die Kommunikation über diese Schnittstellen zu kapseln und damit das Programmieren und den Zugriff auf die Daten zu vereinfachen. Wie Sie die gelesenen Daten danach ausgeben oder weiterverarbeiten, steht Ihnen frei. Mit Hilfe von LaTeX erhalten Sie in jedem Fall ein optisch ansprechendes Ergebnis.
Rückblick
Im ersten Teil dieses Workshops bildete LaTeX den zentralen Punkt, an den alle Erweiterungen andockten [5]. Dabei erfolgte der Zugriff ausschließlich über Bordmittel in Form von LaTeX-Paketen. Selbst die Anfrage an die Datenbank gehörte zum Dokument.
In diesem Teil des Workshops erfolgt der Einsatz von LaTeX nur noch als Mittel zum Zweck. Den Hintergrund dafür stellt das das Zerlegen des Ablaufs in einzelne, leichter zu überschauende und besser zu pflegende Schritte dar. Das eröffnet auch die Möglichkeit für komplexere Datenbankabfragen. Den Zugriff auf das DBS übernehmen nun höhere Programmiersprachen, wie PHP, Python, Perl oder Ruby (Abbildung 1).

Abbildung 1: Das Zerlegen des Ablaufs in separate Schritte vereinfacht das Anpassen an eine veränderte Umgebung.
LaTeX dient dabei lediglich zum Vorbereiten der druckreifen Ausgabe ins Zielformat, etwa nach Postscript oder PDF. Um alles andere – das Generieren des LaTeX-Dokuments mit den Inhalten aus der Datenbank und das Übersetzen ins gewünschte Zielformat – kümmern sich die Programmzeilen aus dem Shell-Skript drumherum (Listing 1).
Listing 1
#!/bin/bash python namensschilder.py > namensschilder.tex pdflatex namensschilder.tex pdfnup --nup 1x2 --no-landscape --outfile a.pdf namensschilder.pdf pdfnup --nup 2x1 --outfile namensschilder_druck.pdf a.pdf python teilnehmerliste.py > teilnehmerliste.tex pdflatex teilnehmerliste.tex
Vorgehen
Alle nachfolgend gezeigten Schritte beziehen sich auf ein praktisches Beispiel, das Erstellen von Unterlagen für eine Veranstaltung. Dazu gehört neben Namensschildern für alle Mitwirkenden (Referenten, Organisatoren, Helfer, Presse) eine Liste der Teilnehmer. Das Ziel besteht darin, aus einer zentralen Datenbasis alle Unterlagen zu erstellen – und das möglichst automatisiert ohne Handarbeit.
Als Inspiration diente der Vortrag “Konferenzmanagement mit LaTeX” von Uwe Ziegenhans [6] sowie die Vorbereitungen zum jährlich im Spätherbst in Potsdam stattfindenden Brandenburger Linux-Infotag (BLIT) [7].
Bei diesem bildet das Konferenzverwaltungssystem Pentabarf [8] die Grundlage zum Verwalten aller beteiligten Personen, Aussteller und Vorträge (Abbildung 2). Ursprünglich für die Veranstaltungen des Chaos-Computer-Clubs entwickelt, hat Pentabarf inzwischen nicht nur in diesem Genre Maßstäbe gesetzt.

Abbildung 2: Die Veranstalter des BLIT setzen zum Verwalten der Teilnehmer auf die bewährte Software Pentabarf.
Das System nutzt PostgreSQL als Datenbank und verfügt über eine Reihe von Filtern zum Export der Daten in Formate wie HTML, XML und CSV. Bislang kommt dabei meist der CSV-Export zum Einsatz, der die Datenbasis für Drucksachen bereitstellt. Der direkte Zugriff auf PostgreSQL wäre zwar möglich, der Weg über CSV genügt aber aufgrund der überschaubaren Datenmenge bislang vollkommen.
Das Drumherum
Das Shell-Skript aus Listing 1 bildet den gesamten Arbeitsablauf ab. Zur Ausgabe der Namensschilder ruft es zunächst das Python-Skript namensschilder.py auf und leitet dessen Ausgabe mittels des Operators > in eine LaTeX-Datei um (Zeile 3). Mittels Pdflatex entsteht danach aus dieser LaTeX-Datei ein PDF-Dokument (Zeile 4). Da dieses keine dynamischen Elemente wie Referenzen oder ein Inhaltsverzeichnis enthält, genügt ein einziger Durchlauf zum Übersetzen.
In den Zeilen 5 und 6 fasst das Skript jeweils zwei einzelne Seiten auf einem Blatt zusammen – zunächst übereinander (Zeile 5) und darauf folgend nebeneinander (Zeile 6). Das Ergebnis besteht aus vier Namensschildern samt passender Schnittmarken pro Blatt, sodass sich dieses direkt für den Druck sowie den maschinellen Schnitt eignet (Abbildung 3). In den Zeilen 8 und 9 entsteht mittels der Daten aus der Datenbank die Liste der Teilnehmer sowie eine passende Übersicht.

Abbildung 3: Druckvorlagen samt passender Schnittmarken mit wenigen Kommandos – die richtige Toolchain aus Datenbank, Skriptsprache und LaTeX macht es möglich.
TIPP
Hintergrundinformationen zum Kommando Pdfnup bietet ein Artikel aus LU 10/2009 [9].
Im Folgenden sehen wir uns nun die Skripte zum Erzeugen der Namensschilder und der Teilnehmerliste genauer an.
Namensschilder
Das Erzeugen der Namensschilder übernimmt das Python-Skript namensschilder.py (Listing 2). In Schritt 1 erstellt es die LaTeX-Kopfzeilen (“Präambel”, Zeile 2). Anschließend entnimmt es die Daten aus der angegebenen Datei (Zeile 4) und erzeugt daraus für jeden Datensatz ein Namensschild (Zeile 5 bis 11). Am Ende fügt das Skript in Zeile 12 noch die LaTeX-Fußzeilen hinzu.
Um jedes Namensschild auf einer separaten Seite auszugeben, sorgen die Zeilen 7 bis 10 für einen Seitenumbruch ab dem zweiten Eintrag und vor jedem Namensschild. Zum Steuern dient die Variable ersteZeile (Zeile 3), die einfach den Wert Null erhält, nachdem das Skript den ersten Datensatz gelesen hat (Zeile 10).
Listing 2
import csv
erzeugeHeader()
ersteZeile = 1
personenliste = csv.reader(open('personen.csv'), delimiter=',', quotechar= '"')
for eintrag in personenliste:
teilnehmer, rolle, projektname = eintrag
if not ersteZeile:
seitenumbruch()
else:
ersteZeile = 0
erzeugeNamensschild(teilnehmer, rolle, projektname)
erzeugeFooter()
Datenbankzugriff
In Listing 2 erfolgt der Zugriff auf eine CSV-Datei. Nach dem Laden des Moduls csv (Zeile 1) liest das Skript die Daten in Zeile 4 aus der angegebenen Datei personen.csv. Als Trennzeichen kommt ein Komma zum Einsatz; der Parameter delimiter bietet die Möglichkeit, das anzupassen: Alternativ lassen sich auch Strichpunkte oder Leerzeichen als Trenner zwischen den Feldern verwenden. Als Zeichen zum Einfassen von Textketten, die Steuerzeichen enthalten (etwa ein Komma oder Semikolon), dient das doppelte Anführungszeichen, was die Option quotechar definiert.
Der Zugriff auf ein DBS erfolgt stets in ähnlicher Weise: Das entsprechende Modul baut zunächst eine Verbindung zur Datenbank auf. Dann übermittelt es an den Rechner (host) den Namen der Datenbank, die Benutzerkennung (user) sowie das passende Passwort. Als Ergebnis liefert es ein Objekt, das entsprechende Methoden für die Transaktionen mitbringt. Den Zugriff auf PostgreSQL zeigen die beiden Kästen “PostgreSQL/Python” und “PostgreSQL/Perl”.
PostgreSQL/Python
Für die Kombination PostgreSQL/Python steht das Modul pg8000[10] bereit. Listing 3 zeigt den Zugriff auf die Daten. Nach dem Import der Klasse DBAPI (Zeile 1) stellt die Funktion DBAPI.connect() die Verbindung zum Rechner server her (Zeile 2). Dabei liefert sie ein Objekt zurück, das einen cursor() mitbringt. Damit positionieren Sie den Zeiger auf den richtigen Datensatz (Zeile 3).
Das Select-Kommando liest die Werte teilnehmer, rolle und Projektname aus der Tabelle personenliste. Die Zeilen 5 bis 7 zeigen schematisch eine Möglichkeit auf, die Werte zu verarbeiten. Die Zeilen 8 und 9 schließen die Verbindung zu PostgreSQL wieder.
Listing 3
from pg8000 import DBAPI
connectionId = DBAPI.connect(host="server", database="veranstaltung", user="tom", password="jerry")
cursor = connectionId.cursor()
cursor.execute("SELECT teilnehmer, rolle, projektname FROM personenliste")
for datensatz in cursor:
teilnehmer, rolle, projektname = datensatz
...
cursor.close()
connectionId.close()
PostgreSQL/Perl
Während es für Python pro DBS ein eigenes Modul gibt, verfolgt Perl ein etwas anderes Konzept: Hinter dem Perl Database Interface, kurz: Perl-DBI, verbirgt sich eine einheitliche Schnittstelle, über die Sie auf alle DBS in der selben Art und Weise zugreifen. Die Auswahl erfolgt über entsprechende Treiber. So nutzen Sie dbi:Pg für PostgreSQL. Listing 4 demonstriert den Einsatz.
Zeile 2 lädt das Perl-DBI-Modul. Die Zeilen 4 bis 6 definieren den Treiber, den Benutzernamen und das zugehörige Passwort. Nun baut Zeile 8 eine Verbindung auf. Zeile 9 bereitet die Transaktion vor, die dann Zeile 10 stattfindet. Die Zeilen 11 bis 13 lesen zeilenweise das Ergebnis der Transaktion aus. Am Ende trennt das Skript die Verbindung zum DBS wieder (Zeile 14) und beendet sich (Zeile 15).
Listing 4
#!/usr/bin/perl -w
use DBI;
$driver = "dbi:Pg:veranstaltung";
$user = "tom"
$password = "jerry"
my $connectionId = DBI->connect($driver, $user, $password);
my $transaction = $connectionId->prepare("SELECT teilnehmer, rolle, projektname FROM personenliste");
$transaction->execute()
while (($teilnehmer, $rolle, $projektname) = $transaction->fetchrow_array){
...
}
$connectionId->disconnect;
exit;
LaTeX-Code erzeugen
In Listing 2 kamen die Funktionen erzeugeHeader(), seitenumbruch(), erzeugeNamensschild() und erzeugeFooter() zum Einsatz. Die Funktion erzeugeHeader() (Listing 5) ist für die Präambel des LaTeX-Dokuments zuständig. Im Programmcode fallen die doppelten Schrägstriche links am Anfang der Zeilen auf: LaTeX-Kommandos beginnen ja mit einem einfachen Schrägstrich links. Da der Python-Interpreter aber Zeichenfolgen, die mit einem solchen Zeichen beginnen, als Steuerzeichen interpretiert, müssen Sie diese für die Ausgabe durch einen weiteren Schrägstrich schützen (“escapen”).
Zeile 4 legt mithilfe des Pakets geometry das Papierformat auf DIN*A6 quer fest. Zeile 10 definiert zusätzlich die Außen- und Innenmaße der Seite. In Zeile 11 kommt das Paket crop [11] hinzu, welches die entsprechenden Schnittmarken generiert. Die vier Optionen sorgen für Schnittmarken an jeder Ecke (cam), ausgerichtet auf das Format DIN A6 (a6), mittig (center) und im Querformat (landscape). Zeile 12 markiert den Beginn des Dokuments.
Die Funktionen seitenumbruch() und erzeugeFooter() fallen vergleichsweise kurz aus und erzeugen lediglich einen Seitenumbruch beziehungsweise bezeichnen das Ende des Inhalts des Dokuments (Listing 6).
Listing 5
def erzeugeHeader():
print("""
\\documentclass{article}
\\usepackage[paper=a6paper,landscape,dvips=false,pdftex=false,vtex=false]{geometry}
\\usepackage[pdftex]{graphicx}
\\usepackage[pdftex]{color}
\\usepackage{palatino}
\\usepackage[T1]{fontenc}
\\usepackage[utf8]{inputenc}
\\geometry{paperwidth=95mm, paperheight=66mm, margin=1mm, bottom=1mm, nohead}
\\usepackage[cam,a6,center,landscape]{crop}
\\begin{document}
""")
Listing 6
def seitenumbruch():
print("\\newpage")
def erzeugeFooter():
print("\\end{document}")
Etwas umfangreicher fällt die Funktion aus, mit deren Hilfe das Namensschild entsteht (Listing 7). Sie definiert zunächst eine minimierte Seite (Zeile 4), in die sie nacheinander den Namen der Veranstaltung (Zeile 6), den des Teilnehmers, dessen Rolle, das Projekt (Zeilen 9 bis 11) sowie eine Grafik anordnet (Abbildung 2).
Listing 7
def erzeugeNamensschild(teilnehmer, rolle, projektname):
print("""
\\hspace{-2mm}
\\begin{minipage}[t]{92mm}
~ \\\\
\\small{9. Brandenburger Linux-Infotag 2012 -- Potsdam}\\\\
\\huge{\\textsf{%
""")
print("\\textbf{~" + teilnehmer + "}\\\\")
print(rolle + "\\\\")
print(projektname + "\\\\")
print("""
}}~ \\\\
\\includegraphics[width=75mm]{footer.png}
\\end{minipage}
""")
Teilnehmerliste
Die Liste der Teilnehmer erzeugen die Veranstalter im Beispiel mittels einer Überschrift und einer simplen Aufzählung der Namen samt Funktion (Abbildung 4). Aufbauend auf Listing 7 ergibt sich eine ähnliche Funktion in wenigen Zeilen Python- und LaTeX-Code (Listing 8).
Listing 8
...
print("""
\\huge{Teilnehmerliste}
\\normalsize \\~\\
\\begin{itemize}
""")
for eintrag in personenliste:
teilnehmer, rolle, projektname = eintrag
print("\\item " + teilnehmer + "(" + rolle + ")")
print("\\end{itemize}")
...
Fazit
Die vorgestellte Kombination wirkt auf den ersten Blick sicher etwas aufwändig und leicht überdimensioniert. Sie spiegelt jedoch die in der Praxis üblichen Vorgehensweisen wieder und nutzt dabei insbesondere die Stärken der einzelnen Werkzeuge effizient aus. Damit bietet sie alle Möglichkeiten, um Daten einerseits mit überschaubarem Aufwand auszulesen und zudem noch hübsch zur Geltung zu bringen.
Die vorgestellte Skript-Lösung hat sich im Alltag bestens bewährt. Bei Veranstaltungen ist es häufig so, dass kurzfristig noch Daten hinzukommen oder sich wieder ändern, etwa wenn ein Teilnehmer absagt und jemand für ihn einspringt. In einem solchen Fall genügt ein einzelner Durchlauf des Skripts, um flink auf die veränderten Anforderungen zu reagieren.
Infos
[1] PostgreSQL: http://www.postgresql.org
[2] Apache CouchDB: http://couchdb.apache.org
[3] SQLite: http://www.sqlite.org
[4] BaseX: http://basex.org
[5] LaTeX mit Daten aus externen Quellen anreichern (Teil 1): Frank Hofmann, “Angefüttert”, LU 05/2013, S. 49, https://www.linux-community.de/28417
[6] Konferenzmanagement mit LaTeX: http://uweziegenhagen.de/?p=141
[7] Brandenburger Linux-Infotag (BLIT): http://www.blit.org
[8] Pentabarf: http://pentabarf.org
[9] PDF und Postscript: Frank Hofmann, “Blattweise”, LU 10/2009, S. 88, https://www.linux-community.de/17410
[10] Python-Modul pg8000: http://pybrary.net/pg8000/
[11] Crop: http://www.ctan.org/pkg/crop
[] Der Autor bedankt sich bei Dinu Gherman, Lars Lingner, Wolfram Eifler und Thomas Winde für deren kritische Anmerkungen und Kommentare im Vorfeld dieses Artikels.






