Pro 版本
AST — 深入參考
本頁是 Pro AST 模組的深度參考,涵蓋公開的 build、cache、mutation、write 與 emit 介面、它們的行為合約,以及失敗模式。此模組會將載入的 PDF 剖析為不可變的 AstDocument 樹,套用有記錄的記憶體內變動,並寫入以 overlay 為基礎的增量更新。AstDocument 與 AstNode 是位於 NextPDF\Ast 命名空間的 Core 值型別;此模組會產生並消費它們。
供應與授權
標題為「供應與授權」的區段此能力隨 NextPDF Pro(nextpdf/pro)出貨,並透過 Pro 層級的授權封套啟用。未持有該授權的部署不會載入此能力的類別。比較版本並取得授權。
沒有逐功能的授權旗標。這是 Pro 版本的能力。建構行為完全由 AstBuildOptions 治理。
公開 API 介面
標題為「公開 API 介面」的區段| 符號 | 參數 | 預設行為 | 回傳 | 拋出或失敗於 | 備註 |
|---|---|---|---|---|---|
AstBuilder::__construct | PdfReader $reader, AstBuildOptions $options, ?AstCache $cache = null | 將載入的 reader 綁定至建構選項;快取為可選 | AstBuilder | — | null 快取代表每次 build() 呼叫都會重建。 |
AstBuilder::build | string $sourceHash(PDF 位元組的完整 SHA-256 十六進位值) | 快取查詢、加密拒絕、結構樹路徑、untagged 回退、邊界框附加、快取存放 | AstDocument | AstUnsupportedEncryptionException, AstBuildLimitException, AstBuildTimeoutException | 快取命中會直接回傳,不重新剖析。 |
AstBuildOptions::__construct | ?int $pageRangeStart = null, ?int $pageRangeEnd = null, int $maxNodes = 100_000, int $maxDepth = 200, ?int $estimatedTokenBudget = null, int $maxMemoryBytes = 268435456, float $timeoutSeconds = 30.0, bool $useHeuristic = false | 不可變的組態值物件 | AstBuildOptions | — | estimatedTokenBudget 是資訊性提示;並不會被強制執行。 |
AstBuildOptions::pageRangeContains | int $pageIndex | 當以 0 為基底的索引落在所設定範圍內時為 True | bool | — | null 邊界為開放式;兩者皆為 null 代表所有頁面。 |
AstBuildOptions::hash | — | 對所有選項值計算的穩定 SHA-256 | string | — | 相同的值在不同實例間會產生相同的雜湊;用作快取鍵的片段。 |
AstCache::__construct | CacheInterface $backend | 包裝任意 PSR-16 後端 | AstCache | — | — |
AstCache::buildKey | string $sourceHash, AstBuildOptions $options | 鍵 = nextpdf_ast_v1_ + 來源雜湊前 32 個十六進位字元 + _ + 選項雜湊前 16 個十六進位字元 | string | — | 選項變更會自動使已快取的結果失效。 |
AstCache::get | string $cacheKey | 透過嚴格的逐欄位驗證解碼 JSON 載荷 | ?AstDocument | 絕不拋出;失敗時回傳 null | 格式錯誤或遭竄改的載荷會以快取未命中的方式安全失敗(fail closed)。 |
AstCache::set | string $cacheKey, AstDocument $document | 以 24 小時 TTL 儲存 JSON,隨後立即回讀驗證 | void | AstWriteVerificationException(Exception 命名空間) | 後端寫入失敗或往返(round-trip)失敗會拋出。 |
AstCache::delete | string $cacheKey | 盡力而為的移除 | void | 絕不拋出 | 後端刪除失敗會被吞掉。 |
AstCache::has | string $cacheKey | 盡力而為的存在性檢查 | bool | 絕不拋出;失敗時回傳 false | — |
AstMutator::updateNode | AstDocument $document, string $nodeId, array $updates | 取代 text_content,記錄一筆 Updated 項目 | AstDocument(新實例) | InvalidArgumentException | 只會套用 text_content 鍵;未知的鍵會被忽略。 |
AstMutator::deleteNode | AstDocument $document, string $nodeId | 從記憶體內的樹移除該節點,記錄一筆 Deleted 項目 | AstDocument(新實例) | InvalidArgumentException | 僅為記憶體內移除;請參閱下方的塗銷(redaction)注意事項。 |
AstMutator::getMutationLog | — | 回傳共用的日誌實例 | MutationLog | — | 將同一個日誌傳給 AstWriter。 |
AstMutator::resetLog | — | 捨棄所有已記錄的變動 | void | — | 開啟一份全新的日誌。 |
MutationLog | record, all, isEmpty, count, forNode, mutatedNodeIds | 僅可附加的記憶體內日誌,保留插入順序 | 視方法而定 | — | forNode 會回傳某節點最近一筆項目;以最後一筆為準。 |
MutationEntry::__construct | string $nodeId, MutationType $type, ?AstNode $originalNode, ?AstNode $mutatedNode, DateTimeImmutable $timestamp | 單一變動的不可變紀錄 | MutationEntry | — | originalNode 對 Inserted 為 null;mutatedNode 對 Deleted 為 null。 |
MutationType | 列舉案例 Updated, Inserted, Deleted | 以字串為底的分類 | — | — | OVERLAY 下的 Deleted 會隱藏內容;並不會抹除位元組。 |
AstWriter::write | string $originalPdfBytes, MutationLog $log | 附加一筆增量更新,其 overlay 串流涵蓋被變動的邊界框 | string(修改後的 PDF 位元組) | AstWriteException | 空的日誌會原封不動回傳輸入。Inserted 項目與沒有邊界框的項目會被略過。 |
AstWriter::writeAndVerify | string $originalPdfBytes, MutationLog $log | 執行 write(),接著進行結構性的輸出檢查 | string(已驗證的 PDF 位元組) | AstWriteException, AstWriteVerificationException(Writer 命名空間) | 驗證是結構性的,而非語意性的。 |
AstPdfEmitter::emit | AstNode $root, BinaryBuffer $buffer, ObjectRegistry $registry, array $pageObjects | 為所提供的樹寫入 StructTreeRoot、StructElem 鏈以及 ParentTree | EmitResult | AstEmitException | root 必須是具有子節點的 Document 節點。用於結構樹驗證的往返 emitter。 |
EmitResult::__construct | int $structTreeRootObject, int $rootElementObject, int $parentTreeObject, int $elementObjectCount, int $parentTreeNextKey | 已發出物件識別碼的不可變紀錄 | EmitResult | — | — |
public function build(string $sourceHash): AstDocumentpublic function updateNode(AstDocument $document, string $nodeId, array $updates): AstDocumentpublic function deleteNode(AstDocument $document, string $nodeId): AstDocumentpublic function write(string $originalPdfBytes, MutationLog $log): stringpublic function writeAndVerify(string $originalPdfBytes, MutationLog $log): string例外階層
標題為「例外階層」的區段NextPDF\Pro\Ast\Exception\AstException繼承RuntimeException——建構階層的基底。AstBuildLimitException繼承AstException——超出了節點、深度或記憶體上限。AstBuildTimeoutException繼承AstBuildLimitException——牆鐘建構逾時已到。AstNoStructTreeException繼承AstException——不存在結構樹。AstBuilder::build()會在內部攔截它並回退;build()的呼叫者不會觀察到它。AstUnsupportedEncryptionException繼承AstException——輸入的 PDF 已加密。NextPDF\Pro\Ast\Exception\AstWriteVerificationException繼承AstException——快取寫入驗證失敗。NextPDF\Pro\Ast\Writer\AstWriteException繼承RuntimeException——writer 輸入或結構失敗。NextPDF\Pro\Ast\Writer\AstWriteVerificationException繼承AstWriteException——寫入後的結構性驗證失敗。
有兩個不同的 AstWriteVerificationException 類別存在於不同的命名空間。AstCache::set() 會拋出 Exception 命名空間的類別;AstWriter::writeAndVerify() 會拋出 Writer 命名空間的類別。請在 catch 子句中對應正確的命名空間。
行為合約
標題為「行為合約」的區段AstBuilder::build($sourceHash) 需要來源位元組的完整 SHA-256 十六進位值。其管線為:可選的快取查詢、加密拒絕、結構樹路徑、untagged 回退、邊界框附加、可選的快取存放。
快取鍵會將來源雜湊與 AstBuildOptions 雜湊結合。選項雜湊在具相同值的不同實例間是穩定的,因此相同的輸入與選項會回傳同一棵樹。當未提供快取時,每次呼叫都會重建。快取載荷為 JSON,絕不使用原生 PHP 序列化:讀取路徑會驗證每個欄位並只實例化 AST 值型別,因此遭下毒的快取項目無法觸發物件注入,而會退化為快取未命中。
結構樹路徑會在存在結構樹時執行。資源上限——節點數、深度、記憶體增量以及牆鐘時間——會在讀取結構樹期間被強制執行,並引發 AstBuildLimitException 或 AstBuildTimeoutException。若讀取器回報沒有結構樹,建構器會切換到 untagged 路徑:當 useHeuristic 為 true 時使用啟發式建構器,否則使用裸回退建構器。邊界框是透過分析每個範圍內頁面的內容串流來附加;無法剖析其內容串流的頁面會被略過,並讓樹的其餘部分維持完整。
AstNode 是不可變的。樹更新會由下而上重建受影響的節點;未變更的子樹會以識別性(identity)回傳。AstMutator 遵循相同的合約:每次變動都會回傳一個新的 AstDocument,只重建從 root 到目標的路徑,並在共用的 MutationLog 中記錄一筆 MutationEntry。
AstWriter 會以 OVERLAY 模式將 MutationLog 套用為僅可附加的增量更新:新的 overlay 內容串流、更新後的頁面物件、只涵蓋新物件的交叉參照區段,以及其 /Prev 指向先前 startxref 的 trailer。原始位元組會維持完整,符合 ISO 32000-2:2020, 7.5.6 的增量更新模型。為 Updated 項目繪製的取代文字會在文字字串(literal string)中跳脫 \、( 與 ),符合 ISO 32000-2:2020, 7.3.4.2。
AstPdfEmitter::emit() 是讀取結構樹的對稱反向操作:讀取器所產生的樹能往返為結構等價的樹,差異僅在於 node-id 重新編號與已記載的正規化(canonicalisation)類別。節點上既有的 MCID 會逐字重新發出,絕不重新配置。
邊界案例與失敗模式
標題為「邊界案例與失敗模式」的區段- 加密的輸入會在任何樹工作之前被拒絕;加密的 PDF 不會有部分樹結果。請先解密。
- 資源上限:最大節點數(預設 100,000)、最大深度(預設 200)、最大記憶體(預設 256 MiB)、牆鐘逾時(預設 30 s)。超出某個上限會引發
AstBuildLimitException;逾時會引發其子類別AstBuildTimeoutException。 - 頁面範圍以 0 為基底且為包含式;null 邊界代表所有頁面。
- 無法剖析其內容串流的頁面會在邊界框附加期間被略過;樹的其餘部分不受影響。
AstCache::get()絕不拋出:格式錯誤、遭竄改或非字串的載荷會回傳 null 並強制重建。當後端寫入或立即回讀失敗時,AstCache::set()會明確地失敗(fail loudly)。- 當找不到 node id 時,
AstMutator會引發InvalidArgumentException。未知的更新鍵會被靜默忽略;只會套用text_content。 - 當輸入缺少
%PDF-標頭或可定位的startxref時,AstWriter::write()會引發AstWriteException。沒有邊界框的項目會被靜默略過。無法透過物件掃描定位的頁面——例如在壓縮交叉參照串流下——會被略過;若無法套用任何 overlay,則會原封不動回傳輸入位元組。 - OVERLAY 輸出並非塗銷(redaction)。白色矩形與重繪的文字是附加上去的;原始內容位元組仍留存於檔案中,且可透過原始擷取還原。請勿將其用於 GDPR Art. 17 抹除或法律塗銷。來源樹中存在一個 reconstruct 模式的 writer,但被標記為內部使用、尚未達生產就緒,且不在受支援的 API 介面範圍內。
- overlay 幾何假設為 A4 直向(595 x 842 pt),因為 writer 不會讀取頁面的 MediaBox。在非 A4 頁面上 overlay 可能會略微錯位;輸出仍然在結構上有效。
writeAndVerify()只檢查結構:標頭、結尾的%%EOF以及輸出增長。它不會在語意上重新剖析被變動的文件。- 當 root 不是 Document 節點或沒有子節點時,
AstPdfEmitter::emit()會引發AstEmitException。本次發行不會發出 OBJR(annotation)伴隨項目。 - 此模組不執行任何密碼學作業,也不定義任何 FIPS 專屬行為。SHA-256 僅作為快取鍵的內容定址而出現。
一致性
標題為「一致性」的區段結構樹路徑會讀取 ISO 32000-2 所定義的 tagged-PDF 邏輯結構機制;撰寫時可用的 RAG 語料庫並未包含邏輯結構條款,因此該陳述是以來源標註為依據的產品層級說明。writer 的增量更新版面遵循 ISO 32000-2:2020, 7.5.6(見下方引用),其文字字串跳脫遵循 ISO 32000-2:2020, 7.3.4.2(見下方引用)。
這些陳述描述的是相對於所引用條款的能力。NextPDF 未持有任何一致性認證,而支援某條款並不構成認證主張。
開發須知
標題為「開發須知」的區段- 每個載入的
PdfReader組合一個AstBuilder。跨多次建構重用同一個AstCache以攤提剖析成本;鍵的設計使選項變更能自我失效。 - 在
AstMutator與AstWriter之間共用一個MutationLog,如此 writer 便能精確套用所記錄的工作階段。在各自獨立的編輯工作階段之間呼叫resetLog()。 - 當版面推導出的分組優於裸回退樹時,為 untagged 文件將
useHeuristic設為 true。 - 對於相同的位元組與選項,建構具決定性;可在快照式測試中依賴這一點。
- 透過
NextPDF\Pro\Ast\Exception階層攔截建構失敗,並透過NextPDF\Pro\Ast\Writer階層攔截寫入失敗;兩者在RuntimeException之下並不共用基底。
發佈邊界
標題為「發佈邊界」的區段本頁僅記載外部可觀察的行為以及受支援的公開 API 介面。內部命名空間路徑、輔助類別、機制表、runbook 檔名以及工單前綴皆不在範圍內。