让各级标题的内容左对齐是一个比较常见的排版需求,在 Word 里可以通过制表符轻易实现。但在 LaTeX 中似乎没有什么现成的方法。google 一通,也只在 StackExchange 上找到一个很复杂的实现方式,不但依赖 KOMA-Script,还要自己码一大堆代码。
Word 里混乱复杂的多级列表设定和各种排版细节上的差强人意(例如标点压缩),总是让我受不了,想用 LaTeX 把工作上的合同重新排版一遍。合同中,把各级编号后的条款内容左对齐是最基本的排版需求。LaTeX 不可能连这么简单的事都搞不定吧……如鲠在喉,想了一个晚上终于搞通了。
思路是这样的:如果能把各个级别的章节编号全都放进统一固定长度的左右盒子里,就能保证章节名称的启始位置全都一致,实现左对齐。这样一来就必须修改\section
、\subsection
等章节指令的内部定义。借助《LaTeX2e文类和宏包学习手册》和\show
指令,得以一窥\section
指令的源码:
% \section 宏在 article 文类中的定义:
\section=\long macro:
->\@startsection {section}{1}{\z@ }{-3.5ex \@plus -1ex \@minus -.2ex}{2.3ex \@plus .2ex}{\normalfont \Large \bfseries }
可以看到\section
是通过\@startsection
指令定义的。进一步查看\@startsection
的定义:
\@startsection=macro:
#1#2#3#4#5#6->\if@noskipsec \leavevmode \fi
\par \@tempskipa #4\relax \@afterindenttrue
\ifdim \@tempskipa <\z@ \@tempskipa -\@tempskipa \@afterindentfalse \fi
\if@nobreak \everypar {}
\else \addpenalty \@secpenalty \addvspace \@tempskipa \fi
\@ifstar {\@ssect {#3}{#4}{#5}{#6}}{\@dblarg {\@sect {#1}{#2}{#3}{#4}{#5}{#6}}}
忽略各种条件判断语句,其中最核心的是\@sect
指令,进一步查看\@sect
指令定义:
\@sect=macro:
#1#2#3#4#5#6[#7]#8->\ifnum #2>\c@secnumdepth \let \@svsec \@empty
\else \refstepcounter {#1}\protected@edef \@svsec {\@seccntformat {#1}\relax }\fi
\@tempskipa #5\relax
\ifdim \@tempskipa >\z@
\begingroup #6{\@hangfrom {\hskip #3\relax \@svsec }\interlinepenalty \@M #8\@@par }\endgroup \csname #1mark\endcsname {#7}
\addcontentsline {toc}{#1}{
\ifnum #2>\c@secnumdepth
\else \protect \numberline {\csname the#1\endcsname }\fi #7}
\else \def \@svsechd {#6{\hskip #3\relax \@svsec #8}\csname #1mark\endcsname {#7}
\addcontentsline {toc}{#1}{
\ifnum #2>\c@secnumdepth \else \protect \numberline {\csname the#1\endcsname }\fi #7}}\fi
\@xsect {#5}
可以发现其中输出章节编号的核心指令是\@seccntformat
,进一步查看其定义:
\@seccntformat=macro:
#1->\csname the#1\endcsname \quad
到此为止就可以破案了。\@seccntformat
利用\csname
将the
字符串与指令参数(即章节定义的名称,如section
、subsection
等)拼凑成了章节编号计数器输出命令(\thesection
、\thesubsection
……),从而输出章节编号,并在其后加上一段\quad
空白。我们只需要修改一下这个定义,将章节编号放进一个左右盒子里即可:
\makeatletter
\def\@seccntformat#1{\makebox[40pt][l]{\csname the#1\endcsname}}
\makeatother
通过修改定义,将章节编号放进了一个40pt宽度的盒子中。由于这一定义会影响所有级别章节的编号输出样式,就实现了所有级别的章节文字内容左对齐。而且因为这样的修改比较底层,不会和上层的各类宏包冲突,之后还能继续使用ctex
等宏包进一步对章节标题的排版进行其他设置。