Pro 版本
Document — 深度参考
Document 模块提供三种 Pro 级装配原语:页面范围拆分、多文档合并,以及 PDF Portfolio(Collection)字典构建。PdfSplitter 会将页面范围提取为独立且结构合规的 PDF,并把整份文档合并为一个重新编号的文件。PdfPortfolio 会构建 Collection 字典,以可排序的 schema 列呈现嵌入文件。每个入口点都会针对恶意输入对输入大小与对象数量设限。
可用性与授权
标题为“可用性与授权”的章节此能力随 NextPDF Pro(nextpdf/pro)一同发布,并在 Pro 级授权信封激活时启用。缺少该授权的部署不会加载此能力的类。比较各版本并获取授权。
公共 API 接口面
标题为“公共 API 接口面”的章节本模块的全部类型都位于 NextPDF\Pro\Document 命名空间。PageRange 与 MergeResult 是来自 NextPDF\Document 的 Core 值对象。
| 符号 | 参数 | 默认行为 | 返回 | 抛出或失败于 | 说明 |
|---|---|---|---|---|---|
PdfSplitter::split() | string $pdfData, list<PageRange> $ranges, int $maxBytes = 100_000_000, int $maxRanges = 1000 | 为每个范围构建一个独立的 PDF 分段 | SplitResult | 缺少 %PDF 头部时抛出 InvalidArgumentException;触及大小、范围数量或闭包守护时抛出 OverflowException | 守护在任何解析之前运行 |
PdfSplitter::splitEvery() | string $pdfData, int $pagesPerSegment | 推导出连续的 N 页范围;最后一段可能更短 | SplitResult | 当 $pagesPerSegment < 1 或缺少头部时抛出 InvalidArgumentException | 以默认上限委托给 split() |
PdfSplitter::extractPages() | string $pdfData, PageRange $range | 将单个范围作为独立 PDF 字节返回 | string | 缺少头部时抛出 InvalidArgumentException;触及闭包守护时抛出 OverflowException | 此路径没有上限参数 |
PdfSplitter::mergeDocuments() | list<string> $pdfs, int $maxInputs = 100, int $maxBytesEach = 100_000_000 | 按顺序把输入合并为一个重新编号的 PDF | MergeResult | 列表为空或输入非 PDF 时抛出 InvalidArgumentException;触及数量、单个输入大小或闭包守护时抛出 OverflowException | 自 3.1.0 起;最高输入版本决定输出头部 |
SplitResult | readonly $segments, $ranges, $totalPages | 携带原始分段字节及源元数据 | — | — | final readonly 值对象 |
SplitResult::count() | — | 统计已生成的分段数 | int | — | — |
SplitResult::segment() | int $index | 返回单个分段的字节 | string | 索引越界时抛出 OutOfRangeException | 从零开始索引 |
PdfPortfolio::__construct() | string $viewMode = 'tile' | 在构造时校验视图模式 | — | 模式非 tile、detail、hidden 时抛出 InvalidArgumentException | — |
PdfPortfolio::addSchema() | PortfolioField $field | 追加一个 schema 列 | self | — | 流式 |
PdfPortfolio::addEntry() | PortfolioEntry $entry | 追加一个文件条目 | self | — | 流式 |
PdfPortfolio::getSchema() | — | 返回累积的 schema 字段 | list<PortfolioField> | — | — |
PdfPortfolio::getEntries() | — | 返回累积的文件条目 | list<PortfolioEntry> | — | — |
PdfPortfolio::count() | — | 统计文件条目 | int | — | — |
PdfPortfolio::generateCollectionDictionary() | — | 发出 Collection 字典字符串 | string | — | 仅在存在字段时才出现 schema 与 sort 块 |
PortfolioEntry | $filename, $data, $description = '', $mimeType = 'application/octet-stream', $customFields = [] | 不可变的文件条目值对象 | — | — | size() 返回数据的字节长度 |
PortfolioField | $name, PortfolioFieldType $type, $displayName = '', $order = 0, $visible = true | 不可变的 schema 列值对象 | — | — | effectiveDisplayName() 会回退到 $name |
PortfolioFieldType | 字符串枚举:Text, Date, Number, FileName, Description, Size, ModDate, CreationDate | 通过 pdfSubtype() 把每个 case 映射到一个 PDF /Subtype | string(S, D, N, F, Desc) | — | 日期类 case 共用子类型 D;数值类 case 共用 N |
入口点签名:
public function split(string $pdfData, array $ranges, int $maxBytes = 100_000_000, int $maxRanges = 1000): SplitResult
public function mergeDocuments( array $pdfs, int $maxInputs = 100, int $maxBytesEach = 100_000_000,): MergeResultpublic function __construct( private readonly string $viewMode = 'tile',)
public function generateCollectionDictionary(): string行为契约
标题为“行为契约”的章节拆分与合并共用同一条对象图流水线:
- 输入必须以
%PDF头部开头。大小与数量守护在解析之前运行,触及即抛出OverflowException。 - 叶子页面通过扫描页面对象标记来检测;页面树节点不计入页数。
- 解析器以流感知的终止符扫描对每个未压缩的间接对象建立索引。对象 id 的首次出现胜出,因此不会应用增量更新的覆盖。
- 可继承的页面树属性(
/Resources、/MediaBox、/CropBox、/Rotate)会沿其/Parent链行走并实体化到每个被提取的页面上,从而使分段自成一体。 - 每个页面的传递性间接引用闭包会被收集(排除
/Parent反向边),并重新编号到一个全新的连续 id 空间中。 - 序列化器会依次发出头部、Catalog、Pages 树、页面对象与闭包对象,接着是带有字节精确偏移的交叉引用表,以及一个指向
xref关键字的startxref。 mergeDocuments会对每个输入在一个共享 id 空间中重复该流水线。最高的输入 PDF 版本决定输出头部。它是被禁用的 Core 合并器的合规替代,而后者保持 fail-closed。- 输出是确定性的。不会发出任何时间戳或随机标识符,因此相同的输入会产生相同的字节。
Portfolio 装配:
- 构造函数会校验视图模式。发出的
/View令牌对 tile、detail、hidden 分别为/T、/D、/H。 generateCollectionDictionary()会发出/Type /Collection、/View令牌、在存在字段时发出/Schema块,以及一个按第一个 schema 字段升序的/Sort指令。- 每个 schema 字段会发出
/Subtype(来自pdfSubtype())、/N(转义后的显示名)、/O(顺序)与/V(可见性)。 - 字段名会被净化为合法的 PDF 名称令牌;非单词字符会变为下划线。字符串值会被转义为 PDF 字面字符串。
- 文件条目通过
getEntries()暴露,供写入层进行嵌入。Collection 字典本身仅携带视图、schema 与 sort。
边界情形与失败模式
标题为“边界情形与失败模式”的章节- 匹配不到任何页面的范围会产生一个最小化的单页分段(612 x 792 MediaBox),而不是一个错误。
- 没有可检测页面标记的文档会被计为一页。
- 存储在对象流内部的页面不会被检测;只有未压缩的间接对象参与提取。
- 当存在重复对象 id 时,会使用偏移最低的修订版;较晚的增量更新修订版会被忽略。
- 每个分段的引用闭包上限为 50,000 个对象;恶意的自引用或扇出图会抛出
OverflowException。 - 默认上限:输入 100 MB、1,000 个范围、100 个合并输入。三者均可按调用逐次调整。
splitEvery()会以InvalidArgumentException拒绝小于 1 的分段大小。SplitResult::segment()会以OutOfRangeException拒绝越界索引。- 两个仅在标点上不同的 schema 字段名会净化为相同的字典键;在发出的 schema 中,较晚的字段会静默覆盖较早的字段。
- 本模块不执行任何密码学操作;FIPS 模式不会改变其行为。
一致性
标题为“一致性”的章节分段与合并输出遵循 ISO 32000-2 的页面对象模型;源码标注了相关条款。可外部核验的主张:
- 尾部布局、
startxref字节偏移及%%EOF终止符遵循 ISO 32000-2:2020, §7.5.5 —— 参见ef0f2a4b563b84f81b3e6428612bc47c510d94fc8096849d339abf0f3247d845。 - Collection 字典的
/View值(/T、/D、/H)遵循 ISO 32000-2:2020, §12.3.5 —— 参见5cefaaeb40f3ff98e3aba135ac57c9424a05c43144c1b9b5156bfd4295e08ddd。 - Collection 字段的
/Subtype、/N、/O与/V条目遵循 ISO 32000-2:2020, §12.3.5(collection field dictionary)—— 参见6300fbfdc8a913a8dc6f6ae34eff99f2bd03c4313a77777cdd5a8dd856d9537a。
这些陈述描述的是经由本模块测试验证的已实现能力。对某个构造的支持并非一致性主张,而一致性也不等于认证;NextPDF 未就本模块持有任何第三方认证。
开发说明
标题为“开发说明”的章节- 本模块的全部类均为
final;结果类型与值对象类型为readonly。拆分器与 Portfolio 类型可追溯至 1.9.0;mergeDocuments()于 3.1.0 加入。 PageRange与MergeResult是 Core 类型,因此调用点保持跨版本可移植。- 分段尾部仅携带
/Size与/Root;不会发出任何/ID文件标识符或/Info字典。 - 对于增量更新或签名工作流,请将分段字节交给 Writer 模块处理,而不要就地对其进行后期编辑。
- 本模块不记录任何文档内容。
发布边界
标题为“发布边界”的章节本页仅记录可外部观察的行为与受支持的公共 API 接口面。内部命名空间路径、辅助类、机制表、运行手册文件名与工单前缀均不在范围之内。