PDF 檔案格式
Portable Document Format,Adobe 的固定版面文件標準。
- MIME 類型
- application/pdf
- 副檔名
- 分類
- 文件
Magic number(檔案簽章)
| 0x00000000 | 255044462D | %PDF- | '%PDF-' + version |
關於 PDF 格式
PDF 是一張由編號物件組成的圖——字典、陣列、串流、數字與字串——加上一份交叉參照表,把每個物件編號對應到它在檔案裡的位元組位移。這個設計解釋了為什麼閱讀器打開 500 頁的 PDF 是瞬間的事:它只讀最後幾百個位元組,順著 startxref 找到交叉參照表,然後直接跳到需要的物件。這也解釋了為什麼 PDF 對「往後追加」異常寬容,對「任何會位移位元組的編輯」異常脆弱,因為表裡每個位移都是絕對值。
檔頭結構,一個位元組一個位元組看
| 位移 | 長度 | 欄位 | 意義 |
|---|---|---|---|
| 0 | 5 | %PDF- header marker | 5 個 ASCII 位元組 25 50 44 46 2D。PDF 在最上層是偏文字的格式,所以這其實就是一行 PostScript 風格的註解;百分比符號是註解字元,整個第一行對完全不懂 PDF 的解析器來說也是合法的。 |
| 5 | 3 | Version number | ISO 32000-1 與更早的 Adobe 版本是 '1.0' 到 '1.7',ISO 32000-2 是 '2.0'。這裡的版本是下限而不是事實:文件 catalog 可以帶一個 /Version 鍵往上覆蓋,所以標成 1.4 的檔案合法地使用 1.7 的功能是可能的。 |
| 8 | 1 | Header end-of-line | 一個 LF(0A)、一個 CR(0D),或是 CR LF 這一對——是後者的話這段就是兩個位元組,底下所有東西跟著往後移一位。標頭這一行不准放其他東西。 |
| 9 | variable | Binary comment line | ISO 32000-1 第 7.5.2 條建議,含有二進位資料的檔案應該在標頭後面接一行註解,裡面至少四個位元組的碼值大於等於 128。它存在的唯一目的,是讓傳輸軟體把這個檔案歸類成二進位而不是文字。常見的產生器會寫 %âãÏÓ;沒有這一行仍然合法,但等於告訴你寫檔的人不太講究。 |
| -5 | 5 | %%EOF marker | 格式正確的檔案,最後一行是 5 個位元組 25 25 45 4F 46。後面可能還跟著換行,所以讀取端是從最後 1024 個位元組往回掃,而不是相信固定位置——另外,檔案裡出現好幾個 %%EOF 不是壞掉,而是它被增量更新過。 |
| variable | variable | startxref and the trailer | %%EOF 前面緊接著關鍵字 'startxref',下一行是一個十進位的位元組位移。讀取端會跳到那個位移去找交叉參照表或交叉參照串流,再由它找到 /Root、/Info 和 /Encrypt 這些項目。這裡沒有任何東西位在固定位置,這正是檔尾那個標記非得用掃描找出來的原因。 |
怎麼跟長得像的格式分開
| 容易搞混的 | 怎麼分 |
|---|---|
| PDF/A | PDF/A 是長期保存用的規範,PDF/X 是送印交換用的,兩者在位移 0 都不會留下任何痕跡——符合性等級是中繼資料,不是簽章。PDF/A 檔案是在 XMP 中繼資料串流裡透過 pdfaid 命名空間宣告自己的 part 與 conformance level,另外還必須內嵌所有字型並帶上 output intent。所以一個檔案可以在 XMP 裡自稱 PDF/A,卻通不過驗證;標頭無論如何都給不了你答案。 |
| Encrypted PDF | 開頭也是一模一樣。線索是檔尾 trailer 字典裡的 /Encrypt 鍵。要注意 PDF 加密在實務上通常代表什麼:只管權限的「擁有者密碼」,使用者密碼是空的,所以內容完全讀得到,禁止列印或複製全靠閱讀器自己守規矩。那和使用者密碼是兩回事,後者才真的會把串流加密。 |
| OLE2 / CFB | 有人說「文件寄給我」的時候,你會遇到的就是這兩種。2007 年以前的 Word 檔是 Compound File Binary 容器,開頭 D0 CF 11 E0;PDF 開頭是 %PDF-。真正影響後續的差別是 PDF 把字元位置釘死在頁面上,.doc 沒有,所以少了字型的機器打開 .doc 會重排,PDF 不會。 |
| PostScript | PDF 的直系祖先,到現在還會被搞混,因為兩者用同一套物件語法、同一個註解字元。PostScript 檔案開頭是 '%!PS-Adobe-';EPS 開頭一樣,前面還可能多一段二進位預覽標頭 C5 D0 D3 C6。PostScript 是一整套從上到下執行的程式語言,PDF 是可隨機存取的靜態物件圖——PDF 之所以取代它,原因就在這裡。 |
常見的壞法
- 會位移位元組的編輯。因為交叉參照表存的是絕對位元組位移,在檔案任何地方插入或刪掉一個位元組,後面每一筆都失效。閱讀器的應對方式是重建整張表——掃過整個檔案找 'N 0 obj' 這種樣式——所以手改過的 PDF 打開時會慢一下、跳個警告,而不是乾淨俐落地載入。
- 下載被截斷。靠前的物件還在,所以重建之後可能還算繪得出一兩頁,但檔尾的交叉參照表和 trailer 沒了,閱讀器根本沒有起點。檢查方式是看最後 1024 個位元組裡有沒有 %%EOF。
- 文字模式傳輸。FTP 開在 ASCII 模式會改寫 FlateDecode 壓縮串流裡的 0D 0A。標頭沒被動到、檔案照樣解析得動,所以打得開——然後個別頁面一片空白或丟出串流解碼錯誤,因為那些 deflate 資料再也 inflate 不回來了。
- 增量更新被接在錯的基底上。PDF 支援用追加的方式記錄變更而不必整份重寫,所以簽過名或加過註解的檔案,是原檔加上第二段指回前一段的交叉參照。如果接上去那一段的 /Prev 位移和前一個 %%EOF 的實際位置對不上,整條鏈就斷了;而檔案的標頭和最後的 trailer 看起來都還好好的。
- 產生器寫了交叉參照串流,卻宣稱版本是 1.4。壓縮的交叉參照串流與物件串流是 PDF 1.5 才有的。老實照著宣告版本走的讀取端根本不會去找它們,於是文件看起來一個物件都沒有,儘管每個位元組都在、都正確。
這個格式現在還在用嗎
只要文件的外觀不能跑掉,PDF 就是唯一被廣泛接受的格式,三十年下來沒有東西取代得了它這個角色。合約、發票、對帳單、論文、表單、送印稿,以及任何需要歸檔或簽署的東西,最後都變成 PDF,因為它會內嵌字型、把每個字元的位置釘死,不會為了配合閱讀器而重排。它不適合的場合是「之後還要再編輯」——PDF 存的是繪圖指令而不是段落,所謂「編輯 PDF」等於把一段段文字逆推回句子——以及在手機上閱讀,固定的 A4 頁面在小螢幕上正好是最糟的形狀,能重排的 EPUB 完勝。它的專門變體各自佔住了穩固的位置:PDF/A 用於長期典藏,要求所有字型都必須內嵌、不得依賴外部資源;PDF/X 用於送印交件。這個格式現在是 ISO 標準而不是 Adobe 的私產,瀏覽器也各自內建了算繪引擎,所以 PDF 是極少數在任何裝置上不用裝東西就打得開的檔案類型。
在這個站上打開 PDF 檔
這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。
相關格式
容易和 PDF 搞混的格式,可以比對它們的開頭位元組。
| 格式 | 開頭位元組 | 副檔名 |
|---|---|---|
| OLE2 / CFB | D0 CF 11 E0 A1 B1 | .doc |
| RTF | 7B 5C 72 74 66 31 | .rtf |
| Word 文件(docx) | 50 4B 03 04 | .docx |