Kaffeetischbuch (Linux-Magazin, März 2022)

Eigentlich hatte ich Papierbüchern ja schon abgeschworen, denn wenn ich heutzutage noch irgendein Werk aus Papier, meist gebraucht, kaufe, wandert es sofort durch den sogenannten Schnetzler, dann den Scanner, und landet schließlich digitalisiert auf meinem Luxus-iPad, um erst dort gelesen zu werden.

Doch eine Art von Papierbuch finde ich immer noch interessant und sogar der digitalen Form überlegen: Den dicken Fotoband, den man zufällig hingestreut auf einem Tischchen in der Hotel-Lobby oder einer Ferienwohnung findet. Man lässt sich in einen danebenstehenden Sessel plumpsen, lehnt sich zurück, legt die Füße hoch, und fängt gemütlich an zu blättern.

Ein "Coffee Table Book" nennt man so etwas auf amerikanisch, wohl weil es meist auf den niedrigen Kaffeetischchen ausliegt, und man kann solche Werke auch selbst mit eigenen Fotos produzieren. Die Online-Anbieter Shutterfly, Snapfish oder Blurb bieten Browser-Tools, um Fotos hochzuladen und über die Seiten eines virtuellen Papierbuchs zu verstreuen. Drückt der Kunde dann den Submit-Knopf und bezahlt, bringt der Postbote nach ein, zwei Wochen ein hochwertiges Buch mit Harteinband ins Haus.

Abbildung 1: Ein vom Autor erzeugtes und online gedrucktes Fotobuch.

Besser selbstgemacht

Der Gestaltungsprozess online ist jedoch eher beschränkt, bietet keinerlei Versionskontrolle und man hat dauernd Angst, dass dass die schludrig programmierte Applikation gleich die bisher investierte Arbeit verwirft und man von vorne beginnen muss. Wie wäre es mit einem handgestrickten Programm für zuhause, das Fotobücher als PDF erzeugt, die man dann einfach und billiger in einem Rutsch bei einem Service wie Lulu.com drucken und binden lassen kann?

Und wieso sollte ein Mensch beim Arrangieren der Bilder mit der Maus herumfitzeln müssen, bis diese alle auf einer Linie sind, wenn ein Satzprogramm wie TeX die Fotos auf Kommando automatisch dorthin dirigiert wo sie hingehören? Allerdings kann man kaum einem User zumuten, sich mit der schrulligen TeX-Syntax herumzuschlagen und Fotobücher im Texteditor einzugeben, mittels gefährlich aussehender Kommandos, die mit umgekehrten Schrägstrichen beginnen und den Text in geschweiften Klammern sehen wollen.

Kleister aus Ruby

Mit etwas Kleister-Code aus Ruby können aber auch Nicht-Programmierer Druckvorlagen für ihre Kaffeetischbücher erstellen. Listing 1 zeigt die Definition eines Fotobands, und der Kontaktabzug in Abbildung 2 das davon generierte PDF. Der User nutzt pro Seite ein Template (z.B. "cover" für die Umschlagseite, "four" für eine Seite mit vier Bildern) und gibt die dazugehörigen Fotos als photos und den untermalenden Text als text hinzu. Schwupps, spuckt die abschließende Methode print() in Zeile 35 ein PDF unter dem in Zeile 5 definierten Namen ("mybook"), das ein PDF-Viewer wie zum Beispiel evince mit evince mybook.pdf anzeigt. Ein Online-Druckservice macht daraus ratz-fatz einen Prachtband mit Hardcover. Nicht schlecht, oder?

Listing 1: mkbook.rb

    01 #!/usr/bin/ruby
    02 require_relative "bm"
    03 
    04 b = Bm.new
    05 b.name = "mybook"
    06 
    07 b.add "intro"
    08 
    09 b.add "cover",
    10   text: "Unstoppable Mike Schilli",
    11   photos: ["ring.jpg"]
    12 
    13 b.add "chapter"
    14 
    15 b.add "single",
    16   text: "On the Beach",
    17   photos: ["ob-jump.jpg"]
    18 
    19 b.add "twotowers",
    20   text: "Planet of the Apes",
    21   photos: ["icpf.jpg", "icp.jpg"]
    22 
    23 b.add "four",
    24   text: "All Along the Coast",
    25   photos: ["thornton-jump.jpg",
    26            "beach-rock.jpg",
    27            "heron.jpg",
    28            "beach-trunk.jpg"]
    29 
    30 b.add "chapter",
    31   text: "The End"
    32 
    33 b.add "outro"
    34 
    35 b.print

Abbildung 2: Ein PDF-Viewer zeigt das fertige Fotobuch an, bevor es in Druck geht.

Unter der Haube erzeugt das Ruby-Interface dabei anhand der Templates TeX-Snippets, und baut hierzu mittels Variablen erläuternden Text und Pfade zu den Dateien der einzubettenden Fotos ein. Die print()-Methode in Zeile 35 klatscht am Ende alles zusammen und ruft das Programm xelatex auf, das daraus ein professionell aussehendes PDF setzt. Das Programm xelatex installiert der Admin zum Beispiel unter Ubuntu mit dem Kommando sudo apt install texlive-xetex. Es nimmt TeX-Dateien entgegen, setzt den Text, und hält leere Rechtecke für die Fotos entsprechend ihrem Format und den Satzvorgaben frei. Der PDF-Treiber pflanzt anschließend die hereingereichten JPG-Fotos in die Löcher und fertig ist der Lack.

Der Vorteil? Jetzt steht die Buchbeschreibung als Code in einer Textdatei (neues Tech-Modewort CoC, oder "Coffeebook as Code"), die der User jederzeit wieder abrufen, modifizieren, oder mittels Versionskontrolle auf einen historischen Stand zurücksetzen kann.

Urgesteine TeX und LaTeX

Allerdings ist das, was TeX unter der Haube treibt, so ausgefuchst, dass es oft schwer vorherzusagen ist, ob das Arrangement der Photos im Template nun den hehren Vorstellungen des Layouters entsprechen wird. Die Chose kompliziert sich außerdem, weil praktisch niemand mehr (außer TeX-Autor Knuth selbst) reines TeX schreibt, um Bücher zu setzen, sondern meist das daraufsitzende Makro-Paket LaTeX nutzt. Aber die richtige Zauberformel zu finden, damit LaTeX nicht plötzlich unvermittelt die Seite umbricht oder verrückte Entscheidungen trifft, erfordert oft nervenzehrende Fitzelei am Code, bis der TeX-Compiler das richtige Layout ausspuckt.

Ist aber ein Template einmal gemacht, wird es bis ans Ende der Zeit funktionieren. Das Original-Buch von TeX-Erfinder Knuth ([2]) und das Werk seiner LaTeX-Jünger [3] sind übrigens auch heute noch lesenswert, falls man sie in einem Antiquariat findet. Und noch immer publizieren Experten mathematische "Papers" in LaTeX, und auch der Autor dieser Zeilen hat vor mehr als 20 Jahren mal ein Perl-Buch in LaTeX geschrieben ([4]). Diese Wahl setzt einen Autor definitiv einer extrem steilen Lernkurve aus, aber wer Versionskontrolle und Reproduzierbarkeit ohne manuelle Schritte schätzt, hat auch heute kaum eine andere Wahl. Professionelle Satzprogramme wie Adobe InDesign oder QuarkXPress können auch heute noch, knapp 50 Jahre später, immer noch nichts automatisch.

Ruby eleganter als Go

Warum Ruby als Kleister-Code und nicht Go, wie sonst üblich im Programmier-Snapshot? Für reine Textmanipulation und simplen, prägnanten, und einfach zu erweiternden Code, der fast schon wie eine Config-Datei ausschaut, ist eine Skriptsprache wie Ruby einfach nicht zu schlagen. Noch dazu eignet sich Rubys Template-System Emb hervorragend zum Einpflanzen von Text oder Fotodateien in die Snippets mit dem TeX-Salat, nicht ohne Grund nutzen CM-Tools wie Puppet und Chef Ruby als Referenzsprache für die dynamische Anpassung mannigfaltiger Konfigurationsdateien auf individuelle Umgebungen.

Listing 2 zeigt den Ruby-Kleister, der eine Klasse definiert, die User-Skripts zum Generieren von Coffee-Table-Books wie Listing 1 einbinden müssen. Mit require_relative bm finden Skripts die Bibliothek aus Listing 2 im gleichen Verzeichnis und können dann sofort mit Bm.new loslegen. Mit der zurückkommenden Instanz bm erstellen sie mit wenigen Kommandos anspruchsvolle Fotobände.

Handliche Pfade

Die TeX-Templates liegen der Ordnung halber alle in einem Unterverzeichnis tmpl. Damit User sie nur mit Namen aufrufen können (zum Beispiel "cover"), definiert Listing 2 eine Instanzvariable @tmpldir, die auf das Unterverzeichnis mit den Templates zeigt. Geht es ans Auslesen eines Templates, klatscht Zeile 14 vor den hereingereichten Namen der Datei den tmpl-Pfad, hängt hinter den Namen noch eine .tex-Endung an. Schon zeigt der generierte Pfad auf eine real existierende Datei, die der Template-Prozessor ERB in Zeile 19 einlesen kann.

Listing 2: bm.rb

    01 #!/usr/bin/ruby
    02 require 'erb'
    03 
    04 class Bm
    05   attr_accessor :name
    06 
    07   def initialize
    08     @tmpldir = "tmpl"
    09     @photodir = "photos"
    10     @tex = ""
    11   end
    12 
    13   def add(tmpl, text: "", photos: [])
    14     tpath = "#{@tmpldir}/#{tmpl}.tex"
    15 
    16     photos = photos.map {
    17        |path| "#{@photodir}/#{path}" }
    18 
    19     t = ERB.new(File.read(tpath))
    20     @tex += t.result_with_hash(
    21         text: text, photos: photos)
    22   end
    23 
    24   def print
    25       texf = "#{@name}.tex"
    26       File.open(texf, "w") {
    27           |f| f.write(@tex) }
    28       system("xelatex", texf)
    29   end
    30 end

Den hereingereichten Text zur Seitenunterschrift und die Pfade zu den Fotodateien baut der Template-Prozessor mittels der Methode results_with_hash() in Zeile 20 in das jeweilige Template ein. Als Platzhalter in den Templates fungieren dabei <%= text %> für den Seitentext und <%= photos[0] %> für das erste Element des unter dem Parameter photos übergebenen Arrays zu JPG-Pfaden der einzubindenden Fotos. Auch bei den Bildpfaden muss der User also nicht jedesmal umständlich den gesamten Pfad tippen. Solange die Instanzvariable @photodir auf das Verzeichnis zeigt, in dem alle Bilder liegen (im Beispiel photos), muss der User in Listing 1 jeweils nur den pfadlosen Dateinamen angeben.

Der Name, Herr Accessor?

Um den Namen der zu erzeugenden Buchdatei zu bestimmen, setzt das aufrufende Skript das Attribut name eines Bm-Objektes, wie in Zeile 5 in Listing 1. Damit dies funktioniert, definiert Listing 2 mit attr_accessor in Zeile 5 mit :name ein les- und schreibbares Attribut name für alle Objekte der Klasse.

So kann eine Instanz der Klasse Bm in bm einfach mit bm.name = "mybook" wie in Zeile 5 in Listing 1 per Zuweisung den Namen setzen, den Zeile 25 in Listing 2 mit @name von der Instanzvariable abfrägt und innerhalb eines Strings mit "#{@name}" interpoliert. So steht der Name der TeX-Datei fest, in der der TeX-Salat landen soll, und die im nächsten Schritt an den LaTeX-Compiler xelatex geschickt wird.

So braucht Zeile 26 nur die .tex-Datei zum Schreiben zu öffnen, die in der Instanzvariablen @tex angesammelten TeX-Instruktionen dort abzulegen, worauf der system()-Aufruf in Zeile 28 xelatex darauf ansetzt, was, falls alles gut geht, eine gleichnamige .pdf-Datei mit dem fertigen Fotobuch erzeugt.

Anfang und Ende

Wie sehen nun die Templates aus? TeX- oder vielmehr LaTeX-Code ist von rückwärtigen Querstrichen übersäht und begrenzt Textbereiche mittels geschweifter Klammern. Nach einer Präambel aus Dokumenttyp und verwendeten Zusatzpaketen beginnen die eigentlichen Dokumente immer mit der Anweisung \begin{document} und enden mit \end{document}. Dem tragen die Templates "intro.tex" und "outro.tex" (Listing 3 und 4) Rechnung, die den Anfang und das Ende jedes erzeugten Fotoalbums bilden.

Die LaTeX-Pakete color und pagecolor setzen die Farben des Fonts und des Hintergrunds im Dokument. Da Fotobücher im "Dark Mode", also weißer Schrift auf schwarzem Hintergrund irgendwie professioneller aussehen, setzt das Template \color{white} und \pagecolor{black}.

Listing 3: intro.tex

    01 \documentclass[10pt]{book}
    02 \usepackage[export]{adjustbox}
    03 \usepackage{color}
    04 \pagecolor{black}
    05 \color{white}
    06 \usepackage[paperwidth=9in,
    07   paperheight=7in,
    08   total={8in,6.5in}]{geometry}
    09 \pagestyle{empty}
    10 \begin{document}
    11   \sffamily

Listing 4: outro.tex

    1 \end{document}

LaTeX-Dokumente drucken normalerweise die Seitennummern unten auf jede Seite, was bei Fotobänden eher unüblich ist, also unterdrückt der Befehl \pagestyle{empty} dieses Feature. Auch wirkt TeXs Standard-Font Computer Modern heutzutage etwas antiquiert, die Sans-Serif-Version, die das Kommando \sffamily aktiviert, entspricht heutzutage eher dem Geschmack der Zeit. Das Paket adjustbox unterstützt das graphics-Paket beim Einbinden von Bildern und deren Schrumpfung, damit sie in die vom Layout-Roboter vorgegebenen Rechtecke passen.

Das Format des Fotobands legt das Paket geometry ab Zeile 6 von Listing 3 fest. Entsprechend der Vorgaben des verwendeten Druckdienstleisters messen die Seiten 9 mal 7 Inches im Querformat, wobei der tatsächlich bedruckte Bereich mit 8 mal 6,5 Inches etwas kleiner ist, damit noch etwas Rand bleibt. Wer andere Dienstleister nutzt, kann hier Anpassungen am Seitenformat vornehmen.

Das Ende des TeX-Dokuments läutet das outro-Template ein, das lediglich ein \end{document} absetzt, damit TeX später weiß, dass Schicht im Schacht ist.

Mittig schwebend

Ein TeX-Template für eine leere Seite mit einem optionalen zentrierten Textbaustein zeigt Listing 5. Der Template-Prozessor wird den Platzhalter <%= text %> in Zeile 3 beim Einbinden durch den als text gesetzten Parameterwert ersetzen. Damit der Text als Kapitelüberschrift zentriert in der Mitte der Seite zu liegen kommt, setzt Zeile 2 mit \vspace* einen Füllraum oberhalb und Zeile 4 einen gleichgroßen Bereich unterhalb des in Zeile 3 mittels \center horizontal zentrierten Textes in der Größe Huge. Da die beiden Füllräume oben und unten nach den Regeln der gerechten Platzaufteilung gleich große Flächen einnehmen, schwebt der Text in der Mitte der Seite. TeX unterscheidet übrigens zwischen \vspace und \vspace*: Das erste Kommando ohne den Stern wird am Seitenanfang ignoriert, was die Kapitelüberschrift nicht mittig, sondern am oberen Rand der Seite schweben ließe.

Listing 5: chapter.tex

    1 \null\newpage
    2 \vspace*{\fill}
    3 \center{\Huge{<%= text %>}}
    4 \vspace*{\fill}

Guter erster Eindruck

Die Titelseite des Fotobuches zeigt ein Foto mit einer Zeile Text in einem großen Font darunter, beides zum rechten Rand ausgerichtet. Listing 6 leitet hierzu im Template cover.tex mittels \begin{center} eine horizontal zentrierte Umgebung ein, und bindet mit \includegraphics aus dem graphics-Paket der LaTeX-Bibliothek ein 3 Inches hoch gerendertes Foto ein. Der Template-Prozessor erhält später einen Array mit Foto-Dateien, und der Platzhalter <= photos[0] %> setzt den Pfad der ersten Datei in der Liste ein. LaTeX reserviert daraufhin den vom Foto beanspruchten Platz im Dokument, und der PDF-Treiber setzt später das Foto hinein, nachdem er es auf die benötigte Größe skaliert hat.

Listing 6: cover.tex

    01 \vspace*{\fill}
    02 \begin{center}
    03   \hfill
    04   \includegraphics[height=3in,valign=t]{
    05       <%= photos[0] %>}
    06   \hspace*{4cm}
    07 \end{center}
    08 \vspace{2cm}
    09 \begin{center}
    10   \hfill \Huge{<%= text %>}
    11   \hspace*{4cm}
    12 \end{center}
    13 \vspace{1cm}

Für Fotos, die groß rauskommen sollen zeigt Listing 7 ein Template für ein seitenfüllendes Querformatfoto mit darunterstehendem Text, wie in Seite 3 des fertigen Fotobuchs in Abbildung 2 zu sehen. Der Befehl \includegraphics{} stellt im Layout Platz für ein Bild bereit, die Angabe von height beschränkt den Platz in der vertikalen, damit auf der Seite noch Platz für den darunterstehenden Text bleibt. Den platziert Zeile 10 mit einem halben Zentimeter Extraabstand unter dem Foto.

Listing 7: single.tex

    1 \null
    2 \begin{figure}
    3   \centering
    4   \includegraphics[height=5.5in,valign=t]{
    5       <%= photos[0] %>}
    6   \linebreak
    7     \par\vspace{.5cm}\par
    8   \Huge{\sffamily <%= text %>}
    9 \end{figure}

Zwei Türme

Wer im Querformat des Buchs auf einer Seite lieber zwei Fotos im Hochformat nebeneinander sehen möchte, greift auf das Template twotowers.tex (Sinnbild für zwei Bilder im Hochformat) in Listing 8 zu. Es referenziert die beiden als Parameter hereingereichten Fotos mit <%= photos[0] %> und <%= photos[1] %>. Als Beispiel im Fotoband in Abbildung 2 dient ein Bild von einem Stück abgebrochener Straße, dessen spiegelbildlicher Gegenpart das Tool convert aus der Sammlung ImageMagick erzeugt hat. Immer für einen Gimmick gut!

Listing 8: twotowers.tex

    01 \null
    02 \begin{figure}
    03   \centering
    04   \includegraphics[height=5in,valign=t]{
    05       <%= photos[0] %>}
    06   \hspace{1cm}
    07   \includegraphics[height=5in,valign=t]{
    08       <%= photos[1] %>}
    09   \linebreak
    10   \par \vspace{1cm} \par
    11   \Huge{\sffamily <%= text %>}
    12 \end{figure}

Eine Buchseite im Querformat kann aber auch vier quaderförmig angeordnete Querformatfotos aufnehmen. Das Template four.tex in Listing 9 zeigt den dazugehörigen TeX-Code. Damit der Abstand zwischen den Bildern ansprechend rüberkommt, schrumpft das Template die Fotos auf eine Höhe von 2.5 Inches, und setzt den horizontalen Abstand in den Zeilen 5 und 13 auf 0,5cm, und den vertikalen Zwischenraum in Zeile 9 auf 0,75cm. Der darunterliegende Text in der Riesenschrift \Huge ist innerhalb der \centering-Umgebung ebenfalls zentriert und schwebt, wie von Zeile 17 vorgegeben, 0,5cm unterhalb des unteren Bildpaars. Die Anweisung \par leitet in TeX jeweils einen neuen Absatz ein, und \vspace{} weist den Satzautomaten an, zwischen den Absätzen Platz zu lassen, in diesem Fall 0,5cm.

Listing 9: four.tex

    01 \begin{figure}
    02   \centering
    03   \includegraphics[height=2.5in,valign=t]{
    04       <%= photos[0] %>}
    05   \hspace{.5cm}
    06   \includegraphics[height=2.5in,valign=t]{
    07       <%= photos[1] %>}
    08   \par
    09   \vspace{.75cm}
    10   \par
    11   \includegraphics[height=2.5in,valign=b]{
    12       <%= photos[2] %>}
    13   \hspace{.5cm}
    14   \includegraphics[height=2.5in,valign=b]{
    15       <%= photos[3] %>}
    16   \linebreak
    17   \par\vspace{.5cm}\par
    18   \Huge{\sffamily <%= text %>}
    19 \end{figure}

Mit diesen Templates lässt sich schon ein ansprechender Fotoband erstellen, bestehend aus dem Einband, leeren Seiten, oder Kapitel-Überschriften, sowie Fotoseiten mit einem Foto im Querformat, zwei Fotos im Hochformat, oder gleich vier Fotos im Querformat, alle mit optionalem Seitentext. Zum Erzeugen des fertigen Fotobands im PDF-Format muss nur ein Skript wie Listing 1 her, das die Templates abruft und entsprechende Fotos von der Platte einbindet. Wer möchte, kann seiner Kreativität aber noch freien Lauf lassen, beliebige neue TeX-Templates erzeugen und im aufrufenden Ruby-Skript nutzen. Da die gesamte Buchdefinition in einer Textdatei liegt, verwaltet von einem Versionskontrollsystem wie git, kann der User ohne Verlustangst Änderungen einarbeiten, oder auch Teile eines alten Bandes in einen neuen Übernehmen. Programmierte Fotobanderstellung eben.

Noch ein Bonmot fand ich übrigens im 1984 in erster Auflage erschienenen TeX-Book von Donald Knuth ([2]): Auf Seite 110 erklärt er, warum TeX zwar akribisch versucht, Absätze so umzubrechen, dass die Wortabstände in allen Zeilen möglichst gleich erscheinen, bis zu dem Punkt, an dem ein einziges Wort am Ende eines Absatzes zu einem Totalumbruch des ganzen Absatztextes führt. Auf der anderen Seite schaut es aber beim Seitenumbruch nicht mehrere Seiten voraus, um Abbildungen und Textabsätze möglichst schlau über die Seiten zu verteilen. Den Grund für diese Design-Entscheidung nennt Autor Knuth auch gleich: Der RAM-Speicher des Computers wäre schlicht nicht groß genug gewesen, um mehrere Seiten gleichzeitig vorzuhalten. Wie die Zeiten sich ändern.

[1]

Listings zu diesem Artikel: http://www.linux-magazin.de/static/listings/magazin/2022/02/snapshot/

[2]

Donald Knuth, "The TeX Book", Addison-Wesley, 1992

[3]

Michel Goossens, Frank Mittelbach, Sebastian Rahtz, Denis Roegel, Herbert Voss, "The LaTeX Graphics Companion", 2nd Edition, 2007

[4]

Michael Schilli, "Go To Perl 5", 4. Auflage, Addison-Wesley, 1998

Michael Schilli

arbeitet als Software-Engineer in der San Francisco Bay Area in Kalifornien. In seiner seit 1997 laufenden Kolumne forscht er jeden Monat nach praktischen Anwendungen verschiedener Programmiersprachen. Unter mschilli@perlmeister.com beantwortet er gerne Ihre Fragen.

POD ERRORS

Hey! The above document had some coding errors, which are explained below:

Around line 5:

Unknown directive: =desc