標籤:

計算機中的字體文件使用的編碼是什麼?

字體文件可以簡單地理解為字元和字形的對照表。但字元是一個抽象的概念,具體在計算機上應該是以字元編碼的形式來存儲的。那麼,字體文件中所存儲是字形跟哪種字元編碼的對應關係呢?

#網路上搜索了很久,發現關於這個的很少。有個別地方提到用的是Unicode編碼,即所謂的UTF16。大家如果有相關的參考鏈接,也請貼出來。


目前的典型情況是在 TrueType 與 OpenType 字體中用 `cmap` 表記錄 glyph 與 Unicode 字元的對應關係。

也有內部用各種遺留(legacy)編碼的,字體內部會標明自己的 `cmap` 存的是什麼編碼。

對,字元是一個抽象概念,你的理解很正確(如果我沒理解錯你的意思的話),所以,以 Unicode 字體為例,`cmap` 里可以理解為是用16位整數記錄字形所對應的 Unicode 字元,因此你可以把它理解為 UCS-2 或 UTF-16,而且是 big-endian 的(因為 TrueType/OpenType 文件是 big-endian 的)。其他內部編碼的字體就用8位或16位或32位整數記錄其他字符集的編碼。

——但是,注意但是,儘管 `cmap` 表的目的就是存儲字形與字元的映射,但它不是那麼平白地存儲一條一條的「字形ID:字元」數據,而是為了性能與存儲優化而有特殊的結構。比如,如果基本對應範圍確定的字符集,就按字符集里的字元順序挨個排列字形ID,或者如果字形分布很零散,就一段一段地排列字形ID,並記錄每一段之間的差值。

具體技術細節可見 OpenType 規範文檔:Character/Glyph Index Mapping


絕大多數的字體都包含一個或者多個Charmap(就是 @梁海 回答中的cmap),它的作用就是把一個字元從它的字元編碼映射到字形索引。

一般一個字元的渲染步驟是這樣的:

  1. 載入字體文件
  2. 確定要輸出的字體大小
  3. 輸入這個字元的編碼值
  4. 根據字體文件裡面的Charmap,把編碼值轉換成字形索引(就是這個字元對應字體文件中的第幾個形狀)
  5. 根據索引從字體中載入這個字形
  6. 將這個字形渲染成點陣圖,有可能進行加粗,傾斜等變換。注意這裡的傾斜和傾斜字體不同,它只是從演算法上對點陣圖進行變換,與專門製作的加粗字體是不一樣的。

你所說的字元編碼,就是通過步驟3裡面的Charmap來查找對應字形的。常用的Charmap就兩種,一種是Apple Roman編碼的,一種是Unicode編碼的。大部分時候我們用的都是Unicode,Apple Roman是老版本的Mac OS所用的一種只有8bit的字元編碼Mac OS Roman。一般渲染系統也都會優先選擇Unicode的Charmap。具體包含什麼樣的Charmap取決於你的字體。比如Windows內置的宋體就包含Apple Roman和Unicode兩種,雅黑帶了兩個Unicode,Adobe的SourceCode Pro則是帶了兩個Unicode和一個Apple Roman的Charmap。

在選擇Charmap時,如果和你輸入的字元編碼不一樣的話,輸出的字形要麼是錯的,要麼就根本找不到對應的字形。

參考:想要了解一個字元是如何被計算機渲染出來的,最好的辦法就是找一個字體渲染引擎親手拆開看看。推薦FreeType 2 Tutorial。這裡就算不自己編碼,也能了解到它是如何把字元從編碼渲染成字形輸出的。

這裡只介紹了字體渲染的流程,詳細的Charmap結構請參考 @梁海的答案。


硬要去對應的話,最常用的是 Unicode BMP 內 UTF-16 大端,其次是 UTF-32 大端。

記錄的是各種編碼到內部索引(uint16_t,估計有生之年不會看到會擴展這個……)的對應表。


推薦閱讀:

如何評價《中國新歌聲》的logo設計?
iPhone 上的應用 Line 聊天對話框中使用的是哪一種字體?
現有的中文字體里,哪一款或者哪幾款跟FF DIN這款字體搭配使用比較好?
怎樣學習書寫金文?

TAG:字體 | 計算機 |