Page de Nicolas Georgehttp://www.eleves.ens.fr/home/george/info/latex_utf8_oriental.html

LaTeX, l'UTF-8, et les langues orientales

Table des matières

Je vais dans ce document expliquer comment utiliser LaTeX pour taper des langues orientales (vous savez, celles avec tous les caractères bizarres), et en particulier comment utiliser pour ça des polices TrueType, tout en travaillant avec du PostScript.

Je suppose dans à peu près tout le texte que vous disposez d'un environnement de travail correct, avec un interpréteur de commandes de type Unix correct, et un interpréteur Perl. Ce n'est pas complètement indispensable, mais il faudra adapter les commandes à votre sauce si ce n'est pas votre cas.

Préparer les logiciels

Obtenir les polices

Avant toutes choses, il vous faut des polices. Pour du chinois, vous pouvez utiliser les polices Arphic, qui sont libres, et jolies.

Pour du japonais, il est possible d'utiliser les polices Wadalab. Il faudra dans ce cas télécharger également tools/wftodm.c.

Plus généralement, on pourra utiliser des polices internationales comme Arial Unicode (vendue avec je ne sais quel produit microsoft, franchement moche, mais extrêmement complète) ou Bitstream Cyberbit.

Les programmes de conversion

Sauf si vous comptez utiliser uniquement les polices Wadalab, qui sont déjà en Type1, il vous faudra Ttf2pt1. La version que j'ai souffre d'une limitation pénible, à modifier d'urgence : dans global.h, remplacer la définition de ENCTABSZ à 1024 par 65536.

Il peut être également utile de disposer des Type1 utilities, qui permettent de manipuler des polices Type, en particulier de convertir des .pfa en .pfb, ce qui a pour quasi-unique effet de faire gagner de la place.

Les packages LaTeX

Il vous faut une version récente du package UCS, au moins novembre 2002.

Il vous faut égelement le package CJK.

Tous les deux devraient être inclus dans n'importe quelle bonne distribution, mais le package UCS sera peut-être un peu trop vieux.

Construire les polices

TeX a son propre mécanisme de polices, et il va falloir y adapter les polices que vous avez trouvées. En particulier, TeX travaille surtout avec du PostScript, donc du Type1, alors que vous avez plus de chance de trouver du TrueType. Voici comment convertir.

Convertir du TrueType en Type1

Pour commencer, il faut apprendre à ttf2pt1 le nommage habituel des caractères Unicode dans les polices Type1. Pour ça, il faut créer un fichier unicode.map, dont les lignes sont de la forme :

!X U+XXXX uniXXXX UNICODE CHARACTER

Avec x de 0 à 65535. Manque de chance, une exigence pénible de afm2tfm impose que les caractères ASCII (32 à 126) portent leur nom officiel PostScript, qu'on peut obtenir de GhostScript avec la commande StandardEncoding ==. Voici quelques lignes de Perl pour le générer :

@e=qw{space exclam quotedbl numbersign dollar percent ampersand quoteright
parenleft parenright asterisk plus comma hyphen period slash zero one two
three four five six seven eight nine colon semicolon less equal greater
question at A B C D E F G H I J K L M N O P Q R S T U V W X Y Z bracketleft
backslash bracketright asciicircum underscore quoteleft a b c d e f g h i j
k l m n o p q r s t u v w x y z braceleft bar braceright asciitilde};
for $i (0..65535) {
  $c = $i>31 && $i<127 ? $e[$i-32] : sprintf "uni%4.4X", $i;
  printf "!%X U+%4.4X %s UNICODE CHARACTER\n", $i, $i, $c;
}

Quand on a ce fichier, il suffit d'appeler ttf2pt1 pour générer les polices Type1 :

ttf2pt1 -O h -L unicode.map -b police.ttf

Ceci produit deux fichirs : police.pfb, la police proprement dite, qui risque d'être 60% plus grosse que le fichier TrueType original, et police.afm, le fichier de métrique, qui va être nécessaire pour la suite. On met l'option -O h qui désactive les hints pour deux raisons : d'une part ils peuvent faire déborder la capacité de l'interpréteur PostScript (ça m'est précisément arrivé avec Bitstream), et d'autre part dvips ne sait pas les télécharger partiellement, ce qui se traduit par un surcoût fixe de plusieurs méga-octets par police.

Note : la conversion va probablement produire beaucoup de warnings, ils ne sont a priori pas importants.

Générer les fichiers d'encodage

Les polices de TeX sont limitées à 256 caractères. Pour gérer les langues orientales, il faut en combiner plusieurs. Le package CJK définit un certain nombre d'encodages pour ça :

C00
pour le chinois traditionel, calqué sur Big5
C10
pour le chinois simplifié, calqué sur GB
C40
pour le japonais, calqué sur JIS
C42
pour le japonais, calqué sur JIS mais avec une autre méthode de nommage

Un fichier d'encodage est de la forme /NomEncodage [, puis 256 noms de caractères PostScript, puis ] def. Nous allons générer des fichiers d'encodage pour C00, C10 et C40 à partir des fichiers data/uni-*.def présents dans le package UCS. Pour ça, utilisons encore un script Perl, un peu plus complexe cette fois. Il faut l'appeler avec en argument tous les fichiers data/uni-*.def.

Ce script reconnaît les lignes de la forme \uc@dclc{u}{c}{v}, où u est le numéro Unicode d'un caractère (en décimal), c l'encodage (cjkbg5 pour C00, cjkgb pour GB et cjkjis pour C40 et C42), et v les macros TeX nécessaires pour obtenir le caractère u dans une police d'encodage c.

Pour C00, v est de la forme \u@cjk@BgvXXYY, ce qui veut dire le caractère YY (en hexadécimal) de la police XX (en décimal). Pour C10, v est de la forme \u@cjk@GBXXYY, avec le même codage. Pour C40 et C42, v vaut \jischarXXYY, ce qui indique que le caractère est à trouver à la position XX×0x5E+YY-0xB3F (tout en hexadécimal) ; pour C40, le caractère n est simplement le caractère x de la police y est celui de numéro x+y×256, tout simplement.

Générer les fichiers de métriques

Quand on a les encodages, on peut les utiliser pour générer les fichiers .tfm, qui permettent à TeX de savoir où placer les lettres, ainsi que les fichiers .vf dont je n'ai jamais compris l'utilité.

On est maintenant confronté à un détail important : choisir un nom TeX pour la police. La documentation du package CJK conseille d'avoir deux lettres pour évoquer l'encodage (gs pour du GB pour du Chinois Simplifié, etc.), deux lettres pour nommer la police, et deux lettres pour indiquer sa taille normale (dans le cas de polices vectorielles, ce n'est pas très pertinent).

Ici, je fais avec Arphic PL Mingti2L en Big5 (soit avec C00), et je vais l'appeler btmi10.

Pour chacun des fichiers uniC00XX.enc, il faut exécuter la commande suivante :

afm2tfm police.afm -P uniC00XX.enc -v btmi10XX.vpl btmi10XX.tfm

Avec le shell Zsh, on peut faire ça avec une commande de la forme (il y aurait moyen de faire plus simple, mais ça produirait des fichiers vides pénibles pour les polices 3, 4 et 5 de C40) :

for i in uniC00*.enc; do
 j=$i[7,8]
 afm2tfm police.afm -P uniC00$j.enc -v btmi10$j.vpl btmi10$j.tfm
done

Pour chaque police, afm2tfm écrit une ligne de la forme :

btmi10XX ShanHeiSun-Light " LaTeXC00XXEncoding ReEncodeFont " <uniC00XX.enc

Ce sont les lignes nécessaires pour informer dvips de comment les trouver ; ShanHeiSun-Light est le nom PostScript de la police sur laquelle je fais cet exemple. Il faut les regrouper toutes dans un fichier, disons btmi10.map. Hélas, afm2tfm oublie de préciser qu'il faut aussi télécharger la police dans l'imprimante. Il faut donc ajouter à la fin de chaque ligne, avec un espace devant <police.pfb. Dans mon exemple, je me retrouve avec un fichier de 54 lignes presque identiques, dont la première est :

btmi1001 ShanHeiSun-Light " LaTeXC0001Encoding ReEncodeFont " <uniC0001.enc <bsmi00lp.pfb

Pour en finir avec ces polices virtuelles, il faut encore faire deux choses : d'abord compiler les fichier .vpl, ce qui se fait par exemple avec une commande comme celle-ci (toujours en Zsh) :

for i in *.vpl 
  vptovf $i

On eut ensuite supprimer les .vpl. Enfin, il faut informer dvips de l'existence de btmi10.map, ce qui se fait en ajoutant la ligne suivante dans ~/.dvipsrc (ou dans les config.* de dvips pour une installation commune) :

p+ btmi10.map

Le cas des police Wadalab

Les polices japonaises Wadalab ont la particularité d'être déjà en PostScript et déjà découpées en petits fichiers. Il y a dans ce cas nettement moins de travail. La première chose à faire, après avoir décomprimé l'archive, est de construire les polices Type1 en C42. Vous avez besoin du programme wftodm.c. Hélas, dvips n'est pas capable de télécharger partiellement les polices qu'il produit, à cause d'un tout petit problème de forme. Un patch trivial suffira : cherchez les deux lignes suivantes (qui sont tout près l'une de l'autre, au voisinage de la 215 dans la version que j'ai) :

fprintf(ofp,"/DmEncoding [\n");
fprintf(ofp,"/Encoding DmEncoding def\n");

Dans la première, supprimez le Dm, et supprimez entièrement la seconde. Compilez le programme, et appelez-le à peu près ainsi (je prends l'exemple des polices Mincho) :

./wftodm -FontBase jmin min-*.ps

Vous vous retrouvez avec des .pfa (polices Type1) et des .afm (métriques). Vous pouvez convertir les .pfa en .pfb pour économiser de la place si vous voulez. Convertissez les .afm en .tfm (métriques pour TeX avec :

for i in *.afm
  afm2tfm $i

Ceci vous affiche plein de lignes constituées essentiellement de deux fois le nom de base des fichiers. Notez-les dans un fichier jmin.map, en ajoutant à la fin <fichier.pfa (ce qui fait donc trois fois le nom répété chaque ligne).

Déclarer les polices au NFSS

LaTeX apporte une couche d'abstraction supplémentaires pour les polices : le NFSS. Il faut prévenir ce mécanisme que nos polices existent. Pour ça, il faut choisir un nom de famille pour notre police. Ici, disons min. Alors il faut créer un fichier c00min.fd, qui contient simplement :

\DeclareFontFamily{C00}{min}{}
\DeclareFontShape{C00}{min}{m}{n}
 { <-> CJK * btmi10}{}

Écrire le document

Écrire le document lui-même est très facile, c'est bien pour ça qu'on a fait tout ce boulot à l'avance. Il faut quelques packages et quelques options au début :

\usepackage[cjkbg5]{ucs}
\usepackage[utf8]{inputenc}
\usepackage[C00,T1]{fontenc}
\DeclareFontSubstitution{C00}{min}{m}{n}

La première ligne charge le package UCS, et l'informe qu'on va travailler principalement en chinois traditionel (Big5). Je rappelle que les autres options sont cjkgb pour le chinois simplifié, cjkjis pour le japonais, et d'autres que vous trouverez dans la doc d'UCS. Le fait de dire à UCS quel langue on va faire lui permet de bien choisir quand un même caractère est disponible à la fois en Big5 et en GB.

La deuxième ligne dit qu'on va taper en UTF-8, la troisième qu'on va utiliser l'encodage C00 (et il faut aussi charger T1 pour ce qui n'est pas dans C00). Enfin, la dernière indique qu'on veut utiliser min comme famille par défaut en encodage C00.

Pour taper le texte lui-même, il suffit d'utiliser un éditeur en UTF-8. On peut par exemple utiliser mon programme termim, qui permet de taper diverses langues dans un terminal UTF-8 (mais pas (encore) le chinois traditionel).

Il restera peut-être à jouer avec babel pour adapter la typographie aux diverses langues, mais c'est un tout autre problème. Il ne reste plus qu'à compiler. Attention, lancer xdvi sur le résultat peut être hasardeux pour la santé : quand j'ai essayé, il a bouffé presque 1 Go de mémoire avant de se faire tuer par le noyau. On préférera dvips. Attention, certaines versions trop anciennes (mais pas si anciennes que ça) ont de gros bugs qui empêcheront le téléchargement partiel de la police ; ça ne marche pas avec une 5.86, mais ça marche avec une 5.86e.

Pour donner une idée du coût de ces polices, chaque caractère différent utilisé va coûter environ entre 1,5 et 4,5 ko.

Pour finir

Encore un mot sur les noms des polices

Il faut bien l'avouer, les histoires de noms des polices, c'est un fouillis assez immonde. Le pire est atteint pour les noms des polices au niveau de TeX, où l'ancienne limite des 8 caractères, encore vaguement respectée, moins les deux pour le numéro de police, conduit à des contorsions extrêmement pénibles. De plus, il existe différents schémas de nommage plus ou moins incompatibles. Je pense qu'il ne faut pas y prêter trop d'importance : choisissez un schéma, suivez-le vaguement, et ne vous posez pas de questions.

Ranger tout ça

Quand tout marche bien sur deux ou trois exemple, il reste à mettre tout ça en place. Les distributions récentes ont un schéma de répertoires standard pour ça (en supposant que vous mettiez tout dans un répertoire texmf/) :

texmf/fonts/afm/
les fichiers .afm
texmf/fonts/tfm/
les fichiers .tfm
texmf/fonts/vf/
les fichiers .vf
texmf/fonts/type1/
les fichiers .pfa et .pfb
texmf/tex/latex/
les fichiers .fd
texmf/dvips/latex/
les fichiers .enc et .map (sauf unicode.map, qui n'est plus nécessaire)

Si vous respectez cette structure, avec une installation moderne de TeX, il suffit de positionner la variable d'environnement HOMETEXMF (pour une installation personnelle) ou TEXMFLOCAL à ce répertoire pour que tout soit trouvé automatiquement. D'ailleurs, beaucoup d'installations ont des valeurs par défaut de ~/texmf et /usr/local/share/texmf (parfois lib plutôt que share) respectivement.

Mâcher le travail

Comme je suis bonne pâte, je vous mâche le travail : voici une archive (592 ko) contenant les fichiers d'encodage (unicode.map et uniC*.enc). Au moins, si jamais il vous manque l'interpréteur Perl, vous pourrez continuer.

J'aurais pu donner cette archive dès le début, mais on aurait pu se demander d'où sortaient tous ces nombres magiques : je voulais montrer qu'on pouvait se débrouiller sans connaissance préalable autre que la bonne connaissance des formats et des mécanismes. D'ailleurs, les fichiers d'encodages du package UCS ne sortent pas non plus d'un chapeau : ils viennent des bases de données publiées par le Consortium Unicode.

De plus, comprendre la méthode dans ces cas permettra de l'adapter à des situations que je n'ai pas traitées, à commencer par le coréen (qui se traite exactement comme le chinois, à première vue).

Amusez-vous bien.