天天看點

一文搞懂PDF格式

PDF格式學習

PDF是Portable Document Format 的縮寫,可翻譯為“便攜檔案格式”,由Adobe System Incorporated 公司在1992年發明。

PDF檔案是一種程式設計形式的文檔格式,它所有顯示的内容,都是通過相應的操作符進行繪制的。

PDF基本顯示單元包括:文字,圖檔,矢量圖,圖檔

PDF擴充單元包括:水印,電子署名,注釋,表單,多媒體,3D

PDF動作單元:書簽,超連結(擁有動作的單元有很多個,包括電子署名,多媒體等等)

一緻性:在所有可以打開PDF的機器上,展示的效果是完全一緻,不會出現段落錯亂、文字亂碼這些排版問題。尤其是文檔中,本身可以嵌入字型,避免了用戶端沒有對應字型,而導緻文字顯示不一緻的問題。是以,在印刷行業,絕大多數用的都是PDF格式。

不易修改:用過PDF檔案的人,都會知道,對已經儲存之後的PDF檔案,想要進行重新排版,基本上就不可能的,這就保證了從資料源發往外界的資料,不容易被篡改。

安全性:PDF文檔可以進行加密,包括以下幾種加密形式:文檔打開密碼,文檔權限密碼,文檔證書密碼,加密的方法包括:RC4,AES,通過加密這種形式,可以達到資料防擴散等目的。

不失真:PDF檔案中,使用了矢量圖,在檔案浏覽時,無論放大多少倍,都不會導緻使用矢量圖繪制的文字,圖案的失真。

支援多種壓縮方式:為了減少PDF檔案的size,PDF格式支援各種壓縮方式:asciihex,ascii85,lzw,runlength,ccitt,jbig2,jpeg(DCT),jpeg2000(jpx)

支援多種印刷标準:支援PDF-A,PDF-X

根據PDF官方指南,了解PDF格式可以從四個方面下手——Objects(對象)、File structure(實體檔案結構)、Document structure(邏輯檔案結構)、Content streams(内容流)。

整體上分為檔案頭(Header)、對象集合(Body)、交叉引用表(Xref table)、檔案尾(Trailer)四個部分,結構如圖。修改過的PDF結構會有部分變化。

未經修改 

一文搞懂PDF格式

經修改 

一文搞懂PDF格式

檔案頭是PDF檔案的第一行,格式如下:

%PDF-1.7

這是個固定格式,表示這個PDF檔案遵循的PDF規範版本,解析PDF的時候盡量支援高版本的規範,以保證支援大多數工具生成的PDF檔案。1.7版本支援1.0-1.7之間的所有版本。

這是一個PDF檔案最重要的部分,檔案中用到的所有對象,包括文本、圖象、音樂、視訊、字型、超連接配接、加密資訊、文檔結構資訊等等,都在這裡定義。格式如下:

2 0 obj

...

end obj

一個對象的定義包含4個部分:前面的2是對象序号,其用來唯一标記一個對象;0是生成号,按照PDF規範,如果一個PDF檔案被修改,那這個數字是累加的,它和對象序号一起标記是原始對象還是修改後的對象,但是實際開發中,很少有用這種方式修改PDF的,都是重新編排對象号;obj和endobj是對象的定義範圍,可以抽象的了解為這就是一個左括号和右括号;省略号部分是PDF規定的任意合法對象。

可以通過R關鍵字來引用任何一個對象,比如要引用上面的對象,可以使用2 0 R,需要主意的是,R關鍵字不僅可以引用一個已經定義的對象,還可以引用一個并不存在的對象,而且效果就和引用了一個空對象一樣。

對象主要有下面幾種

booleam 用關鍵字true或false表示,可以是array對象的一個元素,或dictionary對象的一個條目。也可以用在PostScript計算函數裡面,做為if或if esle的一個條件。

numeric 包括整形和實型,不支援非十進制數字,不支援指數形式的數字。例: 1)整數 123 4567 +111 -2 範圍:正2的31次方-1到負的2的31次方 2)實數 12.3 0.8 +6.3 -4.01 -3. +.03 範圍:±3.403 ×10的38次方 ±1.175 × 10的-38次方

注意:如果整數超過表示範圍将轉化成實數,如果實數超過範圍就會出錯

string 由一系列0-255之間的位元組組成,一個string總長度不能超過65535.string有以下兩種方式:

十六進制字串 由<>包含起來的一個16進制串,兩位表示一個字元,不足兩位用0補齊。例: \

 表示AA和BB兩個字元 \ 表示AA和B0兩個字元

直接字串 由()包含起來的一個字串,中間可以使用轉義符"/"。例:(abc) 表示abc

(a//) 表示a/

轉義符的定義如下:

轉義字元

含義

/n

換行

/r

回車

/t

水準制表符

/b

倒退

/f

換頁(Form feed (FF))

/(

左括号

/)

右括号

//

反斜杠

/ddd

八進制形式的字元

對象類别(續)

name 由一個前導/和後面一系列字元組成,最大長度為127。和string不同的是,name是不可分割的并且是唯一的,不可分割就是說一個name對象就是一個原子,比如/name,不能說n就是這個name的一個元素;唯一就是指兩個相同的name一定代表同一個對象。從pdf1.2開始,除了ascii的0,别的都可以用一個#加兩個十六進制的數字表示。例:

/name 表示name

/name#20is 表示name is

/name#200 表示name 0

array 用[]包含的一組對象,可以是任何pdf對象(包括array)。雖然pdf隻支援一維array,但可以通過array的嵌套實作任意維數的array(但是一個array的元素不能超過8191)。例:[549 3.14 false (Ralph) /SomeName]

Dictionary 用"<<"和">>"包含的若幹組條目,每組條目都由key和value組成,其中key必須是name對象,并且一個dictionary内的key是唯一的;value可以是任何pdf的合法對象(包括dictionary對象)。例: << /IntegerItem 12 /StringItem (a string) /Subdictionary << /Item1 0.4 /Item2 true /LastItem (not!) /VeryLastItem (OK) >> >>

stream 由一個字典和緊跟其後面的一組關鍵字stream和endstream以及這組關鍵字中間包含一系列位元組組成。内容和string很相似,但有差別:stream可以分幾次讀取,分開使用不同的部分,string必須作為一個整體一次全部讀取使用;string有長度限制,但stream卻沒有這個限制。一般較大的資料都用stream表示。需要注意的是,stream必須是間接對象,并且stream的字典必須是直接對象。從1.2規範以後,stream可以以外部檔案形式存在,這種情況下,解析PDF的時候stream和endstream之間的内容就被忽略掉。例: dictionary stream…data…endstreamstream字典中常用的字段如下:

字段名

類型

Length

整形

(必須)關鍵字stream和endstream之間的資料長度,endstream之前可能會有一個多餘的EOL标記,這個不計算在資料的長度中。

Filter

名字 或 數組

(可選)Stream的編碼算法名稱(清單)。如果有多個,則數組中的編碼算法清單順序就是資料被編碼的順序。

DecodeParms

字典 或 數組

(可選)一個參數字典或由參數字典組成的一個數組,供Filter使用。如果僅有一個Filter并且這個Filter需要參數,除非這個Filter的所有參數都已經給了預設值,否則的話 DecodeParms必須設定給Filter。如果有多個Filter,并且任意一個Filter使用了非預設的參數, DecodeParms 必須是個數組,每個元素對應一個Filter的參數清單(如果某個Filter無需參數或所有參數都有了預設值,就用空對象代替)。如果沒有Filter需要參數,或者所有Filter的參數都有預設值,DecodeParms 就被忽略了。

F

檔案辨別

(可選)儲存stream資料的檔案。如果有這個字段, stream和endstream就被忽略,FFilter将會代替Filter, FDecodeParms将代替DecodeParms。Length字段還是表示stream和endstream之間資料的長度,但是通常此刻已經沒有資料了,長度是0.

FFilter

名字 或 字典

(可選)和filter類似,針對外部檔案。

FDecodeParms

(可選)和DecodeParams類似,針對外部檔案。

Stream的編碼算法名稱(清單)。如果有多個,則數組中的編碼算法清單順序就是資料被編碼的順序。且需要被編碼。編碼算法主要如下:

一文搞懂PDF格式

編碼可視化主要顯示為亂碼,是以提供了隐藏資訊的機會,如下圖的steam内容為亂碼。

NULL 用null表示,代表空。如果一個key的值為null,則這個key可以被忽略;如果引用一個不存在的object則等價于引用一個空對象。

交叉引用表是PDf檔案内部一種特殊的檔案組織方式,可以很友善的根據對象号随機通路一個對象。其格式如下:

xref

0 1

0000000000 65535 f

4 1

0000000009 00000 n

8 3

0000000074 00000 n

0000000120 00000 n

0000000179 00000 n

其中,xref是開始标志,表示以下為一個交叉引用表的内容;每個交叉引用表又可以分為若幹個子段,每個子段的第一行是兩個數字,第一個是對象起始号,後面是連續的對象個數,接着每行是這個子段的每個對象的具體資訊——每行的前10個數字代表這個這個對象相對檔案頭的偏移位址,後面的5位數字是生成号(用于标記PDF的更新資訊,和對象的生成号作用類似),最後一位f或n表示對象是否被使用(n表示使用,f表示被删除或沒有用)。上面這個交叉引用表一共有3個子段,分别有1個,1個,3個對象,第一個子段的對象不可用,其餘子段對象可用。

通過trailer可以快速的找到交叉引用表的位置,進而可以精确定位每一個對象;還可以通過它本身的字典還可以擷取檔案的一些全局資訊(作者,關鍵字,标題等),加密資訊,等等。具體形式如下:

trailer

<<

key1 value1

key2 value2

key3 value3

>>

startxref

553

%%EOF

trailer後面緊跟一個字典,包含若幹鍵-值對。具體含義如下:

值類型

值說明

Size

整形數字

所有間接對象的個數。一個PDF檔案,如果被更新過,則會有多個對象集合、交叉引用表、trailer,最後一個trailer的這個字段記錄了之前所有對象的個數。這個值必須是直接對象。

Prev

當檔案有多個對象集合、交叉引用表和trailer時,才會有這個鍵,它表示前一個相對于檔案頭的偏移位置。這個值必須是直接對象。

Root

字典

Catalog字典(檔案的邏輯入口點)的對象号。必須是間接對象。

Encrypt

文檔被保護時,會有這個字段,加密字典的對象号。

Info

存放文檔資訊的字典,必須是間接對象。

ID

數組

檔案的ID

上面代碼中的startxref:後面的數字表示最後一個交叉引用表相對于檔案起始位置的偏移量

%%EOF:檔案結束符

一文搞懂PDF格式

catalog是整個PDF邏輯結構的根節點,這個可以通過trailer的Root字段定位,雖然簡單,但是相當重要,因為這裡是PDF檔案實體結構和邏輯結構的連接配接點。Catalog字典包含的資訊非常多,這裡僅就最主要的幾個字段做個說明。

字段

Type

name

(必須)隻能為Pages 。

Parent

dictionary

(如果不是catalog裡面指定的跟節點,則必須有,并且必須是間接對象) 目前節點的直接父節點。

Kids

array

(必須)一個間接對象組成的數組,節點可能是page或page tree。

Count

integer

(必須) page tree裡面所包含葉子節點(page 對象)的個數。

從以上字段可以看出,Pages最主要的功能就是組織所有的page對象。Page對象描述了一個PDF頁面的屬性、資源等資訊。Page對象是一個字典,它主要包含一下幾個重要的屬性:

Pages字段 這是個必須字段,是PDF裡面所有頁面的描述集合。Pages字段本身是個字典,它裡面又包含了一下幾個主要字段:

(必須)必須是Page。

(必須;并且隻能是間接對象)目前page節點的直接父節點page tree 。

LastModified

date

(如果存在PieceInfo字段,就必須有,否則可選)記錄目前頁面被最後一次修改的日期和時間。

Resources

(必須; 可繼承)記錄了目前page用到的所有資源。如果目前頁不用任何資源,則這是個空字典。忽略所有字段則表示繼承父節點的資源。

MediaBox

rectangle

(必須; 可繼承)定義了要顯示或列印頁面的實體媒介的區域(default user space units)

CropBox

(可選; 可繼承)定義了一個可視區域,目前頁被顯示或列印的時候,它的内容會被這個區域裁剪。預設值就是 MediaBox。

BleedBox

(可選) 定義了一個區域,當輸出裝置是個生産環境( production environment)的時候,頁面顯示的内容會被裁剪。預設值是 CropBox.

Contents

stream or array

(可選) 描述頁面内容的流。如果這個字段預設,則頁面上什麼也不會顯示。這個值可以是一個流,也可以是由幾個流組成的一個數組。如果是數組,實際效果相當于所有的流是按順序連在一起的一個流,這就允許PDF生成的時候可以随時插入圖檔或其他資源。流之間的分割隻是詞彙上的一個分割,并不是邏輯上或者組織形式的切割。

Rotate

(可選; 可繼承) 順時鐘旋轉的角度數,這個必須是90的整數倍,預設是0。

Thumb

stream

(可選)定義目前頁的縮略圖。

Annots

(可選) 和目前頁面關聯的注釋。

Metadata

(可選) 目前頁包含的中繼資料。

一個簡單例子:

3 0 obj

<< /Type /Page

/Parent 4 0 R

/MediaBox [ 0 0 612 792 ]

/Resources <</Font<<

/F3 7 0 R /F5 9 0 R /F7 11 0 R

/ProcSet [ /PDF ]

/Contents 12 0 R

/Thumb 14 0 R

/Annots [ 23 0 R 24 0 R]

endobj

Outlines字段 Outline是PDF裡面為了友善使用者從PDF的一部分跳轉到另外一部分而設計的,有時候也叫書簽(Bookmark),它是一個樹狀結構,可以直覺的把PDF檔案結構展現給使用者。使用者可以通過滑鼠點選來打開或者關閉某個outline項來實作互動,當打開一個outline時,使用者可以看到它的所有子節點,關閉一個outline的時候,這個outline的所有子節點會自動隐藏。并且,在點選的時候,閱讀器會自動跳轉到outline對應的頁面位置。Outlines包含以下幾個字段:

(可選)如果這個字段有值,則必須是Outlines。

First

(必須;必須是間接對象) 第一個頂層Outline item。

Last

(必須;必須是間接對象)最後一個頂層outline item。

(必須)outline的所有層次的item的總數。

Outline是一個管理outline item的頂層對象,我們看到的,其實是outline item,這個裡面才包含了文字、行為、目标區域等等。一個outline item主要有一下幾個字段:

Title

text string

(必須)目前item要顯示的标題。

(必須;必須是間接對象) outline層級中,目前item的父對象。如果item本身是頂級item,則父對象就是它本身。

(除了每層的第一個item外,其他item必須有這個字段;必須是間接對象)目前層級中,此item的前一個item。

Next

(除了每層的最後一個item外,其他item必須有這個字段;必須是間接對象)目前層級中,此item的後一個item。

(如果目前item有任何子節點,則這個字段是必須的;必須是間接對象) 目前item的第一個直接子節點。

(如果目前item有任何子節點,則這個字段是必須的;必須是間接對象) 目前item的最後一個直接子節點。

Dest

name,byte string, or array

(可選; 如果A字段存在,則這個不能被會略)目前的outline item被激活的時候,要顯示的區域。

A

(可選; 如果Dest 字段存在,則這個不能被忽略)目前的outline item被激活的時候,要執行的動作。

URI字段 URI(uniform resource identifier),定義了文檔級别的統一資源辨別符和相關連結資訊。目錄和文檔中的連結就是通過這個字段來處理的.

Metadata字段 文檔的一些附帶資訊,用xml表示,符合adobe的xmp規範。這個可以友善程式不用解析整個檔案就能獲得檔案的大緻資訊。

其他 Catalog字典中,常用的字段一般有以下一些:

(必須)必須為Catalog。

Version

(可選)PDF檔案所遵循的版本号(如果比檔案頭指定的版本号高的話)。如果這個字段預設或者檔案頭指定的版本比這裡的高,那就以檔案頭為準。一個PDF生成程式可以通過更新這個字段的值來修改PDF檔案版本号。

Pages

(必須并且必須為間接對象)目前文檔的頁面集合入口。

PageLabels

number tree

(可選) number tree,定義了頁面和頁面label對應關系。

Names

(可選)文檔的name字典。

Dests

(可選;必須是間接對象)name和相應目标對應關系字典。

ViewerPreferences

(可選)閱讀參數配置字典,定義了文檔被打開時候的行為。如果預設,則使用閱讀器自己的配置。

PageLayout

(可選) 指定文檔被打開的時候頁面的布局方式。SinglePageDisplay 單頁OneColumnDisplay 單列TwoColumnLeftDisplay 雙列,奇數頁在左TwoColumnRightDisplay 雙列,奇數頁在右TwoPageLeft 雙頁,奇數頁在左TwoPageRight 雙頁,奇數頁在右預設值: SinglePage.

PageMode

(可選) 當文檔被打開時,指定文檔怎麼顯示UseNone 目錄和縮略圖都不顯示UseOutlines 顯示目錄UseThumbs 顯示縮略圖FullScreen 全屏模式,沒有菜單,任何其他視窗UseOC 顯示Optional content group 面闆UseAttachments顯示附件面闆預設值: UseNone.

Outlines

(可選;必須為間接對象)文檔的目錄字典

Threads

(可選;必須為間接對象)文章線索字典組成的數組。

OpenAction

array or dictionary

(可選) 指定一個區域或一個action,在文檔打開的時候顯示(區域)或者執行(action)。如果預設,則會用預設縮放率顯示第一頁的頂部。

AA

(可選)一個附加的動作字典,在全局範圍内定義了響應各種事件的action。

URI

(可選)一個URI字典包含了文檔級别的URI action資訊。

AcroForm

(可選)文檔的互動式form (AcroForm)字典。

(可選;必須是間接對象)文檔包含的中繼資料流。

pdf