本文是對PDF Explained(by John Whitington)第六章《Text And Fonts
》的摘要式翻譯,并加入了一些自己的了解。
文本狀态
文本狀态相關的操作符和參數如下表所示:
操作符 | 操作數(參數) | 描述 | 預設值 | 例 |
---|---|---|---|---|
Tc | charSpace | Tc将字元間距設為charSpace | ||
Tw | wordSpace | Tw将字元間距設為wordSpace | ||
Tz | scale | Tz将水準縮放設定為scale/100 | 100(正常大小) | |
TL | leading | TL将相鄰兩行文本行基線的垂直距離設為leading。隻對T*和’'操作符産生作用。 | ||
Tf | font, size | Tf設定字型為font, 字号為size。 | 無 | |
Tr | render | Tr将文本渲染模式設為render(一個整數) | 詳見手冊5.2.5 Text Rendering Mode | |
Ts | rise | Ts将文本上升值設為rise |
更多較長的描述可參看手冊5.2 Text State Parameters and Operators
文本狀态與圖形狀态一起存儲,使用上面的運算符進行操作。目前文本狀态受堆棧運算符q和Q的影響。
列印文本
在頁面上列印文本需要:
- 選擇字型。
- 選擇位置,大小和方向。
- 選擇間距,顔色,文本渲染模式和其他參數。
- 從字型中選擇字元,并在頁面上顯示。
文本段落
操作符BT表示文本落開始,ET為文本段落結束。用于在頁面的内容流中顯示文本的操作符隻能出現在BT和ET之間。但是用于改變文本狀态的操作符不受這種限制。 例:
1. 0. 0. 1. 50. 700. cm //Position at (50, 700)
BT //Begin text block
/F0 36. Tf //Select /F0 font at 36pt
(Hello, World!) Tj //Place the text string
ET //End text block
複制
這裡,我們使用Tf來選擇字型及大小,使用Tj運算符來顯示文本字元串。 我們依靠圖形運算符cm來設定文本位置。現在,我們将讨論更改文本位置的其他方法。
文本空間和文本定位
顯示文本的坐标系,就是文本空間。從文本空間到使用者空間的轉換決定了文本在頁面上放置的位置。文本字元串中第一個字形的原點位于文本空間的原點。
有兩種矩陣:
- 文本矩陣(Tm):定義了下一個字形的變換方式。可以通過文本定位操作符和文本顯示操作符進行修改。
- 文本行矩陣(Tlm):目前行開頭的文本矩陣的狀态。
當開始一個新的文本段落時,矩陣會被重置為機關矩陣。這兩個矩陣與字型大小,水準縮放和文本上升一起定義了從文本空間到使用者空間的轉換。
文本定位操作符如下表所示:
操作數 | 操作符 | 功能 |
---|---|---|
x, y | Td | 辨別下一行文字的開始處,位置是從目前行的開始處偏移(x,y)。 |
x, y | TD | 辨別下一行文字的開始處,位置是從目前行的開始處偏移(x,y), 同時将前導(leadig)設為-y。x y TD等效于-y TL x y Td |
- | T* | 移到下一行的開頭。等效于0 leading Td, leading為目前的前導值。 |
a,b,c,d,e,f | Tm | 将文本矩陣™和文本行矩陣(Tlm)設定為所有的操作數均為數字。 |
顯示文本
Tj運算符在目前位置顯示文本。為友善起見,提供了三個附加操作符(’,’'和TJ)。 下表為文本顯示操作符:
操作數 | 操作符 | 功能 | 示例 |
---|---|---|---|
string | Tj | 在目前位置顯示字元串 | |
string | ’ | 移到下一行并顯示文本字元串。等效于T* string Tj | |
wordspace, charspace, string | ‘’ | 移動到下一行并顯示文本字元串,使用wordspace作為單詞間距,charspace作為字元間距。等效于wordspace Tw charspace Tc string ' | |
array | TJ | 允許調整單個字形的位置。數組裡包含字串和數字。如果元素是字串則顯示字串。如果是數字,數字的機關是文本空間機關的千分之一,會依據書寫模式将其從目前的水準或垂直坐标中減去,進而改變下一個字形的位置。 |
一些例子
字元和單詞間距
BT
/F0 36 Tf
1 0 0 1 120 350 Tm
50 TL
(Character and Word Spacing) Tj T*
3 Tc
(Character and Word Spacing) Tj T*
10 Tw
(Character and Word Spacing) Tj
ET
複制
說明:
- 使用Tf設定字型/為F0,字号36。
- 使用Tm将文本位置設定為(120,350)
- 使用TL将前導設定為50
- 用Tj顯示一個字元串,用T*移動到下一行
- 将字元間距設定為3,然後再次繪制字元串
- 将單詞間距設定為10,并第三次繪制字元串
效果如下:
文本轉換
在本例中,我們将展示文本轉換如何與圖形轉換相結合。
0.96 0.25 -0.25 0.96 0 0 cm
BT
/F0 48 Tf
48 TL
1 0 0 1 270 240 Tm
(Text and graphics) Tj T*
(transforms combined) Tj T*
(with newlines) Tj
ET
複制
說明:
- 使用cm設定圖形矩陣,圍繞原點逆時針旋轉
- 使用Tf設定字型,TL設定前導
- 使用Tm設定文本矩陣,将起點設為(270,240)
- 使用Tj和T*寫三行文本
效果如下:
文本提升
Ts運算符可用于調整文本的垂直位置:
BT
/F0 72 Tf
1 0 0 1 140 290 Tm
(Text) Tj
20 Ts
(Up) Tj
0 Ts
(and) Tj
-20 Ts
(Down) Tj
ET
複制
效果如下:
字距和字形調整
TJ操作符可用于替代Tj,用于繪制具有水準字形調整的字元串。這種情況通常發生在使用文字處理器或打字機布局的情況下。TJ 操作符是一種對這些資訊進行編碼的便捷方法,無需再為每行文本使用數十個操作符:
BT
/F0 72 Tf
90 TL
1 0 0 1 240 330 Tm
[(PJ WAYNE)] TJ T*
[(P)150(J )(W)150(A)80(YN)20(E)] TJ
ET
複制
我們在這裡使了兩次TJ; 一次顯示文本正常,第二次調整了字距。結果如下圖所示。
文本渲染模式
文本有八種渲染模式(譯者注:原文是seven,但實際取值應該是0~7,原文應該是錯的),可以使用Tr設定。其中四種用于将文本設定為剪切路徑,一種用于編寫不可見文本,這些不在本文讨論範疇。我們來看另外三種情況(模式0,1,2),别分用于區域填充,邊緣填充,邊緣加區域填充。
0.5 g
BT
/F0 72 Tf
1 0 0 1 160 380 Tm
90 TL
(Text Mode Zero) Tj T*
1 Tr
(Text Mode One) Tj T*
2 Tr
(Text Mode Two) Tj
ET
複制
效果如下:
定義和嵌入字型
字型是特定字元集的字形(字元形狀)的集合。在PDF中,字型由字型字典組成, 字典中定義了度量,字元集和編碼(将文本字元串中的字元代碼映射到字型中的字元),以及字型程式(實際的字型檔案)。不同的類型字型(Type 1, TrueType等)有不同的存儲格式。
PDF中的字型類型
PDF中可以使用大多數主流字型格式,包括
- Type 1字型
- TrueType字型
- Type 3字型
- CID字型
- OpenType字型
Type 1字型
我們以Type 1字型為例簡要介紹下字型字典中的條目。下表中,*為必選項, **為14種标準字型以外的字型必選
key | 值類型 | 值 |
---|---|---|
/Type* | 名稱 | 必須是/Font |
/Subtype* | 名稱 | 必須是/Type1 |
/BaseFont* | 名稱 | 字型的PostScript名稱 |
/FirstChar** | 整數 | /Widths數組中的第一個編碼 |
/LastChar** | 整數 | /Widths數組中的最後一個編碼 |
/Widths** | 整數數組 | 數組長度為(/LastChar - /FirstChar + 1),給出這些字元的字形寬度,機關是以千分之一的文本空間機關。 |
/FontDescriptor** | 間接引用字典 | 字型描述符字典,提供字型的度量(字形寬度除外) |
/Encoding | 名稱字典 | 字型的字元編碼,例如/MacRomanEncoding或/WinAnsiEncoding。 |
/ToUnicode | 流 | 一個包含了用于提取文本内容指令的流。 |
有14種标準的Type 1字型是所有PDF應用都必須支援的。不過,目前Adobe建議将所有的字型嵌入文檔,即使這些标準字型也不例外。以下是标準字型清單:
- Times-Roman
- Times-Bold
- Times-Italic
- Times-BoldItalic
- Helvetica
- Helvetica-Bold
- Helvetica-Oblique
- Helvetica-BoldOblique
- Courier
- Courier-Bold
- Courier-Oblique
- Courier-BoldOblique
- Symbol
- ZapfDingbats
下面是一個Tyep 1字型的簡單示例:
1 0 obj
<< /Type /Font
/Subtype /Type1
/BaseFont /Times-Roman
/FirstChar 0
/LastChar 255
/Widths [ 255 255 255 255 ... 744 268 380 380 380 380 380 380 380 380 380 380 ]
/FontDescriptor 2 0 R
/Encoding /WinAnsiEncoding
>>
複制
省略号…是我們省略的内容,不是PDF語言的一部分。稍後我們将讨論/FontDescriptor和/Encoding。該字型共有256個字元,/Widths數組為每個字元提供寬度值。
字型編碼
字型編碼描述字元編碼(内容流字元串中的字元)和字型中的字形描述之間的映射。
最簡單的/Encoding可以隻是一個标準編碼的名子,這些編碼在PDF标準文檔的附錄D中定義。更複雜的編碼則需要通字典來定義。下表列出了編碼字典中的條目:
key | 值類型 | 值 |
---|---|---|
/Type | 名稱 | 必須是/Encoding |
/BaseEncoding | 名稱 | 基礎編碼,/Differences用于定義與該基礎編碼的差異。可以是預定義編碼之一,包括/MacRomanEncoding, /MacExpertEncoding, /WinAnsiEncoding。如果基礎編碼不存在,則/Differences用來描述與自字型檔案内置編碼的差異。 |
/Differences | 整數和名稱的數組 | 定義與基礎編碼的差異。包含0個或多個部分,每個部分以數字n開頭,後跟字元n,n + 1,n + 2等的字形名稱。例如[6 /endash /emdash 34 /space] 表示6映射到/endash,7映射到/emdash,34映射到/space。 |
在下面的示例中,字型的編碼定義了與内置字型編碼的差別,将字元1 替換為字元/bullet(項目符号點)。這意味着PDF 檢視器可以正确剪切和粘貼文本,因為它知道字元編碼1是一個項目符号( /bullet是在Adobe Glyph List中預定義的名稱)。
25 0 obj
<< /Type /Font
/Subtype /Type1
/Encoding 23 0 R //Reference to the encoding dictionary
/BaseFont /Symbol
/ToUnicode 24 0 R //Instructions for conversion to Unicode
>>
endobj
23 0 obj //Encoding dictionary
<< /Type /Encoding
/BaseEncoding /WinAnsiEncoding //The base encoding
/Differences [ 1 /bullet ] //The differences
>>
endobj
複制
嵌入字型
建立PDF檔案時,必須嵌入字型。我們需要如下步驟:
- 提取字型檔案中的各種細節–這些細節用于填寫字型字典,字型度量和字型編碼字典。
- 如果字型格式允許,則從相關字型檔案中删除這些細節,隻留下字形描述–所有這些資訊現在都在字型字典中。這減小了嵌入字型的大小。
- 可以隻保留字型的子集,删除整個字形描述,将字型檔案減少到一個隻包含實際使用的字元的檔案。
下例給出了嵌入字型的示例。
9 0 obj
<</Type /Font
/Subtype /TrueType It's a TrueType font
/BaseFont /GCCBBY+TT8Et00 Font is TT8Et00. GCCBBY+ prefix identifies as a subset font.
/FontDescriptor 8 0 R
/FirstChar 1 There are 41 characters in this font.
/LastChar 41
/Widths
[603 603 603 603 603 603 603 603 603 603 603 603 603 603 The widths. It's a fixed-width font.
603 603 603 603 603 603 603 603 603 603 603 603 603 603
603 603 603 603 603 603 603 603 603 603 603 603 603]
/Encoding 14 0 R
>>
14 0 obj The font encoding
<< /Type /Encoding
/BaseEncoding /WinAnsiEncoding The base encoding
/Differences The changes. In this case, it's a subset font with the characters at position 1 onward.
[1 /w /i /d /g /e /t /s /T /h /space /r /u /l /a /x /bracketleft
/underscore /J /o /n /S /m /quotesingle /A /p /c /bracketright
/one /colon /braceleft /b /k /braceright /v /period /parenleft
/two /parenright /asterisk /y /P]
>>
endobj
8 0 obj The font descriptor, giving the remaining metrics.
<< /Type /FontDescriptor
/FontName /GCCBBY+TT8Et00
/FontBBox [0 -205 602 770]
/Flags 4
/Ascent 770
/CapHeight 770
/Descent -205
/ItalicAngle 0
/StemV 90
/MissingWidth 602
/FontFile2 12 0 R The actual font file, here in TrueType format.
>>
endobj
複制
從文檔中提取文本
通常在檔案的字型詞典中會包含足夠的資訊以便 擷取實際字元辨別(而不僅僅是字形)。這對于Adobe Reader一類的PDF閱讀應用
非常重要,因為有了這些資訊使用者才可以進行文本搜尋和複制。
有兩種機制實作這一點:
- 字型中的/Encoding條目(将字元編碼映射到Adobe字形清單,比如/bullet)。
- 更先進的機制,/ToUnicode條目,提供一段程式,其語言由Adobe定義。程式字元代碼直接映射到Unicode實體。以下是/ToUnicode程式的示例:
23 0 obj
<< /Length 317 >>
stream
/CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo <<
/Registry (Symbol+0) /Ordering (T1UV) /Supplement 0 >> def
/CMapName /Symbol+0 def
1 begincodespacerange <01> <01> endcodespacerange
1 beginbfrange
<01> <01> <2022> //Maps character code 1 to Unicode U+2022, the bullet point.
endbfrange
endcmap CMapName currentdict /CMap defineresource pop end end
endstream
endobj
複制
提取文本的另一個難點是重構内容流中的文本操作符。為了調整字距或對齊等原因,操作符會将文本時行拆分,行尾的連字元可能會中斷字元流。文本操作符甚至有可能出現亂序。不過,大多通常情況下都可以順利完成文本重建。
譯者推薦閱讀
文本空間和文本定位一節中提到了文本矩陣,以下材料有助于你更好的了解這種轉換矩陣
- Text Operators, The Tm Operator一節講述了矩陣中abcdef的意義。
- How to read a PDF text matrix, Affine transformation這兩篇有助于你了解如何通過矩陣對文本進行各種拉伸,旋轉操作。
- 如果你還想了解更多和本章相關的細節,可以閱讀手冊第五章