PDF檔案內部結構:完整解析
可移植文件格式(PDF)已成為文件交換和歸檔的實際標準。瞭解其內部結構對於開發人員、系統管理員以及所有參與文件處理工作流程的人員至關重要。本綜合指南深入探討PDF檔案的複雜佈局和內容,考察其四個主要部分以及構成每個元件的物件的詳細語法。
PDF檔案佈局:四個關鍵元件
每個有效的PDF檔案都遵循嚴格的架構模式,由四個主要部分組成,並按特定的順序排列。這些元件協同工作,建立了一種既結構化又非常適合隨機訪問的格式。
- 標題 (Header) – 標識PDF版本號和二進位制特性
- 正文 (Body) – 包含所有文件物件,包括頁面、字型、影像和圖形內容
- 交叉引用表 – 提供精確的位元組偏移量對映,用於隨機物件訪問
- 預告片。 包含重要的後設資料和導航指標。
剖析完整的PDF檔案: “Hello, World” 示例。
為了理解這些元件如何協同工作,讓我們來分析一個完整的、最小的PDF檔案,該檔案顯示“Hello, World!”文本。這個示例展示了PDF結構中的所有關鍵元素:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
%PDF-1.0 % Header starts here %âãÏÓ 1 0 obj % Body starts here << /Kids [2 0 R] /Count 1 /Type /Pages >> endobj 2 0 obj << /Rotate 0 /Parent 1 0 R /Resources 3 0 R /MediaBox [0 0 612 792] /Contents [4 0 R] /Type /Page >> endobj 3 0 obj << /Font << /F0 << /BaseFont /Times-Italic /Subtype /Type1 /Type /Font >> >> >> endobj 4 0 obj << /Length 65 >> stream 1. 0. 0. 1. 50. 700. cm BT /F0 36. Tf (Hello, World!) Tj ET endstream endobj 5 0 obj << /Pages 1 0 R /Type /Catalog >> endobj xref % Cross-reference table starts here 0 6 0000000000 65535 f 0000000015 00000 n 0000000074 00000 n 0000000192 00000 n 0000000291 00000 n 0000000409 00000 n trailer % Trailer starts here << /Root 5 0 R /Size 6 >> startxref 459 %%EOF |
💡 理解PDF物件圖。
PDF物件形成一個有向圖結構,其中節點是PDF物件,連結是間接引用。這種圖表示允許高效地隨機訪問內容,而無需進行順序的檔案處理。文件目錄(物件5)作為根節點,連線到頁面樹(物件1),後者引用單個頁面及其資源。
頭部:版本控制和二進位制標識。
PDF頭部具有兩個關鍵功能,可確保在不同的系統和應用程式中正確處理檔案:
|
1 2 |
%PDF-1.0 %âãÏÓ |
第一行指定PDF版本(在此示例中為1.0)。PDF具有出色的向後相容性,這意味著較新的閱讀器可以無縫處理較舊的版本。它在一定程度上也具有向前相容性,因為大多數PDF應用程式會嘗試讀取檔案,而不管其宣告的版本號。
第二行包含二進位制字元,其ASCII碼值高於127。這一點至關重要,因為PDF檔案幾乎總是包含二進位制資料,如果在檔案傳輸過程中修改了行尾,這些資料可能會損壞。這些高ASCII字元有助於舊的檔案傳輸程式將檔案識別為二進位制檔案,從而防止自動進行行尾轉換,這會損壞文件。
百分號 (%) 在PDF語法中表示註釋行,而特定的字元 âãÏÓ 是任意的位元組,其值超過ASCII 127,用作傳輸協議的二進位制標記。
Body:所有內容都位於此處。
檔案主體構成主要內容儲存庫,由一系列物件組成。每個物件都遵循嚴格的語法結構:
|
1 2 3 |
[object_number] [generation_number] obj [object_content] endobj |
每個物件都以物件編號、生成編號以及 obj 關鍵字開始,位於同一行,然後是物件內容,最後以 endobj 關鍵字結束。生成編號允許在更新交叉引用條目時重用物件,對於大多數情況,該值保持為零。
例如,檢視我們示例中的物件1:
|
1 2 3 4 5 6 7 |
1 0 obj << /Kids [2 0 R] /Count 1 /Type /Pages >> endobj |
此物件(編號 1,第 0 代)包含一個字典,定義了頁面樹。 /Type /Pages 該條目標識其為一個頁面樹節點, /Count 1 指示它包含一個頁面,並且 /Kids [2 0 R] 引用物件 2 作為其子頁面。
交叉引用表:導航骨幹
交叉引用表代表 PDF 中用於效能最佳化的最巧妙的功能。它提供從物件編號到檔案內位元組位置的直接對映,從而可以在不進行順序掃描的情況下實現隨機訪問:
|
1 2 3 4 5 6 7 8 |
xref 0 6 % Six entries starting at object 0 0000000000 65535 f % Special entry for free objects 0000000015 00000 n % Object 1 at byte offset 15 0000000074 00000 n % Object 2 at byte offset 74 0000000192 00000 n % Object 3 at byte offset 192 0000000291 00000 n % Object 4 at byte offset 291 0000000409 00000 n % Object 5 at byte offset 409 |
每個交叉引用條目由正好 20 個位元組組成:一個 10 位數字的位元組偏移量(帶前導零)、一個 5 位數字的生成編號,以及一個字元(對於正常物件,使用 "n";對於空物件,使用 "f"),後面是必有的空格。這種固定長度的格式可以實現對交叉引用表本身的隨機訪問。
第一個條目(物件 0)始終是一個特殊條目,指向空物件列表的頭部,其生成編號為 65535。這種機制允許 PDF 在增量更新期間重用物件編號,從而在刪除物件時可以重用。
預告片:重要的後設資料和檔案導航。
預告片部分提供了至關重要的資訊,用於 PDF 處理程序導航文件結構。
|
1 2 3 4 5 6 7 8 |
trailer << /Root 5 0 R % Document catalog reference /Size 6 % Number of xref entries >> startxref 459 % Byte offset of xref table %%EOF % End-of-file marker |
預告片以 trailer 關鍵字開頭,後跟包含重要導航資訊的預告片字典。 /Size 條目指定交叉引用表中條目的總數,而 /Root 指向文件目錄,它是物件圖的根元素。
好的。 startxref 關鍵字前面是一個數字,表示交叉引用表開始的位元組偏移量。最後, %%EOF 標記 PDF 檔案的結束。PDF 閱讀器通過找到此檔案結束標記開始處理,然後反向查詢預告片和交叉引用表,然後按需載入物件。
詞彙約定:PDF語法的基石。
PDF檔案是遵循特定詞彙規則的8位位元組序列,用於解析成token。理解這些約定對於PDF處理至關重要:
字元分類
PDF 識別三種類型的字元:
- 普通字元 – 所有非空格和分隔符的字元。
- 空格字元 – 用於token分隔。
- 分隔符 – 特殊字元:
( ) < > [ ] { } / %
PDF 中的空白字元包括:
| Character Code | Meaning |
|---|---|
| 0 | Null |
| 9 | Tab |
| 10 | Line feed |
| 12 | Form feed |
| 13 | Carriage return |
| 32 | Space |
PDF 檔案可以使用 <CR>、<LF> 或 <CR><LF> 序列來結束行。但是,批次更改行尾符很可能會損壞檔案,因為它會影響壓縮二進位制資料部分中的行尾符序列。
PDF 物件型別:完整的分類
PDF 支援八種基本物件型別,這些型別是所有文件內容的構建塊。這些型別分為基本物件、複合物件和連結機制:
基本物件
整數和實數
數字是 PDF 數字系統的基礎。
|
1 2 3 4 5 |
% Integer examples 0 +1 -1 63 % Real number examples 0.0 0. .0 -0.004 65.4 |
整數由十進位制數字(0-9)組成,可以選擇在前面加上正號或負號。 浮點數遵循類似的規則,但可以包含一個小數點,該小數點可以出現在數字的開頭、中間或結尾。 值得注意的是,指數表示法(如 4.5e-6)在 PDF 中是不允許的。
數字的範圍和精度取決於 PDF 的實現,而不是規範。 一些實現會將整數轉換為浮點數,當整數超出可用範圍時。
字串:兩種表示方法
PDF 提供了兩種不同的字串格式,適用於不同的用例。
文本字串
文本字串出現在括號內,並支援轉義序列:
|
1 2 3 4 5 6 7 8 |
% Simple string (Hello, World!) % String with escaped characters (Some \\ escaped \(characters\)) % String with balanced parentheses (no escaping needed) (Red (Rouge)) |
文本字串中的轉義序列包括:
| Sequence | Meaning |
|---|---|
\n |
Line feed |
\r |
Carriage return |
\t |
Horizontal tab |
\b |
Backspace |
\f |
Form feed |
\ddd |
Character code in three octal digits |
十六進位制字串
十六進位制字串提供了一種替代表示方法,尤其適用於二進位制資料:
|
1 2 |
<4F6Eff00> % Bytes 0x4F, 0x6E, 0xFF, 0x00 <48656C6C6F> % "Hello" in ASCII hex |
每兩個十六進位制數字代表一個位元組。當出現奇數個數字時,假設最後一個數字後面跟著一個 0。這種格式使二進位制資料易於人類閱讀,同時保持與字面字串的功能等效性。
名稱:PDF 的識別符號系統
名稱在 PDF 中用作識別符號,充當字典鍵和符號常量:
|
1 2 3 4 |
/French % Simple name / % Valid name (just the slash) /Websafe#20Dark#20Green % Name with encoded spaces (#20 = space) /A#42 % Name with encoded character (#42 = 'B') |
名稱以正斜槓開頭,不允許直接包含空格或分隔符字元。特殊字元使用雜湊編碼,編碼方式為兩個十六進位制數字。名稱區分大小寫,因此 /French 和 /french 代表不同的識別符號。
布林值和空值
PDF 支援標準的布林值和空物件。
|
1 2 3 |
true % Boolean true false % Boolean false null % Null object |
這些用作字典條目的標誌和物件結構中的佔位符值。
複合物件
陣列:有序集合
陣列包含任何 PDF 物件的有序序列,包括其他陣列。
|
1 2 3 |
[0 0 400 500] % Four integers (typical rectangle) [/Green /Blue [/Red /Yellow]] % Mixed types with nested array [1 0 R 2 0 R 3 0 R] % Array of indirect references |
陣列不需要型別一致性,元素可以是數字、字串、名稱、其他陣列或任何 PDF 物件型別。
字典:鍵值對映
字典表示無序的鍵值對集合,其中鍵始終是名稱。
|
1 2 3 4 5 6 7 8 |
<</One 1 /Two 2 /Three 3>> % Simple mappings << % Multi-line dictionary /Type /Page /Parent 1 0 R /Resources 3 0 R /MediaBox [0 0 612 792] /Contents [4 0 R] >> |
字典是 PDF 結構化資料的核心,包含從頁面定義到字型規範的所有內容。 它們可以任意巢狀,形成複雜的層次結構。
流:二進位制資料容器
流將字典與二進位制資料結合,對於影像、字型和壓縮內容至關重要。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
4 0 obj << /Length 65 % Stream length in bytes /Filter /FlateDecode % Optional compression filter >> stream 1. 0. 0. 1. 50. 700. cm BT % Binary or text data /F0 36. Tf (Hello, World!) Tj ET endstream endobj |
流由一個字典(至少包含一個條目),一個關鍵詞,一個換行符,資料位元組,另一個換行符,以及另一個關鍵片語成。 所有流都必須是間接物件,並且通常使用壓縮來提高效率。 /Length entry stream 關鍵詞 endstream 關鍵詞
間接引用:物件連結
間接引用在物件之間建立連結,從而形成圖結構,這使得 PDF 具有高效性。
|
1 2 |
6 0 R % Reference to object 6, generation 0 <</Resources 10 0 R /Contents [4 0 R]>> % Dictionary using references |
該格式由物件編號、生成編號以及關鍵字組成。 R 這種機制允許物件相互引用,而無需嵌入完整的定義,從而實現共享和隨機訪問。
流和過濾器:高階資料處理。
流是 PDF 中儲存二進位制資料的關鍵機制。 大部分 PDF 內容,包括頁面圖形和嵌入的字型,都儲存在流中,通常進行壓縮以提高空間效率。
綜合過濾器型別。
PDF 支援多種壓縮和編碼過濾器,每種過濾器都針對特定資料型別進行了最佳化。
| Filter Name | Description and Use Cases |
|---|---|
/ASCIIHexDecode |
Converts hexadecimal digit pairs to bytes. ‘>’ indicates end of data. Primarily for 7-bit data transmission compatibility. |
/ASCII85Decode |
More efficient 7-bit encoding using printable characters ‘!’ through ‘u’ and ‘z’. Sequence ‘~>’ marks end of data. |
/LZWDecode |
Lempel-Ziv-Welch compression, identical to TIFF implementation. Good general-purpose compression. |
/FlateDecode |
Deflate compression (RFC 1950), used by zlib. Most common PDF compression method. Supports predictors for enhanced compression. |
/RunLengthDecode |
Simple run-length encoding for data with repeated byte sequences. |
/CCITTFaxDecode |
Group 3/4 fax compression. Excellent for monochrome (1-bit) images, poor for general data. |
/JBIG2Decode |
Advanced compression for monochrome, grayscale, and color images. Superior to CCITT methods. |
/DCTDecode |
JPEG lossy compression. Complete JPEG files with headers can be embedded directly. |
/JPXDecode |
JPEG2000 compression supporting both lossy and lossless modes. Limited to JPX baseline feature set. |
多重過濾器鏈。
過濾器可以連結使用,以滿足複雜的處理需求。
|
1 2 |
/Filter [/ASCII85Decode /DCTDecode] % JPEG data then ASCII85 encoded /Filter [/ASCIIHexDecode /FlateDecode] % Deflate compression then hex encoding |
在解碼過程中,過濾器按相反的順序應用,陣列中的最後一個過濾器在讀取資料時首先應用。
高階 PDF 架構
增量更新:非破壞性修改
增量更新允許通過追加更改而不是重寫整個檔案來修改 PDF。這個關鍵特性提供了以下幾個好處:
- 效能 – 僅寫入新的/已更改的物件。
- 數字簽名 – 原始的已簽名內容保持不變。
- 版本歷史 – 可以恢復之前的文件狀態。
- 大型檔案效率。 – 針對大型文件,採用儘可能少的寫入操作。
在增量更新過程中,新的物件和新的交叉引用部分會追加到檔案末尾。新的尾部資訊包含一個。 /Prev 一個條目,指向前一個交叉引用表的位元組偏移量,從而建立一個文件版本的連結串列。
物件和交叉引用流 (PDF 1.5 及以上版本)
現代PDF版本引入了物件流和交叉引用流,以實現更好的壓縮比。
- 物件流 (Object Streams) – 多個物件壓縮在一起形成單個流。
- 交叉引用流 (Cross-Reference Streams) – 交叉引用資料以壓縮流的形式儲存。
- 分組策略 (Grouping Strategy) – 物件按使用模式進行分組(例如,將所有第 1 頁的物件放在一起)。
這種方法在顯著減小檔案大小的同時,保持了隨機訪問能力,尤其適用於包含許多小物件的文件。
線性化 PDF:Web 最佳化結構。
線性化 PDF(在 PDF 1.2 中引入)重新組織檔案結構,以實現最佳的網路瀏覽體驗。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
%PDF-1.4 %âãÏÓ 4 0 obj % Linearization dictionary << /E 200967 % End of first page /H [ 667 140 ] % Hint stream location and length /L 201431 % File length /Linearized 1 % Linearization flag /N 1 % Number of pages /O 7 % First page object number /T 201230 % Traditional xref table offset >> endobj |
線性化檔案可以實現:
- 快速顯示第一頁 – 檔案中首先出現第 1 頁的物件
- 漸進式載入 – 在下載過程中,內容會逐步顯示
- 高效導航。 – 提示表最佳化頁面訪問
- 向後相容 檔案仍然可以被非線性閱讀器讀取。
PDF 檔案處理:技術實現。
讀取演算法:從位元組到物件。
PDF 閱讀器採用複雜的解析策略。
- 頭部驗證。 驗證 PDF 簽名並提取版本資訊。
- 預告片位置。 從檔案末尾向後搜尋以查詢 %%EOF 標記。
- 交叉引用解析. – 從交叉引用表中構建物件位置對映.
- 預告詞典處理. – 提取文件目錄和後設資料.
- 物件載入策略. – 採用按需載入或預載入關鍵物件的方式.
- 內容樹構建. – 從物件圖中構建邏輯文件結構.
此過程處理包括加密、線性化、物件流和增量更新等複雜情況。
寫入演算法:從物件到位元組。
PDF 生成遵循一個更直接的過程:
- 頭部生成。 – 輸出 PDF 版本和二進位制標記。
- 物件圖分析。 – 移除未引用的物件以減小檔案大小。
- 物件重編號。 – 逐個分配從 1 到 n 的序列號。
- 物件序列化。 – 在記錄位元組偏移量的同時寫入物件。
- 交叉引用生成。 – 從記錄的偏移量建立交叉引用表。
- 尾部資訊建立。 – 生成尾部字典和檔案結束標記。
資料結構表示。
一個完整的 PDF 物件可以使用這種遞迴資料結構來表示。
|
1 2 3 4 5 6 7 8 9 10 |
pdfobject ::= Null | Boolean of bool | Integer of int | Real of real | String of string | Name of string | Array of pdfobject array | Dictionary of (string, pdfobject) array | Stream of (pdfobject, bytes) | Indirect of int |
例如,字典物件 << /Kids [2 0 R] /Count 1 /Type /Pages >> 將被表示為:
|
1 2 3 4 5 |
Dictionary [ ("Kids", Array [Indirect 2]); ("Count", Integer 1); ("Type", Name "Pages") ] |
實用工具和專業工作流程
幾個命令列工具可以幫助進行 PDF 分析和操作。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
% Linearize PDF for web optimization pdfopt input.pdf output.pdf % Decompress streams for manual inspection pdftk input.pdf output decompressed.pdf uncompress % Extract and analyze PDF structure pdf-parser --stats document.pdf % Repair corrupted PDF files pdftk broken.pdf output repaired.pdf % Extract specific pages pdftk document.pdf cat 1-3 output pages1-3.pdf % Get comprehensive PDF information pdfinfo -meta -struct document.pdf % Convert PDF to PostScript for analysis pdftops document.pdf document.ps |
安全性和完整性注意事項
理解 PDF 結構對於安全分析至關重要。
- 嵌入內容檢測 識別隱藏的流和物件。
- 惡意程式碼分析。 檢查 JavaScript 和表單操作。
- 後設資料提取。 恢復文件歷史和作者資訊。
- 數字簽名驗證。 驗證增量更新的完整性。
結論:掌握 PDF 架構。
理解 PDF 檔案的結構是進行高階文件處理、法醫分析和應用程式開發的基礎。這種格式優雅的設計——由四個主要部分協同工作——創造了一個既易於人類閱讀(在未壓縮狀態下)又非常適合複雜文件的系統。
從簡單的“Hello, World”示例,到包含數千頁且具有複雜互動功能的企業級文件,相同的基本原則適用。這種一致性使得 PDF 既具有可擴充套件性,又在各種用例中都具有可靠性。
從 PDF 1.0 發展到現在的版本,體現了對向後相容的謹慎考慮,同時引入了強大的功能,例如物件流、高階壓縮和網頁最佳化。理解這些架構決策有助於更有效地進行 PDF 處理和故障排除。
⚠️ 實施注意事項
雖然本指南涵蓋了 PDF 結構的基本概念,但完整的規範包含數百頁的內容,詳細描述了各種特殊情況、可選功能以及相容性要求。對於生產環境的應用,請使用成熟的 PDF 庫(例如)。 HotPDF 元件。或 Delphi PDF 庫) 而不是從頭開始實現解析器。這些庫可以處理許多複雜情況和可選功能,而這些內容在本入門指南中沒有涵蓋。