ADR-0004:语义段落与基础分页
2026-10-14 更新:本文保留初始决策背景。富文本、字体组缺字策略、分页控制、分节、合并表格与中文表单的扩展边界见 ADR-0007。
- 日期:2026-10-14
- 状态:已采纳,阶段三首批范围已实现;不表示表格等阶段目标完成。
- 关联:ADR-0001、ADR-0003
- 后续:ADR-0005 扩展有序混合块、表格行及重复表头;本记录保留首批范围。
背景
阶段二已能计算段落内部的行,但页面仍由调用方显式创建。后续表格与报告布局需要先验证语义内容、页面区域、分页推进和来源映射之间的关系。
基础模型必须保持独立,不能通过引用文本或绘制实现来便利布局。分页也不能把重新换行的责任交给后端。
决策
- 模型增加
Document、Paragraph、ParagraphFormat、ParagraphAlignment、PageSettings和PageMargins。段落使用文档内唯一 ID,普通字符串保留原文,不预建表格或组件空接口。 - 语义格式保存字号、行距、缩进、对齐、间距和保持整块。布局核心显式映射到文本层格式,模型继续只依赖 JDK。
- 新增
export4j-layout-model保存页面、段落片段及来源,新增export4j-layout-core计算分页并转换为 Display List。布局结果通过绘制 API 复用项目自有几何与字形类型,不包含后端对象。 - 使用统一页面设置和调用方传入的全局字体栈,每段先换行一次,再按完整行纵向放置。跨页不重复首行缩进,不改变行内断点和字体。
- 段前段后间距在同页相加,页边缘舍弃。保持整块的段落需放入一张新页;新页不够时明确失败。
- 空文档得到一个空页面,空段落及显式空行保留行高。算法每轮消耗完整行或进入新页,无法放下一行时失败,附加文档级段落、码点、行与页面预算。
- 片段保留段落 ID、段落索引、起始行号、原文 UTF-16 范围和对应字体回退诊断。异常保留处理中的页面和源位置,失败不返回部分布局。
DocumentPainter只平移已有片段并生成黑色正文绘制。页眉页脚与页码仅作为示例后处理,不提前建立组件系统。
考虑的替代方案
| 方案 | 取舍 |
|---|---|
| 模型直接依赖 ParagraphStyle | 少一次映射,但把基础语义模型绑定到文本、字体模块及具体行布局契约 |
| 每到新页重新换行 | 容易重新应用首行缩进或改变断点,难以追踪原始行号 |
| PDF 后端边写边分页 | 减少中间对象,但破坏布局与格式隔离,也难以在输出前验证预算及来源 |
| 一次实现表格、孤行与所有分页节点 | 扩大首批范围,难以独立验证基础推进规则;按实际能力逐步扩展 |
影响与兼容性
本次增加语义与布局类型,保留已有字体、文本和精确绘制公共入口。语义格式与文本格式职责不同,新增字段或对齐能力需同步维护显式映射。
布局结果会持有不可变字体与字形数据,内存随实际内容增长;不是流式写盘布局。当前没有完整 Document Model、字符级格式、分节或布局组件注册机制。
字体资源增加固定版本的霞鹜文楷 TC,替换当前混排示例和测试所选中文字体;原字体及历史验收记录保留。新增资源采用 OFL 1.1,未增加 Maven 依赖,来源与许可见字体资源。
验证方式
通过模型校验与不可变性、跨页来源连续性、页边距和基线、缩进及对齐、间距、保持整块、空内容、浮点边界、预算与失败上下文、并发隔离和绘制转换测试。
集成测试生成五页 PDF 并提取原始中英文行序列,独立 Poppler / pypdf 检查可执行报告的渲染、正文标记、页码、字体嵌入和 Unicode 映射。证据见段落分页验收。
后续 ADR-0006 将第 8 项中的示例装饰边界扩展为核心页区与组件 SPI;本记录的段落分页规则继续有效。