Encodage et fins de ligne

En Shift_JIS, le caractère 本 est la paire d'octets 96 7B, et 7B est le { de l'ASCII ; le caractère 表 est 95 5C, et 5C est une barre oblique inverse. Autrement dit, dès que l'encodage de caractères est faux, la prose japonaise se change, octet après octet, en séquences de contrôle et en accolades LaTeX. Voilà pourquoi le mojibake dans TeX ne se réduit jamais à « des glyphes illisibles », et pourquoi un fichier .tex doit aujourd'hui être enregistré en UTF-8 avec fins de ligne LF. Cette page montre, messages d'erreur réels à l'appui, ce qui se produit face aux restes de Shift_JIS, EUC-JP ou ISO-2022-JP, comment les convertir, et en quoi le répertoire de caractères diffère entre platex et uplatex.

En quoi enregistrer un fichier .tex ? UTF-8 et LF

UTF-8 — avec ou sans BOM — et LF. Sous TeX Live 2024, uplatex, lualatex et xelatex lisent tous UTF-8 par défaut, et Git suppose UTF-8 et LF pour construire un diff. Ce qui rend la chose délicate en japonais, c'est qu'avant la diffusion d'Unicode trois encodages mutuellement incompatibles coexistaient : Shift_JIS sous Windows, EUC-JP sous Unix et ISO-2022-JP — le « code JIS » — pour le courrier, qui devait franchir des canaux sur sept bits. Le même kanji avait trois suites d'octets différentes, et aucun examen du fichier ne dit avec certitude laquelle on tient. Voilà pourquoi l'encodage est le premier suspect à l'ouverture d'un vieux répertoire de laboratoire.

EncodageOù il servaitOù on le rencontre encore
UTF-8standard actuel (Unicode)seul choix pour le neuf, et défaut de TeX Live
Shift_JISanciens Windows, PAO, consolesvieux modèles distribués, annexes de CD-ROM ; aussi appelé CP932
EUC-JPanciens Unix, centres de calcul universitairesles .tex et .sty qui traînent dans les répertoires partagés
ISO-2022-JPcourrier électronique (le « code JIS »)schéma sept bits commutant les jeux par séquences d'échappement ; fragments collés depuis des mails

Que se passe-t-il quand un fichier Shift_JIS rencontre une chaîne d'outils UTF-8

L'écran ne se remplit pas de glyphes illisibles. Une cascade d'erreurs apparaît, et la compilation s'effondre sur ! Undefined control sequence. Donnez un fichier Shift_JIS tel quel à uplatex sous TeX Live 2024 : le premier message est ! LaTeX Error: Invalid UTF-8 byte "93., suivi de ! LaTeX Error: Invalid UTF-8 byte sequence (^^ea^^82̕). Jusque-là tout est honnête : des signalements d'octets illégaux. L'ennui vient ensuite : la ligne rappelée s'affiche l.3 ^^93^^fa^^96{^^8c^^ea^^82̕\^^8e, et cela saute aux yeux — le second octet de 本 est devenu un { et celui de 表 une \, tous deux désormais syntaxe TeX active. Le symptôme n'est donc pas « le japonais s'affiche mal » mais « LaTeX déclare indéfinie une commande que je n'ai jamais écrite », ce qui explique que l'encodage soit le dernier suspect.

terminal
$ uplatex sjis.tex
! LaTeX Error: Invalid UTF-8 byte "93.
l.3 ^^93
! LaTeX Error: Invalid UTF-8 byte sequence (^^ea^^82̕).
! Undefined control sequence.
l.3 ^^93^^fa^^96{^^8c^^ea^^82̕\^^8e

$ uplatex -kanji=sjis sjis.tex        # tell the engine what it is reading
Output written on sjis.dvi (1 page, 320 bytes).

L'accident découle nécessairement de la conception de Shift_JIS. Comme le second octet d'un caractère à deux octets peut tomber dans la plage ASCII 0x400x7E, il existe 52 caractères dont le second octet vaut exactement 0x5C (barre oblique inverse) et 50 dont le second octet vaut 0x7B (accolade ouvrante). Le premier groupe comprend 表, 十, ソ, 能, 貼, 暴, 申 et 構 ; le second, 本, 宮, 施, 旬, 養 et 鶏 — tous parfaitement courants en japonais suivi. Les développeurs japonais les ont nommés les « mauvais caractères » et s'en sont longtemps méfiés, car le même accident frappait scripts shell et fichiers de configuration, pas seulement TeX. EUC-JP ignore ce défaut — ses seconds octets valent toujours 0x80 ou plus —, ce qui lui a valu une période de faveur dans le milieu TeX.

Convertir du Shift_JIS en UTF-8 : iconv et nkf

L'outil classique du monde japonais est nkf (Network Kanji Filter), mais il s'installe à part : ni macOS ni la plupart des distributions Linux ne le fournissent. Essayez d'abord iconv : utilitaire POSIX standard, présent en /usr/bin/iconv sur macOS comme sur Linux. iconv -f CP932 -t UTF-8 old.tex > new.tex suffit pour passer de Shift_JIS à UTF-8. Si nkf est disponible, nkf -w -Lu --overwrite *.tex réécrit sur place de nombreux fichiers en une ligne : cela vaut son installation quand on hérite d'un répertoire entier. Dans tous les cas, faites une copie ou versionnez sous Git avant de lancer la commande : --overwrite détruit l'original, exactement comme annoncé.

terminal
# iconv -- always present; safest one file at a time
iconv -f CP932  -t UTF-8 old.tex > new.tex     # Shift_JIS -> UTF-8
iconv -f EUC-JP -t UTF-8 old.tex > new.tex     # EUC-JP    -> UTF-8

# whole tree, keeping a backup of every original
for f in *.tex; do cp "$f" "$f.bak"; iconv -f CP932 -t UTF-8 "$f.bak" > "$f"; done

# nkf, if installed: detect first, then convert in place to UTF-8 + LF
nkf -g old.tex
nkf -w -Lu --overwrite *.tex

Avec iconv, nommez l'encodage CP932 et non SHIFT_JIS. On croit couramment que c'est la même chose : ce n'est pas le cas. Demandez à l'iconv de macOS de convertir en SHIFT_JIS un texte contenant (le tilde ondulé) ou (un chiffre cerclé) : il s'arrête sur iconv: iconv(): Illegal byte sequence ; avec CP932, il réussit. SHIFT_JIS est la définition étroite, fidèle à JIS X 0208, et exclut les caractères d'extension NEC et IBM — chiffres cerclés, chiffres romains et le reste. Les fichiers venus de Windows sont en pratique du CP932 : c'est donc le bon défaut. Le raisonnement vaut dans l'autre sens, lorsqu'il faut reconvertir de l'UTF-8 en Shift_JIS pour un vieil outil.

Option nkfEffetÉquivalent iconv
-gdétecter l'encodage et la fin de ligne actuels, sans convertirpas d'équivalent — utiliser file ou un détecteur comme chardetect
-wconvertir en UTF-8 sans BOMiconv -t UTF-8
-s / -e / -jconvertir en Shift_JIS / EUC-JP / ISO-2022-JPiconv -t CP932 / -t EUC-JP / -t ISO-2022-JP
-Lu / -Lw / -Lmnormaliser les fins de ligne en LF / CRLF / CRpas d'équivalent — sed, dos2unix ou eol=lf de Git
--overwriteréécrire sur place les fichiers indiquéspas d'équivalent — iconv écrit sur la sortie standard : rediriger vers un nouveau fichier

-kanji= : indiquer au moteur comment lire, sans convertir

Quand on ne veut pas — ou ne peut pas — réécrire le fichier, on le dit au moteur. (u)platex accepte -kanji=, avec -kanji=sjis, -kanji=euc, -kanji=jis et -kanji=utf8. Le fichier Shift_JIS qui produisait un mur d'erreurs lu en UTF-8 se compile sous uplatex -kanji=sjis sjis.tex en un sobre Output written on sjis.dvi (1 page, 320 bytes). Mais c'est un premier secours. L'éditeur, Git, grep et tout fichier appelé par \input continuent de supposer de l'UTF-8 : dès que les encodages se mélangent, un autre accident survient. On s'en sert pour compiler une fois un manuscrit reçu, juste assez pour le lire — puis on convertit. À noter : lualatex et xelatex n'ont pas d'option -kanji= ; ils lisent toujours de l'UTF-8.

platex et uplatex : le même binaire, deux mondes de caractères

La différence porte sur le répertoire de caractères, pas sur le programme. Lancez platex --version et uplatex --version sous TeX Live 2024 : les deux annoncent e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6le même binaire. Seule la parenthèse change : (utf8.euc) pour platex, (utf8.uptex) pour uplatex. Ils chargent des formats différents, et le format décide de la façon dont les caractères sont stockés en interne. La conséquence est concrète : mettez 髙 (U+9AD9, variante de 高 fréquente dans les patronymes japonais) dans un document et platex s'arrête sur ! LaTeX Error: Unicode character ^^e9^^ab^^99 (U+9AD9), tandis qu'uplatex le compose sans un mot. Le platex simple reste enfermé dans le répertoire JIS X 0208, et tout ce qui en sort est refusé à la porte. Il n'y a plus de raison de choisir platex pour un document neuf. Avec uplatex par défaut, 髙, 𠮟 et les variantes d'une liste de noms passent toutes.

terminal
$ platex --version | head -1
e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6 (utf8.euc) (TeX Live 2024)
$ uplatex --version | head -1
e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6 (utf8.uptex) (TeX Live 2024)

$ platex takashima.tex          # the document contains 髙 (U+9AD9)
! LaTeX Error: Unicode character ^^e9^^ab^^99 (U+9AD9)
$ uplatex takashima.tex
Output written on takashima.dvi (1 page, 304 bytes).

Fins de ligne et BOM : LF, CRLF et CR cassent-ils vraiment quelque chose ?

LaTeX lui-même les accepte tous sans broncher. Donnez à uplatex un fichier .tex écrit en \r\n (CRLF, Windows), ou commençant par un BOM UTF-8 (EF BB BF) : sous TeX Live 2024, uplatex comme lualatex compilent sans le moindre avertissement. L'idée reçue selon laquelle un BOM laisserait un caractère invisible en tête de document ne vaut pas pour ces deux moteurs aujourd'hui. Ce n'est pas LaTeX qui souffre, mais l'outillage autour. Un fichier mêlant CRLF et LF apparaît dans Git comme intégralement modifié, ce qui rend toute relecture impossible. Un .sty gardant des \r en fin de ligne peut désarmer une ancre de fin de ligne dans grep. Uniformiser en LF relève donc du travail collectif, non de la composition. Sous Git, une ligne dans .gitattributes est la solution la plus sûre, et elle absorbe à l'extraction les différences entre les machines des contributeurs.

terminal
# .gitattributes -- normalise on checkin, hand out LF on checkout
*.tex text eol=lf
*.sty text eol=lf
*.bib text eol=lf
*.pdf binary

# one-off cleanup of a file that arrived with CRLF
sed -i.bak $'s/\r$//' old.tex
  • Uniformiser tout, ancien comme neuf, en UTF-8 + LF. C'est le défaut des trois moteurs de TeX Live 2024 et cela correspond à ce qu'attend Git.
  • Commencer les conversions par iconv -f CP932 -t UTF-8. Écrire CP932 et non SHIFT_JIS, sinon chiffres cerclés et tilde ondulé échouent.
  • Si nkf est installé, nkf -w -Lu --overwrite *.tex est le plus rapide — mais il n'accompagne ni macOS ni la plupart des distributions Linux.
  • Copier le fichier ou le versionner avant de convertir. --overwrite est irréversible.
  • Démarrer les documents neufs sur uplatex (ou lualatex). Le platex simple échoue sur les caractères hors JIS X 0208, comme 髙 (U+9AD9).
  • -kanji=sjis est un premier secours. Une fois le document lisible, convertir le fichier lui-même en UTF-8.