Index

L'index en fin d'ouvrage — la liste des termes accompagnés des pages où ils figurent — n'est pas fabriqué par LaTeX lui-même. Le travail de LaTeX s'arrête à la collecte des marques \index{…} posées dans le texte et à leur écriture dans une liste brute nommée .idx. Trier cette liste et lui donner la forme d'un index revient à un programme distinct, makeindex. Cette division du travail a une histoire : la documentation de makeindex attribue à Leslie Lamport, l'auteur de LaTeX, une contribution notable à sa conception. Cette page va du paquet makeidx et de la déclaration \makeindex à la syntaxe des entrées (! pour les sous-entrées, @ pour les clés de tri), aux passes de compilation, jusqu'à la raison pour laquelle « Ångström » se classe après « Zulu ».

Les quatre pièces d'un index — et pourquoi makeidx ne fait que huit lignes

Construire un index demande quatre pièces : \usepackage{makeidx} et \makeindex dans le préambule, un \index{terme} partout où un terme apparaît, et \printindex là où la liste doit être composée. La surprise, c'est que deux de ces quatre — \makeindex et \index — figurent déjà dans le noyau de LaTeX (latex.ltx). Ce qu'ajoute le paquet makeidx, c'est \printindex ainsi que \see et \seealso pour les renvois : environ huit lignes de code, pas davantage. Le choix est parlant : le gros du travail d'indexation devait se faire hors de LaTeX dès l'origine.

  • \usepackage{makeidx} — fournit \printindex ainsi que \see/\seealso (préambule).
  • \makeindex — la déclaration qui ouvre \jobname.idx et redéfinit \index en la version qui écrit réellement (préambule seulement). Le terminal annonce Writing index file mydoc.idx.
  • \index{terme} — la marque posée là où un terme apparaît. Elle n'imprime rien ; seul le numéro de page de cet endroit est enregistré.
  • \printindex — la commande qui compose l'index achevé. Elle revient à lire le fichier .ind et se place en général à la fin du document.

Il vaut la peine d'insister : \index est une marque invisible. Le mot lui-même s'écrit toujours dans le corps du texte, et \index{…} vient juste après : random numbers\index{random numbers} are used. Et il existe un piège qui compte : si \makeindex manque dans le préambule, \index avale son argument et ne fait rien — c'est littéralement la définition par défaut du noyau. Ni erreur ni avertissement ; seul l'index ressort vide. Quand des dizaines d'appels à \index ne produisent absolument rien, c'est cette ligne manquante qu'il faut soupçonner d'abord.

latex
\documentclass{article}
\usepackage{makeidx}
\makeindex                        % without this line, \index does nothing
\begin{document}

METAFONT\index{METAFONT} draws the shapes,
TeX\index{TeX} sets the type.
We cover random numbers\index{random numbers|textbf} here,
and touch on groups\index{group} and rings\index{ring}.
The treatment of algorithms\index{algorithm|(} starts here ...

% ... several pages later ...
... and the treatment of algorithms\index{algorithm|)} ends here.

\printindex
\end{document}

Écrire une entrée : les quatre caractères !, @, | et le guillemet droit

L'argument de \index possède sa petite syntaxe, bâtie sur quatre caractères spéciaux. Ce qu'il faut retenir : ces quatre-là sont interprétés par makeindex, pas par LaTeX. Pour LaTeX, l'argument n'est qu'une chaîne, déversée telle quelle dans le .idx. Une erreur de syntaxe ne suscite donc aucune protestation à la composition ; elle n'apparaît qu'au passage de makeindex, sous forme d'avertissement dans le journal .ilg.

Les sous-entrées passent par !. Le point d'exclamation sépare les niveaux : \index{animals!cats} place « cats » sous l'entrée principale « animals ». En répétant ! on imbrique davantage, jusqu'à trois niveaux (0, 1 et 2) — la limite prévue par makeindex. Les clés de tri passent par @. Sous la forme sortkey@display, elle sépare la chaîne servant au classement de celle réellement imprimée : \index{alpha@$\alpha$} imprime α dans l'index mais le range à la place d'« alpha ». Pour les symboles et les formules, dont les glyphes se classent n'importe comment, ce n'est pas facultatif.

Le formatage du numéro de page passe par |. Après la barre verticale, on nomme une commande à un argument (sans la barre oblique inverse initiale) et ce seul numéro de page est composé avec elle. \index{cat|textbf} est la manière classique de mettre en gras la page où un terme est défini ; |textit ou une commande personnelle conviennent tout autant. Les plages de pages passent par |( et |). Quand un sujet court sur plusieurs pages, on ouvre par \index{recursion|(} et on ferme par \index{recursion|)} pour obtenir 12--15. À noter également : makeindex contracte de lui-même trois pages consécutives ou plus en une plage ; l'option -r désactive ce comportement automatique.

Les renvois se placent eux aussi après |. \index{dog|see{pets}} imprime « dog, see pets » à la place d'un numéro de page, et |seealso{…} donne « see also ». Tous deux appellent les commandes \see et \seealso définies par makeidx ; les mots imprimés se changent donc via \seename (« see » par défaut) et \alsoname (« see also ») pour une autre langue. Enfin, le guillemet droit échappe : pour faire figurer !, @, | ou le guillemet lui-même comme caractère ordinaire, on le fait précéder d'un guillemet droit — \index{C"!} produit l'entrée « C! ». C'est là que les index du C et de C++ achoppent le plus souvent.

CaractèreRôleExemple
!Sous-entrée, jusqu'à trois niveaux\index{animals!cats}
@Clé de tri : sépare le classement de l'impression\index{alpha@$\alpha$}
|( |)Ouvrir et fermer une plage de pages\index{recursion|(}\index{recursion|)}
|cmdComposer ce numéro de page avec une commande (gras, …)\index{cat|textbf}
|see |seealsoRenvoyer à une autre entrée au lieu d'un numéro de page\index{dog|see{pets}}
"Traiter le caractère spécial suivant littéralement\index{C"!} donne « C! »

Lancer makeindex : du .idx au .ind, et le symptôme No file mydoc.ind.

Un index ne s'achève pas en une compilation. Comme pour bibtex, il s'agit de trois étapes avec un programme externe au milieu. LaTeX rassemble d'abord les appels \index dans mydoc.idx — un fichier tout simple, fait de lignes \indexentry{terme}{page}, qu'on peut ouvrir et lire. makeindex le trie ensuite en un mydoc.ind composable. Une dernière exécution de LaTeX permet enfin à \printindex de lire mydoc.ind, et l'index apparaît dans le document. Le compte rendu du tri reste dans mydoc.ilg : c'est là qu'on regarde quand la syntaxe d'une entrée était fautive.

shell
pdflatex mydoc        # writes mydoc.idx  ("Writing index file mydoc.idx")
makeindex mydoc       # mydoc.idx -> mydoc.ind, log in mydoc.ilg
pdflatex mydoc        # \printindex reads mydoc.ind

# -s picks a style file, -o names the output, -t names the log
makeindex -s style.ist -o mydoc.ind -t mydoc.ilg mydoc.idx

Oubliez l'étape du milieu et le symptôme est remarquablement discret : ni erreur ni avertissement, seulement cette ligne dans le journal — No file mydoc.ind.. La raison tient au mécanisme lui-même : \printindex se ramène à un appel de \@input@, qui lit le fichier s'il existe et imprime sinon exactement cette ligne. C'est ce silence qui permet à un document de paraître impeccable alors que tout son index manque. En pratique, cependant, latexmk fait l'aller-retour à votre place : il appelle makeindex dès que le .idx change et relance LaTeX autant de fois qu'il le faut, de sorte que les trois étapes se tapent de moins en moins à la main.

Pourquoi « Ångström » se classe après « Zulu » : l'ordre de makeindex

Ce que classe makeindex, ce n'est pas le mot visible mais la clé de tri — et sans @, la clé est tout simplement le texte de l'entrée. L'ordre par défaut est documenté : symboles, puis nombres, puis lettres, les lettres étant comparées d'abord sans égard à la casse, la majuscule ne l'emportant qu'à orthographe identique. Comme conception pour l'anglais, c'est parfaitement suffisant. L'ennui vient de l'étendue du mot « lettres » : pour makeindex, ce sont l'alphabet anglais et les chiffres, rien de plus. Confiez Ångström et émile tels quels à makeindex 2.17 livré avec TeX Live 2024, et ils se rangent non sous A et E mais tout à la fin de l'index, après Zulu.

shell
# entries written with no sort key at all:
#   +plus   9nine   apple   sea lion   seal   Zulu   Angstrom   emile
# (the last two really spelled Ångström and émile)

makeindex mydoc      # default: word ordering
  +plus / 9nine / apple / sea lion / seal / Zulu / Ångström / émile

makeindex -l mydoc   # letter ordering: blanks do not count
  +plus / 9nine / apple / seal / sea lion / Zulu / Ångström / émile

# the fix is an ASCII sort key, not an accented one:
#   \index{Angstrom@Ångström}   files under A
#   \index{emile@émile}         files under E

Deux conséquences. D'abord, donnez aux mots accentués une clé de tri en ASCII : \index{Angstrom@Ångström} imprime toujours Ångström mais le range sous A. Une méprise courante consiste à croire que \index{Ångström@Ångström} arrange quoi que ce soit — non, car le côté clé reste non ASCII. Ensuite, makeindex propose un choix d'ordre. Par défaut règne l'ordre par mots, où une espace précède toute lettre : « sea lion » vient donc avant « seal ». Avec -l s'applique l'ordre par lettres, où les espaces ne comptent pas du tout et « seal » passe devant. Un classement de dictionnaire réclame -l ; le style annuaire garde la valeur par défaut. Pour l'allemand existe en outre -g, conforme à la norme DIN 5007.

Dans un document où les accents sont trop nombreux pour que des clés de tri écrites à la main soient réalistes, le plus rapide est de changer le programme de tri lui-même. xindy — accessible depuis LaTeX via texindy — est bâti autour d'une collation multilingue et est livré avec TeX Live. Confiez le même Ångström à texindy -L english -C utf8 : il se place correctement entre abacus et zebra, donc sous A, sans la moindre clé de tri. Plus l'index grossit, plus il revient bon marché de remplacer le collateur plutôt que de saisir des clés.

Index japonais : mendex et upmendex

Le raisonnement de la section précédente s'applique tel quel au japonais et au chinois, et le symptôme y est pire. Confiez \index{群}, \index{環} et \index{体} à makeindex : ils ressortent dans l'ordre des codes de caractères, sans le moindre avertissement — un ordre sans aucun rapport avec la lecture des mots. Comme rien n'échoue, un index censé suivre l'ordre syllabique peut n'être trié selon rien du tout. La réponse est ici la même qu'avec hyperref : passer à l'outil conçu pour celamendex pour pLaTeX, upmendex pour upLaTeX et LuaLaTeX. Tous deux sont compatibles makeindex : il suffit de remplacer le mot que l'on tapait déjà.

Le gain, c'est le tri par lecture. À l'époque de makeindex, chaque entrée devait recevoir sa lecture sous la forme lecture@affichage, et les marques de sonorisation se normalisaient à la main. upmendex s'appuie sur la collation d'ICU (International Components for Unicode) pour ordonner correctement les kana, ce qui supprime l'essentiel de ce travail. En outre, un fichier dictionnaire passé avec -d enregistre des lectures en bloc, si bien que les entrées peuvent souvent se passer entièrement de la lecture en @. Règle pratique : mendex avec pLaTeX, upmendex avec upLaTeX et LuaLaTeX ; fournir les lectures via @ fonctionne dans les deux cas.

shell
uplatex mydoc                 # writes mydoc.idx
upmendex -s style.ist mydoc   # kana sorted via ICU -> mydoc.ind
uplatex mydoc                 # \printindex reads mydoc.ind

# readings can still be given by hand with @, in either program:
#   \index{さくいん@索引}
#   \index{Knuth@クヌース}

Changer l'aspect de l'index : le fichier de style .ist

L'aspect d'un index est régi par un fichier de style (.ist), transmis avec -s, comme dans makeindex -s style.ist mydoc. Son format est dépouillé : une liste de paires paramètre valeur, les chaînes entre guillemets droits, % ouvrant un commentaire jusqu'à la fin de la ligne. Ce qu'on y écrit s'adresse à makeindex et non à LaTeX, et détermine directement le contenu du fichier .ind. Les styles de mendex et upmendex sont compatibles vers le haut avec makeindex : un .ist existant se réutilise tel quel.

  • headings_flag — non nul, il insère un titre de groupe (la lettre A, B, … ou le groupe des symboles) à chaque changement de groupe (0 par défaut).
  • heading_prefix / heading_suffix — les chaînes placées avant et après ce titre.
  • symhead_positive — le titre donné au groupe des symboles quand headings_flag est positif ("Symbols" par défaut).
  • delim_0 / delim_1 / delim_2 — le séparateur entre une entrée de chaque niveau et ses numéros de page (", " par défaut pour les trois) ; c'est là que se met une conduite de points.
  • item_0 / item_1 / item_x1 — les chaînes insérées entre les entrées et entre les niveaux (sauts de ligne, retraits).
  • preamble / postamble — le code écrit en tête et en pied du .ind (par défaut \begin{theindex} et \end{theindex}).
  • group_skip — l'espace inséré à la frontière entre deux groupes (par défaut \indexspace).
style.ist
% group headings in bold, and a dotted leader before the page number
headings_flag    1
heading_prefix   "{\\bfseries "
heading_suffix   "}\\nopagebreak\n"
delim_0          "\\dotfill "

La manière moderne : imakeidx et plusieurs index

imakeidx remplace makeidx et apporte deux avantages de poids. D'abord, il appelle le programme d'indexation automatiquement pendant la compilation : un index se comporte alors à peu près comme la table des matières. Ensuite, il gère plusieurs index dans un même document — un index des matières et un index des noms, par exemple. La configuration passe par des options données à \makeindex : name= distingue un index, title= fixe son titre, intoc l'inscrit à la table des matières, program= choisit le programme de tri (makeindex, xindy, texindy, ou mendex / upmendex pour le japonais) et options= transmet des arguments tels que -s style.ist. Un \makeindex par index, l'aiguillage dans le texte par \index[name]{…}, la sortie par \printindex[name].

L'appel automatique repose sur le shell escape, et c'est précisément ce qui dépend de l'environnement. Dans la configuration par défaut de TeX Live 2024, makeindex figure sur la liste blanche du shell escape restreint : imakeidx construit donc l'index même sans -shell-escape (la commande kpsewhich -var-value shell_escape_commands affiche la liste de votre installation). xindy, texindy, mendex et upmendex n'y figurent pas et réclament donc -shell-escape. Là où le shell escape est purement interdit — certains systèmes de soumission, une CI stricte —, l'appel automatique n'est pas disponible ; on revient alors à la séquence en trois temps avec un appel manuel de makeindex, ou l'on confie le tout à latexmk.

latex
\documentclass{article}
\usepackage{imakeidx}

% two indexes, built during the compilation
\makeindex[name=subject, title=Subject index, intoc]
\makeindex[name=people,  title=Index of names, intoc,
           options={-s style.ist}]

\begin{document}
Groups\index[subject]{group} matter here.
Knuth\index[people]{Knuth, Donald} wrote TeX.

\printindex[subject]
\printindex[people]
\end{document}

% makeindex runs under restricted shell escape:
%   pdflatex mydoc
% xindy / mendex / upmendex need the full permission:
%   lualatex -shell-escape mydoc