Pro 版本
Flow Layout — 深度参考
本页是 Pro Flow Layout 模块的深度参考。它涵盖布局引擎、元素模型、分页策略、它们的行为契约以及各自的失败模式。StreamingLayoutEngine 会按顺序遍历一个 FlowElement 值的列表。它为每个元素分配一个从零开始的页面索引以及一个位于 LayoutRegion 内的位置。其结果是一个由不可变的 PlacedElement 记录构成的 LayoutResult。该模块只计算布局;它不进行任何渲染,也不执行任何 I/O。
可用性与授权
标题为“可用性与授权”的章节此能力随 NextPDF Pro(nextpdf/pro)发布,并通过 Pro 级别的授权信封激活。没有该授权的部署不会加载此能力的类。比较版本并获取授权。
不存在逐功能的授权标记。这是一项 Pro 版本级别的能力。
公共 API 接口面
标题为“公共 API 接口面”的章节所有符号都位于 NextPDF\Pro\FlowLayout 命名空间中。所有值对象都是 final 且不可变的。
| 符号 | 参数 | 默认行为 | 返回 | 抛出或失败于 | 说明 |
|---|---|---|---|---|---|
StreamingLayoutEngine::__construct | LayoutRegion $region, PageBreakStrategy $strategy = PageBreakStrategy::Greedy | 将逐页内容区域绑定到一个分页策略 | StreamingLayoutEngine | — | 策略默认为 Greedy。 |
StreamingLayoutEngine::layout | list<FlowElement> $elements | 单次前向遍历;由策略驱动分页的顺序布局 | LayoutResult | 永不抛出 | 空列表产生一个空白页。 |
StreamingLayoutEngine::withStrategy | PageBreakStrategy $strategy | 派生一个区域相同的新引擎 | self | — | 接收方保持不变。 |
StreamingLayoutEngine::withRegion | LayoutRegion $region | 派生一个策略相同的新引擎 | self | — | 接收方保持不变。 |
FlowElement::__construct | FlowElementType $type, string $content, float $widthPt = 0, float $heightPt = 0, float $marginTopPt = 0, float $marginBottomPt = 0, bool $keepWithNext = false | 不可变的元素值对象 | FlowElement | — | Table 元素唯一的构造路径。 |
FlowElement::text | string $content, float $height | 带有调用方测量高度的文本元素 | self(静态) | — | 宽度 0 在布局时解析为区域宽度。 |
FlowElement::image | string $path, float $width, float $height | 图像元素;content 承载路径 | self(静态) | — | 引擎从不打开该文件。 |
FlowElement::spacer | float $height | 内容为空的垂直空白 | self(静态) | — | — |
FlowElement::pageBreak | — | 显式的断页标记 | self(静态) | — | 不产生任何 PlacedElement。 |
FlowElement::totalHeight | — | 高度加上下边距 | float | — | 所有适配检查都使用此值。 |
FlowElementType | 枚举成员 Text、Image、Table、Spacer、PageBreak | 以字符串为后端:text、image、table、spacer、page_break | — | — | — |
FlowElementType::isBreakable | — | Text 与 Table 返回 true;其他返回 false | bool | — | 仅为分类;参见下文的原子布局契约。 |
LayoutRegion::__construct | float $x, float $y, float $width, float $height | 以左上为原点的内容盒,以点为单位度量 | LayoutRegion | — | 不做校验;值按给定取用。 |
LayoutRegion::contains | float $px, float $py | 含边界的点是否在区域内的测试 | bool | — | — |
LayoutRegion::remainingHeight | float $currentY | 区域高度减去已消耗的垂直偏移 | float | — | 一旦游标溢出即为零或负值。 |
LayoutResult::__construct | list<PlacedElement> $placements, int $pageCount, float $totalHeightPt | 不可变的布局结果 | LayoutResult | — | — |
LayoutResult::placementsOnPage | int $pageIndex | 按从零开始的页面索引筛选布局项 | list<PlacedElement> | — | 返回的列表会重新索引。 |
LayoutResult::isEmpty | — | 当没有任何元素被放置时为 true | bool | — | 对空输入与仅含断页的输入均为 true。 |
PageBreakStrategy | 枚举成员 Greedy、AvoidOrphans、KeepTogether | 以字符串为后端:greedy、avoid_orphans、keep_together | — | — | — |
PageBreakStrategy::label | — | 人类可读的策略标签 | string | — | — |
PlacedElement::__construct | FlowElement $element, int $pageIndex, float $x, float $y, float $width, float $height | 不可变的布局记录 | PlacedElement | — | 坐标以点为单位,左上为原点。 |
public function layout(array $elements): LayoutResultpublic function withStrategy(PageBreakStrategy $strategy): selfpublic function withRegion(LayoutRegion $region): selfpublic static function text(string $content, float $height): selfpublic static function image(string $path, float $width, float $height): selfpublic static function spacer(float $height): selfpublic static function pageBreak(): self行为契约
标题为“行为契约”的章节StreamingLayoutEngine::layout() 对输入列表执行一次前向遍历。对每个元素,它检查适配情况,在需要时断页,然后记录一个 PlacedElement。空输入列表返回一个没有任何布局项、页数为 1、总高度为 0 的 LayoutResult。
布局几何是确定性的:
x是区域的左边缘。y是当前游标位置加上元素的上边距。width在元素的widthPt为正时取该值,否则取区域宽度。height是元素的heightPt,与提供的值完全一致。
每次布局后,游标按 totalHeight() 前进,含边距。相同的量会累加到 LayoutResult::totalHeightPt。
分页规则,按求值顺序:
- 显式的
PageBreak元素会递增页面索引,并将游标重置到区域顶部。它不产生任何布局项,也不给总高度增加任何值。 - 当某个元素的
totalHeight()超过剩余高度时,引擎会断页——除非游标已经位于页面顶部。 Greedy不附加任何进一步的条件:能放下的元素总是被放置。AvoidOrphans会在放下一个能容纳的元素之前断页,前提是放置后剩下的空间为正、但低于该元素自身所需高度的一半。参照单位是元素自身的高度,除数固定为二;不涉及任何字体度量。它从不在页面顶部断页。KeepTogether会在放下一个能容纳的元素之前断页,前提是其keepWithNext标志已设置、存在下一个元素、游标不在页面顶部,且两个元素合并的totalHeight()超过剩余空间。位于最后一个元素上的该标志没有作用。
原子布局:引擎将每个元素作为一个整体来放置。它从不把元素内容拆分到多页。FlowElementType::isBreakable() 对调用方可以预先拆分为更小元素的类型进行分类;引擎自身并不查阅它。
无状态性与确定性:引擎只持有它的区域与策略。layout() 在多次调用之间不共享任何状态,相同的输入产生相同的结果。withStrategy() 与 withRegion() 返回新的引擎,从不修改接收方。
边界情形与失败模式
标题为“边界情形与失败模式”的章节- 本模块中没有任何方法会抛出异常。没有需要捕获的异常层级。
- 构造函数不做任何校验。为负或为零的区域尺寸、为负的元素高度以及为负的边距都会被接受,并原样流经算术运算。
- 高于区域的元素仍会被放置。在页面顶部时它被放置在那里并溢出;在其他位置时引擎会先断页,然后它在一个新页上溢出。随后的下一个元素总会触发断页,因此溢出被限定在一页内。
- 位于开头的
PageBreak会把第一个内容元素放到页面索引 1,使页数至少为 2。 - 连续的
PageBreak元素各自推进页面计数,产生空白页。位于末尾的一个会在pageCount中留下一个最终的空白页。 - keep-together 仅在成对的两个元素都能一起放进同一页时才成立。合并高度超过整页的一对仍会被拆开。
- 非正的
widthPt解析为区域宽度;该替换检查严格大于零。 - 一旦游标溢出,
remainingHeight()可能返回零或负值。contains()将区域边界视为在内部。 - 使用越界索引的
placementsOnPage()返回一个空列表。 - 本模块不执行任何密码学操作,也不定义任何 FIPS 特定的行为。
一致性
标题为“一致性”的章节Flow Layout 实现的是 NextPDF 定义的布局行为。它不针对任何外部的布局或排版标准,因此本页不携带任何规范性引用表。这些分页策略是 NextPDF 的语义;它们不是 CSS 分片属性或任何 XSL-FO keep 模型的实现。所有尺寸均以点表示,与 Core writer 所消费的单位一致。
这些陈述仅描述能力。NextPDF 不持有任何一致性认证,也未做出或暗示任何认证声明。
开发说明
标题为“开发说明”的章节- 在上游测量内容。引擎消费调用方提供的高度;它没有字体度量,也不执行任何文本测量。
- 在布局之前,把过长的文本或表格内容预先拆分为多个元素。用
isBreakable()来决定分块器可以拆分哪些类型。 - 每种页面几何复用一个引擎。用
withStrategy()与withRegion()廉价地派生变体。 - 逐页渲染时,用
placementsOnPage()按页对输出进行分组。 - 布局是单次遍历,其复杂度随元素数量线性增长,并且不保留任何文档树。结果是确定性的,这适合黄金文件测试。
- 对于 HTML 转 PDF 的渲染,请改用 Core HTML 流水线;本模块不是 HTML 或 CSS 引擎。
发布边界
标题为“发布边界”的章节本页仅记录外部可观察的行为以及受支持的公共 API 接口面。内部命名空间路径、辅助类、机制表、运行手册文件名以及工单前缀均不在范围之内。