ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LaTeX图片插入完全指南:从浮动体机制到排版实战

LaTeX图片插入完全指南:从浮动体机制到排版实战

LaTeX排版经验总结这个系列,我第一篇就想写图片插入。原因无他——几乎每个刚上手LaTeX的人,都会在插图这一步碰一鼻子灰:明明按教程写了\includegraphics,编译却报"File not found";图片放得好好的,一编译就跑到了下一页;双栏模板里大图把版面撑得乱七八糟。这些问题我全踩过,后来才慢慢明白,LaTeX的图片插入本质上是一套"浮动体管理"逻辑,和所见即所得编辑器里手动拖图完全是两种思路。这篇文章不准备讲高深技巧,只把我写论文、写技术报告期间积累的图片插入经验,从宏包配置、浮动位置、尺寸控制到编译链路和报错排查,完整梳理一遍。适合刚开始用LaTeX写论文的同学,也适合被图片排版折磨过但还没理清思路的朋友。

1. 图片插入的地基:宏包、环境与最小示例

1.1 graphicx宏包:图片插入的入口

一上来先装宏包。LaTeX插图的几乎所有核心能力,都来自graphicx:

\usepackage{graphicx}

这个宏包提供了最常用的\includegraphics命令。不加它,你连最基础的插图都做不了。早期还会有人让你根据编译引擎加[pdftex]或[dvipdfmx]这样的驱动选项,以指定图片应该由哪个底层驱动处理,但现在的主流发行版基本都能自动检测,直接写\usepackage{graphicx}就够了,无须手动指定驱动,加了反而容易出现多余警告。

graphicx真正厉害的地方在于,它把"图片文件"的加载方式抽象成了参数化的形式。你可以通过可选的键值参数控制插入图片的宽度、高度、旋转角度、裁剪区域等等。建议把graphicx加入你的固定宏包清单,凡是写文档,先从它开始。

这里说一下我的习惯:文档里第一次使用某个宏包时,最好把它的名字和用途在注释里记下来。我见过不止一位同学,图插不出来时,翻到导言区才发现自己压根没加载graphicx。这种问题最难排查,因为报错信息往往不是"你忘了宏包",而是"文件找不到"这类间接提示。

1.2 figure浮动环境:图片不是"放进去"的

初学者最常走的弯路,是直接在正文中间写一行\includegraphics{xxx.png},然后奇怪图片为什么和文字挤在一起、编号没法自动管理。正确做法是把图片放进figure浮动环境中:

\begin{figure}[htbp] \centering \includegraphics[width=0.8\textwidth]{figures/architecture.png} \caption{系统架构示意} \label{fig:architecture} \end{figure}

这里每个元素都有讲究。

  • figure环境本身是一个"浮动体",它允许LaTeX在排版时把图片挪到合适的位置,比如页面顶部或者单独一页。这样做的目的是避免图片处留下的巨大空白,把版面利用得更紧凑。
  • \centering让图片在环境中居中显示。注意它不是\centerline,也不是center环境,后两者会产生额外垂直间距。
  • \caption{...}负责生成图注,图片的编号也是由它负责的。
  • \label{fig:architecture}给这个图贴一个标签,之后在正文里用\ref{fig:architecture}就能自动引用编号。写论文时,你经常要写"如图3所示",用这个机制可以省掉手改编号的麻烦。

有一个顺序问题要特别注意:\label一定要放在\caption之后。如果放在\caption前面,\ref引用到的往往是上一张图的编号或者干脆是当前章节的编号,这是很多人遇到的"引用编号不对"问题的根源。

1.3 一个能跑通的最小示例

拿一段可以直接复制编译的示例来说明。

\documentclass{article} \usepackage{graphicx} \begin{document} 本文的系统结构如图\ref{fig:arch}所示。 \begin{figure}[htbp] \centering \includegraphics[width=0.8\textwidth]{figures/arch.png} \caption{系统结构图} \label{fig:arch} \end{figure} \end{document}

把figures/arch.png这个文件放在文档所在目录下的figures子目录里,用pdflatex编译,就能得到带编号、带图注的插图效果。第一次跑这个示例时,我建议你故意把路径写错一次,亲眼看看报错长什么样,以后再遇到就不会慌。比如把figures/arch.png写成arch.png,编译时就会出现类似File 'arch.png' not found的提示。这种"故意制造错误"的练习法,比单纯抄代码更能帮你建立排查直觉。

2. 浮动位置:理解LaTeX的"自动排版"逻辑

2.1 位置参数的真实含义

图不按你的剧本来,是LaTeX新手最大的痛点。你在[htbp]里写的字母,分别代表:

  • h(here):尽量放在当前位置;
  • t(top):放在页面顶部;
  • b(bottom):放在页面底部;
  • p(float page):单独放在一个浮动页上。

很多人以为写了[h],图片就必须原地出现。不是的。h在LaTeX内部是一个"允许项",一个"尽量项",而不是"强制项"。LaTeX会根据版面情况决定是否真的放在当前位置;如果当前页剩余空间放不下,或者浮动体队列里已经积累了太多对象,LaTeX会自行调整。真正的"强制"位置是[H],来自float宏包,用法:

\usepackage{float} \begin{figure}[H] \centering \includegraphics[width=0.6\textwidth]{figures/example.png} \caption{强制定位示例} \label{fig:force} \end{figure}

2.2 为什么[h]经常不生效

这里值得多说一句背后的逻辑。LaTeX的浮动算法会维护一个"浮动体队列",图片不是一下子放到页面上的,而是先进入队列,再在合适的时机被"冲"到页面上。LaTeX会在页面的顶部、底部、文字中间寻找空位。为了防止版面失衡,它有一组内部预设的阈值:

  • \topfraction:页面顶部最多能放多少比例的浮动体;
  • \bottomfraction:页面底部最多能放多少比例;
  • \textfraction:页面上至少保留多少比例的正文文字;
  • \floatpagefraction:浮动页至少填充多大比例才允许成立。

我写论文时经常遇到一种情况:文字写得比较满,图片比例又比较大,于是\textfraction要求当前页必须有足够的正文,图就被整体推到了下一页。这不是bug,是LaTeX在维护排版的美观性。打个比方,它像一条运河里的航运调度:每一张图都是一艘货船,得等水流条件合适、水位够了,才放行通过。

如果你确实想放松限制,可以手动调这几个宏,例如:

\renewcommand{\topfraction}{0.9} \renewcommand{\bottomfraction}{0.8} \renewcommand{\textfraction}{0.1}

这些数值的意思是:允许页面顶部和底部容纳更大的浮动体、允许正文最少只占10%。调完之后,图片被赶走的情况会明显减少。但我要提醒一句:不要为了"图必须在某处"而把阈值改得过激进,否则文章会变得像补丁摞补丁。我在早期的论文里就吃过这个亏,为了把一张图固定在理想位置,一顿操作把版面搞得乱七八糟,最后投稿返工,反而更浪费时间。排版这事,局部有时需要为整体让路。

2.3 [H]到底该不该用

[H]强制定位的优点,是可以按照代码顺序逐图排布,很适合那些"必须紧跟某段论述"的图,比如实验截图、算法流程图。缺点也很明显:它会强制在当前位置插入,如果剩下的页面空间不够,会直接把图片推到下一页并在前一页留下一大块空白;而且[H]对跨栏环境的figure*支持不好,双栏模板中通栏图用了[H]经常出错或无法跨栏。

我的习惯是:正文短、图表少的小文档(比如课程作业、技术备忘录),大胆用[H];正式论文和报告,尽量用[htbp]或[!htbp],让LaTeX自己掌控页面。!的作用是放宽内部启发式条件,相当于告诉LaTeX"别那么多顾虑,尽量按我给的参数来"。

如果确实需要"紧跟文字",还有一个技巧:把图片放在一个不会被打断的小节或段落后面,并用[t]明确让它出现在页面顶部,这样版面比[H]自然很多。

3. 尺寸、子图与样式:让图片真正融入版面

3.1 用相对宽度控制图片大小

控制插图大小是排版中最常做的操作。\includegraphics里三个最常用的参数是:

  • width:图片显示宽度;
  • height:图片显示高度;
  • scale:按原始尺寸缩放的比例。

我几乎不用scale,因为原始图片的物理尺寸千差万别,同样的scale=0.5在不同图片上效果完全不同。推荐用width配合相对单位,最常用的写法是:

\includegraphics[width=0.8\linewidth]{figures/arch.png}

\linewidth是指当前行的可用宽度。在单栏文档里,它约等于文字区域的宽度;在双栏文档里,它等于当前栏的宽度。也就是说,用\linewidth做基准,图片无论放在单栏还是双栏,都会自动适配所属栏的宽度,不会超宽。

这里顺便区分几个容易混淆的概念:

  • \textwidth:整个页面的文字区域宽度,单栏文档中它就是版心宽度;
  • \columnwidth:当前栏的宽度,双栏排版时它是\textwidth的一半;
  • \linewidth:当前"行"实际可用的宽度,在列表、引文等环境中可能比\columnwidth还窄。

在figure环境里用0.8\linewidth是稳妥的。如果某张图特别高,就用height=0.5\textwidth这种写法,让它高度控制在半页左右。想保持原始宽高比时,加上keepaspectratio=true,LaTeX会在width和height两个约束下尽量等比例缩放,避免图片被压扁或者拉长。

3.2 并排图与子图:论文中最常用的组合

写论文时,"左边一张原图,右边一张处理结果"是非常常见的排版需求。最简单的并排方式是用minipage:

\begin{figure}[htbp] \centering \begin{minipage}{0.48\linewidth} \centering \includegraphics[width=\linewidth]{figures/input.png} \caption{原始输入} \label{fig:input} \end{minipage}\hfill \begin{minipage}{0.48\linewidth} \centering \includegraphics[width=\linewidth]{figures/output.png} \caption{处理结果} \label{fig:output} \end{minipage} \end{figure}

注意两个细节:每个minipage的宽度最好都在0.5\linewidth以下,两个加在一起约等于0.96\linewidth,中间用\hfill做弹性空隙,这样无论纸张多宽,两张图都能均匀分布。minipage里的\includegraphics[width=\linewidth]表示图片撑满所在的迷你页面,而不是整个页面的宽度。

如果你需要给子图分别编号,比如生成(a)(b)两个子图,再用一个大编号,那就用subcaption宏包:

\usepackage{subcaption} \begin{figure}[htbp] \centering \begin{subfigure}{0.45\linewidth} \centering \includegraphics[width=\linewidth]{figures/input.png} \caption{输入图像} \label{fig:sub-input} \end{subfigure}\hfill \begin{subfigure}{0.45\linewidth} \centering \includegraphics[width=\linewidth]{figures/output.png} \caption{输出图像} \label{fig:sub-output} \end{subfigure} \caption{某算法的输入与输出} \label{fig:main} \end{figure}

这样正文里可以引用\ref{fig:main}得到总编号,也可以引用\ref{fig:sub-input}得到类似"3a"的子图编号。我在实验章节经常用这种结构,把同一个数据集的多组结果放成两行四列的矩阵,配合\subcaption非常直观。

3.3 旋转、裁剪和花式操作

偶尔会遇到图片方向不对,或者四周有大量空白需要裁掉的情况。旋转用angle参数:

\includegraphics[width=0.5\linewidth, angle=90]{figures/photo.jpg}

裁剪用trim加clip:

\includegraphics[trim=10mm 5mm 15mm 8mm, clip, width=0.6\linewidth]{figures/screenshot.png}

trim后依次是左、下、右、上四个边的裁剪量,单位可用mm、cm、pt等。注意:只写trim不会真正裁掉图片,只是把可视区域缩小,必须同时加上clip关键字,才会以裁剪后的边界作为实际排版边界。我在处理截图时经常用到这个功能,把截图四周无意义的边框裁掉,版面会干净很多。

还有一个容易踩的坑:如果同时指定width和height而没有keepaspectratio,图片会被拉伸变形。有些模板会要求插图的宽度统一,但原图高矮不一,这时候我通常先设置统一的width,让高度自然变化,而不是强行统一height。

4. 路径、格式与编译链路:插图背后的"隐形工程"

4.1 路径策略:把图片组织成可维护的结构

图片路径问题,是File not found类报错的最大来源。我的建议从一开始就养成规范:在文档根目录下建一个figures文件夹,所有图片放进去,在导言区用\graphicspath声明:

\graphicspath{{figures/}{figures/subdir/}}

这里的语法很容易写错。\graphicspath接收的是一组用花括号包裹的路径,每个路径的末尾必须带正斜杠/,整体最外层还要再套一层花括号。比如\graphicspath{{figures/}},外层花括号是参数列表,内层花括号是第一个路径。如果写成\graphicspath{figures/},多半会报错或者干脆不生效。

路径里的分隔符一定要用/,即使Windows系统默认用\,在LaTeX里也要改成/。另外文件扩展名可以写也可以不写——\includegraphics{figures/arch}和\includegraphics{figures/arch.png}都能工作,LaTeX会按编译引擎支持的格式自动尝试。但不写扩展名有个好处:如果你同一目录下既有arch.pdf又有arch.png,LaTeX会优先选择适合当前编译引擎的格式,这在后期切换编译方式时非常舒服。

4.2 图片格式选型:矢量图优先,位图看分辨率

LaTeX对图片格式的支持,与编译引擎强相关。

  • 使用pdflatex编译,直接支持PDF、PNG、JPG,不支持EPS;
  • 使用latex(生成DVI)编译,传统上是EPS的天下;
  • 使用xelatex编译,主流格式都能处理,但内部对EPS的兼容方式也比较微妙,最稳妥还是用PDF。

所以现在的主流工作流里,图片格式基本就三类:

  • 程序生成的示意图、流程图、矢量图:一律导出成PDF,清晰度无损,缩放不变形;
  • 照片、截图、位图素材:用PNG或JPG;
  • 老旧的EPS素材:要么用工具转成PDF,要么加载epstopdf宏包让编译过程自动转换。

关于位图分辨率,我的经验是:普通打印看的话,300dpi基本够;如果图片要放很大,比如整页宽,建议原始分辨率更高一些。一张1280x720的截图,如果拉宽到16cm,算下来只有不到200dpi,印刷会发虚。LaTeX不会帮你"提升"分辨率,它只会忠实地按原始像素放大。所以准备插图时,宁可原图大一点,也不要后期用插值放大。

4.3 编译引擎与图片支持的联动

很多初学者搞不清pdflatex、xelatex、lualatex的区别。简单说:pdflatex是LaTeX最经典的PDF编译路径,速度快但中文字体支持麻烦;xelatex更擅长处理中英文混排,可以直接使用系统字体;lualatex在字体和脚本方面更灵活。具体到图片插入,你只需要记住:无论用哪个引擎,我都建议把图片准备成PDF/PNG/JPG;如果模板要求latex编译,才需要专门考虑EPS。

我在写中文学位论文时,通常用xelatex配合模板自带的宏包。图片插入的写法没有任何区别,照常\includegraphics即可。如果用pdflatex遇到过EPS报错,可以临时在导言区加\usepackage{epstopdf},很多情况下能自动把EPS转成PDF插入,省去手工转换。

还有一个小提示:在一些在线LaTeX编辑平台上,默认用pdfLaTeX编译,左侧菜单可以切换XeLaTeX。如果本地编译正常、在线上却报图片相关错误,优先检查两边的编译引擎是否一致。这个坑我踩过一次,本地用xelatex跑得好好的,在线默认引擎跑出来全是图片格式错误,切换后就正常了。

5. 常见问题与排查技巧实录

5.1 高频报错速查表

报错信息常见原因解决思路
File 'xxx' not found路径错误、文件名拼写错误、大小写不一致、图形文件不在搜索路径检查\graphicspath;检查文件名大小写;把图片放到文档同级目录先排除路径问题
Unknown graphics extension: .epspdflatex下直接插EPS转成PDF,或加载epstopdf宏包,或换xelatex
Not in outer par mode在另一个浮动体或minipage等受限上下文中使用了figure调整结构,把浮动体移到外层合适位置
Division by 0图片尺寸信息异常、高度或宽度参数为0、图片文件损坏打开图片文件确认能否正常显示;去掉异常的尺寸参数
图片显示空白框使用了draft模式,图片未真正嵌入去掉draft或改用final;确认图片文件存在
Overfull \hbox图片或内容超出页面宽度把width改成0.x\linewidth,不要用原图绝对宽度

这张表是我自己排查时反复用到的。遇到报错,先看是哪一类,再按表格里的思路定位,基本能解决绝大部分问题。代码里的报错信息通常还会包含行号,根据行号跳到对应位置,重点检查\includegraphics和figure环境附近有没有写错。

5.2 图片不显示但编译无报错:先怀疑draft模式

有一种特别迷惑的情况:编译没有任何报错,但PDF里的图片位置只有一个空白或者一个带文件名的框。这多半是graphicx以draft模式加载了:

\usepackage[draft]{graphicx}

draft模式会让LaTeX不真正嵌入图片,只画一个和图片尺寸相同的黑框,并在框里写出文件名。这样做的好处是编译速度快,适合草稿阶段反复改文字时使用。到了最终输出时,改成final即可。另外,正文里也可以局部用\setkeys{Gin}{draft=false}恢复,不过一般没必要。

我自己的习惯是:在文章结构还没定稿时开着draft,几天之内写草稿编译速度能快不少;定稿前统一切换成final,再检查一遍所有图片确实都正常显示了。如果你看到别人给的模板里加载graphicx时带draft,记得在提交版本里删掉或改成final,否则审稿人看到的就是一堆空框。

5.3 版面微调:间距、跨栏与文字环绕

第一类:图注与图片之间的间距。默认间距通常没什么问题,但如果你的模板要求"图注与图之间空0.5行",可以用\vspace{0.5\baselineskip}手动调节。要注意\vspace放在\caption前后效果不同,图注在下方时,调整的是图和注之间的空隙。

第二类:双栏排版中的跨栏大图。双栏模板用figure放单栏图,用figure*放通栏图。如果通栏图一直跑不到理想位置,多半是因为双栏浮动体的约束更多,我通常放[t]让它出现在页面上方,配合\dbltopfraction等参数放宽限制。这里不推荐用[H]处理figure*,很多模板会报错或产生奇怪的空白。

第三类:文字环绕图片。需要wrapfig宏包:

\usepackage{wrapfig} \begin{wrapfigure}{r}{0.4\textwidth} \centering \includegraphics[width=0.35\textwidth]{figures/example.png} \caption{文字环绕示例} \label{fig:wrap} \end{wrapfigure}

{r}表示图片在右侧,{l}表示左侧。用wrapfigure有个经验:它必须放在段落开头附近,如果插图前已经写了一整段文字,它可能报not in outer par mode或排版错乱。我一般会在需要环绕的位置另起一段,把wrapfigure放在段落第一行之前。

5.4 一点经验之谈

图片插入之所以劝退很多人,不是因为命令难记,而是因为LaTeX的"浮动体"理念和所见即所得工具完全不同。一旦接受了"让LaTeX决定放置位置、我们只负责提供内容和构图约束"这个设定,大部分问题都会迎刃而解。

我自己现在的固定流程是:先建好figures目录,图片统一命名,导言区配好graphicx和\graphicspath;写正文时只负责把图放进figure环境并给出width;编译后用\ref检查引用编号;最后再针对个别版面问题微调位置参数。

一个让我少走很多弯路的习惯:每插入一张图,就立刻编译一次。图片问题往往有连带效应——一张图路径错了,可能导致整个浮动体系列错乱,到最后一堆报错混在一起,根本无从下手。循序渐进编译,定位问题永远是第一位的。

最后分享一个小技巧:如果你在写一篇长论文,里面有几十张图,建议给每张图的文件名带上简短的含义前缀,比如fig-arch-01.png、fig-arch-02.png,而不是1.png、2.png。图片多了以后,这种命名习惯能让你在\includegraphics和\label之间快速对应,也能避免从别人那里继承来的项目里出现一堆含义不明的文件名。插图这件事,说到底只有两条原则:结构清晰、位置合理。做到了,剩下的排版交给LaTeX就好。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进