headerbild blog

Version 3.1.11 / Layoutvalidierung

Die Version 3.1.11 hat eine Reihe von Neuerungen enthalten. Für mich besonders spannend ist die Möglichkeit, nun auch eine neuere LuaTeX-Version zu benutzen. Dazu hatte ich ja kürzlich schon einen eigenen Beitrag. Daneben habe ich auch wieder einmal den Lua-Filter erweitert. Einerseits um die Möglichkeit, Excel-Dateien zu lesen (und nach XML zu konvertieren) und andererseits habe ich die Möglichkeit der Layoutvalidierung eingebaut. Das erste Feature beschreibe ich noch einmal separat, hier gibt es eine Beschreibung der Validierung.

Validierung mithilfe von Jing

Um RelaxNG-Dateien zu validieren gibt es mehrere Möglichkeiten. Eine davon ist Jing. Seit Version 3.11.1 wird das mit dem Publisher mitgeliefert.

In der Lua-Vorverarbeitung gibt es eine neue Funktion, die die Validierung übernimmt:

runtime.validate_relaxng(<xmldatei>,<schemadatei>)

Die Funktion liefert im Fehlerfall false und die Fehlermeldung zurück. Beispiel:

-- die Pfade natürlich anpassen!
ok, msg = runtime.validate_relaxng("layout.xml","../schema/layoutschema-de.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.

Warum validieren?

Im Beitrag Feature der Woche: Einbinden des RelaxNG Schemas bin ich schon mal auf das Schema eingegangen. Wenn man nicht mit einem XML-Editor die Layoutdatei bearbeitet, der während der Eingabe das Layout überprüft, dann sollte man das Layout wenigstens vor den Änderungen mal validieren, damit eine syntaktisch falsche Layoutdatei als Fehlerquelle ausgeschlossen werden kann.

Der Laufzeitnachteil ist meines Erachtens vernachlässigbar, da man sich die Zeit schnell bei der Fehlersuche spart.

Validieren von Daten

Man kann nicht nur die Layoutdatei auf Korrektheit überprüfen, sondern auch alle anderen XML-Dateien. Hierzu muss man sich jedoch ein eigenes RelaxNG-Schema erstellen. Dazu habe ich im August eine Anleitung geschrieben. 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.

von Patrick Gundlach |

Fontsampler IBM Plex

Die Firma IBM hat seine neue Hausschrift IBM Plex auf Github unter einer OpenSource-Lizenz verfügbar gemacht. Sie kann ohne Einschränkungen (SIL Font License) benutzt werden.

Die Gelegenheit habe ich mal genutzt, um ein Beispielblatt mit den Schriftarten zu erstellen. Den Weg hierzu möchte ich kurz beschreiben:

Als erstes habe ich die Schriftarten heruntergeladen und entpackt. Da der Publisher sowohl mit OpenType als auch mit TrueType umgehen kann, habe ich mich für die erste Variante entschieden. Die Schriftdateien (*.otf) habe ich einfach in ein Verzeichnis kopiert und mir die passende data.xml und layout.xml (siehe unten) erstellt. Hier ist die Verzeichnisstruktur abgebildet:

$ tree
.
├── data.xml
├── fonts
│   ├── IBMPlexMono-Bold.otf
...
│   └── IBMPlexSerif-ThinItalic.otf
└── layout.xml

Datendatei (data.xml)

Die Datendatei lässt sich einfach erstellen. Mit sp --list-fonts erhält man eine Ausgabe wie:

Filename                                 PostScript Name
-----------------------------------      ---------------
IBMPlexMono-Bold.otf                     IBMPlexMono-Bold
IBMPlexMono-BoldItalic.otf               IBMPlexMono-BoldItalic
IBMPlexMono-ExtraLight.otf               IBMPlexMono-ExtraLight
IBMPlexMono-ExtraLightItalic.otf         IBMPlexMono-ExtraLightItalic
IBMPlexMono-Italic.otf                   IBMPlexMono-Italic
IBMPlexMono-Light.otf                    IBMPlexMono-Light
IBMPlexMono-LightItalic.otf              IBMPlexMono-LightItalic
IBMPlexMono-Medium.otf                   IBMPlexMono-Medium
...

daraus kann man mit einem guten Texteditor leicht die folgende XML-Struktur erstellen:

<fonts>
  <font filename="IBMPlexMono-Bold.otf" psname="IBMPlexMono-Bold" />
  <font filename="IBMPlexMono-BoldItalic.otf" psname="IBMPlexMono-BoldItalic" />
  <font filename="IBMPlexMono-ExtraLight.otf" psname="IBMPlexMono-ExtraLight" />
  <font filename="IBMPlexMono-ExtraLightItalic.otf" psname="IBMPlexMono-ExtraLightItalic" />
  <font filename="IBMPlexMono-Italic.otf" psname="IBMPlexMono-Italic" />
  <font filename="IBMPlexMono-Light.otf" psname="IBMPlexMono-Light" />
  <font filename="IBMPlexMono-LightItalic.otf" psname="IBMPlexMono-LightItalic" />
  <font filename="IBMPlexMono-Medium.otf" psname="IBMPlexMono-Medium" />
 ...
</fonts>

Das ist dann die fertige Datendatei.

Layoutdatei (layout.xml)

Die Layoutdatei ist ähnlich einfach gestrickt.

<Layout xmlns="urn:speedata.de:2009/publisher/en"
  xmlns:sd="urn:speedata:2009/publisher/functions/en">

  <Record element="fonts">
      <ProcessNode select="font"/>
  </Record>

  <Record element="font">
    <LoadFontfile name="{@psname}" filename="{@filename}"/>
    <DefineFontfamily name="text" fontsize="10" leading="12">
      <Regular fontface="{@psname}"/>
    </DefineFontfamily>

    <PlaceObject>
      <Textblock>
        <Paragraph>
          <Value select="@psname"/>
          <Value>: Hello world! 1234567890 äöüß</Value>
        </Paragraph>
      </Textblock>
    </PlaceObject>
  </Record>

</Layout>

Hier wird für das Wurzelelement nur die Anweisung ausgeführt, für alle Kindelemente (font) weitere Anweisungen auszuführen.

Dort wird erst eine Schriftart geladen und dann eine Schriftfamilie definiert. Mit @psname und @filename greife ich auf die Attribute in der Datendatei des aktuellen Elements zu. Die geschweiften Klammern sind dann nötig, wenn der Befehl an dieser Stelle eigentlich einen festen Wert erwartet, ich aber einen dynamischen Wert angeben möchte.

Anschließend wird für jeden Font eine Textzeile ausgegeben.

Das Ergebnis

Nach dem Aufruf von sp im aktuellen Verzeichnis wird das PDF erzeugt und sieht so aus:

Die ersten Zeilen des Fontsamplers.

Die Dateien (ohne die Schriftdateien) kann man hier herunter laden.

von Patrick Gundlach |

LuaTeX 1.0.4

Der Publisher nutzt bekanntermaßen LuaTeX als Satzengine. LuaTeX ist eine moderne Variante von TeX, was ja für seine exzellenten Satzeigenschaften bekannt ist. LuaTeX wird in der Installation des Publishers mitgeliefert, so dass sich der Anwender hierüber keine Gedanken machen muss. Auch kommt er in nicht direkt mit TeX in Berührung, daher werden auch keine speziellen TeX-Kenntnisse benötigt, um den Publisher zu bedienen.

Die mitgelieferte Version von LuaTeX ist zwar schon recht alt (0.79.1 von März 2014), hat aber alle Funktionalität, die ich für die PDF-Ausgabe benötige. Ein paar Nachteile gibt es dennoch:

  • Ganz spezielle OpenType Fonts werden nicht unterstützt.
  • Die Version lässt sich nicht einfach mit externen Bibliotheken (z.B. HarfBuzz) verbinden.
  • Neuerungen (z.B. PDF 2.0) lassen sich nicht oder nicht so leicht mit der alten Version realisieren.

Daher habe ich mal ausprobiert, den Publisher mit Version 1.0.4 von LuaTeX zu verwenden. Das ist die Version, die mit dem aktuellen TeXLive ausgeliefert wird.

Im Nachhinein war die Unterstützung doch recht leicht. Ein paar Dinge haben sich aus Sicht des Programmierers geändert:

  • Viele Funktionen aus der tex-Bibliothek sind in die pdf-Bibliothek gewandert.
  • Die Eigenschaften der dehnbaren Leerräume (glue) sind nun direkt im Knoten beschrieben, nicht mehr im Hilfsknoten (der glue_spec-Knoten ist weggefallen).
  • Die Funktion tex.linebreak() erlaubt keine Metatabelle als zweiten Parameter, warum auch immer.

Meine Änderungen sind fast alle in dem Commit 753af8d7 zu finden, der einigermaßen überschaubar ist.

Das sind doch recht kleine Änderungen, mit denen man leicht umgehen kann. Eine Sache ist aber etwas verstörend. Die Leerräume in einer Zeile werden leicht unterschiedlich verteilt.

Das ist eine Testdatei mit LuaTeX 0.79.1

Und dies hier mit LuaTeX Version 1.0.4:

Die Ausgabe scheint identisch zu sein, doch ein paar kleine Unterschiede werden mit einem automatischen PDF-Vergleich sichtbar:

Einige Buchstaben sind um einen absolut minimalen Wert verschoben. Warum das so ist, habe ich noch nicht heraus gefunden. Ich gehe davon aus, dass dem Anwender diese Änderungen nie im Leben auffallen, doch für einen automatischen Test ist das schon wichtig.

Was ändert sich für die Anwender?

Die Kurzantwort: nichts. Ich vermute, dass LuaTeX 1.0.4 (oder eine neuere Version) in der Version 4 des Publishers als Voreinstellung enthalten ist. Bis dahin müssen noch viele Tests durchgeführt werden, damit sich auch wirklich nichts ändert. Die klitzekleinen Änderungen in der Ausgabe sind zwar etwas lästig, aber meines Erachtens nicht relevant.

Ich freue mich schon auf die Fähigkeit, Zusatzbibliotheken leicht einzubinden. Dank FFI kann man auf einen komplizierten Wrapper oftmals verzichten.

von Patrick Gundlach |

Lua Preprocessing: CSV nach XML-Konverter

Neu in Version 3.1.10

Die neue Funktionalität des Lua-Präprozessors habe ich um Schnittstellen zu CSV und XML erweitert. Man kann nun beliebige CSV-Dateien einlesen und nach XML konvertieren, ohne eine Zeile Code zu schreiben1.

CSV nach XML nach PDF

In dem neuen Beispiel (https://github.com/speedata/examples/tree/master/technical/csvreader) ist das ausführlich in einem lauffähigen Layout gezeigt. Grundlage ist eine CSV-Datei. In dem Verzeichnis liegt eine Datei mit ausgedachten Personen:

"First name","Last name","Company name",          ...
"James",     "Butt",     "Benton",                ...
"Josephine", "Darakjy",  "Chanay, Jeffrey A Esq", ...
...

Um eine XML-Datei daraus zu erstellen, muss man als Präprozessor einfach nur das mitgelieferte Skript csv2xml.lua aufrufen. Das geht mit dem Kommandozeilenparameter --filter csv2xml.lua oder mit einem Eintrag in der Konfigurationsdatei publisher.cfg (siehe Beispiel).

Das reicht aus, um die Datendatei zu erstellen. Die Form der Datendatei ist

<data>
  <row>
    <cell>First name</cell>
    <cell>Last name</cell>
    <cell>Company name</cell>
  </row>
  <row>
    <cell>James</cell>
    <cell>Butt</cell>
    <cell>Benton, John B Jr</cell>
  </row>
  <row>
    <cell>Josephine</cell>
    <cell>Darakjy</cell>
    <cell>Chanay, Jeffrey A Esq</cell>
  </row>
  ....

Das, zusammen mit dem sehr einfach gehaltenen Layout, reicht aus, um beliebige CSV-Daten in eine Tabelle in PDF zu konvertieren.

von Patrick Gundlach |

Lua anstelle von XProc

Die neueste Version des Publishers (3.1.9) hat ein kleines, aber feines Feature dazu bekommen: ab sofort ist es möglich, vor dem Rendering Lua-Skripte auszuführen.

Lua

Lua ist eine ganz passable Programmiersprache, ähnlich wie JavaScript, und ideal, um in Programme eingebettet zu werden. Der Publisher selbst ist zu 95% in Lua geschrieben, daher bietet sich die Programmiersprache auch an, um die Startskripte damit zu erstellen.

Aufruf

Mit sp --filter meinprogramm.lua wird also das angegebene Lua-Programm ausgeführt, bevor der eigentliche Rendering-Prozess aufgerufen wird. Das ist übrigens derselbe Befehl, mit dem auch die XProc-Pipelines gestartet werden können. Ob die XProc-Schnittstelle weiter beibehalten wird, weiß ich noch nicht. Ich tendiere dazu, sie abzuschalten (sie wird in der Praxis vermutlich gar nicht benutzt, und wenn, nur um eine Transformation zu starten).

Praxis

Eine übliche Anwendung für einen Pre-Process ist, die Daten von einem Format in ein anderes zu transformieren. In dem Lua Modul runtime steht nun der Befehl run_saxon() zur Verfügung. Eine Vorverarbeitung könnte nun so aussehen:

ok, err = runtime.run_saxon("transformation.xsl","quelldatei.xml","data.xml")

-- den Publishing-Prozess abbrechen, wenn die Transformation fehl schlägt.
if not ok then
    print(err)
    os.exit(-1)
end

(Kommentare werden mit zwei Strichen eingeleitet). Speichern unter transformation.lua und dann den Publisher starten mit

sp --filter transformation.lua

bzw. in die Konfigurationsdatei

filter=transformation.lua

eintragen.

Ebenfalls gibt es in dem Modul runtime die Konstante projectdir, die den Pfad zum aktuellen Projektverzeichnis enthält.

Ausblick

Die Möglichkeiten, die sich durch die Integration mit dem Startprogramm ergibt, sind vielfältig. Man könnte z.B. den XML-Baum durchgehen und Daten abfragen und spontan verändern. Auch ist es denkbar, CSV-Dateien oder Excel-Tabellen in ein XML-Format zu transformieren und so weiter. Hier ist in den nächsten Versionen noch einiges zu erwarten.

Hinweis

Die Implementierung ist noch in einem frühen Stadium. Insofern ist es wahrscheinlich, dass es Änderungen an der API gibt. Ich bitte um Rückmeldung, wenn jemand diese Funktionalität nutzt!

Außerdem kann man noch nicht auf den eigentlichen Rendering-Prozess Einfluss nehmen. Wer weiß, was hier noch kommt.

Und was ist mit XProc?

Oben habe ich ja schon angedeutet, dass die Unterstützung für XProc über kurz oder lang wegfällt. Ich habe die XProc-Version schon länger nicht mehr aktualisiert, was damit zusammenhängt, dass die neuen Distributionen (Calabash, Morgana) um ein vielfaches Größer sind, als die Datei, die derzeit mit dem Publisher mitgeliefert wird. Meine Bereitschaft, die Installationsdatei nur für XProc um ein mehrfaches zu vergrößern, ist nicht so sonderlich hoch.

Ein gravierender Nachteil des nicht-updaten ist, dass die mitgelieferte Version von Saxon uralt ist, und noch kein XSLT 3 unterstützt. Das hat sich nun mit der Lua-Unterstützung geändert.

Insofern vermute ich, dass die Version 4 des Publishers noch XProc enthält, danach aber nicht mehr. Daher stelle ich die Unterstützung von XProc nun auf »deprecated« und hoffe, dass ich damit keine vorhandenen Workflows beeinträchtige.

von Patrick Gundlach |

Kompatibilität mit macOS High Sierra (10.13) / Go

Offensichtlich gab es Probleme mit dem Publisher und MacOS High Sierra (10.13). Das ist nun behoben.

Ich habe das Startprogramm sp in Go geschrieben und erzeuge die Binaries unter Linux. Das funktioniert auch soweit ganz gut, doch die aktuelle Version von MacOS scheint wohl nur mit einer neuen Version von Go unterstützt zu werden.

Auf dem Build-Server habe ich nun die Version 1.9.1 installiert und alles läuft wieder rund.

Go

An dieser Stelle möchte ich nochmal meine Freude zum Ausdruck bringen, wie einfach es ist, mit Go Binaries für andere Plattformen zu erstellen.

package main

import "fmt"

func main() {
	fmt.Println("Hello, world!")
}

Übersetzt wird das Programm mit

go build hello.go

nun wird eine Datei main erzeugt. Um nun zum Beispiel ein Binary für Windows zu kompilieren, reicht es, die Umgebungsvariable GOOS zu setzen:

GOOS=windows go build main.go

Die nun erzeugte Datei main.exe ist tatsächlich unter Windows lauffähig:

$ file main.exe
main.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows

Einfacher kann es meiner Meinung nach nicht sein. Es wird keine weitere Software benötigt (JVM, nodejs, Ruby, Python, …), damit das Programm lauffähig ist.

von Patrick Gundlach |

Veranstaltungshinweise

Ich erlaube mir mal, auf zwei Veranstaltungen hinzuweisen, die im Umfeld des Publishers interessant sein können.

Einmal gibt es die XMLPrague (8. bis 10. Februar 2018), die ich regelmäßig besuche und dort auch Workshops zum Publisher gebe. Es werden Vorträge und ähnliches gesucht, auch gerne kurze Beiträge. Der Weg nach Prag lohnt sich definitiv.

Keine Erfahrung habe ich hingegen mit den ZUGFeRD-Entwicklertagen (18./19. Januar 2018 in Bielefeld), das könnte aber auch ganz interessant sein. Der Publisher kann seit einiger Zeit ZUGFeRD-konforme Rechnungen erzeugen, insofern ist ein Austausch dort vielleicht ganz interessant.

Gerne nehme ich auch weitere Veranstaltungshinweise entgegen.

von Patrick Gundlach |

XML User Group Berlin Reihe »Satzautomatisierung«

Ich erlaube mir einen Hinweis auf die Reihe »Satz-Automation aus XML« der XML User Group Berlin (XUG BER). Nächste Woche starte ich mit einer Veranstaltung zum Publisher, den ich dort vorstelle und Fragen dazu beantworte.

Die Veranstaltung findet statt am Mittwoch, 20. September 2017 18 Uhr bei De Gruyter (Genthiner Str 13, Berlin Tiergarten).

Infos gibt es auf der Meetup-Seite und Fragen könnt ihr mir auch gerne direkt mailen.

von Patrick Gundlach |

RELAX NG Tutorial und Asciidoctor

»Wenn du etwas lernen möchtest, halte einen Vortrag drüber« war ein Tipp eines Freundes.

Nun nutze ich schon seit Jahren die XML Schemasprache RELAX NG, mit RELAX NG ist auch das Schema für das Layoutregelwerk des Publishers definiert, doch ich hatte nie das Gefühl, die Sprache vollständig verstanden zu haben.

Um die Sprache mal endlich ganz zu verstehen, habe ich zwar keinen Vortrag gehalten, aber ein Tutorial geschrieben. Dies ist als HTML und als EPUB verfügbar und die Asciidoctor-Quellen sind auf der Entwicklerseite. Die Lizenz ist CC BY-SA 3.0.

Die Quellen sind, wie geschrieben, in Asciidoctor geschrieben, ein ziemlich schickes Markup-Format, ähnlich wie Markdown. Der Vorteil gegenüber Markdown ist, dass Asciidoctor wesentlich umfangreicher ist und fast keine Wünsche übrig lässt, was die Erstellung eines Handbuches anbelangt. Natürlich ist das auch begrenzt, aber die Grenzen sind viel weiter als bei anderen Markup-Sprachen. Asciidoctor kann man nach HTML (der Default) und DocBook konvertieren. Andere mitgelieferte Konvertierungen sind meines Erachtens unbrauchbar, wenn man auch nur einen minimalen Anspruch an die Typographie hat. Per Zusatzprogramm (Asciidoctor EPUB3) kann man sehr bequem auch EPUB exportieren.

Die Aufrufe (Makefile), Quellen und die Ergebnisse (index.html, relaxng.epub) meines Dokuments liegen im Github-Repository.

Viel Spaß damit und schön weiter weiter erzählen!

von Patrick Gundlach |

PDF und ISO-Normen

Kürzlich ist die Version 2.0 von PDF erschienen. Die PDF-Branche feiert das natürlich mit zahlreichen Artikeln rund um die Neuerungen bei dem neuen Standard, siehe z.B. die Seite bei pdflib.com. Grund zur Freude? Sicherlich, aber einen etwas schalen Geschmack hat die Sache meiner Meinung nach.

PDF und freie Standards

Früher™, in den guten alten Zeiten, war es so, dass die Spezifikation von PDF frei verfügbar war. Es gab zwar ein paar Startschwierigkeiten, aber das ist laaaange her. Seitdem ist die Spezifikation als großes PDF-Dokument auf der Homepage von Adobe herunterladbar und als Buch erschienen. Damit konnte jedermann und jederfrau einen eigenen PDF-Parser oder einen eigenen PDF-Writer schreiben. Nicht zuletzt solche großen Erfolge wie PDFTeX sind daraus entstanden.

Ähnlich wie das Web (HTML + CSS), das durch die W3C spezifiziert wird (als sogenannte Recommendations, aber das ist nur ein anderer Name) ermöglicht die freie Verfügbarkeit der Spezifikation eine große Verbreitung, speziell auch durch OpenSource Entwickler. Niemand muss viel Geld auf den Tisch legen und wird durch Urheberrechte daran gehindert, die Stellen aus einer Spezifikation zu teilen.

PDF-Entwickler nun ein elitärer Kreis?

Inzwischen sind die meisten Erweiterungen und Festlegungen zum Thema PDF nur noch als ISO-Standard verfügbar. Die Wikipedia-Seite zu PDF/X z.B. listet einige relevante Standards auf:

Bei anderen Themen (PDF/A, …) ist es nicht anders.

Damit ist eine Community von freiwilligen Mitarbeitern quasi aus dem Entwicklungsprozess für PDF ausgeschlossen. Natürlich liegt hier und dort mal eine ungeschützte Version zum Download bereit oder jemand beschreibt, was er oder sie in einer Spezifikation gelesen hat, das ist aber keine Basis, um OpenSource Software zu entwickeln. Ich finde diese Entwicklung sehr bedauernswert.

Dazu passend fällt mir ein Zitat aus dem Titanic Magazin ein

»PDF – Die Datei der Besserverdienenden«

von Patrick Gundlach |