David Madore's WebLog: De la difficulté de marier TeX et Unicode

Index of all entries / Index de toutes les entréesXML (RSS 1.0) • Recent comments / Commentaires récents

Entry #2114 [older|newer] / Entrée #2114 [précédente|suivante]:

(lundi)

De la difficulté de marier TeX et Unicode

J'avais déjà parlé de la question il y a quelques années, mais il est peut-être temps que je fasse une mise au point un peu plus détaillée sur ce qu'il en est.

Le merdier

Commençons par rappeler les personnages de la pièce. D'un côté, on a TeX, le logiciel de typographie scientifique écrit vers la fin des années '70 par Donald E. Knuth pour pouvoir mettre en page son Art of Computer Programming, et qui à ce jour semble être le seul programme capable de faire de la mise en page scientifique (voire, de la mise en page tout court) de bonne qualité — et qui est, en tout cas, universellement utilisé pour leurs publications par les mathématiciens, les physiciens et les informaticiens. De l'autre, on a Unicode, le jeu de caractères démarré vers la fin des années '80 et qui est en train de devenir le standard pour la représentation de toutes les formes d'écriture de toutes les langues du monde.

De cette description il résulte que si on veut mettre en page un document de qualité typographique correcte mélangeant un grand nombre de langues, on va vouloir utiliser TeX et Unicode. L'ennui, c'est qu'ils ne s'aiment pas. Mais alors, vraiment pas.

Le problème vient principalement de ce que Knuth, quand il programme, écrit du code certes absolument parfait du point de vue des bugs (il a fait 427 corrections à TeX depuis 1982, dont 11 depuis le début de ce millénaire, ce qui est plutôt pas mal quand on considère que Mozilla/Firefox approche les 850000 bugs rapportés depuis 1998 — même si ça ne compte pas exactement la même chose ça donne une idée), mais absolument abominable du point de vue de l'extensibilité future. TeX est donc bourré de bizarreries et d'idiosyncrasies poussiéreuses[#], de code ad hoc, de constantes magiques, et d'hypothèses faites à niveau profond — comme le fait qu'il n'y aura pas plus que 256 caractères dans une police, ou que toutes les dimensions sont exprimés en une unité fondamentale qui vaut 127/23681433600 mètres (soit environ 5.362851nm). En plus de ça, c'est un des langages de programmation les plus abominables qui soient (basé sur l'expansion de macros, sans aucune notion de portée bien définie ou de transparence référentielle — et avec des limitations incroyablement lourdingues comme 255 variables globales et 255 variables locales pour chaque type de données, aucune multiplication ou division ou arithmétique flottante, bref, une vraie fosse à goudron de Turing). Soit dit en passant, le source de TeX lui-même est tout aussi abominable (pas seulement parce qu'il est écrit en Pascal, mais aussi parce qu'il fait tout ce qu'on dit de ne surtout pas faire dans les cours de compilation, comme stocker des paramètres et variables de retour dans des variables globales).

Comme si ce n'était pas suffisamment atroce, au-dessus de TeX s'est accumulée toute une couche de sédiments, à savoir des packages pour TeX qui tentent de l'enrichir dans différentes directions. Et notamment tout l'édifice de LaTeX qui essaie de fabriquer quelque chose de propre au-dessus du merdier sous-jacent d'une façon qui revient à peu près à essayer de construire une ville au-dessus d'un tas de fumier, pour se retrouver, en fait, avec une construction complètement bancale et qui ressemble plus à la citadelle de Kowloon qu'à la Cité interdite. (Bon, j'en conviens, mes métaphores sont complètement pourries.) Certains de ces paquets sont carrément impressionnants (comme TikZ), mais le problème est que (1) la compatibilité des packages entre eux est souvent très douteuse, car chacun peut modifier tout le système dans des directions carrément imprévisibles, et (2) les hypothèses douteuses du cœur (comme le fait que les polices sont limitées à 256 caractères) sont démultipliées par tout le code qui se greffe au-dessus.

Et ce n'est pas tout : les tentatives pour réparer le merdier vont parfois l'empirer. Dès le début, le fait que TeX n'était pas vraiment adapté pour autre chose que l'anglais était évident, et les gens voulaient s'en servir pour d'autres langues : ils ont donc inventé des douzaines de systèmes permettant de taper telle ou telle langue avec TeX, et ces systèmes sont complètement incompatibles entre eux (il y a des gens qui ont fait des choses permettant de taper parfaitement du chinois, ou du grec polytonique, ou du sanskrit, ou de l'elfique, ou des hiéroglyphes, mais si vous voulez mélanger plusieurs de ces langues, vous pouvez pleurer dans votre coin). Puis LaTeX a mis en place des mécanismes pour tenter d'uniformiser la gestion des encodages d'entrées et de polices, et, quand Unicode est arrivé, de rendre le bouzin compatible avec Unicode, mais ces mécanismes entérinent eux-mêmes les limitations de TeX en tentant de les contourner.

Et ce n'est vraiment pas beau à voir : en principe, dans un système informatique bien conçu, l'encodage du fichier d'entrée ne devrait avoir aucune incidence sur ce qui se passe après, il devrait juste se transformer en une suite de caractères Unicode qui sera manipulée de la même manière quelle que soit la façon dont elle était représentée au départ — mais le fonctionnement même de TeX rend la chose impossible, donc un ‘é’ sera manipulé de façon différente selon qu'il a été tapé en Latin-1 ou en UTF-8 (pour ne pas dire quand il a été entré comme \'e), parce que TeX ne voit pas le ‘é’ de UTF-8 comme un seul caractère mais comme une suite de deux octets "C3"A9, et LaTeX s'arrange pour rendre ces octets magiques (actifs) et pour leur associer des fonctions qui vont décider la séquence UTF-8, calculer le numéro Unicode correspondant, et agir en conséquence. Évidemment, l'encodage de sortie, c'est-à-dire celui de la police, va aussi jouer : il va falloir demander « à la main » à LaTeX d'aller chercher une police de tel ou tel encodage (par exemple, si je tape un document qui contient mélange du français et du russe, il faut que je demande à changer l'encodage de police à chaque fois — si je tente d'écrire du russe avec l'encodage T1 qui sert pour le français, ça ne marchera pas du tout, et si je tente d'écrire du français avec l'encodage T2A qui sert pour le russe, ce sera plus insidieux, ça semblera marcher, mais si on sélectionne les caractères accentués dans le fichier PDF produit on verra qu'ils ont été produits en plaçant un accent au-dessus d'une lettre plutôt qu'en mettant le « vrai » caractère Unicode accentué).

Mais les problèmes de langues ne se limitent pas aux problèmes d'encodage. Il y a par exemple la césure des lignes, et diverses conventions typographiques. LaTeX est associé à un truc appelé Babel qui gère tout ça : l'ennui, c'est justement qu'il gère un peu trop de choses à la fois, c'est une tour compliquée et assez instable que Babel, par exemple la version française rend magiques les caractères ‘;’, ‘:’, ‘?’ et ‘!’ parce qu'elle veut vous imposer des règles de typographie française avec lesquelles je ne suis d'ailleurs pas forcément d'accord (et en tout cas je ne suis pas d'accord avec l'idée qu'il serait une faute d'écrire du français avec les règles typographiques courantes de l'anglais ou vice versa, et donc qu'il faudrait lier langue et conventions typographiques), et, à moins de désactiver cette fonctionalité douteuse, la conséquence concrète est de poser des difficultés avec d'autres paquets LaTeX (par exemple TikZ). Mais il est très difficile[#2] de trouver comment obtenir de LaTeX qu'il utilise les règles de césure du français sans charger la version française de Babel.

Je ne parle même pas des langues qui s'écrivent de droite à gauche. Parce que là il y a une sorte de miracle cosmique qui s'est produit, à savoir que les mécanismes prévus par le TeX de Knuth ne sont pas terriblement difficiles à étendre pour gérer les écritures de droite à gauche, ou même verticales.

Je finis par le merdier des polices : en quelque sorte, c'est la racine de tous les maux. TeX était prévu pour fonctionner avec METAFONT, le système de génération de polices aussi écrit par Knuth (et dont la mascotte est une lionne alors que celle de TeX est un lion). METAFONT utilise un langage spécifique (et qui ressemble vaguement à TeX) pour produire des polices qui sont éventuellement paramétrables par tout un tas de réglages (d'où le META), ce qui permet de fabriquer énormément de variantes de la même police ; en un sens, c'est beaucoup plus moderne que les systèmes actuels de polices qui sont — que je sache — conçus fastidieusement variante par variante. Mais l'ennui avec METAFONT c'est qu'il produit des polices bitmap, c'est-à-dire qu'elles ont besoin de connaître la résolution de l'imprimante sur laquelle on les utilisera ; or de nos jours, on veut pouvoir fabriquer des documents qui s'afficheront à l'écran avec un niveau de zoom arbitraire : et pour ça, il faut avoir des polices vectorielles. Plusieurs mécanismes de polices vectorielles sont apparus, d'abord dans PostScript/PDF (qui utilise des polices Adobe Type 1, lesquelles délimitent les caractères par des courbes de Bézier cubiques), puis dans TrueType (qui a choisi de se limiter aux courbes de Bézier quadratiques, plus simples à tracer efficacement — malheureusement ça veut dire que les deux types de police ne sont pas facilement intertransformables) ; mais les polices METAFONT ne se convertissent pas aisément en l'un ou l'autre de ces formats, parce que le programme trace les caractères en déplaçant un pinceau possiblement elliptique le long de courbes de Bézier cubiques, et l'enveloppe du parcours d'une ellipse se déplaçant (et peut-être tournant) le long d'une courbe de Bézier cubique monte un peu en degré — je n'ai pas fait le calcul, mais il faudrait un jour que quelqu'un se fatigue à écrire un logiciel de calculs exacts en géométrie algébrique réelle pour pouvoir manipuler tous ces objets. Mais je digresse. Toujours est-il que pour permettre à TeX d'utiliser des polices modernes. indépendamment d'Unicode, il a fallu (1) faire des polices en question, et (2) changer le moteur de gestion des polices.

A New Hope : différents moteurs TeX

Il est rapidement apparu que simplement ajouter des couches de macros au-dessus de TeX n'était pas une vraie solution. Il fallait toucher au moteur lui-même. Et en quelque sorte, quand j'ai laissé entendre que LaTeX était un jeu de macros sur TeX, ce n'est pas (ce n'est plus) tout à fait vrai : LaTeX, ou au moins LaTeX 2ε est basé sur ε-TeX. Ce dernier est une extension du TeX de Knuth, c'est-à-dire un moteur différent qui ajoute (si on choisit de les activer) quelques fonctionalités en plus : un nombre de variables locales et globales de 65536 de chaque type (au lieu de 256, ce qui ne suffisait plus à LaTeX), une primitive \middle pour des parenthèses « au milieu »[#3], quelques autres extensions mineures, et, moins mineure, une extension permettant d'écrire de droite à gauche[#4] (comme je l'ai dit, ça a été miraculeusement facile à mettre en place).

Ce moteur ε-TeX a lui-même eu des descendants : pdfTeX, qui est le plus utilisé de nos jours (ne serait-ce que dans son mode ε-TeX, puisqu'il a complètement remplacé ce dernier), ajoute, comme son nom l'indique, la capacité de produire directement du PDF (et toutes sortes de commandes pour manipuler la structure du PDF ou accéder à certaines fonctionalités de ce format comme la couleur, la transparence, les hyperliens), il a quelques fonctionnalités graphiques dans le moteur lui-même et permet par exemple de tourner arbitrairement du texte (ce que, sauf erreur de ma part, le TeX d'origine ne permet pas) ou d'inclure certains formats d'images, il comprend les polices PostScript/PDF et permet de les combiner sous forme de polices « virtuelles » ; il a une gestion de la microtypographie (c'est-à-dire les possibilités de : faire varier l'espace entre les lettre, faire varier la largeur des lettres elles-mêmes, et faire légèrement dépasser les lettres des marges — tout ceci automatiquement pour améliorer le rendu des paragraphes ; je dois dire que personnellement je trouve ça plutôt déplaisant visuellement, au moins les deux derniers effets que j'ai mentionnés, mais apparemment il y a des gens qui aiment ça, dont notre maître à tous Hermann Zapf).

Mais pour gérer vraiment Unicode dans TeX, il faut surtout lever la limitation des caractères à 256. Ce n'est pas évident à faire, surtout que comme je l'ai souligné, cette limitation devient une hypothèse de beaucoup de jeux de macros qui s'empilent au-dessus. Par ailleurs, Unicode ne se limite pas à une liste de caractères : il faut aussi gérer les ligatures complexes de l'arabe, du thaï ou des langues brahmiques, les caractères de ces dernières qui se réordonnent, etc.

Une première tentative a vu le jour dans les années '90 sous le nom de Omega, qui est plus ou moins morte et a été reprise sous le nom de Aleph (en y ajoutant les extensions de ε-TeX que j'ai déjà mentionné). Une des idées d'Omega était d'ajouter un mécanisme assez général de traduction (OTP : Omega Translation Process), qui est compilé vers des automates finis, et qui permet d'effectuer à la fois les changements d'encodages et les différentes manips du genre ligatures, réordonnement de caractères, etc.

Puis d'autres gens sont partis dans une tout autre direction et ont produit XeTeX. L'idée est de remplacer complètement tous les mécanismes de gestion de police de TeX par ceux du système d'exploitation sous-jacent (et qui servent donc pour tous les autres programmes) : initialement, XeTeX ne fonctionnait que sous Mac OS, parce que celui-ci était le seul à avoir une gestion correcte des polices OpenType, mais maintenant XeTeX tourne aussi sur les principales autres plateformes. Le support pour la microtypographie dans XeTeX a été ajouté un peu après celui de pdfTeX. En quelque sorte, XeTeX est un TeX en Unicode et débarrassé de toutes ses fonctionalités archaïques (et c'est lui que j'utilise pour mettre en page mes fragments littéraires gratuits). Le problème avec ça, c'est que (A) du coup, taper des maths cesse d'être évident en XeTeX, précisément parce que le fait de passer par les polices du système fait qu'on n'a pas forcément de quoi écrire des formules mathématiques, et surtout pas avec les mécanismes de TeX, et (B) le résultat peut dépendre du système sur lequel on compile (alors que TeX a toujours eu pour principe que la sortie doit être rigoureusement la même partout) ; ces deux faits ont été au moins en partie corrigé par la suite.

Tout ceci part un peu dans tous les sens. Le dernier né de la famille, LuaTeX, essaie de réunir tout ce qu'il y a de bien dans tout ça :

  • il inclut toutes les fonctionnalités de pdfTeX (et, par transitivité, de ε-TeX, sauf sa gestion primitive des écritures de droite à gauche, lui préférant le modèle plus sophistiqué d'Aleph),
  • il reprend des morceaux de Aleph, dont sa gestion de la directionalité (les dernières versions ne reprennent plus le système de traducteurs de Omega, mais LuaTeX en a au moins toutes les capacités),
  • il tâche de recouvrir les fonctions de XeTeX (et d'être compatible avec son modèle de gestion des caractères Unicode), mais de façon indépendante de la plateforme,
  • il inclut la gestion des polices OpenType tirée du programme FontForge,
  • il incorpore une bibliothèque de gestion du langage METAPOST, ce qui lui donne des fonctionnalités graphiques internes proches de celles de METAFONT (avec un langage analogue),
  • et il rassemble tout ça avec un interpréteur du langage Lua (qui n'est peut-être pas le meilleur langage au monde mais il n'est pas mal, et évidemment par rapport à TeX à peu près n'importe quel autre langage paraît merveilleux), et de la colle pour permettre à Lua de parler au reste.

L'ensemble est relativement cohérent. C'est loin d'être parfait, mais je pense que c'est à peu près le meilleur part qu'on pouvait tirer de tout le bordel.

Tout est-il résolu ?

Il ne suffit pas d'avoir un moteur TeX qui gère correctement Unicode pour que tous les problèmes disparaissent pour autant. Parce que comme je l'ai signalé toutes les couches de macros construites au-dessus risquent de faire des hypothèses incorrectes ; et par ailleurs, il faut aussi des paquets pour permettre d'utiliser commodément les fonctions avancées du moteur.

Pour ce qui est de la sélection des polices, le travail qui avait déjà été fait pour XeTeX a pu être adapté pour fonctionner aussi avec LuaTeX : il s'agit du paquet fontspec, qui, couplé à des polices OpenType, est capable de produire des résultats assez impressionnants. Pour ce qui est de taper des formules mathématiques avec des caractères Unicode (c'est-à-dire à la fois saisir les formules en ayant la possibilité — mais non l'obligation — de taper les caractères Unicode à la place des commandes TeX traditionnelles, et aussi que le document de sortie encode les symboles mathématiques à leur place attribuée par Unicode, ce qui permettra par exemple de rechercher le signe ⊗ dans le document PDF), il y a unicode-math : combiné à des polices appropriées, il fournit un nombre assez conséquent de symboles mathématiques ; celui-ci a en outre l'avantage de permettre de régler enfin correctement des questions sottes du type il n'y a vraiment aucune raison que le grec majuscule soit typographié en caractères droits dans les formules alors que le grec minuscule et le latin majuscule et en italiques (après, par contre, on peut maudire le consortium Unicode pour avoir eu l'idée de mettre un alphabet mathématique grec sans-sérif gras et un alphabet mathématique latin sans-sérif non gras mais pas d'alphabet mathématique grec sans-sérif non gras : qu'est-ce qui a bien pu leur passer par la tête ?).

En revanche, si on veut taper des caractères cyrilliques dans une formule mathématique (par exemple un Ш pour désigner le groupe de Tate-Šafarevič), il faut mettre les mains dans le cambouis : le paquet unicode-math a beau déclarer des polices qui les contiennent, il ne va pas donner aux caractères des \mathcode nécessaires à TeX (enfin, LuaTeX) pour les utiliser dans le mode mathématique : il faut le faire à la main, et pour ça il faut récupérer le numéro de famille qu'il a attribué à la police (stocké dans \um_symfont_tl juste après la définition) pour le passer à \Umathchardef ou \Umathcode, c'est plutôt moche. Et si j'arrive éventuellement à faire un Ш droit ou gras avec des commandes différentes, arriver à faire en sorte qu'il soit choisi correctement selon que j'ai mis \mathbf devant, je ne sais pas faire. Bref, il y a encore des soucis : il faut dire que le consortium Unicode est un peu pénible d'avoir uniquement mis des alphabets mathématiques spécifiques pour le latin et grec. [Ajout : voir aussi l'entrée suivante.]

Le système Babel pour la gestion des langues ne marche pas — ou marche très mal — avec LuaTeX ou XeTeX. Pour ce dernier, il y a un remplaçant qui sappelle Polyglossia, mais il ne supporte pas encore LuaTeX (je ne sais pas vraiment pourquoi). Quant aux écritures de droite à gauche, même si elles sont correctement gérées (à bas niveau) par LuaTeX, il manque un paquet pour rendre leur gestion plus agréable (l'équivalent du paquet bidi).

Enfin, un problème qui vaut la peine d'être mentionné, c'est que tout ça est lent. Pas vraiment que LuaTeX soit lent, mais les paquets comme fontspec et unicode-math semblent vouloir charger un nombre faramineux de gadgets associés à LaTeX3, et tout ceci prend beaucoup de temps.

[#] Ce n'est sans doute pas sans rapport avec le fait que dans Art of Computer Programming il implémente tous ses algorithmes en assembleur pour un processeur imaginaire, le MIX, qui ressemble maintenant à une curiosité d'un musée de la paléoinformatique. (Il promet — dans une prochaine édition et dans les nouveaux livres à sortir — de les réécrire dans un assembleur plus moderne, le MMIX, mais à moins qu'il vive largement centenaire, ce que je lui souhaite, il n'arrivera pas à terminer tout ce boulot.)

[#2] Pour épargner à d'autres de s'arracher comme moi les cheveux à trouver : \bbl@patterns{french} — ou, à encore plus bas niveau, \language=\l@french\relax.

[#3] Par exemple, la formule \left(\sum_i u_i\middle|\sum_j v_j\right) = 0 compilera avec ε-TeX mais pas avec le TeX d'origine.

[#4] Pour démonstration, faites \TeXXeTstate=1\beginR Ceci est un texte écrit à l'envers.\endR — mais surtout, essayez avec un texte un peu plus long à l'intérieur d'un paragraphe écrit normalement.

↑Entry #2114 [older|newer] / ↑Entrée #2114 [précédente|suivante]

Recent entries / Entrées récentesIndex of all entries / Index de toutes les entrées