跳到內容
getnextpdf.com

Pro 版本

Writer — 深入參考

Writer 模組會寫入 PDF 增量更新(incremental-update)修訂版,並將小物件封裝進 Object Stream。增量 writer 會強制執行一條 fail-closed 的唯附加(append-only)規則:緩衝區在某次修訂版之前所持有的每一個位元組,在該修訂版之後都必須維持不變。Object Stream 建構器會把符合資格的物件分組到單一個 FlateDecode 壓縮的 /Type /ObjStm 物件中,並受限於一個有界的大小上限。

此能力隨 NextPDF Pronextpdf/pro)出貨,並以 Pro 層級授權信封啟用。缺少該授權資格的部署不會載入此能力的類別。比較版本並取得授權。沒有個別功能的授權旗標;程式碼隨 Pro 版本一併出貨。

本模組位於 NextPDF\Pro\Writer 命名空間之下。所有公開符號列於下方。Value object 為不可變的 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過濾掉不符資格的物件,再將其餘物件封裝進 writer,並受大小與數量上限約束。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 上限適用於未壓縮的索引加主體。請將加密字典與其他被排除的物件型別,以直接間接物件(direct indirect objects)放置。
  • /Type 排除容忍任意的 token 間空白與 #xx 十六進位轉義。諸如 /Type /Encrypt/Type\n/Encrypt/Type /#45ncrypt 等形式都會被拒絕,而不僅限於標準字面拼法。
  • 任何帶有 generation 編號非零的物件都會被視為不符資格,並落入一般的 N G obj … endobj 序列化,因為壓縮物件的 generation 隱含為零。
  • writeToBuffer 必須在所有不符資格的物件都寫入之後、且在發射交叉參照之前執行。已封裝的物件不得再另行單獨序列化。

Writer 模組不進行任何密碼學運算。它透過在某個較早位元組會改變時拒絕發射,來保護已簽署的位元組——這是一項位元組相等性測試,而非密碼學測試。用於簽署與雜湊的 FIPS 演算法選擇由簽署模組管轄,而非由本 writer 管轄。啟用或停用 FIPS 模式不會改變任何 Writer 方法的行為。

NextPDF 依 ISO 32000-2:2020 實作本模組。增量 writer 遵循 §7.5.6 增量更新文法:每個修訂版會附加一個僅涵蓋新增、變更或刪除物件的交叉參照區段,以及一個其 /Prev 項目給出前一個交叉參照偏移的 trailer。Object Stream 建構器遵循 §7.5.7 object-stream 模型:一個由物件編號與偏移對組成的索引(偏移自 /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 檔名與工單前綴皆不在範圍內。