在XSL-FO排版体系里,列表并不是像HTML那样有现成的无序或有序标签自动渲染符号,而是完全依靠list-block以及内部的list-item、list-item-label、list-item-body组合来手工搭建。理解这套结构,是控制印刷文档中条目样式的前提。很多从CSS转来做XSL-FO的人,会习惯性寻找list-style-type之类的属性,结果在规范里找不到对应内容,就是因为两者的抽象层级不同。

list-block的基础结构与标签职责
list-block在XSL-FO中是一个块级格式化对象,它的作用类似于一个纵向容器,负责把多个list-item按顺序排下去。它本身并不绘制任何项目符号或编号,仅仅提供列表整体的外边距、缩进和间距控制。真正决定每一行长什么样的是list-item内部的拆分:左边是list-item-label,右边是list-item-body。这种左右分栏模型,让标签和内容可以独立设置字体、对齐方式。
一个最简单的list-item必须同时包含list-item-label和list-item-body,缺少任何一个都会导致格式化器报错。在list-item-label里通常会放一个block,里面写星号、横线或者数字;而list-item-body里的block则是具体的条目文本。两者通过provisional-distance-between-starts属性约定标签起点与正文起点的水平距离,这个距离直接决定了缩进量。
下面的示例展示了最基础的实心方块列表写法。可以看到,符号是自己写在label里的,而不是由处理器生成。这种做法虽然繁琐,但带来了极高的可控性,比如同一个列表里可以混用不同符号。
<fo:list-block provisional-distance-between-starts="10mm"
provisional-label-separation="2mm">
<fo:list-item>
<fo:list-item-label>
<fo:block>■</fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block>第一条:系统初始化配置说明</fo:block>
</fo:list-item-body>
</fo:list-item>
<fo:list-item>
<fo:list-item-label>
<fo:block>■</fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block>第二条:运行时环境变量加载</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
通过属性控制缩进、对齐与编号样式
要让列表看起来专业,仅靠基础结构还不够,必须精细调节list-block与list-item上的几个核心属性。provisional-distance-between-starts设定的是从列表左边到正文内容开始位置的距离,而provisional-label-separation则是标签块和正文块之间的最小间隙。如果前者设置过小,长一点的标签(比如三位数编号)就会和正文重叠。
另一个关键属性是relative-align,它出现在list-item上,取值可以是before或baseline。当设为before时,label的顶部和body的顶部对齐,适合方块或图片类符号;设为baseline时,则按文字基线对齐,适合数字编号场景,能让序号和正文首行文字视觉上平稳。对于多行正文条目,基线对齐能避免符号孤零零悬在上方。
如果希望实现自动递增编号,XSL-FO本身没有计数器机制,但可以在XSLT转换阶段利用xsl:number生成序号文本,再塞进list-item-label。这样既能保持FO层简洁,又免于手工维护数字。下面代码演示了XSLT如何为每一个条目算出序号并输出带点的编号:
<xsl:template match="item">
<fo:list-item>
<fo:list-item-label>
<fo:block>
<xsl:number format="1."/>
</fo:block>
</fo:list-item-label>
<fo:list-item-body>
<fo:block><xsl:value-of select="text"/></fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
对于无序列表的符号变换,常见做法是在list-item-label的block中使用不同Unicode字符,例如•、◦、▪来表现层级。配合font-family和color,可以轻松模拟出CSS里disc、circle、square的效果,而不依赖任何内置样式关键字。
嵌套列表与复杂排版场景实践
实际业务中经常遇到多级条款,例如法律文本或标准文档。XSL-FO允许在list-item-body内部再嵌入一个list-block,从而形成嵌套。每一层都可以独立设置缩进距离,外层用数字编号,内层用字母或符号,层次感通过缩进自然体现。需要注意的是,嵌套越深,provisional-distance-between-starts应当逐层递增,否则内层会和外层左边界挤在一起。
在制作技术手册目录时,我们常把一级章节作为数字编号,二级作为小数点编号。此时如果纯靠XSLT算号,可以用xsl:number的level属性输出多级编号,再格式化为1.1.这样的字符串。然后将结果写入对应层级的list-item-label。这种方案比在FO里做条件判断更清晰,也方便后期修改编号规则。
下面给出一个两级嵌套的示例,外层为章节,内层为小节,各自使用不同的标签符号与缩进,演示了如何在同一个list-block体系下保持视觉条理:
<fo:list-block provisional-distance-between-starts="8mm">
<fo:list-item>
<fo:list-item-label><fo:block>1.</fo:block></fo:list-item-label>
<fo:list-item-body>
<fo:block>安装与部署
<fo:list-block provisional-distance-between-starts="6mm">
<fo:list-item>
<fo:list-item-label><fo:block>-</fo:block></fo:list-item-label>
<fo:list-item-body><fo:block>环境依赖检查</fo:block></fo:list-item-body>
</fo:list-item>
<fo:list-item>
<fo:list-item-label><fo:block>-</fo:block></fo:list-item-label>
<fo:list-item-body><fo:block>服务端口配置</fo:block></fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
除了嵌套,还有一种场景是横排术语表:用list-block做定义列表,label放术语,body放解释,通过relative-align="baseline"让术语和解释首行对齐。这种用法已经超出了传统列表范畴,但正好说明list-block的本质是一个双栏对齐工具,只要掌握label与body的分离思想,就能衍生出丰富的版式。
XSL-FOlist-block列表样式修改时间:2026-08-16 09:18:33