XSL-FO「流」(flow & static-content)最完整实战讲解
(2025 年仍在银行、发票、图书排版里天天被用到的核心机制)
XSL-FO 里所有内容必须放在「流」里,分为两大类:
| 类型 | 元素名称 | 放哪里? | 每页是否重复? | 典型内容 |
|---|---|---|---|---|
| 流动内容(Flow) | <fo:flow> | 只能流向 region-body | 不重复,一次性排完 | 正文、表格、图片、段落 |
| 固定内容(Static) | <fo:static-content> | 流向 region-before/after/start/end | 每页都重复 | 页眉、页脚、边注、背景水印 |
记住一句话:
正文用 flow,页眉页脚用 static-content,缺一不可!
1. 标准完整结构(直接复制的母版)
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="A4" page-height="297mm" page-width="210mm"
margin="20mm">
<fo:region-body margin-top="20mm" margin-bottom="20mm"/>
<fo:region-before extent="15mm"/> <!-- 页眉区 -->
<fo:region-after extent="12mm"/> <!-- 页脚区 -->
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="A4">
<!-- 固定内容:页眉(每页都出现) -->
<fo:static-content flow-name="xsl-region-before">
<fo:block font-size="10pt" text-align="center" border-bottom="0.5pt solid #999">
××公司 2025 年度增值税专用发票
</fo:block>
</fo:static-content>
<!-- 固定内容:页脚(每页都出现) -->
<fo:static-content flow-name="xsl-region-after">
<fo:block font-size="9pt" text-align="center" color="#666">
第 <fo:page-number/> 页 共 <fo:page-number-citation-last ref-id="LAST"/> 页
</fo:block>
</fo:static-content>
<!-- 流动内容:正文(只出现一次,从上往下排) -->
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="18pt" font-weight="bold" text-align="center" space-after="20pt">
增值税专用发票
</fo:block>
<!-- 你的所有表格、段落、图片都放在这里 -->
<fo:block id="LAST"> <!-- 标记最后一页,供页脚引用 -->
……发票明细表格、金额大写、收款人、复核人……
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
2. 常见流组合(99% 项目都属于下面这几种)
| 场景 | 需要哪些流? | 说明 |
|---|---|---|
| 普通报表、发票 | 1 个 flow + 2 个 static-content | 最常见 |
| 图书、长报告(奇偶页不同) | 1 个 flow + 4 个 static-content(奇偶各一套) | 用 page-sequence-master 切换 |
| 封面页不要页眉页脚 | 第1个 page-sequence 只有 flow 第2个开始加 static-content | 两组 page-sequence |
| 每章单独重新页码 | 每个章节一个 page-sequence,各自带 static-content | initial-page-number=”1″ |
| 背景水印(“机密”“作废”) | 用 region-before 放 absolute-position 的 block | 固定内容里也可以做水印 |
3. 实战技巧(血泪经验)
| 需求 | 正确做法 |
|---|---|
| 页码显示“第 2 页 共 10 页” | 在最后一个 block 上加 id=”LAST”,然后页脚用 <fo:page-number-citation-last ref-id="LAST"/> |
| 第一页不显示页眉页脚 | 用两个 page-sequence:第一个只有 flow,第二个加 static-content |
| 正文内容太多,自动分页 | 完全自动!flow 会自动流向 region-body,排满一页就自动新开一页 |
| 想在页脚放大表格(审批签字表) | 仍然用 static-content + fo:table + absolute-position 定位 |
| 奇数页页眉居左,偶数页居右 | 用 fo:page-sequence-master + conditional-page-master-reference 做奇偶模板 |
4. 最小可运行例子(30 秒出 PDF)
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="p" page-height="297mm" page-width="210mm" margin="20mm">
<fo:region-body/>
<fo:region-before extent="10mm"/>
<fo:region-after extent="10mm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="p">
<fo:static-content flow-name="xsl-region-before">
<fo:block text-align="center">我的测试文档 - 页眉</fo:block>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after">
<fo:block text-align="center">第 <fo:page-number/> 页</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<fo:block font-size="16pt" space-after="10mm">Hello XSL-FO 流机制!</fo:block>
<!-- 重复 200 次就会自动分页 -->
<fo:block>这是一段测试文字。</fo:block>
<!-- ... 复制很多行 ... -->
</fo:flow>
</fo:page-sequence>
</fo:root>
一句话总结:
一个 page-sequence 里只能有 1 个 <fo:flow>(正文) + 0~多个 <fo:static-content>(页眉页脚),flow 负责内容,static-content 负责每页重复的东西。
需要我给你「发票」「对账单」「图书双面」三种经典流模板吗?说一声就发。