紫夢孤影的專欄
本篇文章主要翻譯freetype官網上有關字型的介紹。
一、基本的排版概念
1、字型檔案、格式和資訊(Font files, format and information)
font是用於顯示或列印的文字的影象集合。在某一個font中的影象擁有一些共同的屬性,如外觀、風格、襯線等。就排版而言,我們必須清楚分辨font family和font face的區別。例如,‘Palatino Regular’ 和’Palatino Italic’ 是出自同一個font family的兩個不同的font face, 並且都可以被稱之為‘Palatino’。
術語“font”,通常用於不必清楚區分family和face的時候,當然具體也要區分語境。大多數的families是由幾個不同的檔案來實現的,如,TrueType字型通常使用單獨的一個被稱之為font的檔案來表示一種face,如arial.ttf
ariali.ttf
表示‘Arial Italic’。
因而,digital font就是包含了一個或者多個font faces的資料檔案,每一個檔案中都包含有文字影象、文字的度量以及其他一些對文字編碼和佈局非常重要的資訊。在一些文字格式(format)中,例如Adobe的Type 1,某種font face是用幾個檔案共同描述的(如,某個檔案儲存字形影象,另一個檔案儲存字形的度量)。FreeType 2能夠正確地處理多個檔案構成的字型,但是我們在接下來的文件中會忽略掉這些實現細節,並且認為digital font就是一個檔案。
方便起見,我們將包含多個face的字型檔案稱之為字型集(font collection)。通常這種情況在西文中較少見,但是在一些亞洲字型中卻又很常見,因為這些字型通常會有水平和豎直兩種佈局方式。2、文字影象和對映(Character images and mappings)
文字影象被稱之為字形(glyph)。出於所使用的指令碼、用途或者使用環境的考慮等,某一個字形可以有不同的影象表示,而且不同的文字也可以擁有相同的影象表示,如,羅馬連寫 ‘fi’ and ‘fl’就是同一個字形影象。文字和字形之間的關係是非常複雜的,我們不做繼續討論。此外,一些字型格式或多或少使用了複雜的模式(scheme)來儲存和獲取字形,而為了簡潔起見,我們在談論FreeType時只保留如下的一些概念:
- 一個字型檔案中包含了一個字形的集合;每一個字形被儲存為bitmap、矢量表示(a vector representation),或者其他任何模式(scheme)(大多數可縮放格式都是用了數學表示和控制資料/程式相結合的方式)。這些字形可以儲存在字型檔案的任意位置,而且一般都能通過簡單的索引獲取。
- 字型檔案包含一個或者多個表,稱之為文字對映(character maps,‘charmaps’,‘cmaps’),用於將給定編碼的文字碼轉換成字形的索引。某個字型face可能包含多個charmaps。一個font face中可以包含多個charmaps,如,許多TrueType字型包含了Apple特定的和Unicode特定的charpmap,以便於經字型應用與Mac平臺和Windows平臺。
3、文字和字型度量(Character and font metrics)
每個字形影象都關聯著許多度量值來描述如何放置字形以及如何管理文字渲染;度量值與字形的位置、游標的步進以及文字的佈局相關,而這些值在渲染文字流時是及其重要的。 每種可縮放的字型格式還包含一些用概念性的(如字型)單位表示的全域性度量,用以同一個face中字形的一些屬性。比如,最大字形邊界盒、升高、降低和字型中文字的高度。 不可縮放的文字也有一些度量,但是僅用於給定的文字維度和解析度,而且通常的單位是畫素點。
二、字形輪廓(Glyph Outlines)
該部分描述了字形影象的可縮放表示,稱之為輪廓(outline)。
三、字形度量(Glyph Metrics)
1、基線、畫筆和佈局(baseline、pens and layout)
基線是一條假想出來的線,在渲染字型的時候起參照作用。基線可以是水平的,也可以是豎直的,例如拉丁文一般是水平排版的,而一些中文、日文有時卻是豎直排版的。此外,我們假想在基線上存在一個點,稱之為畫筆位置或者原點(origin),並用這個點來定位每個字形。
- 水平佈局中,字形總是坐落在基線上。文字在渲染時,無論是從左到右還是相反方向,都是通過遞增畫筆位置來完成的。 連續的兩個畫筆之間的距離是由特定的字形決定的,比如下圖中’A’ ‘b’ 's’的寬度都不一樣,我們將這個畫筆距離稱之為“步進寬度”(advance width)。值得注意的是,即使在渲染一些RTL(right to left)文字(如阿拉伯語)時,步進寬度也是一個正數值,所不同的只是文字渲染的方式。
- 對於水平佈局,字形在基線兩側勻稱分佈,如下圖所示:
板式度量和邊界盒(Typographic metrics and bounding boxes)
一個給定字型中的字形需要用到很多度量值來描述。
- Ascent 從基線到用於放置輪廓點(outline point)的網格座標最高點的距離。因為網格座標的Y軸是豎直向上的,因而它是一個正值。
- Descent 從基線到用於放置輪廓點的網格座標的最低點的距離。在FreeType中,由於網格座標的Y軸方向是豎直向上的,所以該值是個負數值。注意,在某些字型格式中該值為正值。
- Linegap
兩行文字之間的距離,即行間距。基線到基線的距離按如下方式計算:
linespace = ascent - descent + linegap
另外,還有一些更簡單的度量:
- Bounding box
這是一個想象中的將字形儘可能緊密包裹起來的盒子,它用四個值表示,
xMin
,yMin
,xMax
,yMax
。這些值在原始輪廓(original outline)中可以用字型單位(font units)度量,或者在縮放輪廓中(scaled outline)使用整型畫素單位來度量。 - Internal leading
這一概念直接來源於傳統的排版界,它表示一個字形的前導空間的大小,前導空間是位於EM方塊之外的為一些字形特性而保留的,如重音標記,通常可以由如下方式計算:
internal leading = ascent - descent - EM_size
- External leading 行間距的另一種叫法。
軸承和步進(bearings and advances)
每一個字形都有稱為軸承和步進的距離量。鑑於字形既可以被渲染成水平方向的也可以被渲染成豎直方向的,因而這些標量的值取決於字型的佈局方式。
- Left side bearing
從當前畫筆位置到字形的邊界盒的左邊界的距離。對於水平佈局,該值是正值,對於豎直佈局,該值是負值。
在FreeType API中,該值被稱為
bearingX
,另一種簡稱為lsb
。 - Top side bearing
從基線到字形邊界盒最頂部的豎直距離。對於水平佈局,該值為正,對於豎直佈局,該值為負。
在FreeType API中,該值被稱為
bearingY
。 - Advance width
當處理一串文字時,對於LTR書寫習慣,是字形與字形間的水平增加距離量,對於RTL書寫習慣,則是字形與字形之間水平減少的距離量。在水平佈局時,該值為正值,豎直佈局時,該值為零。
在FreeType API中,該值被稱為
advanceX
- Advance height
在渲染完一個字形後,畫筆位置在水平方向上的增加量。於豎直佈局而言是個正值,於水平佈局而言總是零。
在FreeType API中,該值被稱為
advanceY
- Glyph width
字形的水平方向寬度。對於未縮放的字型座標而言,
glyph width = bbox.xMax - bbox.xMin
對於已縮放的字形,計算該值需要特殊的處理,具體見grid-fitting章節。 - Glyph height
字形的豎直高度。對於猥瑣方的字型座標,
glyph height = bbox.yMax - bbox.yMin
對於縮放的字形,計算該值需要特殊的處理,具體見grid-fitting章節。 - Right side bearing
只在水平佈局中用於描述邊界盒右邊界到步進寬度的距離。大多數情況下是一個非負值:
right side bearing = advance_width - left_side_bearing - (xMax-xMin)
一般簡記為rsb
四、字距(Kerning)
五、文字處理(Text Process)
該部分主要介紹渲染文字的演算法,這些演算法使用了前面介紹的概念並且不必考慮文字佈局的因素。我們假設所要渲染的文字是簡單文字(simple text),例如拉丁文字系統或是西里爾文字系統,這些文字系統輸入的文字碼和輸出的字形索引間是一對一的關係,而不像阿拉伯文字系統或者高棉文字系統需要額外的塑性引擎(shaping engine)支援才能完成文字碼到字形索引的轉換,當然,這些特殊文字系統並不在下面的討論之列。
1、書寫簡單的文字字串(Writing simple text strings)
在第一個例子中,我們會產生一個簡單的拉丁文字系統的文字字串,例如水平方向上從左到右佈局。使用獨特的畫素度量,整個過程如下:
- 將文字串轉換成字形索引序列;
- 將畫筆放置到游標位置;
- 獲取或者載入字形影象;
- 平移字形並使得字形的“原點”與畫筆位置重合;
- 將字形渲染到特定裝置上;
- 以字形的步進寬度(畫素單位表示)為度量增加畫筆位置;
- 為下面的每個字形重複3~6步驟;
- 當所有的字形渲染完畢,設定文字游標為新的畫筆位置。 注意:該演算法並未考慮到字間距。
2、偽子畫素定位(Pseudo-subpixel positioning)
使用子畫素定位有時會很有用。這時的文字渲染過程與1小節類似,只有如下些許不同:
- 畫筆位置被表示為小數畫素;
- 因為平移一個微調後的並且以小數表示距離的輪廓時會破壞grid-fitting,在渲染文字影象之前,字形原點的位置必須取整。
- 步進寬度一小數表示,並且不一定是整數。 所以,就有如下改進的演算法版本:
- 將文字串轉換成字形索引序列;
- 將畫筆放置到游標位置,可以是非整數點;
- 獲取或者載入字形影象;
- 平移字形並使得字形的“原點”與取整後的畫筆位置重合;
- 將字形渲染到特定裝置上;
- 以字形的小數表示的步進寬度(畫素單位表示)為度量增加畫筆位置;
- 為下面的每個字形重複3~6步驟;
- 當所有的字形渲染完畢,設定文字游標為新的畫筆位置。
注意:在使用小數畫素位置時,字母之間的空間並非固定的,而是取決於在之前字形定位過程中產生的舍入誤差的累加值。對於自動微調的字形而言,這一問題可以使用
lsb_delta
和rsb_delta
來緩和。
3、簡單的字間距(Simple kerning)
為簡單的文字字串增減字間距非常簡單:當發現字間距對(kerning pair)時,在步驟4前簡單地將縮放後的字間距增加到畫筆位置上即可,注意雖然在演算法2中不必要取整,但在演算法1中這一距離應當是取整後的值。
4、從右向左佈局(Right-to-left layout)
處理從右向左佈局的文字系統時(如希伯來文字系統)與上述的處理過程非常相似。唯一的區別在於,在字形渲染前畫筆位置要減去一個步進寬度(記住:即使是希伯來字形,步進寬度也一定時正值)。
5、豎直佈局(Vertical layout)
排版豎直文字的過程與前面說的一樣,只有如下的區別:
- 基線時豎直的,必須用豎直度量替換水平度量;
- 左軸承通常是負值,但是字形的原點還是一定坐落在基線上;
- 步進高度始終是正值,所以如果有誰想要從下向上書寫,畫筆位置必須減去步進高度(假定Y軸是向上的)。