天天看點

PDF Explained(翻譯)第六章 文本和字型文本狀态列印文本定義和嵌入字型從文檔中提取文本譯者推薦閱讀

本文是對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
PDF Explained(翻譯)第六章 文本和字型文本狀态列印文本定義和嵌入字型從文檔中提取文本譯者推薦閱讀

更多較長的描述可參看手冊5.2 Text State Parameters and Operators

文本狀态與圖形狀态一起存儲,使用上面的運算符進行操作。目前文本狀态受堆棧運算符q和Q的影響。

列印文本

在頁面上列印文本需要:

  1. 選擇字型。
  2. 選擇位置,大小和方向。
  3. 選擇間距,顔色,文本渲染模式和其他參數。
  4. 從字型中選擇字元,并在頁面上顯示。

文本段落

操作符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)設定為所有的操作數均為數字。
PDF Explained(翻譯)第六章 文本和字型文本狀态列印文本定義和嵌入字型從文檔中提取文本譯者推薦閱讀

顯示文本

Tj運算符在目前位置顯示文本。為友善起見,提供了三個附加操作符(’,’'和TJ)。 下表為文本顯示操作符:

操作數 操作符 功能 示例
string Tj 在目前位置顯示字元串
string 移到下一行并顯示文本字元串。等效于T* string Tj
wordspace, charspace, string ‘’ 移動到下一行并顯示文本字元串,使用wordspace作為單詞間距,charspace作為字元間距。等效于wordspace Tw charspace Tc string '
array TJ 允許調整單個字形的位置。數組裡包含字串和數字。如果元素是字串則顯示字串。如果是數字,數字的機關是文本空間機關的千分之一,會依據書寫模式将其從目前的水準或垂直坐标中減去,進而改變下一個字形的位置。
PDF Explained(翻譯)第六章 文本和字型文本狀态列印文本定義和嵌入字型從文檔中提取文本譯者推薦閱讀

一些例子

字元和單詞間距

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           

複制

說明:

  1. 使用Tf設定字型/為F0,字号36。
  2. 使用Tm将文本位置設定為(120,350)
  3. 使用TL将前導設定為50
  4. 用Tj顯示一個字元串,用T*移動到下一行
  5. 将字元間距設定為3,然後再次繪制字元串
  6. 将單詞間距設定為10,并第三次繪制字元串

效果如下:

PDF Explained(翻譯)第六章 文本和字型文本狀态列印文本定義和嵌入字型從文檔中提取文本譯者推薦閱讀

文本轉換

在本例中,我們将展示文本轉換如何與圖形轉換相結合。

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           

複制

說明:

  1. 使用cm設定圖形矩陣,圍繞原點逆時針旋轉
  2. 使用Tf設定字型,TL設定前導
  3. 使用Tm設定文本矩陣,将起點設為(270,240)
  4. 使用Tj和T*寫三行文本

效果如下:

PDF Explained(翻譯)第六章 文本和字型文本狀态列印文本定義和嵌入字型從文檔中提取文本譯者推薦閱讀

文本提升

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           

複制

效果如下:

PDF Explained(翻譯)第六章 文本和字型文本狀态列印文本定義和嵌入字型從文檔中提取文本譯者推薦閱讀

字距和字形調整

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; 一次顯示文本正常,第二次調整了字距。結果如下圖所示。

PDF Explained(翻譯)第六章 文本和字型文本狀态列印文本定義和嵌入字型從文檔中提取文本譯者推薦閱讀

文本渲染模式

文本有八種渲染模式(譯者注:原文是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 Explained(翻譯)第六章 文本和字型文本狀态列印文本定義和嵌入字型從文檔中提取文本譯者推薦閱讀

定義和嵌入字型

字型是特定字元集的字形(字元形狀)的集合。在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檔案時,必須嵌入字型。我們需要如下步驟:

  1. 提取字型檔案中的各種細節–這些細節用于填寫字型字典,字型度量和字型編碼字典。
  2. 如果字型格式允許,則從相關字型檔案中删除這些細節,隻留下字形描述–所有這些資訊現在都在字型字典中。這減小了嵌入字型的大小。
  3. 可以隻保留字型的子集,删除整個字形描述,将字型檔案減少到一個隻包含實際使用的字元的檔案。

下例給出了嵌入字型的示例。

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這兩篇有助于你了解如何通過矩陣對文本進行各種拉伸,旋轉操作。
  • 如果你還想了解更多和本章相關的細節,可以閱讀手冊第五章