Pro 版本
Document — 深入參考
Document 模組提供三個 Pro 組裝原語:頁面範圍切割、多文件合併,以及 PDF Portfolio(Collection)dictionary 建構。PdfSplitter 會把頁面範圍抽取為獨立、結構合規的 PDF,並把整份文件合併成單一重新編號的檔案。PdfPortfolio 建構 Collection dictionary,以可排序的 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 dictionary 字串 | string | — | Schema 與排序區塊僅在欄位存在時出現 |
PortfolioEntry | $filename, $data, $description = '', $mimeType = 'application/octet-stream', $customFields = [] | 不可變的檔案項目值物件 | — | — | size() 回傳資料的位元組長度 |
PortfolioField | $name, PortfolioFieldType $type, $displayName = '', $order = 0, $visible = true | 不可變的 schema 欄位值物件 | — | — | effectiveDisplayName() 會回退為 $name |
PortfolioFieldType | 字串 enum:Text, Date, Number, FileName, Description, Size, ModDate, CreationDate | 透過 pdfSubtype() 將每個 case 對應到 PDF /Subtype | string(S, D, N, F, Desc) | — | 日期類 case 共用 subtype 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。 - 葉節點頁面透過掃描頁面物件標記偵測;page-tree 節點會被排除於計數之外。
- 剖析器以具備 stream 感知的終止符掃描為每個未壓縮的間接物件建立索引。物件 id 的首次出現者勝出,因此漸進更新的覆寫不會被套用。
- 可繼承的 page-tree 屬性(
/Resources、/MediaBox、/CropBox、/Rotate)會沿著/Parent鏈行走而具現化到每個被抽取的頁面上,因此各分段是自足的。 - 每個頁面的遞移間接參照閉包會被收集(排除
/Parent反向邊),並重新編號進一個全新的連續 id 空間。 - 序列化器發出標頭、Catalog、Pages tree、頁面物件與閉包物件,接著發出具備位元組精確位移的交叉參照表,以及指向
xref關鍵字的startxref。 mergeDocuments針對每個輸入重跑管線並匯入同一個共用 id 空間。最高的輸入 PDF 版本決定輸出標頭。它是已停用之 Core 合併器的合規替代品,後者維持 fail-closed。- 輸出具決定性。不發出任何時間戳記或隨機識別碼,因此相同輸入產生相同位元組。
Portfolio 組裝:
- 建構子驗證檢視模式。發出的
/Viewtoken 對 tile、detail、hidden 分別為/T、/D、/H。 generateCollectionDictionary()發出/Type /Collection、/Viewtoken、欄位存在時的/Schema區塊,以及對第一個 schema 欄位遞增的/Sort指令。- 每個 schema 欄位發出
/Subtype(來自pdfSubtype())、/N(跳脫後的顯示名稱)、/O(順序)與/V(可見性)。 - 欄位名稱會被淨化為有效的 PDF name token;非文字字元會變成底線。字串值會被跳脫為 PDF 字面字串。
- 檔案項目透過
getEntries()對外揭露,供寫入層做內嵌。Collection dictionary 本身僅攜帶檢視、schema 與排序。
邊界案例與失敗模式
標題為「邊界案例與失敗模式」的區段- 未對應到任何頁面的範圍會產出一個最小化的單頁分段(612 x 792 MediaBox),而非錯誤。
- 沒有可偵測頁面標記的文件會被計為一頁。
- 儲存在物件串流內的頁面不會被偵測;只有未壓縮的間接物件參與抽取。
- 當存在重複的物件 id 時,會採用位移最低的版本;較後的漸進更新版本會被忽略。
- 每個分段的參照閉包上限為 50,000 個物件;惡意自我參照或扇出的圖會拋出
OverflowException。 - 預設上限:輸入 100 MB、1,000 個範圍、100 個合併輸入。三者皆可由呼叫端逐次調整。
splitEvery()會以InvalidArgumentException拒絕小於 1 的分段大小。SplitResult::segment()會以OutOfRangeException拒絕越界的索引。- 兩個僅在標點上不同的 schema 欄位名稱會淨化為相同的 dictionary 鍵;在發出的 schema 中,較後的欄位會靜默地遮蔽較早的欄位。
- 此模組不執行任何密碼學運算;FIPS 模式不會改變其行為。
一致性
標題為「一致性」的區段分段與合併的輸出遵循 ISO 32000-2 的頁面物件模型;原始碼已標註相關條款。可外部檢核的主張:
- Trailer 配置、
startxref位元組位移與%%EOF終止符遵循 ISO 32000-2:2020, §7.5.5 — 參考ef0f2a4b563b84f81b3e6428612bc47c510d94fc8096849d339abf0f3247d845。 - Collection dictionary 的
/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。splitter 與 Portfolio 型別可追溯至 1.9.0;mergeDocuments()於 3.1.0 加入。 PageRange與MergeResult是 Core 型別,因此呼叫端維持版本可攜。- 分段的 trailer 僅攜帶
/Size與/Root;不發出/ID檔案識別碼或/Infodictionary。 - 對於漸進更新或簽章工作流程,請將分段位元組交給 Writer 模組,而非就地後製編輯。
- 此模組不記錄任何文件內容。
出版邊界
標題為「出版邊界」的區段本頁僅記載可外部觀察的行為與受支援的公開 API 介面。內部命名空間路徑、輔助類別、機制表、runbook 檔名與工單前綴皆不在範圍內。