ADR-0007:进阶布局与字体组缺字策略
背景
基础分页与组件已形成闭环,但报告需要标题层级、富文本、混合纸张、合并表格、脚注、嵌套续页和中文可编辑字段。调用方还要求按字体或字体组明确选择缺字报错,或显示占位符并返回警告。
模型仍须独立于字体句柄、绘制和 PDFBox;布局核心不能反向依赖组件实现。新增能力必须有可运行输出与独立验证。
决策
- 模型增加分页策略、行内样式、命名样式、标题、显式分页、分节与语义引用。字体角色在布局入口解析,真实粗斜体使用真实字体资源。
- 字体模块以
FontGroup携带有序成员与缺字策略。默认ERROR,显式PLACEHOLDER_WITH_WARNING才能替换;先遍历整组。占位符也缺字、非法 Unicode、复杂塑形仍失败。 - 文本层同时保留绘制码点与原文码点,替换不改变原 UTF-16 来源范围。返回结构化
WARNING,日志输出由调用方负责。所有正文、目录、页区和标准嵌套组件传递同一组策略。 - 核心增加
PageableComponentSPI,扩展一次测量返回有序片段;首片段可使用当前剩余高度。标准组件负责 FlowColumn、FlowText、Row 和合并 GridTable;核心只依赖接口。 - 每节从新页开始,首页/奇偶模板预留最大页区。分离物理页索引与可重置显示页码;目录、引用和交互均在最终物理页确定后解析。
- 编号与题注在组件编排层降低为段落,引用使用固定单行区域。脚注采用每节最大区域的单调预留,最多 32 次,接受保守留白;尾注追加至最后一节。
- 图像由显式字节有界解码,保留 alpha。SVG 只支持离线白名单矢量子集,对未知特性明确失败,并裁剪至 viewBox。
- 中文表单完整嵌入一个显式字体;下拉框与单选组保留规范字段树、值和外观。表单不使用正文的缺字替换策略,避免改变填写数据。
考虑的替代方案
- 默认替换缺字会隐藏排版质量问题,因此保留严格默认值;系统字体扫描会破坏可复现性,因此仍只使用显式字体。
- 在核心直接实现全部组件会产生反向依赖,因此分页组件通过 SPI 接入。
- 引用文字依赖分页且可反过来改变分页,采用固定区域避免无上限重排;溢出明确失败。
- 按每页精确脚注高度反复重排可能振荡,首版采用单调的全节预留;未来可独立扩展更紧凑的算法。
- 表单仅嵌入初始字形会阻止后续新字符编辑,因此使用完整字体并执行输出预算。
- 完整 SVG、复杂塑形与 Word 兼容需要独立契约和测试集,不以当前子集替代。
影响
保留常用旧构造器和字体列表重载,但 record 结构、新枚举与 sealed 成员变化不保证已有二进制、反射序列化或 exhaustive switch 兼容。模型仍为 JDK-only,公共 API 不泄漏第三方类型。
GridTable 的跨行连通组不可拆分,Row 整体不可拆分;脚注可能引入留白;引用不自动扩展区域。库返回诊断,不自动打印日志。自定义组件是可信 Java 代码,不属于执行沙箱。
验证
JUnit 覆盖最低行数、保持链、分页推进、富文本来源、字体组策略、嵌套片段、合并组、分节页码、最终目标、脚注、Unicode 表单和透明图像。综合示例以 pypdf 检查结构及保存重开,用 Poppler 与 PDFium 渲染全部页面,并保存经人工检查的像素基线。基准记录 5,000 段落布局,指标与限制见进阶验收记录。