XeTeX

Des trois moteurs modernes, XeTeX est le seul à ne pas écrire de PDF. La composition achevée, il émet un flux d'octets dans un format nommé .xdv (extended DVI), qu'un programme distinct, xdvipdfmx, transforme en PDF. Vu de l'extérieur, un seul xelatex document.tex fait tout ; à l'intérieur, il y a deux étapes. Le savoir change la façon de lire le journal le jour où une image ou une police incorporée pose problème. Cette page traite de ce que XeTeX a apporté à LaTeX — lire l'UTF-8 tel quel et nommer, via fontspec, les polices déjà installées sur le système —, de ce qu'est réellement un .xdv, et de ce qui casse effectivement lorsqu'un manuscrit pdfLaTeX passe à XeLaTeX.

Qui a écrit XeTeX, et pour quoi faire

Tout commence par le travail de Jonathan Kew chez SIL International ; la première version publique paraît en avril 2004, pour Mac OS X uniquement. SIL travaille sur les écritures et orthographes des langues minoritaires du monde entier : l'exigence était donc là dès le départ — composer de l'Unicode, avec les polices réellement conçues pour ces langues. Le premier XeTeX s'appuyait en conséquence sur la technologie de composition du Mac de l'époque, AAT (Apple Advanced Typography). Un portage Linux suit en 2006, Windows peu après, et à partir de TeX Live 2007 il est livré sur toutes les plateformes. Sous TeX Live 2024, xetex --version affiche une ligne de copyright mentionnant SIL International, Jonathan Kew and Khaled Hosny, puis énumère les bibliothèques liées : ICU 74.2, HarfBuzz 8.3.0, Graphite2 1.3.14, FreeType2 2.13.2 — et, sur macOS, les frameworks Core Text et Cocoa. La meilleure façon de comprendre XeTeX est d'y voir la couche qui raccorde TeX à ce que livrent réellement le système d'exploitation et l'industrie typographique.

Le numéro de version raconte lui aussi une petite histoire. xetex --version renvoie XeTeX 3.141592653-2.6-0.999996 (TeX Live 2024). Le 3.141592653 initial est le TeX de Knuth : à chaque correction de bogue il ajoute une décimale, de sorte que le nombre tend vers π, et il a écrit que l'ultime changement, à effectuer après sa mort, fixera la version à π exactement, moment où tous les bogues restants deviendront des fonctionnalités. pdfTeX et LuaTeX portent les mêmes chiffres : les trois moteurs partagent ce préfixe. Le 0.999996 final appartient à XeTeX ; le fichier NEWS livré avec montre qu'il avance d'un cran chaque février, de 0.999991 en 2019 à 0.999996 en février 2024 — toujours plus de neuf, toujours juste en deçà de 1. Aucun document n'annonce d'intention de converger vers 1 : c'est une observation, non une règle.

Nommer une police du système : fontspec et \setmainfont

Charger \usepackage{fontspec}, écrire \setmainfont{Helvetica Neue} avec le nom que connaît le système d'exploitation, et c'est tout. Aucun TFM à fabriquer, aucune ligne à ajouter dans un fichier map. Compilez cela avec xelatex et pdffonts confirme que HelveticaNeue est incorporée en CID TrueType. Les fontes de labeur, sans empattement et à chasse fixe s'appellent \setmainfont, \setsansfont et \setmonofont ; pour lever une famille distincte destinée aux titres, \newfontfamily\headingfont{...}. Les polices livrées avec TeX Live, en revanche, relèvent d'un autre régime, et c'est là que la plupart butent d'abord. La section suivante y est consacrée.

latex
\documentclass{article}
\usepackage{fontspec}   % no inputenc, no fontenc needed
% An OS font is named the way the system knows it, e.g.
%   \setmainfont{Helvetica Neue}
% A font that ships with TeX Live is safest named by file name:
\setmainfont{texgyretermes-regular.otf}[
  BoldFont       = texgyretermes-bold.otf,
  ItalicFont     = texgyretermes-italic.otf,
  BoldItalicFont = texgyretermes-bolditalic.otf,
]
\setsansfont{texgyreheros-regular.otf}
\setmonofont{texgyrecursor-regular.otf}
\newfontfamily\headingfont{texgyreadventor-regular.otf}
\begin{document}
Unicode goes in literally: naïve, Straße, ¿cómo?, œuvre.
\textbf{Bold} and \textit{italic} come from the files named above.
\[ E = mc^2 \]
\end{document}

L'essentiel est ce qu'on n'écrit pas : ni inputenc, ni fontenc. L'entrée UTF-8 et les polices Unicode sont la norme, si bien que les formules d'encodage de l'ère pdfLaTeX disparaissent entièrement. Lorsqu'une police reste introuvable, le message est ! Package fontspec Error: The font "..." cannot be found. — et le premier suspect n'est pas TeX mais la résolution des noms du côté du système. Les réglages fins — Ligatures, Numbers=OldStyle, SmallCapsFeatures, RawFeature pour transmettre des balises OpenType brutes, et Renderer pour choisir le moteur de rendu (HarfBuzz / AAT / Graphite) — relèvent de fontspec et sont traités sur sa propre page. Pour porter les polices Unicode jusque dans les formules, on y adjoint unicode-math.

The font "..." cannot be found. — quand une police livrée ne répond pas à son nom

XeTeX interroge la base de polices du système d'exploitation, pas les répertoires de TeX Live. C'est là que la plupart butent d'abord. Sur une machine macOS où TeX Live 2024 est installé tel quel, \setmainfont{Helvetica Neue} fonctionne, tandis que \setmainfont{TeX Gyre Termes}, \setmainfont{Latin Modern Roman} et \setmainfont{TeX Gyre Pagella} échouent tous sur ! Package fontspec Error: The font "..." cannot be found. — et tous trois passent sous LuaLaTeX. La différence tient à la manière de chercher : le luaotfload de LuaTeX parcourt lui-même l'arborescence TeX et en construit un index, alors que XeTeX s'adresse à la machinerie de polices du système — Core Text sous macOS —, de sorte qu'une police livrée par la distribution et jamais déclarée au système reste introuvable par son nom.

Le remède est simple : désigner les polices livrées par leur nom de fichier. \setmainfont{texgyretermes-regular.otf} fonctionne — et la casse compte, si bien que TeXGyreTermes-Regular.otf échoue. Nommer ainsi un fichier fait perdre l'appariement automatique du gras et de l'italique : il faut donc déclarer explicitement BoldFont, ItalicFont et BoldItalicFont. L'exemple ci-dessus est écrit sous cette forme et se compile aussi bien avec xelatex qu'avec lualatex, sans une erreur ni un caractère manquant. On peut aussi enregistrer les polices de la distribution auprès du système (la configuration texlive-fontconfig sous Linux, par exemple), mais cela varie d'une machine à l'autre et ne se reproduira pas chez un collaborateur ; pour un manuscrit, les noms de fichiers restent plus sûrs. Quand quelqu'un signale que XeLaTeX ne trouve pas une police, faites-lui d'abord essayer le nom de fichier.

Les polices japonaises font apparaître la même règle par l'autre bout. Un nom que le système d'exploitation connaît sera résolu ; un nom que seul TeX Live connaît ne le sera pas. Sur cette machine macOS équipée de TeX Live 2024, \setmainfont{Hiragino Mincho ProN}, \setmainfont{Hiragino Sans} et \setmainfont{YuMincho} se compilent tous sous XeLaTeX, car ce sont exactement les noms qu'affiche le panneau de polices du système. \setmainfont{Noto Sans JP}, en revanche, échoue avec cannot be found tant que la police n'a pas été installée au niveau du système. Le test est donc simple : regarder si la police figure dans la liste de polices du système. Si elle y figure, XeTeX la trouvera ; sinon, sa présence dans TeX Live ne rendra pas le nom utilisable. Pour un manuscrit japonais exigeant l'écriture verticale ou des règles de césure strictes, luatexja ou upLaTeX reste plus commode que xeCJK.

Ce qu'est un .xdv, et pourquoi XeTeX n'écrit pas le PDF lui-même

Un .xdv, c'est du DVI étendu. Lancez xelatex -no-pdf et vous en tenez un : ses premiers octets sont f7 07 — la commande DVI pre suivie d'un identifiant de format 7 et non du 2 du DVI standard. À l'intérieur, après un commentaire XeTeX output 2026.08.13:0452, les polices employées sont inscrites sous forme de chemins de fichiers (lmroman10-regular.otf, par exemple). Un .xdv sait donc déjà quel glyphe de quel fichier de police va à quelle coordonnée — mais la police elle-même n'y est pas encore incorporée. Reste à ouvrir ces fichiers, à en extraire un sous-ensemble et à les empaqueter dans un PDF : c'est précisément le travail de xdvipdfmx.

Une exécution normale de xelatex ne laisse aucun .xdv sur le disque, car XeTeX envoie ces octets dans l'entrée standard du pilote par un tube. L'option -output-driver=CMD permet de substituer le pilote : en la pointant vers un script qui se contente de faire cat, on attrape le flux au passage — 820 octets dans l'essai ci-dessus, terminés par la queue traditionnelle du DVI, df df df df. Deux conséquences pratiques en découlent. D'abord, lorsqu'un package comme graphicx demande quel pilote est utilisé, la réponse est xetex, c'est-à-dire xdvipdfmx. Ensuite, il faut déterminer de laquelle des deux étapes provient une erreur. Vous avez tapé xelatex, mais un échec sur une image ou une incorporation de police remonte sous forme de ligne xdvipdfmx. Lire le journal en deux moitiés — la première erreur TeX, la dernière ligne du pilote — est la voie la plus rapide.

shell
# Stop after the first stage and keep the intermediate XDV file.
$ xelatex -no-pdf document.tex
Output written on document.xdv (1 page, 820 bytes).

# The second byte is the DVI format id: 7 for XDV, 2 for plain DVI.
$ xxd document.xdv | head -2
00000000: f707 0183 92c0 1c3b 0000 0000 03e8 1d20  .......;.......
00000010: 5865 5465 5820 6f75 7470 7574 2032 3032  XeTeX output 202

# Run the second stage by hand. xdvipdfmx has been the default
# driver on every platform since XeTeX 0.997.
$ xdvipdfmx document.xdv
document.xdv -> document.pdf

# The driver is a replaceable external command.
$ xelatex -output-driver="/path/to/save-stdin.sh" document.tex

Ce qui casse en passant un document de pdfLaTeX à XeLaTeX

Le cas dangereux est précisément celui qui ne lève aucune erreur. Donnez un vieux préambule tel quel à xelatex : la plupart du temps, il compile. \usepackage[utf8]{inputenc} est ignoré avec un seul avertissement, Package inputenc Warning: inputenc package ignored with utf8 based engines., ce qui, isolément, ne fait aucun mal. L'ennui vient du couple \usepackage[T1]{fontenc} et \usepackage{lmodern}. Si ces deux lignes restent, XeLaTeX demeure sur le chemin 8 bits du NFSS : ce qui est incorporé est une police TFM nommée ec-lmr10, que pdffonts signale comme Type 1C. Ce n'est pas le même objet que le CID Type 0C obtenu via fontspec — autrement dit, le changement de moteur n'a presque rien apporté.

Une seule ligne de journal distingue les deux cas. Avec [T1]{fontenc} encore chargé, écrire donne Missing character: There is no 日 ("65E5) in font ec-lmr10! — le point de code en hexadécimal à la mode TeX, "65E5, et un nom de police 8 bits. Dans le même document passé à fontspec, on lit Missing character: There is no 日 (U+65E5) in font [lmroman10-regular]...la notation devient U+65E5 et le nom de police devient un fichier OpenType. Si vous voyez la première forme, vous êtes retombé sur le chemin 8 bits. Il en va de même des diacritiques combinants (ec-lmr10 ne connaît pas U+0301 ; la voie OpenType les compose sans broncher). La bonne migration consiste à supprimer d'un coup toute la famille des déclarations de polices héritéesinputenc, fontenc, lmodern, times et consorts — et à faire de fontspec l'unique source de sélection.

Ligne de l'ancien préambuleComportement sous XeLaTeXQue faire
\usepackage[utf8]{inputenc}ignoré, avec une ligne d'avertissementla supprimer
\usepackage[T1]{fontenc}maintient silencieusement sur le chemin 8 bits du NFSSremplacer par fontspec
\usepackage{lmodern}incorpore ec-lmr10 (Type 1C)utiliser \setmainfont{lmroman10-regular.otf}
microtypeprotrusion seule ; pas de dilatationle charger tel quel
babelfonctionne, mais faible pour le RTL et les écritures complexesenvisager polyglossia

Pourquoi la dilatation des glyphes ne fonctionne pas sous XeLaTeX

Parce que le moteur n'a pas la fonctionnalité. Des deux piliers de microtype, XeTeX n'implémente que la protrusion. En sondant les primitives une à une, on constate que XeTeX possède \lpcode et \rpcode, qui fixent le débord par caractère, mais pas \efcode, qui fixe l'amplitude d'étirement. Même le commutateur de protrusion n'est pas le \pdfprotrudechars de pdfTeX mais un \XeTeXprotrudechars au nom distinct. Chargez \usepackage{microtype} sous XeLaTeX : le journal affiche Character protrusion enabled (level 2). tandis que la ligne Automatic font expansion enabled, qui l'accompagnerait sous pdfLaTeX ou LuaLaTeX, disparaît sans un mot. Demandez-la explicitement et l'arrêt est net : ! Package microtype Error: Font expansion does not work with xetex.

Écritures complexes et droite-à-gauche : HarfBuzz et les primitives \XeTeX...

La raison principale du succès de XeTeX est qu'il traite correctement les écritures dont les lettres changent de forme selon le contexte — l'arabe, la famille indienne. Le façonnage (shaping) revient à HarfBuzz : la version 0.9999 (mai 2013) a délaissé l'ancien ICU LayoutEngine au profit de HarfBuzz, et la compilation livrée dans TeX Live 2024 lie HarfBuzz 8.3.0. Le moteur apporte aussi ses primitives propres : \XeTeXinputencoding pour changer d'encodage en cours de document, \XeTeXcharclass et \XeTeXinterchartoks pour classer les caractères et réagir à leur voisinage, \XeTeXlinebreaklocale pour choisir les règles de coupure selon la langue, \XeTeXgenerateactualtext pour inscrire le texte réel dans le PDF en vue de l'extraction, et \XeTeXpicfile et \XeTeXpdffile pour les images. Le changement de langue revient à polyglossia, la composition droite-à-gauche à bidi (que polyglossia charge de lui-même dès qu'on demande l'arabe ou apparenté), et le chinois, le coréen et le japonais à xeCJK — même si, pour la composition verticale japonaise et les règles de coupure exigeantes, luatexja ou upLaTeX offrent un chemin plus doux.

Quand choisir XeLaTeX, et quand s'en abstenir

  • Quand on veut utiliser les polices déjà installées. Le nom OpenType ou TrueType dans \setmainfont suffit ; aucun fichier de configuration à toucher.
  • Quand on veut écrire l'Unicode littéralement. L'entrée UTF-8 est la valeur par défaut et tout le préambule d'encodage disparaît.
  • Pour les documents multilingues et droite-à-gauche. polyglossia avec bidi a fait ses preuves en arabe et en hébreu, et HarfBuzz assure le façonnage.
  • Raison de s'abstenir, un : la dilatation est nécessaire. XeTeX n'a pas de \efcode ; la dilatation de microtype ne peut donc pas fonctionner. Si la qualité typographique prime, choisir pdfLaTeX ou LuaLaTeX.
  • Raison de s'abstenir, deux : on veut programmer le moteur. XeTeX n'embarque aucun langage de script. Pour une intervention de bas niveau, passer à LuaLaTeX.
  • Raison de s'abstenir, trois : la composition verticale japonaise. Elle dépasse la vocation de xeCJK ; essayer tôt luatexja ou upLaTeX fait gagner du temps.

La règle en une phrase : si l'on veut seulement utiliser les polices du système par leur nom, XeLaTeX ; si l'on veut entrer dans la composition elle-même, LuaLaTeX. La comparaison complète des trois moteurs figure sur la page « Choisir un moteur ». Un dernier mot sur le travail collectif : les noms de polices diffèrent d'un système à l'autre, et \setmainfont{Helvetica Neue} échouera tout simplement sur la machine Linux d'un collègue. Pour un manuscrit partagé, obtenez d'abord une compilation qui passe avec les polices livrées dans TeX Live, désignées par leur nom de fichier comme ci-dessus, puis ne substituez que les polices réellement nécessaires. Si vous ne remettez qu'un PDF, passez pdffonts et vérifiez que toutes les polices sont incorporées avant l'envoi.