El índice impreso en la página uno de tu PDF no lo escribió la compilación que produjo ese PDF. \tableofcontents, \listoffigures y \listoftables leen cada una un archivo pequeño que dejó la compilación anterior de LaTeX: .toc, .lof y .lot. Y esos archivos no son cachés de texto sino programas cortos, una línea por entrada, que la siguiente pasada ejecuta. De ese único hecho se deduce casi todo lo desconcertante de estos tres comandos: por qué un documento nuevo no muestra nada, por qué los números de página fallan la primera vez, por qué una nota al pie dentro de un título de sección revienta en la segunda compilación y no en la primera, y por qué LaTeX no avisa ni una sola vez de que el índice impreso está obsoleto.
Qué contiene realmente un archivo .toc, .lof o .lot
Un comando por entrada, y siempre \contentsline. Las tres listas comparten un mismo mecanismo: \tableofcontents gobierna el .toc, \listoffigures el .lof y \listoftables el .lot, y cada uno de esos archivos lleva el nombre del archivo raíz. \contentsline toma cuatro argumentos: el tipo de entrada, el texto que se imprime, el número de página y un destino de enlace. El cuarto queda vacío en LaTeX puro; al cargar hyperref se rellena con un destino PDF como section.1.1. Un .toc no es, por tanto, un borrador del índice, sino una secuencia de instrucciones entregada a la siguiente pasada.
% one \contentsline per entry: unit, text, page, link target
\contentsline {chapter}{\numberline {1}Body}{5}{chapter.1}%
\contentsline {section}{\numberline {1.1}Short form}{5}{section.1.1}%
% and in mydoc.lof, written by \caption inside a figure:
\addvspace {10\p@ }
\contentsline {figure}{\numberline {1.1}{\ignorespaces Short caption}}{5}{figure.1.1}%Esas líneas, sin embargo, no se escriben directamente en el archivo del índice. Primero se acumulan en el .aux como \@writefile{toc}{...}, y solo cuando LaTeX cierra y vuelve a leer el .aux en \end{document} pasan al .toc. El rodeo tiene dos consecuencias prácticas. Primera: el canal de escritura lo abre el propio \tableofcontents, de modo que si ese comando no aparece en ninguna parte, no se produce ningún archivo .toc; las entradas se quedan en el .aux. Segunda: como la escritura ocurre de golpe al final, \tableofcontents puede ir en cualquier sitio. Colocado en la última página sigue dando un índice completo, con los títulos que lo preceden incluidos.
| Comando | Archivo que escribe | Origen de las entradas |
|---|---|---|
\tableofcontents | .toc | los títulos de \chapter a \subparagraph, más \addcontentsline{toc}{...} |
\listoffigures | .lof | \caption dentro de una figure; el argumento opcional corto si se da |
\listoftables | .lot | \caption dentro de una table; mecánicamente idéntico al .lof |
\addcontentsline | la extensión indicada | una línea escrita a mano; el número de página es el \thepage de ese instante |
\addtocontents | la extensión indicada | material en vez de una entrada: espaciado, comandos de formato |
Por qué el índice sale vacío y por qué LaTeX nunca avisa
Porque en la primera pasada no hay ningún .toc que leer. El registro muestra la única línea No file mydoc.toc., y \tableofcontents compone su encabezado y sigue adelante. El archivo se escribe al final de esa pasada, así que las entradas llegan al papel en la segunda. Y en la segunda el índice ya ocupa páginas, lo que desplaza todos los números posteriores; a veces hace falta una tercera pasada para que todo se asiente. Una pasada guarda la información y otra la recupera: es exactamente el mecanismo en dos tiempos de \label y \ref, que la página de referencias cruzadas explica al detalle.
Y aquí está la mitad que casi nunca se menciona: LaTeX no avisa ni una vez de este desfase. Una referencia indefinida produce LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right., pero solo porque las etiquetas se comparan con su valor anterior en el .aux. El índice no recibe esa comparación. Cuando la tabla impresa contradice al .toc recién escrito, el registro calla por completo: revisa el registro de una pasada que produjo un índice en blanco y no encontrarás ni un aviso. Ese es el argumento real para dejar que una herramienta como latexmk cuente las pasadas en tu lugar: repite hasta que el .toc deja de cambiar.
El mismo silencio aparece en una forma más desagradable. Si se pasa un manuscrito de report a article, el viejo .toc sigue conteniendo líneas del tipo \contentsline {chapter}{...}. article no define ningún \l@chapter, y como \contentsline se limita a llamar a \csname l@chapter\endcsname, un nombre indefinido se convierte silenciosamente en \relax: el título y el número de página quedan volcados en el índice como texto corriente. Ni error ni aviso, solo una línea enigmática del estilo «1 Alpha2». Siempre que el índice parezca corrupto tras cambiar de clase o de estructura de carpetas, lo más rápido es borrar el .toc (y el .aux) y volver a compilar.
Solo hay un tocdepth: el ajuste que vacía tu lista de figuras
tocdepth es un contador que nombra el nivel más profundo que se imprime en el índice: \setcounter{tocdepth}{1} se detiene en secciones, {2} llega a subsecciones. Los valores por defecto son 3 en article y 2 en book y report. Pero no es un contador exclusivo del índice. En article.cls y book.cls, \l@figure es \@dottedtocline{1}{1.5em}{2.3em}: cada entrada de la lista de figuras se compone en el nivel 1, y \l@table es un alias suyo. Y aquello con lo que \@dottedtocline se compara es el único tocdepth, compartido por las tres listas.
La consecuencia es cruel. En un book, decidir que el índice liste solo capítulos y escribir \setcounter{tocdepth}{0} vacía la lista de figuras y la de tablas. El .lof sigue conteniendo todas las entradas, pero el nivel 1 supera un tocdepth de 0, así que no se imprime ni una línea. No se emite ningún error. El remedio es breve: envolver \listoffigures en un grupo y subir ahí el tocdepth. Que tocdepth filtre al releer el archivo —razón por la cual cambiar la profundidad nunca obliga a regenerar el .toc y solo cuesta una pasada más— se trata en la página sobre la estructura del documento.
\setcounter{tocdepth}{0} % contents: chapters only
% ... but this alone would print an EMPTY list of figures.
% Raise the depth for the float lists only:
\begingroup
\setcounter{tocdepth}{1}
\listoffigures
\listoftables
\endgroup
% Because the .toc is a program, a depth change can also be
% injected into the middle of it, taking effect from here on:
\addtocontents{toc}{\protect\setcounter{tocdepth}{1}}Lo que se registra es el título corto: el argumento opcional de \section[...]
El corto, entre corchetes, es el que entra en el .toc; el largo, entre llaves, solo aparece en el cuerpo. Escribe \section[Forma corta]{Un título largo que se extiende por la página} y el título sigue siendo largo en la página mientras el índice y el encabezado toman la forma corta. \caption[Leyenda corta]{Una explicación larga} sigue la misma regla, y es la versión corta la que llega al .lof y al .lot; la página sobre leyendas trata ese lado en detalle. Lo que importa aquí es que este argumento opcional no es un lujo para embellecer.
Un título se escribe hacia el .toc: se vuelca en un archivo y se relee en la pasada siguiente. Así que si metes dentro un comando frágil, como \section{Título con nota\footnote{nota}}, la primera compilación pasa sin quejarse mientras la segunda se derrumba justo al releer el archivo. Runaway argument?, luego ! Paragraph ended before \contentsline was complete., luego ! Argument of \@sect has an extra }.: mensajes que parecen ajenos a cualquier título, aunque el culpable es la nota alojada en el .toc escrito un momento antes. El error llega con una pasada de retraso exactamente por la misma razón que el índice. La receta es el argumento opcional: \section[Título con nota]{Título con nota\footnote{nota}} mantiene la nota fuera del .toc y ya nada se rompe.
% the bracketed form is what lands in .toc, .lof and the running head
\section[Short form]{A long section title that would wrap in the contents}
% fragile material belongs in the braces only, never in the file
\section[Title with a note]{Title with a note\footnote{note text}}
\begin{figure}
\includegraphics{plot}
\caption[Short caption]{A long caption explaining every detail}
\end{figure}El mismo hecho —que un título se usa en tres sitios— reaparece con otro rostro en cuanto se carga hyperref. El título se reutiliza para los marcadores del PDF, y un marcador es texto puro: no admite matemáticas. Escribe \section{Propiedades de $\mathcal{A}$} y obtendrás Package hyperref Warning: Token not allowed in a PDF string (Unicode) mientras la fórmula desaparece en silencio. La salida es \texorpdfstring{$\mathcal{A}$}{A}, que entrega una versión al compositor y otra a la cadena; la página de hyperref lo explica.
Meter un título con estrella en el índice: addcontentsline y dónde ponerlo
Coloca \addcontentsline{toc}{section}{Introducción} en la línea inmediatamente posterior al título. Un \section* o \chapter* no lleva número ni escribe nada en el .toc, así que si quieres que aparezca has de inyectar tú mismo esa línea. Los tres argumentos son obligatorios.
ext— la extensión del archivo auxiliar de destino:tocpara el índice,lofpara las figuras,lotpara las tablas.unit— el tipo de entrada. Entocespart,chapter,section,subsection, etc., y se emplean el formato y la sangría de ese nivel; enlofesfigurey enlot,table.text— la cadena que se listará. Anteponer\protect\numberline{}alinea el título con las entradas numeradas; delante de cualquier comando frágil hace falta\protect.
La ubicación decide el resultado. Abre la definición en latex.ltx y \addcontentsline se limita a escribir \contentsline{unit}{text}{\thepage}{}: graba el número de página vigente en el instante en que esa línea se ejecuta. Como \chapter* abre una página nueva, poner la línea despistadamente antes del \chapter* registra la página anterior. El experimento es rotundo: la entrada colocada antes apuntaba a la página 2, y la colocada justo después, a la 3. El lector abre esa página y allí no hay ningún capítulo. LaTeX aporta el número por su cuenta, así que nunca se escribe en text.
% right: the line runs after the page break that \chapter* causes
\chapter*{Acknowledgements}
\addcontentsline{toc}{chapter}{Acknowledgements}
\section*{Introduction}
\addcontentsline{toc}{section}{Introduction}
% \addtocontents injects material, not an entry
\addtocontents{lof}{\protect\vspace{2ex}}Su compañero, \addtocontents{ext}{text}, inyecta material en lugar de una línea. Solo toma la extensión de destino y el contenido que se escribirá, sin número de página. Asómate a un .lof y encontrarás una línea que dice \addvspace {10\p@ }: es exactamente por esa vía como el propio LaTeX inserta espacio cada vez que cambia de capítulo. En resumen: una línea con número de página va por \addcontentsline; el espaciado y el formato van por \addtocontents. Ambos escriben pensando en la siguiente pasada, de ahí que un comando frágil como \vspace necesite \protect.
Listar las propias listas: tocbibind
Una sola línea, \usepackage{tocbibind}, y la lista de figuras, la de tablas, la bibliografía y el índice analítico aparecen solos en el índice. Esos encabezados no llevan número —\section* en article, \chapter* en book y report—, así que por su cuenta nunca aparecen. Puedes alinear llamadas a \addcontentsline a mano, pero con una bibliografía o un índice que ocupan varias páginas la ubicación se equivoca con facilidad, y el paquete es la apuesta más segura.
Por defecto también lista el índice dentro del índice, razón por la cual nottoc es la opción que casi todo el mundo busca primero. Las exclusiones son nottoc, notlof, notlot, notbib y notindex. En sentido contrario, numbib y numindex componen la bibliografía y el índice analítico como capítulos o secciones numerados en vez de encabezados sin número. El tocbibind que viaja en TeX Live 2024 es la v1.5k de 2010, de Peter Wilson, el mismo autor de tocloft.
% list everything except the contents itself
\usepackage[nottoc]{tocbibind}
% the manual equivalent, one line per list
\cleardoublepage
\addcontentsline{toc}{chapter}{\listfigurename}
\listoffiguresRehacer sangrías, fuentes y líderes punteados con tocloft
Carga \usepackage{tocloft} y cada nivel obtiene su propia sangría, ancho de número, fuente y líder punteado. Los nombres de comandos son sistemáticos: un prefijo de nivel (toc para part, chap para chapter, sec para section, subsec para subsection, fig para figuras, tab para tablas) combinado con una función. Sangría y ancho de número se fijan juntos, como en \cftsetindents{section}{1.5em}{2.5em}; si los números se ensanchan hasta chocar con el título, se amplía el tercer argumento. Las fuentes son distintas para el título de la entrada (\cftsecfont) y para su número de página (\cftsecpagefont).
El líder punteado esconde un truco que los nombres no revelan. El espaciado de los puntos es la longitud \cftdotsep (4.5 por defecto): más pequeño los junta, más grande los separa. Y \cftnodots, lo que se usa para quitar un líder, no es un indicador: dentro de tocloft.sty es sencillamente el número 5000, una separación tan ancha que no cabe ni un punto en la línea. El mismo truco explica un detalle visto mil veces sin reparar en él: \cftpartdotsep y \cftchapdotsep valen por defecto \cftnodots, y por eso en un índice estándar solo las líneas de parte y de capítulo carecen de puntos.
| Comando | Qué controla | Cómo se ajusta |
|---|---|---|
\cftsetindents | Sangría y ancho de número de un nivel | \cftsetindents{section}{1.5em}{2.5em} |
\cftsecfont | Fuente del título de una entrada de sección | \renewcommand{\cftsecfont}{\bfseries} |
\cftsecpagefont | Fuente del número de página de una entrada de sección | para capítulos, \cftchappagefont |
\cftsecleader | Líder punteado de una entrada de sección | sustituir el \cftdotfill{\cftdotsep} que contiene |
\cftdotsep | Espaciado de puntos; 4.5 por defecto, menor es más denso | \renewcommand{\cftdotsep}{2} |
\cftnodots | El número 5000: una separación en la que no cabe ningún punto | para eliminar por completo un líder |
\cftloftitlefont | Fuente del encabezado de la lista de figuras | para el índice, \cfttoctitlefont |
\usepackage{tocloft}
\renewcommand{\cftsecfont}{\bfseries}
\renewcommand{\cftsecpagefont}{\bfseries}
\renewcommand{\cftsecleader}{\bfseries\cftdotfill{\cftdotsep}}
\renewcommand{\cftdotsep}{2} % tighter dots
\cftsetindents{section}{1.5em}{2.5em} % indent, number width
% drop the leader on section lines altogether
\renewcommand{\cftsecleader}{\cftdotfill{\cftnodots}}Cuando tocloft se queda corto: titletoc y etoc
Mientras tocloft ajusta medidas y fuentes de las líneas existentes, titletoc y etoc reescriben la estructura de la línea. El corazón de titletoc (de Javier Bezos, en el mismo conjunto que titlesec) es \titlecontents, que define, nivel a nivel, el material previo a la línea, cómo se compone el número, el título, el relleno que lleva hasta el número de página y lo que sigue a la línea. Si bastan puntos sencillos, existe el atajo \dottedcontents. Más allá, \startcontents, \printcontents, \stopcontents y \resumecontents permiten colocar un índice parcial de un solo capítulo a la cabeza de ese capítulo. En un documento cuyos títulos ya moldea titlesec, el índice puede moldearse con el mismo idioma.
etoc (de Jean-François Burnol) va aún más lejos y rediseña el índice por completo mediante un marco de dos capas de «estilos de línea» y «estilos globales». Su pieza central es \localtableofcontents, que extrae del mismo .toc un índice parcial por capítulo cuantas veces haga falta; a este nivel incluso un índice en forma de árbol entra en el radio de acción. Como procedimiento de decisión, tres pasos funcionan bien: ajustar medidas y fuentes con tocloft; pasar a titletoc en cuanto haya que cambiar cómo se ensambla una línea; recurrir a etoc cuando se quiera asumir el diseño del índice en sí.