标题说明与子图

每幅图的编号都没错,偏偏 \ref 印出另一个数字——LaTeX 中报告最多的「图号错乱」,几乎总是因为把 \label 写在了 \caption 之前。既不报错也不警告,只有印出来的那个数字在悄悄撒谎。题注命令看着朴素,但编号、交叉引用和图目录三件事全在这里定下。本页讲 \caption 为何只能在浮动体内工作、\label 该放在哪里、用 caption 宏包调整体裁、在浮动体之外使用的 \captionof,以及标注 (a)(b) 的子图。

\caption 为什么只能在浮动体里用

\caption{…} 给图表添加说明文字,但只能在 figuretable 内部使用。直接写在正文里会以 ! LaTeX Error: \caption outside float. 中止。原因在于这条命令并不只是在排字,它会查看自己身处哪一类浮动体,并推进相应的计数器:在 figure 里推进 figure 计数器,在 table 里推进 table 计数器。浮动体之外没有这个信息,也就无从编号。这同时说明编号根本不用你自己写——也不该写。

latex
\begin{figure}
  \centering
  \includegraphics[width=0.6\textwidth]{plot}
  \caption{Measured values against the theoretical curve}
  \label{fig:plot}   % after \caption -- always
\end{figure}

也说说放置惯例:图的题注通常在图下方,表的题注通常在表上方。 表格是自上而下读的,先说明这是什么表更自然。LaTeX 会在你写 \caption 的位置原样排出,所以要放上方就写在图版之前,要放下方就写在之后;编号与正文之间的间距等格式本身不随位置改变。不过这个方向多半由投稿要求决定,请先查阅期刊的体例指南。

\label 写在 \caption 之前会发生什么

\label 必须写在 \caption 之后。 \label 所做的只是记录「当前编号」,而把当前编号更新为图号的正是 \caption。所以先写 \label,它记下的就是最后变动过的那个计数器——通常是节号。实测:在第 5 节里排一幅本应编号为 2 的图,把 \label 放在 \caption 之前,辅助文件里会写下 \newlabel{fig:before}{{5}{1}...},正文则印出「图 5」。既不报错也不警告。 除非亲眼核对输出,这个错误会一直活到定稿。

这个陷阱是有固定形状的。人们本能地想在 \includegraphics 之后紧接着写 \label,仿佛是在给图片挂名牌。但对 LaTeX 来说,\label 指向的不是图片,而是一个计数器,而设定该计数器的正是 \caption。对策很简单:永远让 \caption\label 挨在相邻两行。中间不夹任何东西,就没有写错顺序的余地。子图同理——subfigure 环境里的 \label 要放在该子图自己的 \caption 之后。\label\ref 的通用机制由交叉引用页面详述。

短标题与图目录:\caption[...]{...}\caption*\ContinuedFloat

题注很长,而图目录里只想放一行——为此准备的可选参数就是 \caption[短标题]{长正文}方括号里的内容进入 \listoffigures 一览,花括号里的内容排在图旁边。 省略方括号,完整正文也会灌进目录,只要有几幅图带三行题注,目录就完了。在测试文档里写 \caption[Short entry for the list]{A long caption that is only shown under the figure} 并输出 \listoffigures.lof 文件里只会记下短的那一条。当题注含有公式或 \cite 时,把方括号那一版写得朴素些,还能避免目录排版出事故。

载入 caption 宏包后,还能用上两个变体。\caption*{…} 产生的题注既不编号也不进目录,适合卷首插图或章首图版。\ContinuedFloat 则让一个浮动体沿用前一个浮动体的编号,这正是必须把大图拆成数个浮动体时所需要的。在 \begin{figure} 开头写上 \ContinuedFloat 再写 \caption{Continued},印出来的是仍标着「图 1」的第二张。要把跨越数页的大型一览图拆开而不让编号膨胀,这就是标准做法。

latex
% short entry for the list, full text under the figure
\caption[Measured values and the model]{Measured values (dots) against the
  theoretical curve (solid). Error bars are one standard deviation.}

% no number, no entry in the list of figures
\caption*{Frontispiece}

% second sheet of a split figure keeps the previous number
\begin{figure}
  \ContinuedFloat
  \centering\includegraphics[width=\linewidth]{survey-part2}
  \caption{Continued}
\end{figure}

用 caption 宏包调整体裁:\captionsetup

要改变字体、编号与正文之间的分隔符或对齐方式,就用 caption 宏包。作者是 Axel Sommerfeldt,版权声明可上溯到 1994 年,TeX Live 2024 里的是 2023 年 8 月的 v3.6o——一件由一人之手维护了三十年的工具。用 \usepackage{caption} 载入,用 \captionsetup{键=值, …} 配置。这条命令的要点在作用域:写在导言区就管整篇文档,写在浮动体内部就只管那一条题注

常见值作用
formatplain / hang正文的排法;hang 让续行按标签宽度缩进
labelsepcolon / period / space / quad / newline编号与正文之间的分隔;默认是 colon
fontsmall / footnotesize / it …整条题注的字体和字号
labelfontbf / sc / it …仅「Figure 1」标签部分的字体
textfontit / rm …仅说明文字部分的字体
justificationjustified / centering / raggedright对齐方式;默认两端对齐
width长度(如 0.8\textwidth收窄题注的换行宽度
singlelinechecktrue / false默认开启;无论 justification 为何,都把单行题注居中

最后一个 singlelinecheck 是最容易与意图相左的默认值。明明写了 justification=raggedright,短题注却偏偏居中,原因就在这里:能放在一行内的题注会被自动居中。要始终左对齐,请加上 singlelinecheck=false。若想让图和表体裁不同,就在方括号里指定浮动体类型\captionsetup[figure]{…}\captionsetup[table]{…}。期刊模板往往自带 \captionsetup,添加自己的设置之前,最好确认一下有没有覆盖掉模板的。

latex
\usepackage{caption}
\captionsetup{labelfont=bf, labelsep=period, font=small,
              justification=raggedright, singlelinecheck=false}

% different rules per float type
\captionsetup[figure]{justification=centering}
\captionsetup[table]{font=footnotesize}

给浮动体之外的东西加题注:\captionof

想让图固定在正文的这个位置而不随浮动体漂移,同时编号和引用照常生效——为此准备的命令就是 \captionof{类型}{正文}。在 \caption 无法使用的 minipagecenter 内部,写 \captionof{figure}{…} 就能推进 figure 计数器、进入图目录,\ref 也能正确解析。实际在 minipage 里排一遍:若前一个浮动体是「图 1」,这一个就顺理成章成为「图 2」,.lof 里也多出一行。这条命令的要点正在于此:只卸掉浮动机制,保留编号机制。

有一点要注意:\captionof 不是 LaTeX 标准命令,不载入 caption 就写它会得到 ! Undefined control sequence.。如果只想要这条命令而不想改题注体裁,可以用轻量的 capt-of 宏包,它只提供 \captionof。另外,用 \captionof 时,\label 依然要放在它之后,这条规则不变。

latex
\usepackage{caption}   % or the lightweight capt-of

\begin{center}
  \includegraphics[width=0.5\textwidth]{diagram}
  \captionof{figure}{A figure fixed in the text, outside any float}
  \label{fig:inline}
\end{center}

用 subcaption 把图拆成 (a)(b)

在一个图里并排放小图,各自带上 (a)(b)(c) 小标签——当前的标准是 subcaption 宏包,同样出自 Sommerfeldt 之手。写 \usepackage{subcaption} 就会在幕后载入 caption,不必两个都写。主角是 subfigure 环境(表格用 subtable),它需要必填的宽度参数,如 \begin{subfigure}[b]{0.45\textwidth}。它本质上是个 minipage,里面放 \includegraphics\captionsubfigure 内部的 \caption 会成为 (a)/(b) 小标签,放在外部的 \caption 则负责整幅图的编号。

latex
\usepackage{graphicx}
\usepackage{subcaption}   % loads caption itself

\begin{figure}
  \centering
  \begin{subfigure}[b]{0.45\textwidth}
    \centering
    \includegraphics[width=\linewidth]{before}
    \caption{Before}
    \label{fig:before}
  \end{subfigure}
  \hfill
  \begin{subfigure}[b]{0.45\textwidth}
    \centering
    \includegraphics[width=\linewidth]{after}
    \caption{After}
    \label{fig:after}
  \end{subfigure}
  \caption{Before and after processing}
  \label{fig:compare}
\end{figure}

排出这个例子,整幅图成为「图 1」,两个子图排成「(a) Before」和「(b) After」。正文里 \ref{fig:compare} 返回「1」,\ref{fig:before} 返回 「1a」——对子图的引用就是图号与子标签的拼接。若只想要 (a) 那部分,\subref{fig:before} 给出裸的「a」,\subref*{fig:before} 给出「(a)」。另外还有可写成一行的 \subcaptionbox[目录条目]{小标题}[宽度][内部位置]{内容}。它的语法要求\label 放进小标题参数里,写作 \subcaptionbox{Before\label{fig:before}}{\includegraphics{…}}。对于只放一张图的子图,它比写好几行 minipage 短得多。

subfigure、subfig、subcaption:三代包与一场无声的冲突

子图宏包有三代,名字相近,误用不断。最早的 subfigure 已废弃,使用 \subfigure 命令;它的后继 subfig 提供 \subfloat,但也不再积极维护。新文档请选 subcaption。麻烦出在迁移途中——没注意到模板已经载入了 subfig,又自己加上了 subcaption

这场冲突讨厌之处在于:载入的时候什么也不会发生。 两个都写进导言区,subcaption 也只在日志里写一行信息 Package subcaption Info: The counter 'subfigure' was already defined by...,既不警告也不报错,然后悄悄放弃定义自己的 subfigure 环境。文档一路顺畅,直到遇上第一个 \begin{subfigure} 才轰然倒塌,抛出 ! LaTeX Error: Environment subfigure undefined.,随后连锁出现 Missing numberIllegal unit of measure。也就是说,报错的地点并不是出问题的地点。 见到这种症状,请先在整个导言区搜索 subfigsubfigure——使用期刊模板时中招的概率尤其高。

  • subcaption — 当前标准:subfigure / subtable 环境、\subcaptionbox\subref。会自动载入 caption
  • subfig — 上一代,提供 \subfloat。既有文档里能用,新文档不要选它。
  • subfigure — 最早一代,提供 \subfigure。已废弃,不要使用。
  • 不可混用 — 只能选其一。添加之前先确认模板是否已经载入了一个。