Índice analítico

El índice alfabético del final de un libro —la lista de términos con las páginas en que aparecen— no lo construye LaTeX por sí mismo. El trabajo de LaTeX termina al recoger las marcas \index{…} colocadas en el texto y escribirlas en una lista en bruto llamada .idx. Ordenar esa lista y darle forma de índice corresponde a un programa aparte, makeindex. Ese reparto tiene su historia: la propia documentación de makeindex atribuye a Leslie Lamport, autor de LaTeX, una contribución notable a su diseño. Esta página recorre el paquete makeidx y la declaración \makeindex, la sintaxis de las entradas (! para subentradas, @ para claves de orden), las pasadas de compilación y la razón por la que “Ångström” se archiva después de “Zulu”.

Las cuatro piezas de un índice: por qué makeidx tiene solo ocho líneas

Construir un índice requiere cuatro piezas: \usepackage{makeidx} y \makeindex en el preámbulo, un \index{término} allí donde aparezca un término, y \printindex donde deba componerse la lista. La sorpresa es que dos de esas cuatro —\makeindex e \index— ya están en el núcleo de LaTeX (latex.ltx). Lo que aporta el paquete makeidx es \printindex más \see y \seealso para los renvíos: unas ocho líneas de código, no más. El diseño dice algo: el trabajo pesado de indexar debía ocurrir fuera de LaTeX desde el principio.

  • \usepackage{makeidx}: aporta \printindex y los comandos \see/\seealso (preámbulo).
  • \makeindex: la declaración que abre \jobname.idx y redefine \index en la versión que escribe de verdad (solo en el preámbulo). La terminal informa Writing index file mydoc.idx.
  • \index{término}: la marca que se coloca donde aparece un término. No imprime nada; solo se registra el número de página de ese punto.
  • \printindex: el comando que compone el índice terminado. Se reduce a leer el archivo .ind y suele ir al final del documento.

Conviene insistir en que \index es una marca invisible. La palabra se sigue escribiendo en el cuerpo del texto y \index{…} va justo detrás: random numbers\index{random numbers} are used. Y hay una trampa que importa: si falta \makeindex en el preámbulo, \index se traga su argumento y no hace nada —esa es literalmente la definición por defecto del núcleo—, de modo que no aparece error ni advertencia y solo el índice sale vacío. Cuando decenas de llamadas a \index no producen absolutamente nada, sospeche primero de esa línea ausente.

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}

Escribir una entrada: los cuatro caracteres !, @, | y la comilla recta

El argumento de \index tiene una pequeña sintaxis propia, construida sobre cuatro caracteres especiales. Lo que hay que retener: esos cuatro los interpreta makeindex, no LaTeX. Para LaTeX el argumento es solo una cadena, vertida tal cual en el .idx. Un error de sintaxis no provoca queja alguna al componer; solo aflora cuando corre makeindex, como advertencia en el registro .ilg.

Las subentradas usan !. El signo de exclamación separa niveles: \index{animals!cats} coloca “cats” bajo la entrada principal “animals”. Repitiendo ! se anida más, hasta tres niveles (0, 1 y 2): el límite previsto por makeindex. Las claves de orden usan @. Escrita como sortkey@display, separa la cadena que sirve para ordenar de la que realmente se imprime: \index{alpha@$\alpha$} imprime α en el índice pero lo archiva donde corresponde a “alpha”. Para símbolos y fórmulas, cuyos glifos se ordenan sin sentido, esto no es opcional.

El formato del número de página pasa por |. Tras la barra vertical se nombra un comando de un argumento (sin la barra invertida inicial) y solo ese número de página se compone con él. \index{cat|textbf} es la manera clásica de poner en negrita la página donde se define un término; |textit o un comando propio sirven igual. Los rangos de páginas usan |( y |). Cuando un tema abarca varias páginas, se abre con \index{recursion|(} y se cierra con \index{recursion|)} para obtener 12--15. Nótese además que makeindex contrae por su cuenta tres o más páginas consecutivas en un rango; la opción -r desactiva ese comportamiento automático.

Los renvíos también van después de |. \index{dog|see{pets}} imprime “dog, see pets” en lugar de un número de página, y |seealso{…} da “see also”. Ambos funcionan llamando a los comandos \see y \seealso que define makeidx, de modo que las palabras impresas se cambian con \seename (“see” por defecto) y \alsoname (“see also”) para otro idioma. Por último, la comilla recta escapa: para meter !, @, | o la propia comilla como carácter corriente en una entrada, se le antepone una comilla recta — \index{C"!} produce la entrada “C!”. Ahí es donde suelen encallar los índices de C y C++.

CarácterFunciónEjemplo
!Subentrada, hasta tres niveles\index{animals!cats}
@Clave de orden: separa el clasificar del imprimir\index{alpha@$\alpha$}
|( |)Abrir y cerrar un rango de páginas\index{recursion|(}\index{recursion|)}
|cmdComponer ese número de página con un comando (negrita, …)\index{cat|textbf}
|see |seealsoRemitir a otra entrada en vez de a un número de página\index{dog|see{pets}}
"Tratar literalmente el carácter especial siguiente\index{C"!} da “C!”

Ejecutar makeindex: del .idx al .ind, y el síntoma No file mydoc.ind.

Un índice no se termina en una sola compilación. Igual que con bibtex, son tres etapas con un programa externo en medio. Primero LaTeX reúne las llamadas \index en mydoc.idx, un archivo sencillo hecho de líneas \indexentry{término}{página} que se puede abrir y leer. Después makeindex lo ordena y lo convierte en un mydoc.ind componible. Por último, otra ejecución de LaTeX permite que \printindex lea mydoc.ind, y el índice aparece en el documento. El registro del ordenamiento queda en mydoc.ilg: ahí se mira cuando la sintaxis de una entrada estaba mal.

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

Si se olvida el paso intermedio, el síntoma es notablemente silencioso: ni error ni advertencia, solo esta línea en el registro: No file mydoc.ind.. La razón está en el propio mecanismo, porque \printindex se reduce a una llamada a \@input@, que lee el archivo si existe y, si no, imprime exactamente esa línea. Ese silencio es lo que permite que un documento parezca impecable con todo su índice ausente. En la práctica, sin embargo, latexmk hace el trayecto por usted: llama a makeindex cada vez que cambia el .idx y vuelve a ejecutar LaTeX tantas veces como haga falta, de modo que los tres pasos se teclean a mano cada vez menos.

Por qué “Ångström” va después de “Zulu”: cómo ordena makeindex

Lo que ordena makeindex no es la palabra visible sino la clave de orden, y sin @ la clave es sencillamente el texto de la entrada. El orden por defecto está documentado: símbolos, luego números, luego letras, comparando las letras primero sin atender a mayúsculas y minúsculas, y dando prioridad a la mayúscula solo cuando la grafía es por lo demás idéntica. Como diseño para el inglés resulta del todo suficiente. El problema es el alcance de “letras”: para makeindex son el alfabeto inglés y los dígitos, nada más. Entregue Ångström y émile tal cual a makeindex 2.17, el que trae TeX Live 2024, y no acaban bajo la A y la E sino al final del índice, detrás de 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

De ahí se siguen dos cosas. Primera: dé a las palabras acentuadas una clave de orden en ASCII. \index{Angstrom@Ångström} sigue imprimiendo Ångström pero lo archiva bajo la A. Un malentendido frecuente es creer que \index{Ångström@Ångström} arregla algo: no lo hace, porque el lado de la clave sigue siendo no ASCII. Segunda: makeindex ofrece una elección de orden. Por defecto rige el orden por palabras, en el que un espacio precede a cualquier letra, de modo que “sea lion” va antes que “seal”. Con -l se aplica el orden por letras, donde los espacios no cuentan y “seal” pasa delante. Un listado de diccionario pide -l; el estilo de guía telefónica se queda con el valor por defecto. Para el alemán existe además -g, conforme a DIN 5007.

En un documento con tantos acentos que escribir claves a mano deje de ser realista, lo más rápido es cambiar el propio programa de ordenación. xindy —al que se llega desde LaTeX mediante texindy— está construido en torno a la colación multilingüe y viene con TeX Live. Entregue el mismo Ångström a texindy -L english -C utf8 y se coloca correctamente entre abacus y zebra, bajo la A, sin clave de orden alguna. Cuanto más crece el índice, más barato sale cambiar el colador que teclear claves.

Índices en japonés: mendex y upmendex

El razonamiento de la sección anterior se aplica sin cambios al japonés y al chino, y el síntoma es peor. Entregue \index{群}, \index{環} y \index{体} a makeindex y saldrán en orden de código de carácter, sin una sola advertencia: un orden sin relación alguna con la lectura de las palabras. Como nada falla, un índice pensado para seguir el orden silábico puede acabar sin orden ninguno. La respuesta aquí es la misma que con hyperref: cambiar a la herramienta hecha para ellomendex para pLaTeX, upmendex para upLaTeX y LuaLaTeX. Ambos son compatibles con makeindex, así que basta con sustituir la palabra que ya se tecleaba.

Lo que se gana es la ordenación por lectura. En la época de makeindex, cada entrada necesitaba su lectura en la forma lectura@visualización, y las marcas de sonorización se normalizaban a mano. upmendex recurre a la colación de ICU (International Components for Unicode) para ordenar correctamente los kana, lo que suprime buena parte de ese trabajo. Además, un archivo de diccionario pasado con -d registra lecturas en bloque, de modo que las entradas pueden a menudo prescindir por completo de la lectura con @. Regla práctica: mendex con pLaTeX, upmendex con upLaTeX y LuaLaTeX; suministrar lecturas mediante @ funciona en ambos.

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@クヌース}

Cambiar el aspecto del índice: el archivo de estilo .ist

El aspecto de un índice lo rige un archivo de estilo (.ist), entregado con -s, como en makeindex -s style.ist mydoc. Su formato es sencillo: una lista de pares parámetro valor, las cadenas entre comillas rectas y % abriendo un comentario hasta el fin de línea. Lo que se escribe ahí instruye a makeindex, no a LaTeX, y determina directamente el contenido del archivo .ind. Los estilos de mendex y upmendex son compatibles hacia arriba con makeindex, así que un .ist existente se reutiliza sin cambios.

  • headings_flag: distinto de cero, inserta un título de grupo (la letra A, B, … o el grupo de símbolos) cada vez que cambia el grupo (0 por defecto).
  • heading_prefix / heading_suffix: las cadenas colocadas antes y después de ese título.
  • symhead_positive: el título dado al grupo de símbolos cuando headings_flag es positivo ("Symbols" por defecto).
  • delim_0 / delim_1 / delim_2: el separador entre una entrada de cada nivel y sus números de página (", " por defecto en los tres); aquí va también una guía de puntos.
  • item_0 / item_1 / item_x1: las cadenas insertadas entre entradas y entre niveles (saltos de línea, sangrías).
  • preamble / postamble: el código escrito al principio y al final del .ind (por defecto \begin{theindex} y \end{theindex}).
  • group_skip: el espacio insertado en la frontera entre dos grupos (por defecto \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 forma moderna: imakeidx y más de un índice

imakeidx sustituye a makeidx y aporta dos ventajas de peso. Primero, llama al programa de indexación automáticamente durante la compilación, de modo que un índice se comporta casi como el índice general. Segundo, admite más de un índice en un mismo documento: un índice de materias y otro de nombres, por ejemplo. Se configura pasando opciones a \makeindex: name= distingue un índice, title= fija su título, intoc lo incluye en el índice general, program= elige el programa de ordenación (makeindex, xindy, texindy, o mendex / upmendex para el japonés) y options= transmite argumentos como -s style.ist. Un \makeindex por índice, el reparto en el texto con \index[name]{…} y la salida con \printindex[name].

La invocación automática se apoya en el shell escape, y esa es justamente la parte que depende del entorno. En la configuración por defecto de TeX Live 2024, makeindex figura en la lista blanca del shell escape restringido, de modo que imakeidx construye el índice incluso sin -shell-escape (con kpsewhich -var-value shell_escape_commands se consulta la lista de la propia instalación). xindy, texindy, mendex y upmendex no figuran en ella, así que esos sí requieren -shell-escape. Allí donde el shell escape está prohibido del todo —algunos sistemas de envío, una CI estricta— la invocación automática no está disponible; se vuelve entonces a la secuencia de tres pasos llamando a makeindex a mano, o se deja el asunto en manos de 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