如何影响 LaTeX 中图形和表格等浮动环境的位置?
这是一个普遍的问题,应该收集所有用户有用的答案。我希望我们可以将其作为参考
答案1
更新:已发布文章已存在
应许多人的要求,这些答案已被转成一篇 TUGboat 文章,该文章已于不久前发表(TUB 35/3, 2014)。您可以从以下网址阅读和下载 pdf 文件https://www.latex-project.org/publications(以及来自 LaTeX 项目团队的一系列其他论文和会议演讲)。
=========================================
要回答这个问题,首先必须了解控制 LaTeX 浮动标准放置的基本规则。了解这些规则后,就可以进行调整,例如,通过修改浮动参数,或通过添加某些包来修改或扩展基本功能。
LaTeX 浮动术语
浮动类
LaTeX 中的每个浮点数都属于一个类。默认情况下,LaTeX 知道两个类,即和。可以通过文档类或包添加更多类。浮动所属的类会影响浮动位置的某些方面,例如其默认放置规范(如果没有被浮动本身覆盖)figure
。table
浮动放置算法的一个重要方面是 LaTeX 永远不会违反放置顺序之内浮动类。例如,如果文档中有图 1、表 1、图 2,则图 1 将始终放置在图 2 之前。但是,表 1(属于不同的浮动类)将独立放置,因此可以出现在图之前、之后或之间。
浮动区域
LaTeX 知道一列中可以放置浮动元素的 2 个区域:列的顶部区域和底部区域。在双列布局中,它还知道跨越两列的顶部区域。在双列模式下,没有用于页面宽度浮动元素的底部区域
另外,LaTeX 可以制作浮动列和浮动页,即只包含浮动元素的列和页。
最后,LaTeX 可以将浮动字符内联到文本中(如果这样指示的话)。
浮点位置说明符
要将浮点数放置到这些区域之一,必须将浮点数放置说明符作为浮点数的可选参数提供。如果没有提供此类可选参数,则使用默认放置说明符(取决于上述浮点数类)。浮点数放置说明符可以由以下字符组成以任何顺序:
!
表示应忽略某些限制(稍后讨论)h
表示允许浮动放置在行内t
表示允许浮动进入顶部区域b
表示允许浮子进入底部区域p
表示允许浮动元素进入浮动页面或列区域
这些字符的顺序确实不是影响算法如何放置浮点数(例如,[ht] 或 [th] 没有区别!这是常见的误解之一,例如人们认为这bt
意味着应该先尝试底部区域。
然而,如果没有字母,那么相应的区域将不会被尝试。
浮点算法参数
大约有 20 个参数会影响布局。基本上它们定义了
有多少浮标可以进入某个区域,
一个区域可以变得多大,
一页上必须有多少文本(换句话说,顶部和底部可以占据多少区域)以及
将插入多少空间
区域中连续浮点数之间以及
该区域和上方或下方的文本之间。
浮动参考点
浮动在源文档中的位置会影响浮动在输出中的位置,因为它决定了 LaTeX 第一次看到浮动的时间。重要的是要了解,如果浮动位于段落中间,则此参考点是段落中下一个换行符或分页符,位于源文档中实际位置之后。
LaTeX 浮动机制的基本行为规则
有了这些知识,我们现在就可以深入研究算法的行为了。
首先,我们必须明白,LaTeX 的所有排版算法都是为了避免任何形式的回溯而设计的。这意味着 LaTeX 会读取文档源,格式化它找到的内容,然后(或多或少)立即对其进行排版。这种设计选择的原因是为了限制复杂性(现在仍然相当高),同时也是为了保持合理的速度(请记住,这是 80 年代初的事情)。
对于浮点数,这意味着算法是贪婪的即,一旦遇到浮点,它就会立即尝试放置它,如果成功,它永远不会改变其决定。这意味着它可能会选择一个根据稍后收到的数据可能被视为较差的解决方案。
例如,如果允许将图形放在顶部或底部区域,LaTeX 可能会决定将此图形放在顶部区域。如果此图形后面跟着两个只允许放在顶部的表格,这些表格可能就放不下了。在这种情况下,一个可行的解决方案(但没有尝试过)是将图形放在底部区域,将两个表格放在顶部区域。
基本顺序
以下是该算法运行的基本序列:
- 如果遇到浮点数,LaTeX 会尝试根据其规则立即放置它(稍后详细说明)
- 如果成功,则放置浮点数,并且该决定永远不会改变;
- 如果不成功,那么 LaTeX 会将浮动字号放入保留队列,以便在下一页开始时(但不会更早)重新考虑。
- 一旦页面完成,LaTeX 就会检查这个保留队列并尽可能地清空它。为此,它首先会尝试生成尽可能多的浮动页面(希望将浮动从队列中移除)。一旦这种可能性耗尽,它接下来会尝试将剩余的浮动放入顶部和底部区域。它会查看所有剩余的浮动,然后将其放置或推迟到后面的页面(即再次将它们重新添加到保留队列)。
- 之后,它开始处理该页的文档材料。在此过程中,它可能会遇到更多浮动。
- 如果已到达文档末尾或遇到
\clearpage
,LaTeX 将开始一个新页面,放宽所有限制性浮动条件,并将保留队列中的所有浮动元素放置在浮动页面上进行输出。
遇到浮动时的详细放置规则
每当 LaTeX 在源中遇到浮点环境时,它都会首先查看保留队列,检查队列中是否已经有相同类别的浮点。如果是,则不允许放置,浮点会立即进入保留队列。
如果不是,LaTeX 将查看此浮点数的浮点位置说明符,要么是可选参数中的显式说明符,要么是浮点类中的默认说明符。每个浮点类的默认值在文档类文件中设置(例如article.cls
),并且通常会解析为tbp
,但这并不能保证。
- 如果说明符包含
!
,则算法将忽略与可放入区域的浮点数或区域可占用的最大大小相关的任何限制。否则将应用参数定义的限制。 - 下一步它将检查是否
h
已指定。 - 如果是,它会尝试将浮动元素放置在遇到的位置。如果成功,即如果有足够的空间,则会放置浮动元素,并结束对该浮动元素的处理。
- 如果没有,它会继续查找
t
,如果指定了,它会尝试将浮动放置在顶部区域。如果没有限制阻止它,则放置浮动并停止处理。 - 如果不存在,它最终会检查是否
b
存在,如果存在,它会尝试将浮点数放入底部区域(再次遵守如果!
没有给出则适用的任何限制)。 - 如果这也不起作用或者由于未给出说明符而不允许,则浮点数将添加到保持队列中。
- 上述过程中不使用说明
p
符(如果存在)。只有在下一个页面边界清空保留队列时才会查看说明符。
当在文档中遇到浮点数时,处理就结束。
清空页面边界处的保持队列
完成一页后,LaTeX 会查看保留队列并尝试尽可能将其清空。为此,它会首先尝试构建浮动页面。
参与浮动页面(或列)的任何浮动都必须p
在其浮动位置规范中有一个浮动说明符。如果没有,浮动就不能进入浮动页面,而且,如果没有说明符,则p
同一类的任何后续浮动也无法进入该页面!
如果浮动元素可以放在那里,它将被标记为包含在浮动页面中,但如果浮动页面没有被“充分”填充(取决于浮动页面的参数设置),处理器仍可能中止尝试。只有在文档的最后,或者发出时\clearpage
,这些限制才会解除,然后浮动元素将被放置在浮动页面上,即使它没有p
并且是该页面上唯一的浮动元素。
浮动页面的创建一直持续到算法没有其他浮动元素可放置或无法生成浮动页面为止。在后一种情况下,所有尚未放置的浮动元素都将被考虑纳入下一页(或列)的顶部和底部区域。
那里的过程与上面描述的相同,只是
- 说明
h
符不再具有任何意义(因为现在我们已经远离了原来的“这里”),因此被忽略 - 并且这次浮点数不是来自源文档而是一个接一个地来自保持队列。
任何无法放置的浮点数都会被放回到保留队列,这样当 LaTeX 准备查看文档中的进一步文本输入时,保留队列可能已经包含浮点数。这样做的结果是,文档中遇到的浮点数可能会立即被推迟,因为同一浮点数类别的较早浮点数已经被保留。
限制/影响放置的参数的详细信息
有四个计数器控制可以进入区域的浮点数:
totalnumber
(默认 3)是文本页面上的最大浮点数(!)topnumber
(默认 2)是顶部区域的最大浮点数bottomnumber
(默认 1)是底部区域的最大浮点数dbltopnumber
(默认值为 2)是双列模式下文本列上方全尺寸浮点数的最大数量。
区域的大小通过参数(用 更改\renewcommand
)控制,这些参数定义区域的最大(或最小)大小,以页面高度的分数表示:
\topfraction
(默认值 0.7)顶部区域的最大尺寸\bottomfraction
(默认值 0.3)底部区域的最大尺寸\dbltopfraction
(默认值 0.7)双列浮动元素顶部区域的最大尺寸\textfraction
(默认 0.2)最低限度文本区域的大小,即必须不是被浮动物体占据
区域内浮动元素之间的间距,以及浮动区域和文本区域之间的间距,通过以下参数定义(所有参数都是弹性长度,即可以包含一些拉伸或收缩分量)。它们的默认值取决于文档字体大小,并在使用类选项(如11pt
或 )时更改12pt
。我们仅显示 10pt 默认值:
\floatsep
(默认 12pt 加 2pt 减 2pt)顶部或底部区域浮动之间的间隔\dblfloatsep
(默认 12pt 加 2pt 减 2pt)两列页面上双列浮动之间的间隔\textfloatsep
(默认 20pt 加 2pt 减 4pt)顶部或底部区域与文本区域之间的分隔\dbltextfloatsep
(默认 20pt 加 2pt 减 4pt)\textfloatsep
两列浮点数的类似值
对于内联浮动(已放置在“此处”),与周围文本的分离由以下方式控制
\intextsep
(默认 12pt 加 2pt 减 2pt)
对于浮动页面或浮动列(即,页面或页面的列仅包含浮动元素),诸如\topfraction
等参数不适用。相反,它们的创建是通过以下方式控制的:
\floatpagefraction
(默认 0.5)需要由浮动元素占据的页面(或列)的最小部分,以形成浮动页面(或列)
算法的后果
浮动元素在文档中的出现时间可能早于其在源文件中的位置
浮动环境在源文件中的位置决定了它在最终文档中出现的最早点。它可能会在视觉上向后移动一定程度,因为它可能被放置在当前页面的顶部区域。但是,由于 LaTeX 不会回溯,并且较早的页面已经排版,因此它不能像周围的文本一样出现在更早的页面上。
因此,浮动元素通常会放置在源中靠近其第一个调用的位置(即“参见图 5”之类的文本),因为这将确保浮动元素要么出现在与此文本相同的页面上,要么出现在后面的页面上。但是,在某些情况下,您可能希望将浮动元素放置在前一页上(如果该页面在调用中仍然可见)。这只能通过移动源中的浮动元素来实现。
双列浮动总是先延迟
当 LaTeX 在双列模式下遇到页面宽度的浮动环境(在*
环境名称末尾用 a 表示,例如figure*
)时,它会立即将其移动到延迟队列。这种行为的原因再次在于其算法的“贪婪”行为:如果 LaTeX 当前正在组装该页面的第二列,则第一列已经组装并存储起来;回想一下,由于 LaTeX 不会回溯,因此无法将浮动内容放在当前页面上。为了使算法保持简单,即使在第一列上工作,它也会执行相同的操作(理论上即使没有回溯也可以做得更好)。
因此,为了将此类浮动元素放置在当前页面上,必须手动将其移动到源中的较早位置 - 当前页面开始之前。如果这样做,显然文档中的任何进一步更改都可能使此调整过时;因此,最好(如果有的话)仅在文档制作的最后阶段进行此类调整 - 当所有材料都已编写并且重点是微调视觉外观时。
双列浮动没有底部浮动区域
这与其说是算法的结果,不如说是算法的一个事实。对于双列浮动,唯一可能的位置是顶部区域或浮动页面。因此,如果有人向此类浮动添加h
或浮动位置说明符,它就会被忽略。这意味着,直到遇到 或到达文档末尾,才会对这个浮动进行排版。b
{figure*}[b]
\clearpage
所有浮动参数(通常)都会限制放置可能性
这可能很明显,但值得重复:任何浮动参数都会限制 LaTeX 放置浮动的能力。总有一种方法可以设置参数,使其完全不影响放置。不幸的是,这样做会导致放置效果相当差。
默认情况下,LaTeX 的设置相当宽松。例如,要接受浮动页面,浮动必须占据可用页面的至少一半。换句话说,这意味着允许这样的页面有一半是空的(在大多数情况下,这当然不是最佳位置)。
经常发生的情况是,用户尝试改进此类设置,然后惊讶地发现所有浮动元素突然都堆积在文档末尾。继续这个例子:如果将参数更改\floatpagefraction
为需要浮动页面的 ,则0.8
占据0.75
页面 的 的浮动元素将不允许单独形成浮动页面。因此,如果没有其他可以添加的浮动元素并且实际上适合剩余空间,则该浮动元素将被推迟,同一类的所有其他浮动元素也是如此。但更糟糕的是,这个特定的浮动元素太大,无法进入下一个顶部区域,因为默认的最大允许区域是0.7
。因此,所有浮动元素都将被推迟到下一个\clearpage
。
因此,在编写文档时最好不要干预参数,或者至少不要以让算法更难将浮动元素放置在其调用点附近的方式来干预。对于校对而言,在引用位置旁边放置图形比避免页面半空更为重要。下面将讨论对已完成的文档进行微调的可能性。
这里要得出的另一个结论是,一些浮点参数之间存在依赖关系;在更改它们的值时考虑这些依赖关系非常重要。
“这里”的意思其实只是“如果合适就在这里”
... 而且通常它不适合。这对很多人来说有点令人惊讶,但算法的设计方式h
说明符不是一个无条件命令。如果需要无条件命令,扩展包(如float
包)提供了H
一个替代说明符,它实际上意味着“这里”(并在必要时首先开始新的一页)。
浮点说明符不定义优先顺序
如上所述,该算法尝试将浮动元素按照算法中硬连接的明确定义的顺序放入可用的浮动区域中:“此处”、“顶部”、“底部”以及 - 在页面边界上 - 首先是“页面”,并且只有当不再可能时才将浮动元素放置在“顶部”,然后是下一页的“底部”。
因此[bt]
指定不是意思是先尝试底部,然后才尝试顶部。它只是意味着允许此浮动进入顶部或底部区域(但不能进入浮动页面),就像[tb]
会一样。
浮点数和脚注的关系
这不完全是算法的结果,而是算法的实现之一:每当 LaTeX 尝试决定浮动字符(或\marginpar
!)的位置时,它都必须触发输出例程来执行此操作。作为此过程的一部分,页面上的任何脚注都将从样张中的当前位置移除并收集到框中\footins
。放置浮动字符(或推迟浮动字符)后,LaTeX 会将页面材料返回到样张,但是由于输出例程行为,样张现在已经改变:LaTeX 必须将脚注放在某个地方,但要放在一个地方。它所做的是将脚注(准确地说是\footnotetext
)重新插入样张的末尾。这样做有一些很好的理由,其中之一是 LaTeX 希望返回的所有材料仍然适合当前页面。
但是,如果出于某种原因,页面最终在较早的位置被截取,那么脚注将显示在错误的页面或列中。这种情况不太可能发生,但如果发生,请检查所选分页符附近是否有浮动,然后移动浮动或使用显式分页符来引导算法。可以找到示例在这个问题中。
事实上,这个特殊案例值得强调:不是将浮动元素直接放在标题后面,除非标题总是用于开始一页。原因是标题通常形成非常大的对象(因为它们会阻止在其后直接分页)。但是,将浮动元素放在中间意味着在 LaTeX 决定在哪里分页之前触发输出例程,并且任何脚注都会被移动到错误的位置
算法的文档
根据要求,这里提供了一些有关现有文档的信息。算法及其实现记录在文件中,ltoutput.dtx
作为 LaTeX 内核源代码的一部分。这可以独立排版,也可以作为整个内核的一部分(即通过排版source2e.tex
--- 忽略校验和错误,抱歉)。
该文档是一个有趣的历史文物。部分内容显示了可追溯到 LaTeX2.09 的半格式化伪代码;换句话说,它来自 Leslie Lamport 的原始文档。实际代码使用文档样式进行记录,部分内容或多或少进行了正确记录(从头开始),可追溯到 1994 年左右,当时 Chris Rowley 和我调整并扩展了 LaTeX2e 的原始算法。它还相当公开地记录了算法和/或其实现的各种问题 --- 在许多情况下,我们不敢更改它,因为存在许多依赖关系,当然,也因为搞砸了太多隐含地依赖于当前行为的现有文档的危险。在结尾处,您会发现当时对算法编译的评论列表,但代码文档中也散布着评论、问题和任务 (? :-)。
该文件的一个有趣方面(我完全忘记了)是它包含了跟踪算法在现实生活中的行为所需的所有代码。不幸的是,我从未正式公布过它,或者看起来是这样。它可能需要大量清理和更好地格式化它生成的跟踪输出才能供公众使用。但即使是目前的形式,它也确实提供了一些关于算法行为以及某些决策如何产生的有趣见解。
因此,如果有人想玩它或者想要追踪一些奇怪的浮动位置,那么所需要做的就是(手指交叉)制作一个fltrace.ins
包含以下内容的短文件:
\input docstrip
\generateFile{fltrace.sty}{t}{%
\from{ltoutput.dtx}{fltrace,trace}
}
\endbatchfile
通过 LaTeX 运行将生成样式文件fltrace.sty
。然后您可以通过以下方式在文档中使用它
\usepackage{fltrace}
\tracefloats % start float tracing
该命令\tracefloatvals
显示几个浮点参数的当前状态,并\notrace
再次关闭跟踪。如前所述,这不是官方软件包,但它可能在某种情况下有用,或者在研究文档后纯粹出于兴趣。
去做:添加针对特定主题的其他问题的参考。
答案2
控制浮动位置有多种可能。我在这里看到最多的问题是“如何在源文档中列出图像/表格的位置插入图像/表格?”。
首先,我认为需要注意的是,你不要需要使用浮点数。includegraphics
不需要环绕figure
, 也不tabular
需要环绕table
。 如果需要标题,\captionof
则caption
可以使用包(也许需要将它们装箱以防止内容和标题之间出现分页符)。
如果需要浮动环境,但“浮动量”必须受到限制,以使内容相对接近源中定义的位置,那么\FloatBarrier
来自placeins
可以使用包。此命令指定浮标不得通过的障碍。
最后,如果内容应该放置在源文档中定义的准确位置,H
则float
包可用于实现此目的。这与第二段中讨论的无浮动解决方案不同,因为它确实使用了浮动(即使它实际上并没有浮动在任何地方)。例如,如果整个文档中使用了某种浮动样式(例如浮动包中的规则和框样式),并且我们希望获得一致的外观,那么这可能很有用。