写双栏论文时最令人血压升高的事,莫过于跨栏图表不受控制地乱跑。你在正文第三页放了一个figure*环境,编译完发现那张架构图跑到倒数第二页去了,参考文献前面孤零零地躺着一张图,正文里的引用和图片之间隔了两屏。这个问题我踩了无数次,几乎可以确认:每一个认真写过双栏会议论文、期刊论文或学位论文的人,早晚都要和跨栏浮动体"斗智斗勇"一次。解决这个难题最常用的工具,就是这个叫dblfloatfix的小宏包。
这篇文章就围绕双栏浮动结构和跨栏图表排版展开,我会先讲清楚浮动体机制为什么会造成"图乱跑",再手把手演示dblfloatfix的正确用法、参数配置、实战案例,最后把我这些年用下来的踩坑记录和替代方案一起列出来。适合正在写双栏论文、被跨栏图逼疯的LaTeX用户,也适合刚入门、想少走弯路的新手。
1. 跨栏浮动体的"不听话"从何而来
1.1 LaTeX浮动体机制:为什么图不是"插"进去的
很多新手第一次在双栏模板里放跨栏大图时,都会产生一个天真的疑问:我明明把代码写在这里,编译完图片凭什么跑到后面去?要回答这个问题,得先理解LaTeX浮动体的底层逻辑。
LaTeX里的figure、table并不是"插在文字流中"的普通对象,而是一种会被排版算法单独调度的"浮动体"。你可以把它们理解成机场里排队等待登机的乘客:你在源码里写下figure环境,只是给这位乘客办理了值机手续,它到底从哪个登机口、哪个时刻出发,由调度系统决定。调度系统会综合当前页剩余空间、浮动体尺寸、页面上已存在的浮动体数量、正文与浮动体的比例等多重因素,做出一个全局安排。
这个设计的初衷是好的:避免图片和表格在排版时撑破页面结构,保证正文阅读的连续性。但代价就是位置不可控,尤其当浮动体又大又多时,"图物分离"严重到让人想砸键盘。
1.2 figure和table:天生受限的"跨栏选手"
在双栏文档里,图表有两条路可以走:
- 普通
figure/table环境:占据单栏宽度,只能在自己所在栏内浮动。 - 带星号的
figure*/table*环境:宽度是整个版心,可以横跨左栏到右栏。
跨栏浮动体听起来很爽,但LaTeX标准文档类(article、report、book)对它的位置参数支持非常有限。普通figure支持htbp四个位置参数,而figure*的默认实现里,b(bottom,页面底部)基本是无效的。
原因也不难理解:双栏页面的底部结构比单栏复杂,跨栏浮动体如果放在页面底部,不仅涉及左栏右栏的同步问题,还和页脚、页码、底部注记等元素冲突。早期内核干脆就没实现这条路径。
实际表现就是:\begin{figure*}[t]可以生效,\begin{figure*}[p]可以生效,但你要是写\begin{figure*}[b],要么被静默忽略,要么产生一个类似"Float too large for page"的警告,然后图还是跑到顶部或者后面去了。
1.3 "图乱跑"造成的排版惨案
在期刊投稿、会议投稿阶段,图乱跑的代价比想象中大得多。我之前投一篇双栏会议论文时,文中有三张跨栏架构图,编译出来三张图全部挤在最后一页,正文从头到尾找不到图的影子。审稿人看到这种"图文分离"的稿子,第一印象就是"作者不专业"。
更麻烦的是交叉引用。有一次我在figure*内部用\label做标记,正文里\ref出来的图号是对的,但\pageref指出的页码和图实际出现的位置相差好几页。投稿系统要求图片必须出现在被引用的那一页附近,这种错位直接导致返修,白白多等了一轮审稿周期。
我自己还遇到过一种让人摸不着头脑的情况:同一个源码,在本地用TeX Live编译图出现在第3页,到了导师的旧版MiKTeX上编译,图跑到了第5页。两个环境下的浮动体调度结果不一样,说明这类问题非常依赖编译环境的具体实现,不是简单改改代码就能稳定复现的。
2. 认识dblfloatfix:它到底是修什么的
2.1 一个小宏包解决两个老Bug
dblfloatfix这个宏包,熟悉LaTeX生态的人应该不陌生。它出自LaTeX维护团队核心成员Frank Mittelbach之手,体积很小,但命中要害。宏包文档里说得很直白:它修复的是LaTeX2e里两个长期存在的跨栏浮动体bug。
第一个bug,也是大多数人使用它的原因:双栏浮动体无法放到页面底部。dblfloatfix通过修改双栏浮动体的放置逻辑,让figure*和table*也支持b位置参数。这意味着原本只能挤在页面顶部等待空间的跨栏图,现在可以充分利用页面底部的空间,整个版面的利用率明显提升。
第二个bug:跨栏浮动体和hyperref宏包的交互问题。早期版本中,如果文档里用了hyperref,双栏浮动体在某些情况下会导致PDF书签和内部链接锚点的位置产生偏移。点击正文里的"图1"标签,跳转过去却是别的地方。dblfloatfix对hyperref的锚点处理做了修复,这类跳转错位问题得到了改善。
这两件事听起来不大,但对双栏论文来说都是实际痛点。前者关乎版面效率,后者关乎电子版阅读体验。
2.2 dblfloatfix与LaTeX内核的"抢跑"关系
这里有必要梳理一下历史,否则很多人会疑惑:我的TeX Live已经是最新版了,官方内核难道还没修这个问题吗?答案是:确实在持续修补,但dblfloatfix依然有它不可替代的价值。
LaTeX2e在2020年10月经历了一次比较大的内核更新,不少历史遗留bug被合并进了format,包括一些浮动体相关的修复。但跨栏浮动体的底部放置和hyperref锚点修复,并没有以完全一致的方式进入内核的默认行为。不同的文档类、不同的编译引擎(pdfLaTeX、XeLaTeX、LuaLaTeX)对这类底层逻辑的处理不完全统一。
dblfloatfix作为一个独立宏包,不依赖内核的某项具体修复。只要你加载它、配合合理的文档类,它在绝大多数发行版上的行为是一致的。这种"行为一致性"在投稿和合作改稿时特别重要:你用最新版TeX Live编译没问题,导师的旧环境不一定没问题,而dblfloatfix可以帮你抹平很多环境差异。
2.3 它的适用场景与边界
dblfloatfix不是万能的,一开始就把它吹上天,后面用起来会失望。先说清楚它不覆盖的场景:
- 单栏文档不需要它,单栏浮动体本就可以正常使用
htbp; beamer等演示文档类有自己的浮动处理逻辑,加载它没有意义;- 用
multicol宏包临时搭建的多栏区域中,figure*并不能跨栏浮动,dblfloatfix也不会"开启"这种能力; - 如果页面空间确实不够、浮动体又多到排队,再强的位置控制也只是矮子里拔将军。
换句话说,dblfloatfix解决的是"浮动体放置规则太死板"的问题,而不是"浮动体太多放不下"的问题。先把这两件事分开,后面的使用心态会健康很多。
3. 配置与实操:让跨栏图表听你的话
3.1 加载宏包的正确姿势
在TeX Live和MiKTeX里,dblfloatfix是自带的,不需要手动下载。导言区写一行:
\usepackage{dblfloatfix}推荐放在graphicx之后、hyperref之前。顺序上的逻辑是:graphicx负责图片插入能力,dblfloatfix负责浮动体规则,hyperref负责链接和书签。一个典型的最小导言区如下:
\documentclass[twocolumn]{article} \usepackage{graphicx} \usepackage{dblfloatfix} \usepackage{caption} \usepackage{subcaption} \usepackage{booktabs} \usepackage[hidelinks]{hyperref}注意hyperref尽量放在最后,这和大多数LaTeX项目的习惯一致。dblfloatfix官方文档里也建议自己不要和hyperref的顺序反着来,否则锚点修复效果可能打折扣。
另外要提醒一句:dblfloatfix体积很小,没有任何可选参数,加载它就是全部操作。不要觉得这个宏包太简单不够用,它的设计哲学就是"做一件事,把它做对"。
3.2 位置参数实测:t、b、p到底哪个能生效
我自己在标准article类上做过一组简单实验:一个跨栏图分别用[t]、[b]、[!bt]、[p]四种位置参数编译,观察它最终落在哪里。
| 位置参数 | 未加载dblfloatfix | 加载dblfloatfix后 |
|---|---|---|
[t] | 顶部,正常 | 顶部,正常 |
[b] | 无效,常被忽略或产生警告 | 底部,可正常生效 |
[p] | 可能被放到独立浮动页 | 行为一致 |
[!bt] | 顶部为主,无法到底部 | 顶部或底部,按空间取舍 |
这个表格里的"底部"要理解成"页面版心底部区域",不是页脚。图放在底部时,上方依然是两栏正文,跨栏图横跨在页面下部。
实际使用中,我给跨栏图最常用的参数是[!t]。感叹号的作用是忽略LaTeX对浮动体数量、尺寸的大部分"美学限制",让算法优先满足位置意愿。但[!t]也有副作用:如果图片太高,会把正文挤得非常靠下,页面底部留下一大块空白。这个我们后面在踩坑部分细说。
3.3 几个和浮动体相关的比例参数
dblfloatfix并不是加载完就万事大吉。LaTeX浮动体算法有一组比例参数,决定浮动体可以占页面多少空间。双栏场景下,和跨栏浮动体最相关的是\dbltopfraction和\dblfloatpagefraction,前者控制跨栏浮动体在页面顶部允许占用的最大比例,后者控制独立浮动页至少要有多少内容是浮动体。
我通常会做这样一组设置,让跨栏图的放置更灵活:
\renewcommand{\dbltopfraction}{0.9} \renewcommand{\dblfloatpagefraction}{0.8}\dbltopfraction设为0.9意味着页面顶部允许最多90%的区域被跨栏浮动体占据,给文字留出至少10%。这个值如果太接近1,文字会被挤到几乎没有,看起来像一张图配一行字,投稿很容易被拒。0.8到0.9是我个人体验比较稳妥的区间。
另外还有单栏浮动体相关的\topfraction、\bottomfraction、\textfraction,它们虽然不直接管跨栏浮动体,但会间接影响整个页面的浮动节奏。如果你发现整页文字被浮动体挤压得太厉害,可以把\textfraction调高一点,强制正文占据更大比例。
4. 双栏论文中的跨栏图表演示案例
4.1 案例一:单张跨栏大图
最基础的需求:在双栏论文中放一张横跨两栏的架构图。源码结构如下:
\begin{figure*}[!t] \centering \includegraphics[width=\textwidth]{architecture.pdf} \caption{系统总体架构与数据流} \label{fig:architecture} \end{figure*}这里有两个细节需要注意。第一,宽度用的是\textwidth而不是\columnwidth。跨栏浮动体的可用宽度是整个版心的宽度,也就是两栏宽度加栏间距,所以图片用\textwidth才能撑满。第二,图片格式最好用矢量图(PDF、EPS),会议论文尤其如此,避免位图在打印或缩放时模糊。
这张图如果放在第一节末尾、第二节之前的源码位置,配合[!t]参数,编译后大概率会出现在当前页顶部,比较接近正文引用位置。如果还是跳到后面,多半是当前页顶部空间不够,或者是前面已经有另一个跨栏浮动体占据了顶部位置。
4.2 案例二:跨栏并排双子图
跨栏空间大,非常适合放"左右对比"的图组。比如系统优化前后的效果对比,或者两个模块的实验结果对比。用minipage并排放两张图是最直观的写法:
\begin{figure*}[!t] \centering \begin{minipage}[t]{0.48\textwidth} \centering \includegraphics[width=\linewidth]{before.pdf} \subcaption{优化前} \label{fig:before} \end{minipage} \hfill \begin{minipage}[t]{0.48\textwidth} \centering \includegraphics[width=\linewidth]{after.pdf} \subcaption{优化后} \label{fig:after} \end{minipage} \caption{优化前后对比效果} \label{fig:compare} \end{figure*}这里用0.48\textwidth而不是0.5\textwidth,是给中间的间距留空间。\hfill把两个minipage推到左右两端。\subcaption来自subcaption宏包,需要提前加载。如果不需要子图编号,也可以直接用\captionof替代。
经验之谈:跨栏双子图的高度控制比单张图更严格。两张图宽度各占一半,但高度如果一高一矮,整体会很难看。我一般会在生成图片时就把两张图的画布裁剪成同样高度,或者在\includegraphics里统一设置height参数,而不是单独设置width,这样左右才能对齐。
4.3 案例三:跨栏表格table*
表格跨栏同样常见。三线表、数据量较大的宽表在单栏内容纳不下时,就要放进table*环境:
\begin{table*}[!t] \centering \caption{不同模型在测试集上的性能对比} \label{tab:results} \begin{tabular}{lcccc} \toprule 模型 & 准确率 & 召回率 & F1 & 推理时间(ms) \\ \midrule Baseline & 82.3 & 80.1 & 81.2 & 45 \\ Ours & 91.7 & 90.5 & 91.1 & 52 \\ \bottomrule \end{tabular} \end{table*}跨栏表格最常见的坑是列数太多,导致表格超出\textwidth,进而在页面边缘溢出。处理办法按优先级排列:先精简列数,再用tabularx或tabular*控制总宽度,最后考虑缩小字号(\small、\footnotesize)。字号调整要谨慎,尤其期刊模板对正文字号有要求,表格内过小虽然能排版,但审稿人阅读体验不好。
还有一点容易被忽视:\toprule、\midrule、\bottomrule来自booktabs宏包,需要加载\usepackage{booktabs}。跨栏表格的技术难点本来就多,不要在基础宏包上再出错。
4.4 案例四:跨栏图加子图编号的排列组合
当子图数量超过两个时,并排一行放不下,就要考虑"上面两个、下面两个"的布局。用两个minipage行,或者直接使用subcaption宏包的subfigure环境配合多行:
\begin{figure*}[!t] \centering \begin{subfigure}{0.45\textwidth} \centering \includegraphics[width=\linewidth]{case1.pdf} \subcaption{第一种情况} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics[width=\linewidth]{case2.pdf} \subcaption{第二种情况} \end{subfigure} \vspace{1em} \begin{subfigure}{0.45\textwidth} \centering \includegraphics[width=\linewidth]{case3.pdf} \subcaption{第三种情况} \end{subfigure} \begin{subfigure}{0.45\textwidth} \centering \includegraphics[width=\linewidth]{case4.pdf} \subcaption{第四种情况} \end{subfigure} \caption{四种场景下的处理结果} \end{figure*}这种"2乘2棋盘"布局在各种论文里都很常见。注意第一行和第二行之间用一个\vspace控制垂直间距,避免子图之间贴得太紧。整体来说,dblfloatfix管的是整个figure*放哪,至于figure*内部的子图排列,还是由subcaption、minipage这些工具负责,两者各司其职。
5. 使用dblfloatfix时的六类踩坑记录
5.1 加载了却没生效:先查顺序和日志
dblfloatfix加载之后,如果发现[b]参数仍然无效,第一个要查的是导言区顺序。虽然这个宏包对顺序的容忍度比较高,但如果你在hyperref之后才加载它,某些版本下锚点修复不会生效;如果你在某些自定义文档类(例如模板的.cls文件里自己改写了双栏浮动体算法)中加载,冲突更是不可避免。
第二个要查的是编译日志。编译完成后,在.log文件里搜索dblfloatfix,如果能看到类似"Package: dblfloatfix"的信息,说明宏包确实加载了。如果找不到,说明你的TeX发行版里缺少这个宏包,或者文档类里有特殊的路径配置。TeX Live和MiKTeX的完整安装一般都带它,但精简安装有时候会漏。
5.2 底部浮动体导致页面大片空白
dblfloatfix允许[b]之后,很多人会忍不住把跨栏图都设为[!b],结果发现页面布局变得很奇怪:图确实到了底部,但上方两栏文字被强制压缩,页面版心的底部边缘留出一条难看的空白带。
原因在于,跨栏浮动体放在底部时,LaTeX需要在两栏正文结束之后、页脚之前腾出整个版心宽度的空间。如果浮动体高度是页面高度的40%,那两栏正文就必须在页面60%的位置提前结束,剩下的区域全部交给图。如果这一页文字本身很多,压缩感会特别强烈。
我的建议是:位置参数写[!bt]而非[!b],让算法自己在顶部和底部之间选择;同时对太高的跨栏图,宁可缩小到页面高度的35%以内,也不要强塞底部。
5.3 和caption宏包、hyperref的组合顺序
caption宏包提供\captionsetup等功能,很多人会用来设置图表标题的字体、位置。双栏跨栏图里,标题放在图下方还是图上方,其实没有绝对标准,但多数科技论文偏好"图题在下、表题在上"。
用dblfloatfix时我遇到过一个问题:caption的position=bottom设置配合跨栏底部放置,会导致标题和图之间的间距变得异常。排查了一圈,发现是我在导言区同时用了\captionsetup[figure]{position=bottom}和figure*[b],两个"底部"概念打架了。解决办法是在figure环境内部显式把\caption放在\includegraphics之后,让caption宏包的bottom设置与浮动体底部位置逻辑协同起来。
hyperref的顺序问题在第3节说过,这里再强调一遍:dblfloatfix和hyperref本身不冲突,但一定要保证hyperref在最后加载,否则PDF书签和链接锚点的修复效果会打折扣。
5.4 与论文模板内建设置的冲突
投稿用的论文模板(比如IEEE会议模板、ACM模板、某些期刊的.cls文件)往往自带一堆浮动体参数。这些模板的.cls文件里可能已经修改了\@dbltoproom、\dbltopfraction等底层参数。这时候再加载dblfloatfix,不一定会报错,但效果可能被模板自身的设置覆盖。
我在一篇双栏期刊返修时遇到过:模板里已经设置了\dbltopfraction=0.7,我加载dblfloatfix后把跨栏图设为[!b],编译一切正常,但模板自带的某些浮动物品与底部放置逻辑冲突,导致正文里出现了一条意外的横线。最后定位是模板.cls里有一段针对双栏浮动体的fix代码,和dblfloatfix重复了。解决方案也很简单:在那份模板里,删掉我自己的\usepackage{dblfloatfix},改用模板自带的机制。
所以原则是:先用模板自带的默认行为编译一遍,确认真有问题,再加dblfloatfix做增量修改。不要一上来就叠加,最怕两个fix打架。
5.5 在multicol多栏环境中失效
dblfloatfix管的是标准双栏页面结构。如果文档里用multicol宏包在某个区域临时切换成多栏,例如摘要、致谢区域的特殊排版,这个区域内的figure*并不能被dblfloatfix控制。multicol有自己的浮动处理方式,跨栏浮动体在multicol环境中本身就不被支持。
遇到这种情况,我的做法是避免在multicol区域里放置跨栏内容,把大图放到区域之前或之后的正常双栏页面中。如果一定要在区域内有跨栏图,就拆成两张单栏图分别放入左右两栏,或者在multicol区域前后使用\twocolumn[...]这样的机制,但后者复杂度会高不少。
5.6 浮动体高度超出可放空间
最后一个坑最直白:图太高,页面放不下。dblfloatfix只是改了放置规则,没有超能力。如果你的跨栏图宽度设置为\textwidth、高度又接近整页高度,任何位置参数都很难处理。LaTeX会给出警告,然后把图推到后续页面。
处理办法只有三种:缩小图的高度、把图拆成上下两半分别放入两个figure*、或者在画图阶段就把整张架构图做成更大画布但内容更紧凑的版本。千万别为了让图能放下而把字体缩到看不清,审稿人最反感这种投机取巧的排版。
6. 如果dblfloatfix还不够:替代方案与救急技巧
6.1 先别急着上宏包:调整源码位置
很多人忽略了最原始的手段:把figure*环境的源码挪到"希望图出现的位置"附近。虽然LaTeX浮动体算法有自己的调度逻辑,源码位置并不是最终位置,但源码位置靠前,浮动体进入队列的时间就早,在后续页面有空间时被优先放出的概率就更高。
我写双栏论文时有一个习惯:每写完一小节,就把这一节要引用的大图源码放在这一节末尾,而不是集中在文档最后。这样即使dblfloatfix没有完全解决位置问题,图也不至于迟到得太离谱。这个方法零成本,但效果往往被低估。
6.2 伪跨栏图:用minipage手工拼版
如果dblfloatfix因为某种原因不能使用(比如模板冲突严重),可以退而求其次:不追求真正的跨栏浮动体,而是把一张宽图放进单栏figure环境,再通过调整宽度参数让它"侵占"旁边的栏。这个方案在视觉上并不完美,因为它不会真正跨栏,而是让图的一部分悬在栏外,存在溢出风险。
更稳妥的伪跨栏方案是把大图拆成左右两块,分别放进左栏和右栏的figure环境,通过两个并排的单栏浮动体形成视觉上的"跨栏"效果。缺点是左右两栏正文的浮动时机不同,两张图很可能不在同一水平线,需要反复编译调整。
6.3 stfloats、float宏包的适用边界
有读者可能听过stfloats宏包,它的目标之一是让浮动体可以放在页面底部。从功能上看,stfloats和dblfloatfix有重叠,但侧重点不同。stfloats更偏向单栏浮动体的底部放置,对跨栏浮动体和hyperref的锚点问题处理不如dblfloatfix彻底。
float宏包的H参数(强制放在当前位置)看起来很美,但它是给单栏figure/table用的,对figure*没有可靠支持。如果你想用H参数强行固定跨栏图,大概率会得到一个警告,甚至编译报错。所以日常使用中,跨栏浮动体还是优先考虑dblfloatfix。
6.4 终极手工控制:\twocolumn[ ]等非常规手段
最后提一个"重武器":用\twocolumn[...]在双栏页面中插入一个单栏宽度的区域,然后把图表写在这个区域里。这种写法会把跨栏内容当作页面结构的一部分,而不是浮动体,因此位置完全可控,但代价是LaTeX需要重新分栏,正文排版可能出现较大波动。
这个方案我一般只在论文最终定稿阶段才用,因为它是彻底的"手工干预",后续任何文字改动都可能导致排版重新抖动,维护成本很高。对一个反复修改的项目来说,用它会比较痛苦。
我在实际投稿过程中还有一个体会:dblfloatfix解决的是"能不能放"的问题,但真正决定版面是否美观的,是你对图片尺寸和页面比例的估算能力。拿到一张跨栏图,先目测它的宽高比,如果高度超过版心高度的40%,就要主动缩小或者在画图阶段重新设计构图,不要寄希望于参数能解决一切。排版这件事,功夫在平时,多积累几套组合拳,投稿时能少掉不少头发。