Flux DVI / PS / PDF

Compilez deux fois le même document LaTeX d'une page : le DVI pèse 956 octets, le PostScript que dvips en tire, 169 769 — un facteur 178. Pas un seul octet de cet écart n'est une information nouvelle. La seule différence tient à ceci : le DVI nomme neuf fontes, le PostScript les contient toutes les neuf. Ce fait unique explique presque tout : ce qu'est un DVI, pourquoi PostScript est resté vingt ans l'étape intermédiaire, et en quoi les trois chemins vers le PDF diffèrent aujourd'hui.

Ce qu'il y a vraiment dans un fichier DVI : des boîtes et des noms de fontes

Un DVI ne contient que des positions et des références de fontes — pas une seule forme de lettre. Passez-le à dvitype, livré avec TeX Live, et cela se lit directement. fntdef1 27: cmr10 n'est qu'une déclaration : la fonte numéro 27 sera cmr10 ; setchar49 n'est qu'une instruction : poser ici le caractère 49 de la fonte courante. Le caractère 49, c'est le chiffre 1. Les push, down4 et right3 intercalés déplacent le point courant. Un DVI est donc une liste d'ordres du type « place ici tel numéro de caractère pris dans telle fonte nommée » — et il ignore totalement à quoi ressemblent ces caractères.

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

C'est à la fois pourquoi un DVI est petit et pourquoi un DVI ne se regarde pas tout seul. Lancez strings sur ces 956 octets : tout ce qui se lit, ce sont les mots du texte et une liste de noms de fontes — cmr10, cmmi7, cmsy10, cmex10 et les autres. Celui à qui vous remettez un DVI ne peut donc reproduire la page que si sa machine trouve des fontes portant ces noms. Cette division du travail était précisément le but dans les années 1980 : s'arrêter à une forme intermédiaire indépendante du périphérique, et laisser à un pilote situé plus loin le soin de s'ajuster au papier ou à l'écran réels. Le nom DVI est la décision de conception.

Présent ou non dans un DVISignification
setchar / putun numéro de caractère ; jamais la forme de la lettre
fntdef / fntnumune fonte désignée seulement par nom, taille de dessin et somme de contrôle
push / pop / down / rightle positionnement des boîtes : le résultat composé lui-même
xxx (\special)l'échappatoire pour ce que DVI ne modélise pas ; le pilote décide
glyph outlinesabsentes. D'où l'impossibilité d'afficher un DVI sans les fontes
color, paper sizeabsents du DVI proprement dit ; ils voyagent en \special

Pourquoi un DVI mesure tout en 1/65536 de point

La réponse figure en tête de la sortie de dvitype. Dans numerator/denominator=25400000/473628672, le dénominateur 473628672 vaut 65536 × 7227, et 7227 points font exactement 100 pouces. Une unité DVI vaut donc 1/65536 de point — la même unité entière que TeX emploie en interne : le scaled point, sp. Voilà pourquoi cmr10---loaded at size 655360 DVI units plus haut n'est rien d'autre que 10 × 65536, soit 10pt, et pourquoi cmbx12 scaled 1200 annonce 943718 : 12pt × 1,2 = 14,4pt multiplié par 65536. TeX compose la page avec ces seuls entiers, sans le moindre flottant — c'est exactement pour cela que la même source donne le même résultat au sp près sur n'importe quelle machine.

Le format a été conçu en 1979 par David R. Fuchs, étudiant de Knuth ; la spécification a paru sous le titre « The format of TeX’s DVI files » dans TUGboat, volume 3, numéro 2, en octobre 1982. Plus de quarante ans après, le premier octet de doc.dvi vaut toujours 247 — l'opcode pre — et le deuxième toujours 2, le numéro d'identification DVI. La compatibilité a tenu parce que le format ne supposait rien du périphérique : les machines de sortie pour lesquelles il fut écrit ont disparu depuis longtemps, et seule la partie qui ne supposait rien est encore debout.

Pourquoi PostScript est resté au milieu : regardez dans un \special

Le prix de l'indépendance vis-à-vis du périphérique, c'est que DVI ne sait exprimer ni couleur, ni image, ni rotation. Fuchs n'a donc laissé qu'une seule échappatoire : \special{...}, dont le contenu n'est jamais interprété mais transmis tel quel au pilote. Et la première chose à remplir cette trappe fut PostScript. Chargez \usepackage{graphicx}, écrivez \rotatebox{30}{rotated}, fabriquez un DVI avec latex et regardez dedans : du PostScript brut y est déposé. Le DVI ne sait même pas que c'est du PostScript. Il ne fait que transporter une chaîne de caractères.

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'

À partir de là, ce fut une voie à sens unique. Dès lors que l'échappatoire d'un DVI est remplie de PostScript, seule une machine comprenant PostScript peut achever l'interprétation. Quand, au milieu des années 1980, les imprimantes laser équipées du PostScript d'Adobe devinrent le standard de fait, la route passant par dvips — le DVI traduit en PostScript — s'imposa. Et en traduisant, dvips incorpore les fontes : le .ps produit s'ouvre sur la ligne %%DocumentFonts: CMBX12 CMR10 CMEX10 CMSY7 CMR7 CMMI10 CMMI7 CMR5 CMSY10, et le corps porte ensuite un bloc %%BeginFont: pour chacune de ces neuf fontes. Les 956 octets et les 169 769 octets du début de cette page diffèrent exactement de ces neuf fontes.

PostScript a quitté la scène, mais ses empreintes sont partout. L'exécutable dvipdfmx, qui va du DVI directement au PDF, embarque un petit interpréteur pour exécuter du PostScript en ligne, avec jusqu'au diagnostic Stack not empty after execution of inline PostScript code. C'est pourquoi le ps: ci-dessus est bel et bien rendu comme une rotation sur cette route. Son --help mentionne aussi -D template PS->PDF conversion command line template [none], le crochet permettant de confier à un programme externe comme Ghostscript le PostScript qu'il ne sait pas traiter. Et le fichier .xbb qui enregistre la taille d'une image se lit %%BoundingBox: 0 0 8 8 — la convention de commentaires de PostScript elle-même, toujours en service.

Les trois chemins actuels, et ce que chacun coûte

Texte occidental : PDF direct avec pdflatex et compagnie. Japonais : (u)platex puis dvipdfmx. Une pile de PSTricks ou d'EPS anciens : latex, puis dvips, puis ps2pdf. Voilà toute la décision pratique. Ce qui est faux, en revanche, c'est que le même document donne le même PDF. Le document du haut de cette page, passé par les trois routes, a donné direct.pdf à 85 509 octets, viadvi.pdf à 14 693 et viaps.pdf à 17 851. Un coup d'œil avec pdffonts l'explique aussitôt : pdfTeX incorpore les fontes en Type 1, tandis que dvipdfmx et Ghostscript les convertissent d'abord dans la forme compacte Type 1C. Les pages sont identiques ; les fichiers varient du simple au sextuple.

CheminFormats traversésOù il gagne
pdflatex / lualatex.tex → PDFune seule étape ; le défaut pour le texte occidental
xelatex.tex → XDV → PDFfontes système ; l’étape XDV est seulement cachée
dvipdfmx.tex → DVI → PDFla référence pour le japonais ((u)pLaTeX) ; place PNG et JPEG directement
dvips + ps2pdf.tex → DVI → PS → PDFPSTricks, un fonds d’EPS, une remise qui présuppose PostScript
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

XeLaTeX saute-t-il vraiment le DVI ?

Non. xelatex -no-pdf doc.tex affiche Output written on doc.xdv (1 page, 2476 bytes). et laisse un doc.xdv. Son premier octet vaut 247, exactement le même opcode pre que dans doc.dvi. Seul l'octet suivant change : 2 pour le DVI, 7 pour le XDV. Ce que XeTeX écrit, c'est du DVI étendu, et ce qui en fait un PDF, c'est xdvipdfmx. Le README livré avec TeX Live le dit lui-même : « In the installation, dvipdfmx is a symlink to xdvipdfmx. » — et de fait, dvipdfmx et extractbb pointent vers le même exécutable. Dire que XeLaTeX « produit directement un PDF » signifie que l'étape DVI est cachée, pas qu'elle a disparu.

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

Quand survient Cannot determine size of graphic ... (no BoundingBox)

C'est le premier mur que rencontre quiconque place un PNG ou un JPEG sur la route DVI. Le message complet est ! LaTeX Error: Cannot determine size of graphic in sample.png (no BoundingBox)., et la cause n'est ni l'image ni dvipdfmx, mais l'option de pilote de graphicx. latex ne produit pas de PDF et n'a donc aucun moyen de lire lui-même les dimensions ; le pilote dvips par défaut ne sait lire qu'une ligne PostScript %%BoundingBox, et l'en-tête d'un PNG n'en est pas une. Deux remèdes : nommer le pilote, \usepackage[dvipdfmx]{graphicx}, ou lancer d'abord extractbb sample.png pour disposer d'un fichier .xbb portant %%BoundingBox: 0 0 8 8. Si pdflatex n'affiche jamais cette erreur, c'est simplement que pdfTeX sait lire un en-tête PNG.

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

L'ordre de décision, donc. Choisir d'après la langue et les packages, non d'après le format de sortie : japonais signifie (u)platex puis dvipdfmx, PSTricks signifie la route PostScript, tout le reste signifie PDF direct. Puis harmoniser les formats d'image : sur la route DVI, PDF ou EPS, ou bien des PNG et JPEG passés par extractbb. Puis nommer le pilote explicitement : écrivez [dvipdfmx] sur graphicx comme sur hyperref, et celui qui rebâtira le document par une autre route plus tard ne tombera pas dans le même trou. Le DVI, lui, continue d'ignorer quelle couleur et quelle image il transporte.