DVI-/PS-/PDF-Workflow

Dasselbe einseitige LaTeX-Dokument zweimal übersetzt: Das DVI hat 956 Bytes, das PostScript, das dvips daraus macht, 169.769 – Faktor 178. Kein einziges Byte dieses Zuwachses ist neue Information. Der Unterschied besteht allein darin, dass das DVI neun Schriften nennt, während das PostScript alle neun enthält. Diese eine Tatsache erklärt fast alles: was ein DVI ist, warum PostScript zwei Jahrzehnte lang die Zwischenstation war und worin sich die drei Wege zum PDF heute unterscheiden.

Was wirklich in einer DVI-Datei steckt: Boxen und Schriftnamen

Ein DVI enthält nur Positionen und Schriftverweise – keine einzige Buchstabenform. Durch dvitype, das mit TeX Live kommt, lässt sich das direkt ablesen. fntdef1 27: cmr10 ist bloß die Erklärung, dass Schrift Nummer 27 cmr10 sein soll; setchar49 bloß die Anweisung, Zeichen 49 der gerade gewählten Schrift hierhin zu setzen. Zeichen 49 ist die Ziffer 1. Die dazwischenliegenden push, down4 und right3 verschieben den aktuellen Punkt. Ein DVI ist also eine Liste von Anweisungen der Form „setze hier das Zeichen mit dieser Nummer aus jener benannten Schrift“ – und wie diese Zeichen aussehen, weiß es nicht.

terminal
dvitype -output-level=4 -page-start='*' doc.dvi

# numerator/denominator=25400000/473628672
# magnification=1000;       0.00006334 pixels per DVI unit
# Postamble starts at byte 723.
# Font 27: cmr10---loaded at size 655360 DVI units
# Font 37: cmbx12 scaled 1200---loaded at size 943718 DVI units
#  (this font is magnified 120%)
# 145: fntdef1 37: cmbx12
# 167: fntnum37 current font is cmbx12
# 168: setchar49 h:=4063232+530841=4594073, hh:=291

Das erklärt zugleich, warum ein DVI klein ist und warum es sich allein nicht betrachten lässt. Ein strings über diese 956 Bytes zeigt nur die Wörter des Textes und eine Liste von Schriftnamen: cmr10, cmmi7, cmsy10, cmex10 und so fort. Wer ein DVI bekommt, kann die Seite also nur reproduzieren, wenn seine Maschine Schriften dieser Namen findet. Genau diese Arbeitsteilung war in den 1980er Jahren beabsichtigt: bei einer geräteunabhängigen Zwischenform haltmachen und das Anpassen an echtes Papier oder einen echten Bildschirm einem nachgelagerten Treiber überlassen. Der Name DVI ist die Entwurfsentscheidung.

Im DVI enthalten oder nichtBedeutung
setchar / puteine Zeichennummer; nie die Zeichenform selbst
fntdef / fntnumeine Schrift, nur über Name, Entwurfsgröße und Prüfsumme benannt
push / pop / down / rightdie Positionierung der Boxen – das gesetzte Ergebnis selbst
xxx (\special)die Notluke für alles, was DVI nicht abbildet; der Treiber entscheidet
glyph outlinesnicht vorhanden. Darum zeigt ein DVI ohne die Schriften nichts an
color, paper sizenicht im DVI selbst; beides reist als \special-Anweisung

Warum ein DVI alles in 1/65536 Punkt misst

Die Antwort steht gleich am Kopf der dvitype-Ausgabe. In numerator/denominator=25400000/473628672 ist der Nenner 473628672 gleich 65536 × 7227, und 7227 Punkt sind genau 100 Zoll. Eine DVI-Einheit ist also 1/65536 Punkt – dieselbe ganzzahlige Einheit, die TeX intern benutzt: der scaled point, sp. Deshalb ist cmr10---loaded at size 655360 DVI units oben nichts anderes als 10 × 65536, also 10pt, und deshalb meldet cmbx12 scaled 1200 den Wert 943718: 12pt × 1,2 = 14,4pt, multipliziert mit 65536. TeX setzt die Seite allein mit diesen ganzen Zahlen, ganz ohne Gleitkomma – genau darum liefert dieselbe Quelle auf jeder Maschine dasselbe Ergebnis, auf 1 sp genau.

Entworfen wurde das Format 1979 von David R. Fuchs, einem Studenten Knuths; die Spezifikation erschien im Oktober 1982 als „The format of TeX’s DVI files“ in TUGboat, Band 3, Nummer 2. Über vierzig Jahre später ist das erste Byte von doc.dvi immer noch 247 – der pre-Opcode – und das zweite immer noch 2, die DVI-Kennnummer. Die Kompatibilität hielt, weil das Format nichts über das Gerät annahm: Die Ausgabemaschinen, für die es geschrieben wurde, sind längst verschwunden; geblieben ist genau der Teil, der nichts annahm.

Warum PostScript die Zwischenstation war: ein Blick in ein \special

Der Preis der Geräteunabhängigkeit ist, dass DVI weder Farbe noch Bilder noch Drehungen ausdrücken kann. Fuchs ließ deshalb genau eine Notluke offen: \special{...}, dessen Inhalt nie interpretiert, sondern unverändert an den Treiber gereicht wird. Und das Erste, was diese Luke füllte, war PostScript. \usepackage{graphicx} laden, \rotatebox{30}{rotated} schreiben, mit latex ein DVI bauen und hineinsehen: Da liegt rohes PostScript in der Datei. Das DVI weiß nicht einmal, dass es PostScript ist. Es transportiert nur eine Zeichenkette.

terminal
# what latex actually wrote into the DVI for \rotatebox and \textcolor
dvitype -output-level=4 -page-start='*' spec.dvi | grep xxx

#  88: xxx 'header=l3backend-dvips.pro'
# 116: xxx 'papersize=614.295pt,794.96999pt'
# 247: xxx 'color push rgb 1 0 0'
# 347: xxx 'ps: gsave currentpoint currentpoint translate
#            30 neg rotate neg exch neg exch translate'
# 449: xxx 'ps: currentpoint grestore moveto'

Von da an war es eine Einbahnstraße. Ist die Notluke eines DVI mit PostScript gefüllt, kann nur noch ein Gerät die Interpretation zu Ende führen, das PostScript versteht. Als Mitte der 1980er Jahre Laserdrucker mit Adobes PostScript zum faktischen Standard wurden, setzte sich der Weg über dvips durch – DVI, übersetzt nach PostScript. Beim Übersetzen bettet dvips die Schriften ein: Die erzeugte .ps beginnt mit der Zeile %%DocumentFonts: CMBX12 CMR10 CMEX10 CMSY7 CMR7 CMMI10 CMMI7 CMR5 CMSY10, und im Rumpf folgt für jede dieser neun ein %%BeginFont:-Block. Die 956 und die 169.769 Bytes vom Anfang dieser Seite unterscheiden sich genau um diese neun Schriften.

PostScript hat die Bühne verlassen, doch die Fingerabdrücke sind überall. Die ausführbare Datei dvipdfmx, die von DVI direkt zu PDF geht, enthält einen kleinen Interpreter für eingebettetes PostScript, samt der Diagnose Stack not empty after execution of inline PostScript code. Genau darum wird das obige ps:-Special auch auf diesem Weg als echte Drehung umgesetzt. Ihr --help führt außerdem -D template PS->PDF conversion command line template [none] auf – die Schnittstelle, um nicht zu bewältigendes PostScript an ein externes Programm wie Ghostscript weiterzureichen. Und die .xbb-Datei, die die Bildgröße festhält, lautet %%BoundingBox: 0 0 8 8 – PostScripts eigene Kommentarkonvention, weiterhin im Dienst.

Die drei Wege heute und was jeder kostet

Westlicher Text: direkt zum PDF mit pdflatex und Verwandten. Japanisch: (u)platex, dann dvipdfmx. Ein Stapel PSTricks oder altes EPS: latex, dann dvips, dann ps2pdf. Damit ist die praktische Entscheidung erschöpft. Nicht zutreffend ist dagegen, dass dasselbe Dokument dasselbe PDF ergibt. Das Dokument vom Anfang dieser Seite ergab auf allen drei Wegen direct.pdf mit 85.509 Bytes, viadvi.pdf mit 14.693 und viaps.pdf mit 17.851. Ein Blick mit pdffonts erklärt es sofort: pdfTeX bettet die Schriften als Type 1 ein, dvipdfmx und Ghostscript wandeln sie zuvor in die kompakte Form Type 1C um. Die Seiten sehen gleich aus; die Dateien unterscheiden sich um das Sechsfache.

WegDurchlaufene FormateWo er gewinnt
pdflatex / lualatex.tex → PDFein Schritt; Standard für westliche Texte
xelatex.tex → XDV → PDFSystemschriften; der XDV-Schritt ist nur verborgen
dvipdfmx.tex → DVI → PDFStandard für Japanisch ((u)pLaTeX); setzt PNG und JPEG direkt
dvips + ps2pdf.tex → DVI → PS → PDFPSTricks, EPS-Bestände, Abgaben, die PostScript voraussetzen
terminal
# 1. straight to PDF
pdflatex doc.tex

# 2. via DVI (the Japanese route)
uplatex doc.tex && dvipdfmx doc
#   doc.dvi -> doc.pdf
#   [1]
#   14692 bytes written

# 3. via PostScript
latex doc.tex && dvips doc -o doc.ps && ps2pdf doc.ps

# in practice latexmk drives all three for you

Überspringt XeLaTeX das DVI wirklich?

Nein. xelatex -no-pdf doc.tex meldet Output written on doc.xdv (1 page, 2476 bytes). und lässt doc.xdv zurück. Dessen erstes Byte ist 247 – exakt derselbe pre-Opcode wie in doc.dvi. Nur das folgende Byte weicht ab: 2 beim DVI, 7 beim XDV. XeTeX schreibt also erweitertes DVI, und was daraus ein PDF macht, ist xdvipdfmx. Die mit TeX Live gelieferte README sagt es selbst: „In the installation, dvipdfmx is a symlink to xdvipdfmx.“ – und tatsächlich verweisen dvipdfmx und extractbb auf dieselbe ausführbare Datei. „XeLaTeX erzeugt PDF direkt“ heißt: Die DVI-Stufe ist verborgen, nicht abgeschafft.

terminal
xelatex -no-pdf doc.tex
# Output written on doc.xdv (1 page, 2476 bytes).

# same container, different id byte:
#   doc.dvi   byte 0 = 247 (pre)   byte 1 = 2
#   doc.xdv   byte 0 = 247 (pre)   byte 1 = 7

xdvipdfmx doc.xdv          # this is what xelatex runs for you

Wenn Cannot determine size of graphic ... (no BoundingBox) erscheint

Das ist die erste Wand für alle, die auf dem DVI-Weg ein PNG oder JPEG einbinden. Die Meldung lautet vollständig ! LaTeX Error: Cannot determine size of graphic in sample.png (no BoundingBox)., und die Ursache liegt weder beim Bild noch bei dvipdfmx, sondern bei der Treiberoption von graphicx. latex erzeugt kein PDF und kann Bildmaße daher nicht selbst lesen; der voreingestellte dvips-Treiber liest allein eine PostScript-Zeile %%BoundingBox, und ein PNG-Header ist keine. Es gibt zwei Abhilfen: den Treiber benennen, \usepackage[dvipdfmx]{graphicx}, oder zuvor extractbb sample.png laufen lassen, sodass eine .xbb-Datei mit %%BoundingBox: 0 0 8 8 bereitliegt. Dass pdflatex diesen Fehler nie zeigt, liegt schlicht daran, dass pdfTeX selbst einen PNG-Header lesen kann.

terminal
# ! LaTeX Error: Cannot determine size of graphic in sample.png (no BoundingBox).

# fix 1 - name the driver in the preamble:
#   \usepackage[dvipdfmx]{graphicx}

# fix 2 - write the bounding box out first:
extractbb sample.png
cat sample.xbb
#   %%Title: sample.png
#   %%Creator: extractbb 20240305
#   %%BoundingBox: 0 0 8 8
#   %%HiResBoundingBox: 0.000000 0.000000 8.000000 8.000000

Die Reihenfolge der Entscheidung lautet also: nach Sprache und Paketen wählen, nicht nach Ausgabeformat – Japanisch heißt (u)platex und dvipdfmx, PSTricks heißt PostScript-Weg, alles Übrige heißt direkt zum PDF. Dann die Bildformate angleichen – auf dem DVI-Weg PDF oder EPS, oder PNG und JPEG, die durch extractbb gegangen sind. Dann den Treiber ausdrücklich nennen: [dvipdfmx] auf graphicx wie auf hyperref, damit niemand, der das Dokument später auf einem anderen Weg baut, in dieselbe Grube fällt. Das DVI selbst weiß derweil noch immer nicht, welche Farbe und welches Bild es transportiert.