TrueType 字型檔案格式
TrueType 外框字型,字形是二次貝茲曲線,副檔名 .ttf。
- MIME 類型
- font/ttf
- 副檔名
- .ttf
- 分類
- 字型
Magic number(檔案簽章)
| 0x00000000 | 00010000 | ···· | sfnt version 1.0 |
關於 TrueType 字型格式
TrueType 由 Apple 在 1980 年代末設計、後來被微軟採用,它把每個字形描述成一組二次貝茲曲線,外加一段 hinting bytecode 程式,告訴點陣化引擎在小字級時該怎麼把曲線扣到像素格上。檔案本身是 sfnt 容器:前面一段簡短的目錄用四字元標籤列出字型裡每一張表,每張表都是獨立的資料塊、用絕對檔案位移定址,所以表可以增刪或重排而不動到其他任何東西。位移 0 的版本標籤 00 01 00 00,就是宣告這些外框是 TrueType 而不是 PostScript 的地方。
檔頭結構,一個位元組一個位元組看
| 位移 | 長度 | 欄位 | 意義 |
|---|---|---|---|
| 0 | 4 | sfntVersion | TrueType 外框是 00 01 00 00。同一個欄位還有另外三個合法值,各代表一種不同的檔案:4F 54 54 4F(OTTO)是帶 PostScript 外框的 OpenType 字型、74 74 63 66(ttcf)是一次裝好幾個字體的集合、74 72 75 65(true)是較舊的 Apple 標籤,有些 Mac 上做的字型到現在還帶著。只認 00 01 00 00 的偵測器會默默漏掉最後那一種。 |
| 4 | 2 | numTables | 目錄後面接著幾筆表記錄。大端序——sfnt 檔案裡每個多位元組數字都是大端序,不管寫它的是哪種處理器——所以這是少數幾種「小端序讀取端一讀就明顯出錯」的格式之一。 |
| 6 | 2 | searchRange | 不大於 numTables 的最大 2 次方,再乘以 16。它只是給表記錄做二分搜尋的提示,而實際出貨的字型把它寫錯的頻率高到(連 Apple 系統字體都在內)解析器乾脆忽略它,改成線性走過所有記錄。 |
| 8 | 2 | entrySelector | 同一個 2 次方取以 2 為底的對數。也是二分搜尋提示的殘留物,同樣可以放心忽略;幾十年來沒有任何點陣化引擎依賴過它。 |
| 10 | 2 | rangeShift | numTables 乘 16 再減掉 searchRange。真實字型把它寫錯的比例高得驚人,這就是「這三個欄位只是為了向後相容留著的贅重」最直白的證據。 |
| 12 | variable | Table records | 每筆 16 個位元組:四字元標籤、檢查碼、絕對檔案位移與長度,全部大端序。記錄依標籤字母排序,所以目錄裡第一筆是「排序最前面」的那個,不是「檔案裡最前面」的那個;表本身可以任意順序擺放,所以位移是唯一可靠的定位方式。 |
| variable | 54 | head table | 透過它的目錄記錄才找得到,不在固定位置。它往內 12 個位元組是 magic number 5F 0F 3C F5,這是「這個檔案真的是 sfnt」最可靠的單一檢查;unitsPerEm 在 +18,而決定 loca 表存的是 16 位元還是 32 位元位移的 indexToLocFormat 在 +50。 |
| variable | variable | glyf and loca tables | loca 是一個指進 glyf 的位移陣列,每個字形一筆再加一個結束項,glyf 則裝著輪廓本身。因為 loca 每一筆的寬度是由 head 裡那一個欄位決定的,indexToLocFormat 對不上的字型會畫出一堆垃圾外框,而檔案其他每個部分都驗證得乾乾淨淨。 |
怎麼跟長得像的格式分開
| 容易搞混的 | 怎麼分 |
|---|---|
| OpenType 字型 | 兩者都是 sfnt 檔案,就所有實質意義而言也都是 OpenType;差別在外框類型,而位移 0 那 4 個位元組就宣告了答案。00 01 00 00 代表住在 glyf 表裡的二次 TrueType 外框,4F 54 54 4F(OTTO)代表塞在 CFF 表裡的三次 PostScript 外框。副檔名兩邊都不算證據——很多叫 .otf 的檔案裝的是 TrueType 外框。 |
| TrueType Collection (.ttc) | 集合檔案開頭是 74 74 63 66(ttcf),位移 4 是版本、位移 8 是字型數量,後面接一個位移陣列,每個指向一份普通的表目錄。這樣好幾個字體就能共用同一份字形資料。macOS 多數系統字族都是這樣出貨的,所以把 .ttc 交給一個堅持位移 0 必須是 00 01 00 00 的工具,會被判成損毀——而它其實一點問題都沒有。 |
| WOFF 字型 | 同一個字型,為網頁包了一層。這層外殼把 77 4F 46 46(wOFF)放在位移 0,把原本的 sfntVersion 移到位移 4 當作 flavor 欄位,所以包著 TrueType 的 WOFF 是在位移 4、而不是位移 0 讀到 00 01 00 00。把檔案改名成 .ttf 什麼都沒變;那些表仍然是 zlib 資料流。 |
| WOFF2 字型 | 下一個世代的同樣關係:位移 0 是 77 4F 46 32(wOF2),外框類型一樣在位移 4。因為整個字型主體是單一條 Brotli 資料流,從瀏覽器網路面板存下來、改名成 .ttf 的 WOFF2 沒辦法靠改回名字救回來——得用看得懂這層外殼的工具解壓縮才行。 |
常見的壞法
- 下載被截斷。檔案前面的目錄還在,所以 hex 檢視器會顯示合理的表數量與完整的標籤清單,但後面幾筆記錄的位移與長度指到檔案結尾之外。把目錄裡「位移加長度」的最大值和實際檔案大小比一比,一次就抓得到,完全不用解碼。
- 文字模式傳輸。會轉換換行的 FTP 用戶端或建置步驟,會把 hinting bytecode 與字形資料裡的每個 0D 0A 變成單獨的 0A。目錄本身很少含有那一對位元組,所以它照樣解析得動,字型也打得開、回報正確的字形數量,然後畫出一堆莫名其妙的東西。
- checkSumAdjustment 過期。子集化與修補工具很常改寫表卻不重算 head 表往內 8 個位元組那個值。點陣化引擎不看它,所以字型在使用時到處都正常,但驗證工具會標記這個檔案,而編輯前原本有效的 DSIG 簽章表現在也失去意義了。
- 被改名的網頁字型。從瀏覽器存下 .woff2 再叫它 .ttf,是字型裝不起來最常見的單一原因:那些位元組是 Brotli 壓縮過的、開頭是 wOF2,而作業系統會回報檔案損毀而不是格式不對,於是大家跑去找錯的問題。
- 前面被塞了垃圾。透過會注入錯誤頁的代理伺服器抓字型,或在連線階段過期之後才下載,拿到的檔案位移 0 是一段 HTML doctype,而裡面一個位元組的字型資料都沒有。檔案大小往往看起來很合理,所以在讀到前 4 個位元組之前,什麼都不像有問題。
這個格式現在還在用嗎
TrueType 到現在還是桌面排版的主力。Windows 和 macOS 絕大多數系統字體都是以 TrueType 外框出貨的,通常打包成字型集合;而讓這個格式成名的 hinting bytecode,在低密度螢幕上要把文字算繪得很小的場合仍然重要。在網頁上它幾乎被完全取代:瀏覽器確實接受用 @font-face 直接載入 .ttf,但這麼做很浪費頻寬,因為 WOFF2 送的是一模一樣的那些表,位元組數卻少了大約三分之一。TrueType 仍然是首選的場合,是任何「要安裝而不是下載」的東西——要放進設計軟體、嵌入式裝置、印刷流程或 PDF 的字體——以及任何之後還要再編輯的東西,因為那些表是明擺著存的,每一套字型編輯器都能直接讀寫。OpenType 1.8 加入的可變字型擴充建立在同一套二次外框之上,所以現代的可變字型骨子裡仍然是 TrueType 檔案。
在這個站上打開 TrueType 字型檔
這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。
相關格式
容易和 TrueType 字型搞混的格式,可以比對它們的開頭位元組。
| 格式 | 開頭位元組 | 副檔名 |
|---|---|---|
| OpenType 字型 | 4F 54 54 4F | .otf |
| WOFF 字型 | 77 4F 46 46 | .woff |
| WOFF2 字型 | 77 4F 46 32 | .woff2 |