天天看點

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

第一章 PDF文法

  • PDF Objects
    • Null Objects
    • Boolean Objects
    • Numeric Objects
    • Name Objects
    • String Objects
    • Array Objects
    • Dictionary Objects
      • Name trees
      • Number trees
    • Stream Objects
    • Direct versus Indirect Objects
  • File Structure
    • White-Space
    • The Four Sections of a PDF
      • Header
      • Trailer
      • Body
      • Cross-reference table
    • Incremental Update
    • Linearization
  • Document Structure
    • The Catalog Dictionary
    • The Page Tree
    • Pages
      • PDF units
      • Rects and boxes
      • Inheritance
    • The Name Dictionary
  • What’s Next

我們将通過深入了解PDF檔案格式的建構塊來開始我們對PDF的探索。使用這些塊,您将看到如何建構PDF以生成您熟悉的基于頁面的格式。

PDF Objects

PDF檔案的核心部分是PDF标準(ISO 32000)稱為對象(有時是COS對象)的“事物”集合。

COS代表Carousel Object System,是指Adobe的Acrobat産品的原始名稱/代号。

這些不是“面向對象程式設計”這個詞意義上的對象;相反,它們是PDF的基礎。有九種類型的對象:null, Boolean, integer, real, name, string, array, dictionary, and stream(空、布爾、整數、實數、名稱、字元串、數組、字典和流)。

讓我們看看這些對象類型中的每一種,以及它們是如何序列化為PDF檔案的。從那裡,您将看到如何擷取這些對象類型,并使用它們來建構更進階别的構造和PDF格式本身。

Null Objects

如果實際寫入到檔案中,空對象就是四個字元空。它是缺失值的同義詞,這就是為什麼在PDF中很少看到缺失值的原因。如果您有理由使用空值,請務必仔細咨詢ISO 32000,了解其處理的細節。

Boolean Objects

布爾對象表示邏輯值true和false,并在PDF中相應地表示為true或false。

在編寫PDF時,您将始終使用true或false。但是,如果您正在閱讀/分析的PDF希望對此寬容,請注意,不标準的PDF可能會使用其他形式,包括前導大寫(True或False)或所有大寫(TRUE或FALSE)。

Numeric Objects

PDF支援兩種類型的數字對象:integer(整數)和real(浮點型)——表示它們在數學上的等價物。雖然PDF的舊版本已經聲明了與Adobe的舊實作相比對的實作限制,但這些限制不應該再被視為檔案格式限制(任何供應商的任何特定實作也不應該被視為檔案格式限制)。

雖然PDF支援64位數字(以便啟用非常大的檔案),但您會發現大多數PDF實際上并不需要它們。然而,如果你正在閱讀PDF,你可能确實會遇到它們,是以要做好準備。

整數數字對象由一個或多個十進制數字組成,可選地前面加一個符号,表示有符号的值(以10為基數)。示例1-1顯示了幾個整數示例。

Example 1-1. Integers
1
-2
+100
612
           

實數對象由一個或多個帶可選符号的十進制數字和表示帶符号實數值的前導、尾随或嵌入句點組成。與PostScript不同,PDF不支援科學/指數格式,也不支援非十進制根。

雖然在PDF中使用“real”一詞來表示對象類型,但給定檢視器的實際實作可能使用雙精度、浮點或甚至固定點數。由于實作方式可能不同,精度的小數位數也可能不同。是以,出于可靠性和檔案大小的考慮,建議不要寫入超過四位小數。

示例1-2顯示了PDF中實數的一些示例。

Example 1-2. Reals
0.05
.25
-3.14159
300.9001
           

Name Objects

PDF中的name對象是唯一的字元序列(字元代碼0、ASCII、null除外)通常用于有固定值集的情況。名稱以/(SOLIDUS)字元後跟UTF-8字元串寫入PDF,任何非正則字元都有特殊的編碼形式。非正則字元是定義的字元在0x21(!)到0x7E(~)的範圍之外,以及任何空格字元(請參見表1-1)。這些非規則字元以#(數字元号)開始編碼,然後是字元的兩位十六進制代碼。

由于其獨特的性質,您将寫入PDF的大多數名稱都是在ISO 32000中預先定義的,或者将從外部資料(例如字型或顔色名稱)中導出。

如果您需要建立自己的基于非外部資料的自定義名稱(如私有中繼資料),如果您希望檔案被視為有效的PDF,則必須遵循ISO 32000-1:2008附錄E中定義的第二類名稱規則。第二個類名是以四個字元的ISO注冊字首開頭,後跟下劃線(_),然後是密鑰名。執行個體包括在執行個體1-3的末尾。
Example 1-3. Names
/Type
/ThisIsName37
/Lime#20Green
/SSCN_SomeSecondClassName
           

String Objects

當字元串被序列化為PDF時,它們隻是一系列(零或更多)8位位元組,可以寫成括号(and)中的文字字元,也可以寫成尖括号()中的十六進制資料。

文字字元串由括号中任意數量的8位字元組成。因為字元串中可能會出現任何8位值,是以通過使用反斜杠轉義特殊值,對不平衡括号())和反斜杠(\)進行特殊處理。此外,反斜杠可以與特殊的\ddd符号一起使用,以指定其他字元值。

文字字元串有幾種不同的類型:

ASCII:僅包含ASCII字元的位元組序列

PDFDocEncoded:根據PDFDocEncoding(ISO 32000–1:2008,7.9.2.3)編碼的位元組序列

Text:編碼為PDFDocEncoding或UTF–16BE(帶前導位元組順序标記)的位元組序列

Date:ISO 32000–1:2008,7.9.4中描述了一個ASCII字元串,其格式為D:YYYYMMDDHHMMSOSOHH’mm

日期作為字元串的一種類型,在1.1版中被添加到PDF中。

一系列十六進制數字(0–9,A–F)可以寫在尖括号之間,這對于在字元串對象中包含更多人類可讀的任意二進制資料或Unicode值(UCS-2或UCS-4)非常有用。數字的數量必須始終是偶數,盡管可以在數字對之間添加空格字元以提高人類的閱讀能力。示例1-4顯示了PDF中字元串的幾個示例。

Example 1-4. Strings
(Testing) % ASCII
(A\053B) % Same as (A+B)
(Français) % PDFDocEncoded
<FFFE0040> % Text with leading BOM
(D:19990209153925-08'00') % Date
<1C2D3F> % Arbitrary binary data
           
百分号(%)表示注釋;它後面的任何文本都将被忽略。

之前關于字元串的讨論是關于如何将值序列化為PDF檔案,而不一定是PDF處理器如何在内部處理這些值。雖然這種内部處理超出了标準的範圍,但必須記住,不同的檔案序列化可以産生相同的内部表示(如示例1-4中的(A\053B)和(A+B))。

Array Objects

數組對象是用方括号([and])括起來并用空格分隔的其他對象的異類集合。您可以在一個數組中混合和比對任何類型的對象,而PDF在許多地方都利用了這一點。數組也可以是空的(即,包含零元素)。

雖然一個數組僅由一個次元組成,但可以構造與多元數組等效的數組。這種構造在PDF中不經常使用,但它确實出現在一些地方,例如稱為可選内容組字典的資料結構中的Order數組。(請參閱第151頁的“可選内容組”。)

PDF數組中的元素數量沒有限制。但是,如果您找到了大型數組的替代方案(例如單個Kids數組的頁面樹),出于性能原因,最好避免使用它們。

示例1-5中給出了數組的一些示例。

Example 1-5. Arrays
[ 0 0 612 792 ] % 4-element array of all integers
[ (T) –20.5 (H) 4 (E) ] % 5-element array of strings, reals, and integers
[ [ 1 2 3 ] [ 4 5 6 ] ] % 2-element array of arrays
           

Dictionary Objects

因為它是幾乎所有進階對象的基礎,是以PDF中最常見的對象是字典對象。它是鍵/值對的集合,也稱為關聯表。每個鍵始終是一個名稱對象,但值可以是任何其他類型的對象,包括另一個字典,甚至是null。

當該值為空時,将視為該鍵不存在。是以,最好不要簡單的編寫密鑰,以節省處理時間和檔案大小。

字典用雙尖括号(<<and>>)括起來。在這些括号内,鍵可以按任何順序顯示,後面緊跟着它們的值。字典中出現的鍵将由正在編寫的進階對象的定義(在ISO 32000中)決定。

雖然許多現有的實作傾向于按字母順序編寫鍵,但這既不是必需的,也不是期望的。事實上,不應假設字典的處理方式,字典可以按任何順序讀取和處理鍵。兩次包含同一關鍵字的字典無效,所選值未定義。最後,雖然在鍵/值對之間放置換行符可以提高可讀性,但這也不是必需的,隻用于将位元組添加到總檔案大小中。

字典中鍵/值對的數量沒有限制。

示例1-6顯示了幾個示例。

Example 1-6. Dictionaries
% a more human-readable dictionary
<<
 /Type /Page
 /Author (Leonard Rosenthol)
 /Resources << /Font [ /F1 /F2 ] >>
>>
% a dictionary with all white-space stripped out
<</Length 3112/Subtype/XML/Type/Metadata>>
           

Name trees

名稱樹的作用類似于字典,因為它提供了一種将鍵與值相關聯的方法。然而,與字典不同,鍵是字元串對象而不是名稱,需要根據标準Unicode排序算法進行排序。

這一概念被稱為名稱樹,因為有一個“根”字典(或節點)引用一個或多個子字典/節點,而這些字典/節點本身可以引用一個或者多個子字典或節點,進而建立樹狀結構的許多分支。

根節點儲存一個鍵,Names(對于簡單樹)或Kids(對于更複雜的樹)。在複雜樹的情況下,每個中間節點也會有一個Kids鍵;每個分支的final/terminal節點将包含Names關鍵字。Names鍵的數組值通過交替鍵/值指定鍵及其值,如示例1-7所示。

示例1-7。名稱樹示例

% Simple name tree with just some names
1 0 obj
<<
 /Names [
 (Apple) (Orange) % These are sorted, hence A, N, Z...
 (Name 1) 1 % and values can be any type
 (Name 2) /Value2
 (Zebra) << /A /B >>
 ]
>>
endobj
           

Number trees

數字樹與名稱樹相似,不同的是它的鍵是整數而不是字元串,并且按升序排列。此外,葉(或根)節點中包含鍵/值對的條目作為Nums鍵的值而不是Names鍵。

Stream Objects

PDF中的流是8位位元組的任意序列,可以無限長,可以壓縮或編碼。是以,它們是用于存儲大資料塊的對象類型,這些資料塊采用其他一些标準化格式,例如XML文法、字型檔案和圖像資料。

流對象由對象的資料表示,該對象前面有一個包含流屬性的字典,稱為流字典。使用單詞stream(後面跟着行尾标記)和endstream(前面跟着行尾标志)用于描繪其字典中的流資料,同時也将其與标準字典對象區分開來。流字典從不單獨存在;它始終是流對象的一部分。

流字典始終至少包含一個鍵Length,它表示從stream後面的行開始到endstream前面的行字元結束前的最後一個位元組的位元組數。換句話說,它是序列化到PDF檔案中的實際位元組數。對于壓縮流,它是壓縮位元組數。雖然通常不提供,但原始未壓縮長度可以指定為DL密鑰的值。

流字典中最重要的鍵之一是Filter鍵,它指定在原始資料包含在流中之前對其應用了什麼(如果有的話)壓縮或編碼。使用FlateDecode過濾器壓縮大型圖像和嵌入字型非常常見,該過濾器使用與ZIP檔案格式相同的無損壓縮技術。對于圖像,最常見的兩個過濾器是DCTDecode,它生成JPEG/JFIF相容的流,以及JPXDe代碼,它生成與JPEG2000相容的流。其他過濾器見ISO 32000-12008,表6。示例1-8顯示了PDF中的steam對象的外觀。

示例1-8。示例流

Example 1-8. An example stream
<<
 /Type /XObject
 /Subtype /Image
 /Filter /FlateDecode
 /Length 496
 /Height 32
 /Width 32
>>
stream
% 496 bytes of Flate-encoded data goes here...
endstream
           

Direct versus Indirect Objects

既然已經介紹了對象的類型,了解這些對象可以直接或間接地在PDF中表示是很重要的。

直接對象是那些顯示為“内聯”的對象,在從檔案中讀取對象時直接擷取(是以得名)。它們通常作為字典鍵的值或數組中的條目找到,并且是迄今為止在所有示例中看到的對象類型。

間接對象是指通過引用(間接!)引用的對象,PDF閱讀器必須在檔案周圍跳轉才能找到實際值。為了識别所引用的對象,每個間接對象都有一個唯一的(每個PDF)ID,該ID表示為正數,以及一個生成号,該生成号總是非負數,通常為零(0)。這些數字用于定義對象和引用對象。

雖然最初打算用作跟蹤PDF版本的方法,但現代PDF系統幾乎從未使用過版本号,是以它們幾乎總是零。

要使用間接對象,必須首先使用ID和generation以及obj和endobj關鍵字來定義它,如示例1-9所示。

示例1-9。完全由直接對象構成的間接對象

3 0 obj % object ID 3, generation 0
<<
 /ProcSet [ /PDF /Text /ImageC /ImageI ]
 /Font <<
 /F1 <<
 /Type /Font
 /Subtype /Type1
 /Name /F1
 /BaseFont/Helvetica
 >>
 >>
>>
endobj
5 0 obj
(an indirect string)
endobj
% an indirect number
4 0 obj
1234567890
endobj
           

當你引用一個間接對象時,你可以使用它的ID、它的生成和字元R。例如,很常見的例子1-10,其中引用了兩個間接對象(ID 4和5)。

示例1-10。引用其他間接對象的間接對象

3 0 obj % object ID 3, generation 0
<<
 /ProcSet 5 0 R % reference the indirect object with ID 5, generation 0
 /Font <</F1 4 0 R >> % reference the indirect object with ID 4, generation 0
>>
endobj
4 0 obj % object ID 4, generation 0
<<
 /Type /Font
 /Subtype /Type1
 /Name /F1
 /BaseFont/Helvetica
>>
endobj
5 0 obj % object ID 5, generation 0
[ /PDF /Text /ImageC /ImageI ]
endobj
           

通過使用ID和生成的組合,可以在給定的PDF中唯一地辨別每個對象。使用PDF的交叉引用表功能,可以根據需要從引用中輕松定位和加載每個間接對象。

除非ISO 32000另有規定,否則任何時候使用對象時,它都可以是除流之外的任何一種類型,流隻能是間接的。

File Structure

如果您要檢視一個簡單的PDF檔案,讓我們在PDF檢視器中将其稱為HelloWorld.PDF,它将如圖1-1所示。

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

但是,如果您要在文本編輯應用程式中檢視HelloWorld.pdf,它看起來像示例1-11。

示例1-11。“Hello World.pdf”在文本編輯器中的樣子

%PDF-1.4
%%EOF
6 0 obj
<<
 /Type /Catalog
 /Pages 5 0 R
>>
endobj
1 0 obj
<<
 /Type /Page
 /Parent 5 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
 /Contents 2 0 R
>>
endobj
4 0 obj
<<
 /Type /Font
 /Subtype /Type1
 /Name /F1
 /BaseFont/Helvetica
>>
endobj
2 0 obj
<<
 /Length 53
>>
stream
BT
 /F1 24 Tf
 1 0 0 1 260 600 Tm
 (Hello World)Tj
ET
endstream
endobj
5 0 obj
<<
 /Type /Pages
 /Kids [ 1 0 R ]
 /Count 1
>>
endobj
3 0 obj
<<
 /ProcSet[/PDF/Text]
 /Font <</F1 4 0 R >>
>>
endobj
xref
0 7
0000000000 65535 f
0000000060 00000 n
0000000228 00000 n
0000000424 00000 n
0000000145 00000 n
0000000333 00000 n
0000000009 00000 n
trailer
<<
 /Size 7
 /Root 6 0 R
>>
startxref
488
%%EOF
           

看到這裡,您可能會誤以為PDF檔案是一個文本檔案,可以使用文本編輯器進行正常編輯,但事實并非如此!PDF檔案是一個結構化的8位二進制文檔,由一系列基于8位字元的标記描繪,用空格分隔并排列成(任意長)行。這些标記不僅用于描述各種對象及其類型,如您在上一節中所見,還用于定義PDF的四個邏輯部分的開始和結束位置。(見圖1-2。)

如前所述,PDF中的标記始終以ASCII的8位位元組進行編碼(并是以進行解碼)。它們不能以任何其他方式編碼,例如Unicode。當然,特定的資料或對象值可以Unicode編碼;我們将在出現這些情況時進行讨論。

White-Space

表1-1中所示的空白字元在PDF中用于将名稱和數字等句法結構互相分離。

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next
第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

在除注釋、字元串、交叉引用表條目和流之外的所有上下文中,PDF将任何連續空格字元序列視為一個字元。

CARRIAGE RETURN(0Dh)和LINE FEED(0Ah)字元,也稱為換行符,被視為行尾(EOL)标記。CARRIAGE RETURN和LINE FEED的組合被視為一個EOL标記。EOL标記通常與任何其他空白字元相同。然而,有時需要EOL标記,位于出現在行開頭的标記之前。

The Four Sections of a PDF

圖1-2說明了PDF的四個部分:标題、尾部、正文和交叉引用表。

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

Header

PDF的标題從檔案的位元組0開始,至少由8個位元組組成,後跟行尾标記。這8個位元組用于明确辨別檔案是PDF(%PDF-),并建議檔案符合的标準版本号(例如1.4)。如果您的PDF包含實際的二進制資料(現在,幾乎所有的資料都包含),則後面會出現第二行,該行也以PDF注釋字元%(PERCENT SIGN)開頭。第二行的%後面至少有四個ASCII值大于127的字元。盡管任何四個(或更多)值都可以,但最常用的是âãÏÓ (0xE2E3CFD3)。

第二行是通過簡單地計算高階ASCII值來欺騙執行ASCII與二進制檢測的程式,以確定PDF始終被視為二進制

Trailer

在PDF的标題的另一端,可以找到trailer。示例1-12顯示了一個簡單的示例。trailer主要是一個包含關鍵字和值的字典,它提供了處理文檔本身所需的文檔級資訊。

示例1-12。一輛簡單的trailer

trailer
<<
 /Size 23
 /Root 5 0 R
 /ID[<E3FEB541622C4F35B45539A690880C71><E3FEB541622C4F35B45539A690880C71>]
 /Info 6 0 R
>>
           

兩個最重要的鍵,也是必須的兩個鍵,是Size和Root。Size鍵告訴您應該在尾部字典之前的交叉引用表中找到多少條目。Root鍵的值是文檔的目錄字典,您将從中開始查找PDF中的所有對象。

trailer中的其他常見密鑰是Encrypt密鑰,其存在可快速識别給定已加密的PDF;ID密鑰,它為文檔提供兩個唯一的ID;以及Info鍵,它表示提供文檔級中繼資料的原始方法(如第12章所述,該方法已被替換)。

Body

body是構成實際文檔本身的所有九種類型的對象位于檔案中的位置。您将在第21頁的“文檔結構”中看到更多關于這一點的資訊,因為您會看到各種對象及其組織方式。

Cross-reference table

交叉引用表在概念和實作上很簡單,但它是PDF的核心屬性之一。該表為檔案中的每個間接對象提供了從檔案開頭的二進制偏移量,允許PDF處理器在任何時候快速查找并讀取任何對象。這種随機通路模式意味着可以快速打開和處理PDF,而無需将整個文檔加載到記憶體中。此外,無論頁碼中的“數字跳躍”有多大,頁面之間的導航都很快。将交叉引用表放在檔案末尾還提供了兩個額外的好處:可以在一次掃描中建立PDF(無回溯),并且可以友善地支援文檔的增量更新(示例參見第18頁的“增量更新”)。

交叉引用表的原始格式(從PDF 1.0到1.4)由一個或多個交叉引用部分組成,其中每個部分都是一系列條目(每個對象一行),其中包含對象的檔案偏移量、其生成以及是否仍在使用。最常見的表類型如圖1-3所示,隻有一個部分列出了所有對象。

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next
這種類型的交叉引用表遵循非常嚴格的格式,其中列位置是固定的,并且需要零

您可能會注意到,交叉引用表每行第二列中的數字值始終為零,第一行除外,第一行為65535。該值與f相結合,明确表示具有該ID的對象無效。因為PDF檔案可能永遠不會有ID為0的對象,是以在本例中,第一行看起來總是與您看到的一樣。

然而,當PDF包含增量更新時,您可能會看到一個與示例1-13中類似的交叉引用部分。

示例1-13。更新的交叉引用部分

xref
0 1
0000000000 65535 f
4 1
0000000000 00001 f
6 2
0000014715 00000 n
0000018902 00000 n
10 1
0000019077 00000 n
trailer
<</Size 18/Root 9 0 R/Prev 14207
/ID[<86E7D6BF23F4475FB9DEED829A563FA7><507D41DDE6C24F52AC1EE1328E44ED26>]>>
           

随着PDF文檔越來越大,很明顯,擁有這種非常冗長(且不可壓縮)的格式是一個需要解決的問題。是以,在PDF1.5中,引入了一種稱為交叉引用流的新型交叉引用存儲系統(因為資料存儲為标準流對象)。除了可以壓縮外,新格式更緊湊,支援大小超過10GB的檔案,同時還提供了其他類型的未來擴充(尚未使用)。除了将交叉引用表移動到流之外,這個新系統還可以在另一種特殊類型的流(稱為對象流)中存儲間接對象的集合。通過在多個流中智能地分割對象,可以優化PDF的加載時間和/或記憶體消耗。示例1-14顯示了交叉引用流的外觀。

示例1-14。交叉引用流内部

stream
01 0E8A 0 % Entry for object 2 (0x0E8A = 3722)
02 0002 00 % Entry for object 3 (in object stream 2, index 0)
02 0002 01 % Entry for object 4 (in object stream 2, index 1)
02 0002 02 % . . .
02 0002 03
02 0002 04
02 0002 05
02 0002 06
02 0002 07 % Entry for object 10 (in object stream 2, index 7)
01 1323 0 % Entry for object 11 (0x1323 = 4899)
endstream
           

Incremental Update

如前所述,通過在文檔末尾使用trailer和交叉引用表,PDF的一個關鍵特性是增量更新的概念。如圖1-4所示,由于更改的對象被寫入到PDF的末尾,是以儲存修改非常快速,因為不需要讀取和處理每個對象。

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

第一個交叉引用節之後的每個交叉引用節都指向前面的交叉引用節,該交叉引用節通過尾部字典中的Prev鍵(參見第16頁的“Trailer”),然後僅在新表中列出新的、更改的或删除的對象,如示例1-13所示。

盡管觀看者實際上不提供此功能(除非應用了數字簽名,如第119頁的“簽名字段”),但使用增量更新意味着可以支援跨儲存邊界的多個撤銷。然而,這也會給正在檢視你的PDF檔案的人帶來危險。即使你認為自己從檔案中删除了某些内容,但如果應用了增量更新而不是完全儲存,則該内容可能仍然存在。

在增量更新PDF時,不要将經典交叉引用與交叉引用流混合,這一點非常重要。原始檔案中使用的任何類型的交叉引用也必須在更新部分中使用。如果您将它們混合在一起,PDF閱讀器可能會選擇忽略更新。

Linearization

正如您所看到的,在檔案末尾有交叉引用表提供了各種好處。然而,也有一個很大的缺點,那就是PDF必須通過“流接口”(如web浏覽器中的HTTP流)讀取。在這種情況下,一個普通的PDF檔案必須完整下載下傳,才能閱讀到一個頁面,這不是一個很好的使用者體驗。

為了解決這個問題,PDF提供了一個稱為線性化的功能(ISO 32000-1:2008,附錄F),但更為人所知的是“快速Web視圖”。

線性化檔案與标準PDF有三個不同之處:

  1. 檔案中的對象以特殊的方式排序,使得特定頁面的所有對象被分組在一起,然後以數字頁面順序組織(例如,頁面1的對象,然後是頁面2的對象,等等)。
  2. 标題後面有一個特殊的線性化參數字典,它将檔案辨別為線性化,并包含處理檔案所需的各種資訊。
  3. 部分交叉引用表和尾部放置在檔案的開頭,以允許通路根對象所需的所有對象,以及表示要顯示的第一頁的對象(通常為1)。

當然,與标準PDF一樣,對象仍然以相同的方式引用,繼續允許通過交叉引用表随機通路任何對象。線性化PDF的片段顯示在示例1-15中。

示例1-15。線性化PDF片段

%PDF-1.7
%%EOF
8 0 obj
<</Linearized 1/L 7546/O 10/E 4079/N 1/T 7272/H [ 456 176]>>
endobj
xref
8 8
0000000016 00000 n
0000000632 00000 n
0000000800 00000 n
0000001092 00000 n
0000001127 00000 n
0000001318 00000 n
0000003966 00000 n
0000000456 00000 n
trailer
<</Size 16/Root 9 0 R/Info 7 0 R/ID[<568899E9010A45B5A30E98293
C6DCD1D><068A37E2007240EF9D346D00AD08F696>]/Prev 7264>>
startxref
0
%%EOF
% body objects go here...
           
混合線性化和增量更新可能會産生意想不到的結果,因為将使用線性化交叉引用表而不是更新版本,更新版本僅存在于檔案末尾。是以,必須完全儲存指定用于聯機的檔案,以删除更新并確定正确的線性化表。

Document Structure

現在,您已經了解了PDF中的各種對象,以及它們是如何組合在一起形成實體檔案布局/結構的,現在是将它們組合在一起以形成實際文檔的時候了。

The Catalog Dictionary

PDF文檔是一個對象集合,從Root對象開始(圖1-5)。之是以稱為根,是因為如果您将PDF中的對象視為樹(或有向圖),則該對象位于樹/圖的根。從該對象中,您可以找到處理PDF頁面及其内容所需的所有其他對象。

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

Root始終是Catalog類型的對象,稱為文檔的目錄字典。它有兩個必需的鍵:

  1. Type,其值始終為名稱對象Catalog
  2. Pages,其值是對頁面樹的間接引用(第24頁的“頁面樹”)

雖然能夠進入PDF頁面顯然很重要,但也有二十多個可選鍵(見ISO 32000-1:2008,表28)。這些表示文檔級資訊,包括以下内容:

  • 基于XML的中繼資料(第179頁的“XMP”)
  • OpenActions(“第79頁的Actions”)
  • Fillable forms (可填寫表格,第7章)
  • Optional content (可選内容,第10章)
  • Logical structure and tags (邏輯結構和标記,第11章)

示例1-16顯示了目錄對象的示例。

Example 1-16. Catalog object
<<
 /Type /Catalog
 /Pages 533 0 R
 /Metadata 537 0 R
 /PageLabels 531 0 R
 /OpenAction 540 0 R
 /AcroForm 541 0 R
 /Names 542 0 R
 /PageLayout /SinglePage
 /ViewerPreferences << /DisplayDocTitle true >>
>>
           

讓我們看看你可能會發現在PDF中包含的一些鍵(及其值),以改善使用者體驗:

PageLayout(頁面布局)

PageLayout鍵用于告訴檢視器如何顯示PDF頁面。其值是name對象(請參見第3頁的“name對象”)。要一次顯示一個頁面,請使用SinglePage的值,或者如果您希望所有頁面都是長連續的列,使用值OneColumn。也可以一次為兩頁指定值(有時稱為跨頁),具體取決于奇數頁的位置:TwoPageLeft和TwoPageRight。

PageMode(頁面模式)

除了顯示PDF頁面内容之外,您可能還希望使用者能夠立即通路PDF的一些導航元素。例如,您可能希望書簽或大綱可見(請參見第83頁的“書簽或大綱”了解更多資訊)。PageMode鍵是一個名稱對象,它的值決定顯示哪些(如果有)額外元素,例如UseOut行、UseThumbs或UseAttachments。

ViewerPreferences(檢視器首選項)

與前面兩個示例不同,其中鍵的值是name對象,ViewerPreferences鍵的值為viewer preferences dictionary(參見ISO 32000-1:2008,12.2),要使用的最重要的一個(前提是向文檔添加中繼資料,如第12章所述)如前一個示例所示:DisplayDocTitle。如果該值為true,則訓示PDF檢視器在視窗的标題欄中顯示的不是文檔的檔案名,如圖1-6所示,而是其真實标題,如圖1-7所示。

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

The Page Tree

PDF中的頁面通過頁面樹通路,頁面樹定義了頁面的順序。頁面樹通常被實作為平衡樹,但也可以隻是一個簡單的頁面數組。

建議您在一個葉子節點中的頁數不超過25–50頁。這意味着任何大于此值的文檔都不應該使用單個數組,而是應該建構一個平衡的樹。這樣做的原因是,平衡樹的設計意味着在記憶體或資源有限的裝置上,可以找到任何特定頁面,而不必加載整個陣列,然後依次通路陣列中的每個頁面。

如圖1-8所示,頁面樹中有兩種類型的節點:中間節點(Pages類型)和終端或葉節點(page類型)。中間節點(包括樹的起始節點)提供對其父節點(如果有)和子節點的間接引用,以及樹的特定分支中葉節點的計數。葉節點是實際的Page對象。

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

圖1-8的一部分(以PDF文法表示)可能與示例1-17類似。

Example 1-17. Objects making up a sample page tree
2 0 obj
<<
 /Type /Pages
 /Kids[ 4 0 R ]
 /Count 3
>>
endobj
4 0 obj
<<
 /Type /Pages
 /Parent 2 0 R
 /Rotate 90
 /Kids[ 5 0 R 6 0 R ]
 /Count 3
>>
endobj
5 0 obj
<<
 /Type /Page
 % Additional entries describing the attributes of Page 1
>>
endobj
6 0 obj
<<
 /Type /Page
 % Additional entries describing the attributes of Page 2
>>
endobj
           

Pages

正如剛才讨論的,頁面樹中的每個葉節點都表示一個頁面對象。page對象是一個字典,其Type鍵的值為page;它還包含一些其他必需的鍵,可能包含十幾個或更多可選鍵及其值。

示例1-18顯示了一些示例頁面字典。

Example 1-18. Two sample page dictionaries
% simplest valid page object, with the four required keys
<<
 /Type /Page
 /Parent 2 0 R
 /MediaBox [ 0 0 612 792 ] % Page Size == 8.5 x 11 in (612/72 x 792/72)
 /Resources <<>>
>>
% a real-world page object
<<
 /Type /Page
 /Parent 532 0 R
 /MediaBox [ 0 0 612 792 ]
 /CropBox [ 0 0 500 600 ]
 /Contents 564 0 R
 /Resources <<
 /ExtGState << /GS0 571 0 R /GS1 572 0 R >>
 /Font << /T1_0 566 0 R >>
 /XObject << /Im0 577 0 R >>
 >>
 /Trans << /S /Dissolve >>
 /Rotate 90
 /Annots 549 0 R
 /AA << /C 578 0 R /O 579 0 R >>
>>
           

這裡有幾個要點需要指出,我們将在未來的章節中深入探讨其中的一些要點:

Content

除非您希望PDF中有空白頁面,否則這是頁面字典中最重要的關鍵字,因為它指向包含在頁面上繪制内容的說明的内容流(請參見第35頁的“内容流”)。

Rotate

此鍵可用于以90度為增量旋轉頁面。然而,雖然它是PDF的一個适當和有效的部分,但它經常被許多低端工具忽略。是以,請考慮使用适當大小的頁面和(如有必要)轉換後的内容,如第42頁“Transformations”中所述。

Trans

如果存在,此鍵告訴檢視器,當以“示範樣式”顯示頁面時,它應該使用定義的轉換從它之前的頁面移到此頁面。此鍵值的詳細資訊可以在ISO 32000-1:2008,12.4.4中找到。

Annots

該鍵的值是頁面内容頂部的所有注釋(參見第6章)的數組。

Resources

這些用于幫助完成圖形對象的定義,如要在頁面上繪制所需的字型或顔色。它們将在下一章中介紹。

PDF units

當您使用圖形系統時,通常直接使用輸出裝置的分辨率,例如72或90 dpi(每英寸點數)螢幕或600 dpi列印機。這被稱為裝置空間(圖1-9)。

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

然而,如圖所示,如果您希望無論裝置的特性如何都顯示相同大小的對象,則需要在裝置空間以外的其他空間中工作。對于PDF,這稱為使用者空間,無論輸出裝置如何,它都保持不變(圖1-10)。

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

使用者空間預設為每英寸72個使用者機關(也稱為“點”),原點位于左下角。可以通過在頁面内容中使用坐标變換(參見第42頁的“Transformations”)或在頁面字典中存在UserUnit鍵(如示例1-19所示)來更改使用者單元的數量。坐标系的原點始終為[0 0],但可能與可見PDF頁面的左下角不對應,具體取決于頁面框的值(請參見第30頁的“Rects and boxes”)。

Example 1-19. Example pages that use the UserUnit key
2 0 obj
<<
 /Type /Pages
 /Kids[ 3 0 R 4 0 R 5 0 R ]
 /Count 
 >>
endobj
3 0 obj
<<
 /Type /Page
 /Parent 2 0 R
 /UserUnit 1.0 % default of 72 units/inch
 /MediaBox [ 0 0 612 792 ] % 8.5 x 11 inches
 % more keys here...
>>
endobj
4 0 obj
<<
 /Type /Page
 /Parent 2 0 R
 /UserUnit 2.0 % 144 units/inch (2 * 72)
 /MediaBox [ 0 0 612 792 ] % 17 x 22 inches
 % more keys here...
>>
endobj
5 0 obj
<<
 /Type /Page
 /Parent 2 0 R
 /UserUnit 3.14159 % something funny but perfectly valid
 /MediaBox [ 0 0 612 792 ] % 26.70 x 34.56 inches
 % more keys here...
>>
endobj
           

Rects and boxes

在PDF文法中描述矩形時,使用四個數字的數組。數字的順序是:左、下、寬、高。在PDF文法中,你會發現在不同的地方使用了矩形,但你最常用的矩形類型是定義頁面框中不同區域的大小。

五個頁面框(ISO 32000-1:2008,14.11.2)中的每一個都表示直接或通過注釋在頁面上繪制的圖形元素的矩形檢視區域(“box”)。數組中的四個數字始終以使用者為機關,即使用者空間的機關(見圖1-10)。因為它表示頁面坐标系中的視圖,是以矩形不需要左下角位于[0 0]。

頁面的MediaBox定義将在其上進行繪圖的頁面的大小。通常情況下,這相當于普通紙張尺寸,例如美國信紙(8.5 x 11英寸)或A4(21 x 29.7厘米),盡管它可以是任何尺寸。

預設(1.0)使用者機關的MediaBox為[0 0 612 792],相當于一張美國信紙大小的紙張(8.572=612;1172=792)。

除了MediaBox之外,還有四個其他頁面框可能會出現在頁面上。如圖1-11所示。

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

CropBox用于訓示PDF檢視器顯示或列印頁面時頁面的實際可見區域。這主要用于當您不希望使用者看到頁面上的内容時,是以您“裁剪”它。與圖像編輯器不同,應用CropBox不會删除任何内容;它隻是将其隐藏在可見區域之外。

盡管CropBox可能擴充到MediaBox之外,但PDF檢視器将有效地将值固定到MediaBox的值。

在印刷行業,TrimBox也有類似的用途。它定義了裁切器在列印完紙張後修剪(切割)紙張的位置,進而将TrimBox外的内容從最終紙張上移除。當你有東西想直接到達紙的邊緣,沒有任何空白或空隙時,就可以使用它。為了實作這一點,幾乎總是有一個相關的BleedBox,它定義了“修剪框”外部的區域,其中内容“bleeds”,以便可以正确修剪。

最後一個box,叫做ArtBox,幾乎從未使用過。它最初被認為是用來表示一個區域,它隻覆寫了頁面的“藝術品”,人們可能會使用它來重新調整用途,将其放置或強加到另一張紙上。然而,它從未真正流行起來,您不應該在文檔中使用它們。

Inheritance

正如您在第26頁的“Pages”中看到的,page對象中通常存在的一些值也可以出現在中間節點(Pages對象)中。發生這種情況時,這些值将由該節點的所有子節點繼承,除非它們選擇覆寫它們。例如,如果文檔的所有頁面大小相同,可以将MediaBox鍵放在頁面樹的根節點中。

并非頁面對象中存在的所有鍵都可以繼承,隻有ISO 32000-1:2008表30中辨別的鍵才能繼承。

線性化PDF不能使用繼承。必須直接在每個頁面對象中指定所有值。

The Name Dictionary

PDF檔案中的某些類型的對象可以通過name而不是對象引用來引用。name和對象之間的這種對應關系是通過使用名為name字典文檔的東西來建立的。name字典是通過在文檔的目錄字典中包含名稱鍵來指定的(請參閱第21頁的“目錄字典”)。此字典中可以出現的每個已定義鍵都指定了名稱樹的根,該名稱樹定義了特定類别對象的name。表1-2列出了一些可以通過name引用的對象類型:

第一章 PDF文法PDF ObjectsFile StructureDocument StructureWhat’s Next

What’s Next

在本章中,您學習了PDF的基本文法,從對象的基本類型開始,然後轉到PDF檔案的結構。您還了解了這些對象如何組合在一起形成文檔及其頁面,以及在頁面對象中可以找到哪些鍵。

接下來,您将了解PDF圖像模型、内容流以及如何在頁面上實際繪制内容。