跳转到内容
getnextpdf.com

Pro 版本

Writer — 深度参考

Writer 模块写入 PDF 增量更新修订,并将小对象打包进 Object Stream。增量写入器强制执行一条 fail-closed 的仅追加(append-only)规则:缓冲区在某次修订之前所持有的每一个字节,在该修订之后都必须保持不变。Object Stream 构建器将符合条件的对象在有界大小之下归组为一个 FlateDecode 压缩的 /Type /ObjStm 对象。

此能力随 NextPDF Pronextpdf/pro)交付,并通过 Pro 层级的许可封套激活。缺少该授权的部署不会加载此能力的类。对比版本并获取授权。不存在按单项功能划分的许可标志;代码随 Pro 版本一同交付。

本模块位于 NextPDF\Pro\Writer 命名空间下。全部公开符号列于下方。值对象为不可变的 final readonly 类。

符号参数默认行为返回抛出或失败于备注
IncrementalUpdateWriter::writeRevisionBinaryBuffer $buffer, ObjectRegistry $registry, int $prevXrefOffset, int $catalogObject, array $catalogEntries, array $catalogUpdates, array $newObjectNumbers, string $fileId静态。用合并后的条目重写 catalog,为新对象与被修改对象追加一个传统交叉引用表,并写入一个带 /Size/Root/Prev/ID 的 trailer。写入后校验修订前的前缀逐字节相等。int — 新交叉引用表的字节偏移量当仅追加前缀检查失败时抛出 \NextPDF\Exception\WriterExceptiongetWriterState() 返回 dss-append-only-invariant静态入口点。违规时无可用输出。
ObjectStreamWriter::addObjectint $objectNumber, string $content经大小检查后向待处理流追加一个对象。void当合并后的索引加正文将超过 65,536 字节时抛出 OverflowException$content 不含 N 0 obj / endobj 包裹。
ObjectStreamWriter::canAcceptstring $content估算索引开销并将累计总量与最大值比对。bool不抛出纯谓词;不改变状态。
ObjectStreamWriter::build构建索引、拼接正文、用 FlateDecode 压缩,并包装 /Type /ObjStm 字典。string — 原始 Object Stream 内容当未添加任何对象时,或在 zlib 压缩失败时抛出 ObjectStreamWriteException调用方分配对象号并包装标记。
ObjectStreamWriter::getEntries为累积的对象重新计算相对于正文的偏移量。list<ObjectStreamEntry>不抛出偏移量相对于正文段。
ObjectStreamWriter::count报告累积对象的数量。int不抛出
ObjStmCompressor::__constructint $maxStreamSize = 65536, int $maxObjectsPerStream = 200存储用于归组的大小与对象数上限。不抛出默认值与模块的 Object Stream 调优一致。
ObjStmCompressor::groupObjectslist<array{number: int, generation?: int, content: string}> $objects过滤不符合条件的对象,然后将其余对象在大小与数量上限内打包进多个写入器。list<ObjectStreamWriter>不抛出;不符合条件的对象被跳过非零 generation 的对象会回退到常规序列化。
ObjStmCompressor::isEligiblestring $content, int $generation = 0拒绝流对象、/Encrypt/XRef/Catalog 以及任何非零 generation。bool不抛出/Type 匹配容忍空白与 #xx 转义。
ObjStmCompressor::writeToBufferlist<ObjectStreamWriter> $streams, BinaryBuffer $buffer, ObjectRegistry $registry为每个流分配一个载体对象,登记 type-2 压缩条目,并写入每个 ObjStm 块。list<int> — 载体对象号在罕见的压缩失败时从 build() 传播 ObjectStreamWriteException在不符合条件的对象写入之后、交叉引用写出之前运行。
ObjStmCompressor::estimateSavingslist<ObjectStreamWriter> $streams, int $originalSize构建每个流以将压缩后大小与原始大小比对。ObjStmCompressionResult在罕见的压缩失败时从 build() 传播 ObjectStreamWriteException只读的度量辅助方法。
ObjectStreamEntry::__constructint $objectNumber, string $content, int $offset一个被打包对象及其正文偏移量的不可变记录。不抛出final readonly;公开属性。
ObjStmCompressionResult::__constructint $originalObjectCount, int $streamCount, int $estimatedOriginalSize, int $estimatedCompressedSize不可变的度量容器。不抛出final readonly;公开属性。
ObjStmCompressionResult::savedBytes返回原始大小减去压缩后大小。int不抛出当打包反而使数据膨胀时可能为负。
ObjStmCompressionResult::savedPercent返回缩减的百分比。float不抛出当原始大小为零时返回 0.0
ObjStmCompressionResult::compressionRatio返回压缩后大小除以原始大小。float不抛出当原始大小为零时返回 1.0
ObjectStreamWriteException表示一次 Object Stream 构建失败。继承 RuntimeExceptionbuild() 抛出;为向后兼容可通过 RuntimeException 捕获。
final class IncrementalUpdateWriter
{
public static function writeRevision(
BinaryBuffer $buffer,
ObjectRegistry $registry,
int $prevXrefOffset,
int $catalogObject,
array $catalogEntries,
array $catalogUpdates,
array $newObjectNumbers,
string $fileId,
): int;
}
final class ObjectStreamWriter
{
public function addObject(int $objectNumber, string $content): void;
public function canAccept(string $content): bool;
public function build(): string;
/** @return list<ObjectStreamEntry> */
public function getEntries(): array;
public function count(): int;
}
final class ObjStmCompressor
{
public function __construct(
int $maxStreamSize = 65536,
int $maxObjectsPerStream = 200,
);
/**
* @param list<array{number: int, generation?: int, content: string}> $objects
* @return list<ObjectStreamWriter>
*/
public function groupObjects(array $objects): array;
public function isEligible(string $content, int $generation = 0): bool;
/**
* @param list<ObjectStreamWriter> $streams
* @return list<int>
*/
public function writeToBuffer(array $streams, BinaryBuffer $buffer, ObjectRegistry $registry): array;
/** @param list<ObjectStreamWriter> $streams */
public function estimateSavings(array $streams, int $originalSize): ObjStmCompressionResult;
}

writeRevision 写入一个增量更新修订。它在写入前对既有缓冲区前缀做快照。它用合并后的条目重写 catalog,登记新对象偏移量,写入一个分组为连续子段的传统交叉引用表,并写入一个带 /Size/Root/Prev/ID 的 trailer。写入后,它再次比较该前缀。如果任何更早的字节发生了变化,它会引发一个携带仅追加违规状态的 WriterException,且不返回可用的输出。成功时,它返回新交叉引用表的字节偏移量,以便串接后续修订。允许跨修订混用交叉引用表与流。

ObjectStreamWriter 累积对象。当合并后的索引与正文将超过最大值(未压缩 65,536 字节)时,addObject 引发一个溢出错误。build 在流为空时引发一个错误;否则它压缩索引加正文,并返回带有 /Type /ObjStm/N/First/Length/Filter /FlateDecode 条目的 Object Stream 内容。调用方分配对象号并包装 N 0 obj / endobj 标记。

ObjStmCompressor 决定要打包哪些对象。它排除流对象、加密字典、交叉引用流、文档 catalog,以及任何带非零 generation 号的对象。writeToBuffer 为每个流分配一个载体对象,将每个被打包对象登记为一个 type-2 压缩交叉引用条目,并在当前缓冲区偏移处写入 ObjStm 块。estimateSavings 构建每个流以计算大小度量,而不改动缓冲区。

  • 仅追加检查会复制既有前缀。其代价随已写入文档的大小而增长。此代价是有意为之的,用以保护已签名的字节。
  • Object Stream 限制适用于未压缩的索引加正文。请将加密字典与其他被排除的对象类型作为直接的间接对象放置。
  • /Type 排除容忍任意的记号间空白与 #xx 十六进制转义。诸如 /Type /Encrypt/Type\n/Encrypt/Type /#45ncrypt 这样的形式都会被拒绝,而不仅仅是规范的字面拼写。
  • 任何带非零 generation 号的对象都被视为不符合条件,并回退到常规的 N G obj … endobj 序列化,因为一个被压缩对象的 generation 隐含为零。
  • writeToBuffer 必须在所有不符合条件的对象都已写入之后、交叉引用写出之前运行。被打包的对象不得再被单独序列化。

Writer 模块不执行任何密码学操作。它通过在某个更早的字节将发生变化时拒绝写出来保护已签名的字节,这是一个字节相等测试而非密码学测试。用于签名与散列的 FIPS 算法选择由签名模块管控,而非由此 writer 管控。启用或禁用 FIPS mode 不改变任何 Writer 方法的行为。

NextPDF 依据 ISO 32000-2:2020 实现本模块。增量写入器遵循 §7.5.6 的增量更新文法:每个修订追加一个仅覆盖新增、变更或删除对象的交叉引用段,以及一个 /Prev 条目给出前一交叉引用偏移量的 trailer。Object Stream 构建器遵循 §7.5.7 的对象流模型:一个由对象号与偏移量对构成、偏移量以 /First 条目为基准按递增顺序度量的索引,位于被打包的对象正文之前。两处条款引用均已针对 ISO 32000-2:2020 语料核对。面向 PAdES B-LT 与 B-LTA 工作流的修订串接遵循 ETSI EN 319 142-1 §5.4,如源代码中所注。对某一条款的支持是一项工程能力声明,而非认证;NextPDF 不持有任何正式的一致性认证。

  • 通过 composer require nextpdf/pro:^3 安装本包。这些类在 NextPDF\Pro\Writer 下解析。
  • IncrementalUpdateWriter::writeRevision 是一个静态入口点;它在修订之间不保留任何实例状态。
  • ObjectStreamEntryObjStmCompressionResultIncrementalUpdateWriter 以及压缩器共同构成本模块的公开范围;仓库未随附任何可运行的示例。
  • 来自 writeRevisionWriterException 表示一次仅追加违规。将其视为硬失败并丢弃缓冲区。
  • Object Stream 载体是间接对象;调用方通过 registry 分配其对象号。

本页仅记录外部可观察的行为与受支持的公开 API 范围。内部命名空间路径、辅助类、机制表、runbook 文件名与工单前缀均不在范围内。