整理一下关于LaTeX中插入图片和表格的笔记。目前使用的发行版为 TexLive 2024,系统为 Windows 和 Linux(Ubuntu22)。

图片

只考虑 pdflatexxelatex 这两种编译引擎,考虑的图片格式包括:矢量图(pdf, eps),位图(png, jpg)。两种编译引擎都直接支持这些图片格式。

includegraphics

LaTeX 本身不支持插图功能,需要 graphicx 宏包提供支持(或者更基础的 graphics 宏包,graphicx 宏包相当于其扩展)

1
\usepackage{graphicx}

导入这个宏包之后,就可以使用 \includegraphics 命令来插入图片

1
\includegraphics[<options>]{<filename>}

例如

1
2
\includegraphics{example.png}
\includegraphics{figure/example.png}

这里的图片文件名可以用相对路径或绝对路径表示,但是文件名里既不要有空格或其它特殊符号,否则宏包在解析文件名的过程中会出错。 图片文件的后缀名一般可以省略不写,编译时会自动补充。

在省略图片文件后缀时,如果存在多个仅后缀不同的图片文件,显然有具体的查找顺序,测试发现似乎会优先选择pdf格式。 对于eps格式的图片,因为涉及到自动转换为pdf格式,所有省略后缀与否可能对pdflatex的编译产生明显影响。

完整示例如下

1
2
3
4
5
6
7
8
9
\documentclass{article}
\usepackage{graphicx}

\begin{document}

\noindent
\includegraphics[width=0.5\textwidth]{figure/demo}

\end{document}

graphicx 宏包提供了 \graphicspath 命令,用于声明图片文件的存放目录,在搜索图片时会自动去这些目录中寻找,例如

1
\graphicspath{{./figure/}{./figures/}{./image/}{./images/}{./graphic/}{./graphics/}}

这些目录是常见的存放图片的目录,即使目录不存在也不会报错。

\includegraphics 命令支持如下几个常见选项:

  • width=<width>:将图片缩放到宽度为 <width>
  • height=<height>:将图片缩放到高度为 <height>
  • scale=<scale>:将图片相对于原尺寸缩放 <scale>
  • angle=<angle>:将图片逆时针旋转 <angle>

例如

1
2
% 将图片缩放到宽度为文本宽度的一半,并逆时针旋转 45 度
\includegraphics[width=0.5\textwidth, angle=45]{example.png}

指定宽度是最常见的需求,例如width=0.8\textwidth代表将图片宽度缩放到 80% 的文本宽度。

graphicx 宏包支持草稿选项:当 graphicx 宏包或文档类指定 draft 选项时,图片将不会被实际插入,取而代之的是一个包含文件名的与原图片等大的方框,这有助于加速编译。

浮动体 figure

\includegraphics 命令只能就地插入图片,但是正文中可能包含许多图片和表格等内容,这些内容的尺寸往往太大,导致与文字的排版困难,效果非常混乱,LaTeX 为此引入了浮动体的机制,对浮动体的排版允许脱离原始的上下文,自动填入合适的位置。

LaTeX 预定义了两类浮动体环境:figuretable。习惯上在 figure 中放置图片,在 table 中放置表格,但并没有严格限制,实际上可以在其中放置任意内容。两种浮动体环境的用法类似,下面讨论 figure 环境。

figure环境被用来包裹最基本的命令,得到浮动的图片,例如

1
2
3
4
\begin{figure}[htbp]
\centering
\includegraphics[width=0.8\textwidth]{demo.png}
\end{figure}

习惯上对于浮动图片都会使用\centering命令使图片水平居中放置。

浮动体的使用主要是如下的几个浮动规则参数:

  1. h (here):允许放置在当前上下文位置
  2. t (top):允许放置在当前页或后续页面的顶部
  3. b (bottom):允许放置在当前页或后续页面的底部
  4. p (page):允许放置在一个独立页面中

我们可以选择开启其中的部分规则,例如[hbp]就指定允许基于这三种规则来放置浮动体,默认设置为tbp

可以在选项中加上!来忽略LaTeX对浮动体排版的默认限制,默认限制包括:

  • 浮动体个数:除单独成页外,默认每页不超过 3 个浮动体,其中顶部不超过 2 个,底部不超过 1 个。
  • 浮动体空间占页面的百分比:默认顶部不超过 70%,底部不超过 30% 。

需要注意的是:在实际排版时,对浮动体放置规则的选择顺序是固定的(h-t-b-p),与写法无关,例如[!htp][ph!t]是完全一样的。

在双栏排版环境下,LaTeX 还提供了 table*figure* 环境用来排版跨栏的浮动体,它们的用法与 tablefigure 类似,不同之处为横跨双栏的浮动体只允许使用tp 两个规则。

float 宏包为浮动体环境提供了 H 位置参数,不要与 htbp! 混用。使用 H 位置参数时,会取消浮动机制,将浮动体视为一般的盒子插入当前位置。

浮动体提供了 \caption 命令添加标题,此时会自动给浮动体编号,\caption 命令之后还可以紧跟 \label 命令标记交叉引用。 例如

1
2
3
4
5
\begin{figure}[htbp]
\centering
\includegraphics{demo.png}
\caption{Title}\label{fig:1}
\end{figure}

一个值得注意的细节:\caption 命令的位置也比较重要:

  • 如果 \caption 命令放在 \includegraphics 命令之前,则标题在图片上方;
  • 如果 \caption 命令放在 \includegraphics 命令之后,则标题在图片下方。

习惯上,图片的标题建议放置在图片下方,而表格的标题建议放置在表格上方。

与编号的\caption命令类似,\caption* 命令会生成不带编号的标题,但是这个命令是caption宏包提供的。

下面是实践中常用的插入图片的代码片段:

  • 不含标题的图片

    1
    2
    3
    4
    \begin{figure}[htbp]
    \centering
    \includegraphics[width=0.8\textwidth]{demo.png}
    \end{figure}

  • 含有标题和标签的图片

    1
    2
    3
    4
    5
    \begin{figure}[htbp]
    \centering
    \includegraphics[width=0.8\textwidth]{demo.png}
    \caption{Results}\label{fig:result1}
    \end{figure}

多个子图

我们时常有在一个浮动体里面放置多张图的用法(通常是并列子图),有非常多的方法可以实现这样的效果,有的不依赖宏包,有的依赖宏包。

第一种方案如下,直接在一个浮动体中使用两次\includegraphics命令,在两个图片之间需要指定间距

1
2
3
4
5
6
7
\begin{figure}
\centering
\includegraphics[width=...]{a.jpg}
\hspace{1in} % or \qquad
\includegraphics[width=...]{b.jpg}
\caption{Demo}
\end{figure}

但是这种方案只能使用一个\caption命令,只有一个图片标题(和标签),对两个宽度和间距需要具体调整。

第二种方案如下,创建两个minipage环境,此时两个图片可以有独立的标题和标签

1
2
3
4
5
6
7
8
9
10
11
12
\begin{figure}
\begin{minipage}{...}
\centering
\includegraphics[width=...]{a.jpg}
\caption{Demo 1}\label{fig:a}
\end{minipage}
\begin{minipage}{...}
\centering
\includegraphics[width=...]{b.jpg}
\caption{Demo 2}\label{fig:b}
\end{minipage}
\end{figure}

这种方案也比较麻烦,需要控制很多的宽度。

上面两种方案看起来都太原始了,当我们需要更进一步,给每个图片定义小标题时,就要通过 subcaption 宏包实现。

1
\usepackage{subcaption}

实际上有很多宏包可以实现多个子图的功能,例如subfigure(版本过旧,被弃用),subfigsubcaption。这些宏包相互之间是冲突的,有的版本过旧已经被舍弃,目前只建议使用subcaption

使用 subcaption 宏包可以给每个子图定义单独的标题和标签,子图的编号与父图的编号具有上下级关系,例如Fig. 3.1(a)

使用 subcaption 宏包的代码片段如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
\begin{figure}[htbp]
\centering
\begin{subfigure}[b]{0.47\textwidth}
\centering
\includegraphics[width=\textwidth]{a.png}
\caption{Demo a}\label{fig:subfig-a}
\end{subfigure}
\hfill
\begin{subfigure}[b]{0.47\textwidth}
\centering
\includegraphics[width=\textwidth]{b.png}
\caption{Demo b}\label{fig:subfig-b}
\end{subfigure}
\caption{Demo}\label{fig:example}
\end{figure}

其中的\hfill命令会插入弹性的水平间距,比固定的\qquad更合适。

虽然subfigure宏包已经被弃用了,但是鉴于我之前使用过,还是记录一下对应的代码片段。

1
2
3
4
5
6
7
8
9
10
11
12
% \usepackage{subfigure}

\begin{figure}[htbp]
\centering
\subfigure[Demo a]{
\includegraphics[width=7.25cm]{a.png}
}
\subfigure[Demo b]{
\includegraphics[width=7.25cm]{b.png}
}
\caption{Demo}\label{fig:1}
\end{figure}

表格

在 LaTeX 中,表格功能由 tabular 环境提供,与图片类似的浮动机制由 table 环境提供。常见的表格类型包括普通文本表格、带线表格、跨列/跨行表格、以及更复杂的长表格和三线表格。

tabular

LaTeX 内置的 tabular 环境用于创建最基本的表格,其语法为

1
2
3
4
\begin{tabular}{<alignment>}
<cell_11> & <cell_12> & ... \\
<cell_21> & <cell_22> & ... \\
\end{tabular}

其中 <alignment> 用于指定每一列的对齐方式:

  • l:左对齐(left)
  • c:居中对齐(center)
  • r:右对齐(right)

在表格内部:

  • 每一行的单元格之间使用 & 分隔,实际使用的单元格数目可以等于或少于 <alignment> 声明的列数,此时后面的单元格留空;
  • 表格内部的正常换行不会对应表格换行,必须每一行的末尾使用 \\ 手动换行(包括最后一行)。

一个简单示例如下:

1
2
3
4
5
\begin{tabular}{lcr}
A & B & C \\
AA & BB & CC \\
AAA & BBB & CCC \\
\end{tabular}

输出为一个三行三列的表格,依次为左中右对齐,不含任何边框线。

<alignment> 还支持更丰富的效果,包括:

  • p{<width>}:固定单元格的宽度,如果超出会自动折行
  • @{<string>}:在单元格之间插入固定的文本内容,如果留空 @{} 则会消除默认的单元格之间的间距,例如 @{}ccc@{} 会消除表格两端的空白。

添加边框线

<alignment> 中使用 | 可以添加竖线,在表格内部使用 \hline 可以添加横线。例如:

1
2
3
4
5
6
7
8
\begin{tabular}{|l|c|r|}
\hline
A & B & C \\
\hline
AA & BB & CC \\
AAA & BBB & CCC \\
\hline
\end{tabular}

这样就会得到一个带边框和一些横线的表格。

除了跨越整个表格的横线命令 \hline,还有更灵活的横线命令 \cline,接受两个参数指定横线开始和结束的列(闭区间,允许两个值相同)

1
\cline{i-j}

例如

1
2
3
4
5
6
7
8
9
\begin{tabular}{|c|c|c|}
\hline
4 & 9 & 2 \\
\cline{2-3}
3 & 5 & 7 \\
\cline{1-1}
8 & 1 & 6 \\
\hline
\end{tabular}

浮动体 table

tabular 很少直接使用,通常嵌入浮动体环境 table 中,用法与图片对应的 figure 环境类似。

例如

1
2
3
4
5
6
7
8
9
10
11
12
\begin{table}[htbp]
\centering
\caption{Demo Table}\label{tab:demo}
\begin{tabular}{|l|c|r|}
\hline
A & B & C \\
\hline
AA & BB & CC \\
AAA & BBB & CCC \\
\hline
\end{tabular}
\end{table}

浮动规则 [htbp]、标题命令 \caption 与图片浮动体一致。

习惯上在浮动体中将图片的标题放置在下方,而表格的标题则放置在上方。

合并单元格

表格排版的一个常见需求是合并单元格,包括横向合并与纵向合并。由于 LaTeX 默认按行生成表格,横向合并较为简单,纵向合并则更加复杂,需要额外的宏包支持。

拆分单元格也是一个基本需求,但是在实现上会更加复杂,建议尽量用单元格合并替代单元格拆分。

横向合并

使用 \multicolumn 命令即可完成单元格的横向合并,命令语法如下

1
\multicolumn{<n>}{<alignment>}{<content>}

参数依次为合并的列数,格式以及内容。

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
\begin{table}[htbp]
\centering
\begin{tabular}{|c|c|c|}
\hline
1 & 2 & 3(Center) \\
\hline
\multicolumn{2}{|c|}{12} &
\multicolumn{1}{r|}{3(Right)} \\
\hline
1 & \multicolumn{2}{c|}{23} \\
\hline
\end{tabular}
\end{table}

形如 \multicolumn{1}{<column-spec>}{<item>} 的命令没有进行合并,但是可以用来局部调整某一个单元格的列格式。

纵向合并

纵向合并单元格的命令 \multirowmultirow 宏包提供

1
\usepackage{multirow}

命令语法如下

1
\multirow{<nrows>}{<width>}{<content>}

参数依次为合并的行数,宽度以及内容,宽度参数通常填 * 以使用自然宽度,在其它行被合并的单元格位置需要留空。

注意在涉及到纵向合并的位置,\hline 仍然穿越单元格,因此需要使用 \cline 替换。

例如

1
2
3
4
5
6
7
8
9
10
\begin{table}[htbp]
\centering
\begin{tabular}{c|cc}
\hline
\multirow{2}{*}{S} & B1 & C1 \\
\cline{2-3}
& B2 & C2 \\
\hline
\end{tabular}
\end{table}

有一个名称非常类似的宏包 multicol,但是它不是用在表格中,它提供的环境 multicols 用于在正文中创建多栏布局。

三线表

LaTeX 默认的表格线较粗、视觉效果一般,在某些领域更喜欢使用三线表,可以使用 booktabs 宏包生成更美观的经典三线表

1
\usepackage{booktabs}

此时可以使用几种不同的横线:\toprule(顶线),\midrule(中线),\bottomrule(底线),顶线底线相比中线更粗。例如

1
2
3
4
5
6
7
8
9
10
11
12
\begin{table}[htbp]
\centering
\caption{Comparison of methods}\label{tab:compare}
\begin{tabular}{lcc}
\toprule
Method & Accuracy & Time (s) \\
\midrule
A & 95.2 & 12.5 \\
B & 93.8 & 10.9 \\
\bottomrule
\end{tabular}
\end{table}

在三线表中,不建议与竖线 | 混用,可以保持排版简洁。

补充

关于pdflatex插入eps图片

pdflatex 对于 eps 格式图片的处理比较特殊:

  • 以前很长一段时间都需要依赖 epstopdf 宏包;
  • 在较新的发行版中,不再需要我们手动导入这个宏包,可以直接插入eps图片。(lshort-zh-cn)

但是测试发现,使用pdflatex插入eps图片的处理非常麻烦,很容易出现问题,

例如TexLive2023,在Windows上,使用pdflatex插入eps图片时,必须导入epstopdf这个包(或者hyperref测试发现也可以), 在编译时加上-shell-escape编译选项,否则会出现编译失败。 这个问题在网上也有出现,例如TeXLive2023 pdflatex编译eps图像,出现错误的问题。 换成TexLive2024,或者换成Linux,或者换成xelatex,这个现象都不会发生。

可以使用下面的办法来绕过这类使用情景:

  • 更换编译引擎:相比于pdflatexxelatex对于图片的支持要好得多,无须加入专门的宏包,在各个系统中都可以直接使用,并且带不带.eps后缀没有影响。
  • 更换图片格式:将eps格式图片手动转换成pdf格式其实是最简单的办法,可以直接使用epstopdf命令(TexLive提供)在命令行中完成图片的格式转换。
    1
    2
    # demo.eps -> demo.pdf
    epstopdf demo.eps

pdflatex实际上在编译时会尝试执行类型转换,自动在demo.eps图片所在目录生成demo-eps-converted-to.pdf,前面说的问题就出在这个环节。

当然,最好的方法就是不要纠结BUG,直接卸载TexLive 2023,安装最新的TexLive 2024。