在在 LaTeX 中使用 \begin{section} ... \end{section} 是不是一个坏主意?Jan Hlavacek 说:
当然,通常可以识别某个部分的结束,但实际上部分结束的方式有很多种:另一部分的开始、下一章或下一部分的开始、
\endmatter
参考书目、索引等的开始、或文档的结束。
我该如何编写一个宏来识别何时到达某个部分的末尾?
我希望它能正常工作\afterpage
这样我就可以修复一些问题我的答案到每页顶部都有章节提醒吗? 我相信这意味着它必须使用标记来检测它相对于分段命令的位置,但我对 LaTeX 了解不够多,无法确定。
例如,我希望能够做如下的事情:
\documentclass{article}
\usepackage{afterpage}
\newcommand{\printendedsections}{%
\ifatendofsubsubsection{(end of subsubsection \thesubsubsection)}{}%
\ifatendofsubsection{(end of subsection \thesubsection)}{}%
\ifatendofsection{(end of section \thesection)}{}%
\ifatendofchapter{(end of chapter \thechapter)}{}%
\ifatendofpart{(end of part \thepart)}{}%
}
\begin{document}
% will expand to '(end of subsection 2.1)' at the top of page 2 because
% subsection 2.2 starts right at the top of page 2
\afterpage{\hbox{\printendedsections}}
\section{sec1}
asdf
\printendedsections% expands to nothing because section 1 hasn't ended yet
\subsection{sub1.1}
\printendedsections% expands to nothing because subsection 1.1 hasn't ended yet
asdf
\printendedsections% expands to '(end of subsection 1.1)(end of section 1)'
\section{sec2}
asdf
\printendedsections% expands to nothing because section 2 hasn't ended yet
\subsection{sub2.1}
pretend there is enough content here
to push the start of subsection 2.2
to the top of the next page
\printendedsections% expands to '(end of subsection 2.1)'
\subsection{sub2.2}
asdf
\printendedsections% expands to '(end of subsection 2.2)(end of section 2)'
\end{document}
答案1
根据要求,问题无法解决(输入与示例类似)。TeX 是按顺序工作的,当它\printendedsections
在运行文本中执行时,TeX 无法知道接下来出现的内容是更多文本、一些标题还是...
即使采用多通道解决方案,也几乎不可能确定,至少如果您不另外修改航向命令的话,因为需要确定\printendedsections
和下一个航向命令之间有什么(如果有的话)。
暂时忽略确定某个部分是否从页面顶部开始的额外请求,当某个部分级别结束时自动输出文本的问题可以通过多种方式实现。下面是一种可以自动完成所有操作的方法:
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\patchcmd\@startsection%
{ \par}{\par \outputsectionends{#2}}%
{\typeout{*** SUCCESS ***}}{\typeout{*** FAIL ***}}
\def\perhapssomethingended #1{%
\ifnum \arabic{#1}>\z@
(end of #1 \csname the#1\endcsname)%
\fi
}
\makeatother
\def\outputsectionends #1{%
\ifcase #1\relax
% extend here (+ \chapter command) for report/book class
\or
\perhapssomethingended{subsubsection}
\perhapssomethingended{subsection}
\perhapssomethingended{section}
\setcounter{subsubsection}{0} % need to reset high-level headings as LaTeX is lazy and only resets the next level
\or
\perhapssomethingended{subsubsection}
\perhapssomethingended{subsection}
\or
\perhapssomethingended{subsubsection}
\fi
\par
}
\AtEndDocument{\outputsectionends1}
\begin{document}
\section{sec1} asdf
\subsection{sub1.1} asdf
\subsection{sub1.2} asdf
\subsubsection{sub1.2.1} asdf
\paragraph{sub1.2.1.1} this level is not taken into account (but could of course)
\paragraph{sub1.2.1.2} this level is not taken into account
\section{sec2} asdf
\subsection{sub2.1}
\subsection{sub2.2} asdf
\end{document}
解释:
我们连接到\@startsection
生成标题(除章节外)的标准 LaTeX 接口。我们知道,如果调用此命令,#2
则会生成带有“级别编号”的标题(章节为 1,子章节为 2 ...)。
因此,我们根据数字执行不同的操作:如果我们开始一个部分 (1),那么我们可能已经结束了一个“小部分”,也许结束了一个“小节”,也许结束了一个前一个“部分”。所以我们\perhapssomethingended
对每个部分都运行。此命令查看当前计数器的值(作为其参数给出),例如“小节”,如果该值大于零,则存在前一个标题。因此我们输出一些文本。
只有一个问题:LaTeX 很懒,当它开始一个新标题时,它只会将下一个标题级别重置为零,而不会将所有更高的标题级别重置为零(因为这稍后会以递归方式发生)。然而,对我们来说这是一个问题,因为这意味着在“子部分 1.2.1”之后,它的值将保持在 1,从而在处理“子部分 2.1”时弄乱输出。因此,我们必须明确确保在处理一个部分时重置子部分计数器(您可以通过注释掉该\setcounter
行来尝试)。
最后,我们还需要在文档末尾结束所有内容。这是通过假装这个地方像另一个部分一样运行来实现的,即我们\outputsectionends{1}
在那里运行。
如果你运行这个你将得到:
当然,还有几个可能的改进,例如,如何格式化生成的文本(现在只是一个带有缩进的段落)。
检测页面顶部的部分
显然这行不通,\afterpage
因为这一切都发生在幕后。然而,afterpage 无论如何都不是合适的工具,因为它在主样稿处理完毕后以异步方式插入某些内容,这不会有帮助。
我使用的方法是,在标题添加的断点前后添加标签。因此,如果两者最终都在同一页上,则此时没有发生中断;如果它们最终位于不同的页面上,则中断直接发生在标题前面。在这种情况下,\outputsectionends
可以根据需要更改其行为(显然,在文档稳定之前,这会导致多次运行,特别是如果生成的文本量根据情况发生很大变化)。但我不太确定 OP 想要什么,所以我不会详细说明。
为部分或章节提供此解决方案
在评论中,有人问这个解决方案是否也有效\part
。\chapter
答案是肯定的,也是否定的。上面的代码不是现成的。它需要以两种方式扩展代码
\chapter
和\part
通常不使用 来定义\@startsection
。因此,它们的代码需要以类似的方式进行修补,以便在它们生成标题(以及可能的分页符等)之前运行\outputsectionends
。- 此外,还
\outputsectionends
需要进行更改:的层次结构级别为\chapter
0,的层次结构级别为\part
-1。因此,\ifcase
可以扩展以适应章节(代码中已经有注释说明操作的放置位置),但对于\part
需要额外测试#1
是否为 -1,如果\ifcase
仅运行 0、1、2、...
更新
在评论中,有人问我在断点前后添加标签是什么意思。\@startsection
用于生成可能断点的是:
\addpenalty\@secpenalty\addvspace\@tempskipa
\addpenalty
和命令\addvspace
有点棘手,因此我们不能简单地在前后添加标签。为什么?因为它们会向后查看以检测前面是否有垂直空间,如果有,则将空间移到惩罚之后或将其与额外请求的空间相结合(在 的情况下\addvspace
)。因此,如果我们在它前面放一个标签,\addpenalty
意味着任何先前的空间都被它隐藏了。因此,我们需要提供我们自己的版本来模仿\addpenalty
并添加标签:
\def\addpenaltywithlabel#1#2{%
\ifvmode
\if@minipage
\else
\if@nobreak
\else
\ifdim\lastskip=\z@
\label{#2}% <- one place for the label
\penalty#1\relax
\else
\@tempskipb\lastskip
\vskip -\lastskip
\label{#2}% <- and here if the previous skip is "moved"
\penalty#1%
\vskip\@tempskipb
\fi
\fi
\fi
\else
\@noitemerr
\fi}
在后面添加第二个标签\addvspace
是可以的,因为后面会跟着标题。当然还有很多事情要做,每个实例的标签都需要不同(例如,使用递增的计数器),并且必须为它们生成的页面值编写比较。然后必须编写一些代码来实际利用此信息,使标题出现在顶部(挥手 :-)