Word 编号的内部结构与工作原理

注:本文为发布在少数派上的付费教程节选。利益相关:员工。


在从宏观角度了解 Word 中的编号如何定义和应用后,我们再将镜头拉近,观察编号段落的内部结构。

再次回忆:编号是段落格式的一部分,也必须依附于段落而存在。本质上,编号只是段落首行开头处的一个文本范围(run),和普通段内文本一样有着字体、字号、颜色等文本格式属性,但其特殊之处在于:其一,编号的内容是根据一系列规则自动计算和生成的,而不是手动输入的;其二,编号有自己的对齐和缩进设置,因此会波及地影响其所在段落的实际缩进。

要有一个直观的认识,只要点击任一一个编号数字,灰色底色覆盖的部分就是编号部分的范围。不过,这个看起来自成一体的编号范围,其内部实际上是由很多部分组成的:

上图中的概念很多,我们结合编号设置的主阵地——「定义新多级列表」对话框中的选项逐一介绍。

要打开「定义新多级列表」对话框,可以在任意编号上单击右键,选择「项目符号和编号」,然后单击「多级符号」选项卡下的「自定义」按钮;或者单击工具栏上的「多级列表」按钮,在下拉菜单中选择「定义新的多级列表」。

可以看出,该对话框被一道分隔线分为「编号格式」和「位置」两个部分;此外,「字体」按钮还可以打开一个类似于普通字体对话框的界面。这三个部分也就分别对应了编号格式的三大类——与编号数字、对齐方式和编号字体相关的设置。

1. 与编号数字相关的格式

编号格式: 即以什么数字或文字显示编号。Word 实际支持的编号格式有数十种之多,会根据语言版本不同显示不同选项(例如中文版会提供「壹、贰、叁」等中文大写数字,日文版会提供「壱、弐、参」等日文大写数字),这里不一一列举。

注意这里的下拉菜单只负责设置编号中的变量,即数字部分;数字前后的固定文本(例如「第一章、」中的「第」「章」二字和后接的顿号)需要在旁边的文本框中手动输入。

包含上级编号: 很多场景下,除了需要显示当前级别的编号,还要包含上级编号。这时,可以在「包含的级别编号来自」下拉菜单中选择需要的级别,相应的上级编号会被追加到编号格式文本框中,你可以反复操作直至添加完所有所需的级别。同样地,你需要手动输入组合编号中的固定部分(例如「5.1(a)」中的句点和括号)。

如果需要在某级编号中包含所有上级编号、且全部使用阿拉伯数字(形如 1.22.3.3 等),则可以勾选「正规形式编号」(legal style numbering,直译为法律格式编号,因在英美法律文书中常用,故名)。

起始编号和重新开始编号的间隔: 这两个选项都与编号数字的「起点」相关。其中,「起始编号」很好理解,就是该级数字从序列中的第几个起算,例如如果设置为 2,则该级数字会(取决于数字格式)从 2B 开始。

「重新开始编号的间隔」则指该级数字应在遇到哪一上级后重新起算。注意这里可以选择在任一上级编号后才重置,未必是紧邻的上级编号。例如,在下面的编号序列中,二级编号(一), (二), ... 和三级编号 1. , 2. , ... 都被设置为在一级编号后重置,因此 第一节(二) 下的编号 2. 并未重置,直到遇到下一个一级编号 第二节 才重新从 1. 起算。

不难看出,上述与编号数字相关的设置都仅仅是计数规则,而从未包含数目本身。的确,Word 从不会直接记录任何编号数字,而是根据段落套用的编号规则,现场「掰手指」算出编号的值,显示在段落开头。

因此,如果你看到两个段落位置上紧邻、编号上连续,这并不表明它们是「一家人」,属于同一个编号序列(或用 OOXML 的术语来说,引用同一个编号实例)。例如,两个显示了编号 10.11. 的段落完全可能分属甲乙两个不同序列,其中前一个段落是甲序列中的第十个,后一个段落是乙序列中的第一个,只是乙序列恰好设置为从 11 起算——就好像哪怕马拉多纳穿 10 号球衣,姚明穿 11 号球衣,他们也不是一个球队的。

一组「貌合神离」的编号示例,第 11 项与之前编号不属于同一序列(注意观察底色),因此并不服从于其他编号的格式设置

这种「关公战秦琼」的情况经常发生于在多个文档间来回复制编号的场合。与很多用户想象的不同,当编号被移植到另一个文档时,它未必会自动融入到新文档的编号序列中,而往往会保持原来的编号层级、序列和样式。只是迫于用户的强迫就范——例如手动拖拽标尺、设置字体和滥用编号项右键菜单中的「重新开始编号」(真名是「从这里分拆出一个新编号序列」)和「继续编号」(真名是「从这里缝合两个编号序列」),才显得跟上下文的编号打成一片。一旦事后再次调整编号样式,这些貌合神离的编号就会露出真实面目,显得不听使唤。

要避免这种情况,在移动编号段落时,应当尽量以 Word 粘贴选项中的「纯文本」或「匹配目标格式」方式粘贴,从而去除段落先前带有的编号信息。如果要鉴别编号序列是否存在间断,可以单击要检查的编号,使其处于选中状态,只有与其属于同一序列的其他编号才会一并显示底色。

2. 与对齐方式相关的格式

编号对齐方式和对齐位置: 编号有三种对齐方式,左对齐、居中对齐和右对齐,都是相对于所在段落的左侧边缘而言的,分别指编号文本的左侧边缘、中心线或右侧边缘与段落的左侧边缘对齐。而所谓的「对齐位置」就是指上述对齐基线的位置,这是一个绝对数值,从标尺的零点起算。下图中三个段落的编号对齐位置都是 0 英寸,可以看到,后两个设置了居中对齐和右对齐的编号向左突出了一部分。

可见,仅在编号对齐方式设置为左对齐时,对齐位置等价于于编号段落的首行起点。对于居中或右对齐的编号,对齐位置控制的是编号的中间线或右边缘位置,因此段落的视觉起点会延伸到更靠左的位置(例如本节开头图中的第四行)。

注: 顺带一提,如果你对于经常出现在缩进设置中的 0.63、1.27、1.9和 2.54 这几个默认数值感到眼熟却莫名其妙,是因为……微软是一家满脑子英寸制美国公司,这几个默认数值是从 0.25 英寸、0.5 英寸、0.75 英寸、1 英寸换算成厘米产生的。为简明起见,本文举例时暂将 Word 的单位设置改为英寸值。)

「编号之后」和制表位添加位置: 这组设置决定了编号后的实际文本从何处开始。其中,「编号之后」可在「制表符」(默认)、「空格」和「无」之间选择。比较简单的情况是设置为「无」(或「空格」)。此时,实际文本会紧随编号文本(或附加一个额外空格)后立即开始。

但如果「编号之后」设置为「制表符」(tab),那么编号和实际文本之间会间隔一个制表符的长度。可那到底是多长呢?答案是……「看情况」。

Word 中的制表符是一个长度可变的字符,它很像中学物理课上那辆运动在光滑平面上的小车,会一直延伸到下一个制表位(tab stop,起源于打字机上用于控制对齐的同名装置)为止。对于没有手动设定制表位的段落,Word 默认将标尺上每 0.5 英寸(0.63 厘米)当作一个制表位(段落的悬挂缩进位置也视为一个制表位)。

因此,如果编号内容结束于 0.49 英寸处,那么随后的制表符会延伸到 0.5 英寸的位置。换言之,编号与实际文本只间隔了 0.01 英寸。但如果编号内容结束于 0.51 英寸,那么制表符会延伸到下一个默认制表位,即 1 英寸处,此时编号与实际文本间隔了 0.49 英寸。

这就解释了下图上半部分所示的常见问题:10 以上编号之后的实际文本从很远的位置才开始。这是因为随着编号进入两位数,所占空间变得更大,已经来不及在前一个制表位(下图中的 0.5 英寸位置)之内结束,于是实际文本被「挤」到了下一个制表位(1 英寸位置)。

这时,「制表位添加位置」就能派上用场。它是指在编号段落的指定位置增加一个自定义的制表位,从而覆盖其左侧的全部默认制表位(悬挂缩进除外)。

还是以上图为例,观察到所有编号末端都不会超过 0.6 英寸,故通过在该处添加一个制表位,就可以实现所有让各段的文本起始位置本对齐到 0.6 英寸的效果(如下半部分所示)。

文本缩进位置: 这是一个让人一言难尽的设置。不妨做一个实验:先将该项数值填写为 0,然后不停单击右侧的上箭头,逐渐增大设置,同时观察预览框中的变化。可以发现,该设置最初只会影响第二行开始的缩进距离;可一旦增大到超过编号部分的宽度,继续上调数值就会同时影响首行实际文本的起始位置。

可见,「文本缩进位置」并不是一个独立的参数,它只是在间接调整着编号段落的特殊缩进:

  • 文本缩进位置 < 编号对齐位置时:相当于设置首行缩进,后续行文本将从编号对齐位置(也就是编号段落的左侧缩进)更左边开始;
  • 文本缩进位置 = 编号对齐位置时:相当于关闭特殊缩进,后续行文本将从编号对齐位置开始;
  • 文本缩进位置 > 编号对齐位置时:相当于设置悬挂缩进,后续行文本将从编号对齐位置的右边开始;并且因为悬挂缩进位置被视为制表位,首行文本也将被编号后的制表符(如果有)「推」到缩进位置,于是最终结果就是首行文本和后续行文本对齐。

细心的读者可能发现少了一种情况:但如果想让后续行文本比首行文本的缩进更大呢?答案是使用上文介绍的「制表位添加位置」选项,在文本缩进位置之前添加一个制表位,从而将首行文本「堵」在这个制表位上。

下图演示了这四种不同情况:

显然,「文本缩进设置」设计是复杂且令人困惑的,也违反了尽量避免操作副作用的程序设计原则。但木已成舟,从用户角度,我们能做的也只是提醒自己在设置时多做几个心算,同时注意观察预览图和标尺。

3. 与编号字体相关的格式

这部分相对简单明了,即在编号定义对话框中点击「字体」按钮打开的设置,与普通段内文本的字体设置无异。

不过,在编号定义对话框中做的字体设置会应用到所有编号项中。如果你想单独修改(或重置)某处特定编号的字体设置(例如从别处复制而来的编号段落附带了奇怪的格式),可能会发现编号部分是无法直接用鼠标选中的。如何解决呢?

如之前的段落一章中所讨论,作为段落格式的一部分,编号的字体格式也是由所在段落结尾的换行标记所控制的。因此,只要打开换行标记的显示,然后对换行标记进行字体设置,就能间接地修改编号的字体,比如……

从另一个角度说,这也提示我们要特别保护好段落标记这个编号的「命门」,在复制粘贴、使用格式刷等避免选中不必要的段落标记,导致编号格式被覆盖或丢失。

编号的工作原理

在了解了编号设置方式和内部结构的基础上,我们最后再深挖一下编号的工作原理。

文章开头将编号功能比作「艺术」,并不纯粹是种揶揄。在批评文学艺术时,柏拉图曾说艺术是模仿的模仿、影子的影子,与真理隔了三层。柏拉图认为,现实世界是对理念世界的模仿,而艺术又是对现实世界的模仿。

类似地,Word 中的编号也是从一个抽象的范式出发,经过逐层「模仿」和「套用」,最终变成为文档中显示的实际编号数字。

在这个模仿链条的最上层,是 Word 文档通过内部编号部件(numbering.xml)存储的一系列编号定义(OOXML 标准称「抽象编号定义」,abstract numbering definitions)。定义的开头是名称、类型、链接的段落样式等基本信息,然后依次列举九级编号各自的格式。上文介绍的「定义新的多级列表」对话框中的各个选项,在编号定义中都有对应的属性。

编号定义并不会被直接使用。对于文档中每一套编号,Word 都会以一个现有编号定义为模板,建立一个「实例」(OOXML 标准称「编号定义实例」,numbering definition instances)——描绘出一个「仿制品」。这个「仿制」过程并不完全是照葫芦画瓢,而是可以根据需要覆盖模版中的部分设定。

使用上文介绍的各种方法创建编号时,都会导致新的编号定义和编号实例被创建出来。其中,通过自动编号和多级列表功能创建编号时,Word 文档的编号部件 numbering.xml 中会出现一对新的抽象编号定义以及一个引用该定义的编号实例。而用样式定义功能创建编号时,除了编号定义和编号实例,样式部件 styles.xml 中还会多出一个样式定义,它本身不记录任何编号格式,唯一的作用就是记录这个列表样式的名称。

最后,当将一个段落设为编号段落时,Word 在该段落的属性中写下要套用的编号实例 numId 和所属层级 ilvl。这样,就可以据此逐层查阅编号部件中的实例和定义,确定要显示的编号数字和格式。

以下图中的第二个段落 b) [...] 为例,其所在段落的属性表明该段落的编号层级为第二级、套用 id 为 42 的编号实例;Word 据此前往编号部件中检索相应的编号实例,发现它把皮球踢给了 id 为 24 的抽象编号定义;最终,在这个抽象编号定义中,Word 找到了第二级编号应当具有的格式设置,从而显示在文档中。

Comment