1. 程式人生 > >PDF檔案結構(一)

PDF檔案結構(一)

PDF檔案結構(一)

————物理結構

                             作者:bobob

PDF(Portable   Document   Format,行動式文件結構)是一種很有用的檔案格式,其最大的特點是平臺無關而且功能強大(支援文字/圖象/表單/連結/音樂/視訊等).PDF的解析,首先要熟悉PDF檔案的物理結構和邏輯結構。PDF檔案物理結構可分為以下幾塊:  
1.
檔案頭
檔案頭是PDF檔案的第一行,格式如下:      
 

%PDF-1.4
 

這是個固定格式,表示這個PDF檔案遵循的PDF規範版本,目前PDF的生成工具,除了官方的acrobat,其他生成的以1.4

版本的居多。對於做PDF開發來說,一個最簡單的原則就是生成PDF的時候儘量符合低版本規範,以保證大多數解析器能支援;解析PDF的時候儘量支援高版本的規範,以保證支援大多數工具生成的PDF檔案。

1.4版本以後,PDF檔案的版本並不唯一的只是在這裡表示了,可能後面會改寫(catalogVersion詞條),所以解析PDF的時候,如果這裡的版本大於等於1.4,應該再比較一下catalog裡面的version,取其中高一點的版本。


2.
物件集合這是一個PDF檔案最重要的部分,檔案中用到的所有物件,包括文字/圖象/音樂/視訊/字型/超連線/加密資訊/文件結構資訊等等,都在這裡定義。格式如下:  

2 0 obj  
  ...  
  end obj  

一個物件的定義包含4個部分:

前面的2是物件序號,其用來唯一標記一個物件;0是生成號,按照PDF規範,如果一個PDF檔案被修改,那這個數字是累加的,它和物件序號一起標記是原始物件還是修改後的物件,但是實際開發中,很少有用這種方式修改PDF的,都是重新編排物件號;objendobj是物件的定義範圍,可以抽象的理解為這就是一個左括號和右括號;省略號部分是PDF規定的任意合法物件(一共8種,見後面附A)

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


   
3.
交叉引用表交叉引用表是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的更新資訊,和物件的生成號作用類似),最後一位fn表示物件是否被使用(n表示使用,f表示被刪除或沒有用)。上面這個交叉引用表一共有3個子段,分別有1個,1個,3個物件,第一個子段的物件不可用,其餘子段物件可用。

   
4.trailer:  
通過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檔案,都會有上面這樣的結構(線性化優化的PDF例外,這個後面單獨說)。實際一個pdf檔案是很複雜的,但是上面幾個部分是確定的,只能多不能少.瞭解了PDF檔案的物理結構,就可以提取出一個一個的物件了.PDF中的物件有8種:

1.booleam

用關鍵字truefalse表示,可以是array物件的一個元素,dictionary物件的一個條目.也可以用在PostScript計算函式裡面,做為ififesle的一個條件。

2.numeric

包括整形和實型,不支援非十進位制數字,不支援指數形式的數字.

:

1)整數 1234567+111-2

範圍:231次方-1到負的231次方

2)實數 12.30.8+6.3-4.01-3.+.03

範圍:±3.403×1038次方±1.175×10-38次方

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

3.string

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

1)直接字串

()包含起來的一個字串,中間可以使用轉義符"/".

:

(abc) 表示abc

(a//) 表示a/

轉義符的定義如下:

轉義字元

含義

/n

換行

/r

回車

/t

水平製表符

/b

退格

/f

換頁(Form feed (FF)

/(

左括號

/)

右括號

//

反斜槓

/ddd

八進位制形式的字元

2)十六進位制字串

<>包含起來的一個16進位制串,兩位表示一個字元,不足兩位用0補齊

:

<Aabb> 表示AABB兩個字元

<AAB> 表示AAB0兩個字元

4.name

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

:

/name 表示name

/name#20is 表示name is

/name#200 表示name 0

5.array

[]包含的一組物件,可以是任何pdf物件(包括array).雖然pdf只支援一維array,但可以通過array的巢狀實現任意維數的array(但是一個array的元素不能超過8191)

:

[5493.14false(Ralph)/SomeName]

6.Dictionary

"<<"">>"包含的若干組條目,每組條目都由keyvalue組成,其中key必須是name物件,並且一個dictionary內的key是唯一的;value可以是任何pdf的合法物件(包括dictionary物件).

:

<</IntegerItem12

/StringItem(astring)

/Subdictionary

<</Item10.4

/Item2true

/LastItem(not!)

/VeryLastItem(OK)

>>

>>

7.stream

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

:

dictionary

stream

data

endstream

stream字典中常用的欄位如下:

欄位名

型別

Length

整形

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

Filter

名字陣列

(可選)Stream的編碼演算法名稱(列表)。如果有多個,則陣列中的編碼演算法列表順序就是資料被編碼的順序。

DecodeParms

字典陣列

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

F

檔案標識

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

FFilter

名字字典

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

FDecodeParms

字典陣列

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

8.NULL

null表示,代表空.如果一個key的值為null,則這個key可以被忽略;如果引用一個不存在的object則等價於引用一個空物件.

:()

以上八種物件是按照物件內涵來分的,如果按照物件的使用規則來說,物件又分為間接物件和直接物件。間接物件是PDF中最常用的物件,如前面物件集合裡面的,所有物件都是間接物件,在其他位置通過R關鍵字來引用,在交叉引用表裡面都是通過間接物件來引用的。直接物件就更好理解了,上面的8種物件單獨出現的時候就叫直接物件。