Skip to content

ctex: 设置fixskip=true时,caption下方与正文的间距不统一 #1100

Description

@YDX-2147483647

背景

ctex给章节标题提供了fixskip选项。它默认关闭;若启用,则会减小标题与正文的竖直间距,以更接近beforeskip与afterskip的设置值。该选项之前是在 #248 加的,现在其文档见下。

ctex-kit/ctex/ctex.dtx

Lines 2361 to 2368 in de93c22

% \begin{function}[EXP,added=2016-06-03]{.../fixskip}
% \begin{syntax}
% fixskip = \TFF
% \end{syntax}
% 默认情况下,\cls{article}、\cls{book} 和 \cls{report} 类的标题与正文的距离
% 除了由 \opt{beforeskip} 和 \opt{afterskip} 选项设置的垂直间距外,还会有
% 一些多余的间距。\opt{fixskip} 选项用于抑制这些多余间距。该选项默认不开启。
% \end{function}

然而fixskip=true似乎还意外导致图表题注(caption)与其后文的竖直间距(即下图绿色区域的高度)在全文各处不统一。

Image

我目前观察到的具体现象如下。

  • 若figure紧跟章节标题,则相应caption与其后文的竖直间距在fixskip=true时会意外变小,变化量约等于行距。
  • 若figure紧跟正文段落,则相应caption与其后文的竖直间距不受fixskip影响,并且与「紧跟标题 + fixskip=false」时的间距几乎一致。

无论是否导入caption宏包,两种figure都存在这种差异。

在ctex默认设置下,caption与其后文的竖直间距比较大,变小一点儿也看不太出来,所以这个问题影响不大;然而我们学校研究生院要求的间距比较小,LaTeX模板使用\captionsetup[figure]{belowskip=-12pt}调小了间距,导致一旦变小会十分明显,如下图左。

Image

希望ctex能修补一下,让两种figure的caption与后文的间距都不受fixskip影响。

相关链接:

详细现象

一共有 2×2×2 = 8 种情况(是否导入caption宏包、是否启用fixskip、figure紧跟标题还是正文),总结如下图。

Image

测量caption与其后文的竖直间距,还可得到下表,其中“x, y”表示figure紧跟章节标题时距离为x像素,而紧跟正文段落时距离为y像素。作为参考,同样方法测出来的正文行距都是32或33像素。

fixskip=false fixskip=true
不导入caption宏包 71, 79 51, 80
导入caption宏包 86, 95 67, 95
测量方法

参考下图,在我电脑上查看PDF,统一缩放比例,测量两行的国字框相距的像素数。

Image

注意这样测出来的距离并不等于LaTeX排版模型里caption与后文的距离,参考lua-visual-debug输出的图:

Image

测试用的例子

\documentclass{ctexart}
\usepackage{graphicx}
\usepackage{mwe}

% \ctexset{section/fixskip=true}

% \usepackage{caption}

% \captionsetup[figure]{belowskip=-12pt} % BIThesis 硕博模板的设置

\begin{document}

口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国

口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国

\section{figure紧跟标题}

\begin{figure}[h]
  \centering
  \includegraphics[width=0.6\textwidth]{example-image}
  \caption{口图图图图口图图图图口图图图名}
\end{figure}

口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国

\newpage

口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国

\section{figure紧跟正文}

口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国

\begin{figure}[h]
  \centering
  \includegraphics[width=0.6\textwidth]{example-image}
  \caption{口图图图图口图图图图口图图图名}
\end{figure}

口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国口国国国国

\end{document}

环境信息

  • 引擎:主要用xelatex试验;未发现lualatex存在值得注意的差异
  • ctex宏包版本:2026/08/11 v2.6.5,不过2025年3月BIThesis那边就有人反馈类似问题了
  • 文档类:ctexart、ctexbook都受影响,其它未测试
  • caption宏包版本:2023/08/05 v3.6o

临时解决办法

如果可以换用默认fixskip=false,那直接换就好了,不过要调小设置的beforeskip、afterskip才能保持章节标题上下间距不变。

如果必须用fixskip=true,并且文章已基本完成,能接受逐一手动调整,那么可往figure周围加\vspace{8pt}、\vspace{-5pt}之类的。

如果必须用fixskip=true,并且希望全局调整,那么可以往导言区加下面这段代码。

% 保存 fixskip 覆写前的 \prevdepth。浮动体环境开始时,若发现 \prevdepth < 0 pt,则恢复
\usepackage{etoolbox}
\ExplSyntaxOn
\makeatletter
\skip_new:N \l__ctex_fixskip_prevdepth_skip
\pretocmd\CTEX@fixheadingskip
  { \skip_set_eq:NN \l__ctex_fixskip_prevdepth_skip \tex_prevdepth:D }
  { }
  { }
\AtBeginEnvironment { figure }
  { \ifvmode { \ifdim \tex_prevdepth:D < 0pt \skip_set_eq:NN \tex_prevdepth:D \l__ctex_fixskip_prevdepth_skip \fi } \else: { } \fi: }
\makeatother
\ExplSyntaxOff

人工智能的分析

这是 27B + 35B-A3B 俩小模型得出来的,我没有完全验证,仅供参考。

人工智能的分析

问题描述

当 fixskip=true(ctex 章节选项,默认关闭)开启时,\CTEX@fixheadingskip 设置 \prevdepth=-1000pt 以消除标题后第一行与标题之间的行距,但该值从未被复位。如果标题后紧跟一个 [h] 型浮动体,页面构建器将浮动体插入当前列后,正文首行在排字时 \prevdepth 仍为 -1000pt,导致该行以"接触式"(touching)方式排布,浮动体 caption 与正文之间的间距完全塌缩(正常约 11.5pt → 异常为负值,正文与标题重叠)。

根因分析

  1. \CTEX@fixheadingskip(ctex-heading-book.def L166):

    \par
    \dim_set:Nn \tex_prevdepth:D { -1000pt }
    \skip_sub:Nn \l__ctex_heading_skip { \tex_parskip:D }
    

    \prevdepth=-1000pt 使标题后第一行的行间距变为 0(接触式排布),但这个值在标题之后的所有排字操作中持续存在。

  2. 当 [h] 浮动体紧跟标题时,页面构建器(latex.ltx \@addtocurcol)将浮动框插入列中,此时 \prevdepth 仍为 -1000pt(经 \vadjust 探针在排字时刻实测确认)。

  3. TeX 行放置公式:基线 = 前参考点 + \baselineskip + ht − \prevdepth。当 P=-1000 时,计算位置上移约 1000pt,与上方材料冲突,被钳制为接触式排布——行顶边贴浮动框底边。

  4. 当 strut=off + belowskip 为负值时(如 bithesis 模板设置),浮动框高度因负间距而缩小,框底边更接近标题文字,接触式排布使正文行与标题重叠。

建议修复

在 \CTEX@fixheadingskip 中保存修复前的 \prevdepth 值,并提供恢复机制。例如:

% 在 \CTEX@fixheadingskip 定义中
\skip_new:N \l__ctex_fixskip_orig_prevdepth
\cs_new_protected:Npn \CTEX@fixheadingskip
  {
    \par
    \skip_set_eq:NN \l__ctex_fixskip_orig_prevdepth \tex_prevdepth:D
    \dim_set:Nn \tex_prevdepth:D { -1000pt }
    \skip_sub:Nn \l__ctex_heading_skip { \tex_parskip:D }
  }

然后在浮动体环境或标题后的第一个 \par 后恢复 \prevdepth。更彻底的方案是修复 \CTEX@fixheadingskip 的作用范围——使其只影响标题后的一行,而非持续泄漏到后续所有排字操作中。

用户侧临时 workaround

在用户导言区添加以下补丁(需 \usepackage{etoolbox},ctex 已加载):

\ExplSyntaxOn
\skip_new:N \l__ctex_fixskip_saved
\makeatletter
\pretocmd\CTEX@fixheadingskip
  { \skip_set_eq:NN \l__ctex_fixskip_saved \tex_prevdepth:D }
  { }
  { }
\makeatother
\AtBeginEnvironment { figure }
  { \ifvmode { \ifdim \tex_prevdepth:D < 0pt \skip_set_eq:NN \tex_prevdepth:D \l__ctex_fixskip_saved \fi } \else: { } \fi: }
\AtBeginEnvironment { table }
  { \ifvmode { \ifdim \tex_prevdepth:D < 0pt \skip_set_eq:NN \tex_prevdepth:D \l__ctex_fixskip_saved \fi } \else: { } \fi: }
\ExplSyntaxOff

该补丁保存 fixskip 覆写前的 \prevdepth,在 figure/table 环境开始时若检测到 \prevdepth<0(即 fixskip 已生效)则恢复。不影响标题→正文间距(fixskip 原有意图),也不影响无浮动体或非紧跟标题的场景。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions