字的研究(2)fontTools-字型檔案的解析

多事鬼間人發表於2022-01-25

前言

本文主要介紹如果使用Python第三方庫fontTools對OpenType字型檔案(包括TrueType輪廓和Postscript輪廓)的解析操作。

fontTools簡介

fontTools是由一組操作字型的庫和元件組成的Python第三方庫,要求Python3.6以及更高的版本。其中包括merge(字型合併)、subset(取字型子集)以及ttx(將OpenType轉化為XML)等。

安裝

pip install fontTools

本文中使用的版本為4.28.5

字型檔案的解析

讀取

如前文所述,OpenType字型檔案標準是有sfnt結構封裝的,基於sfnt的表結構,OpenType格式的字型檔案可以分為多個表結構。

建立TTFont例項,通過keys()可以檢視字型檔案的所有表名:

from fontTools.ttLib.ttFont import TTFont
font = TTFont("Resources/simsun.ttf")
print(font.keys()) 

執行結果如下:

['GlyphOrder', 'head', 'hhea', 'maxp', 'OS/2', 'hmtx', 'cmap', 'fpgm', 'prep', 'cvt ', 'loca', 'glyf', 'name', 'post', 'gasp', 'EBDT', 'EBLC', 'GDEF', 'GPOS', 'GSUB', 'MERG', 'meta', 'vhea', 'vmtx']

表名中包含‘glyf‘,所以simsun.ttf是一個使用TrueType輪廓的字型檔案。

換成使用Postscript輪廓的字型檔案:

font = TTFont("Resources/AdobeSongStd-Light.otf")
print(font.keys())

執行結果如下:

['GlyphOrder', 'head', 'hhea', 'maxp', 'OS/2', 'name', 'cmap', 'post', 'CFF ', 'BASE', 'GPOS', 'GSUB', 'VORG', 'hmtx', 'vhea', 'vmtx', 'DSIG']

表名中沒有'glyf'而存在'CFF ',後者是儲存Postscript資訊的表格。

對於TrueType Collection檔案則可以使用如下方法讀取,返回一個TTFont例項的列表

from fontTools.ttLib.ttCollection import TTCollection
collection = TTCollection("Resources/simsun.ttc")
print(list(collection))

執行結果如下:

[<fontTools.ttLib.ttFont.TTFont object at 0x000001F8BA66A700>, <fontTools.ttLib.ttFont.TTFont object at 0x000001F8BE072AF0>]

直接從這些表格提取到具體資訊是複雜的,但TTFont提供了一些方法以方便地獲取資訊:

font.getGlyphOrder() # 返回一個字形名稱列表,以其在檔案中的順序排序
font.getGlyphNames() # 返回一個字形名稱列表,以字母順序排序
font.getBestCmap() # 返回一個字形ID為鍵、字形名稱為值的字典
font.getReverseGlyphMap() # 返回一個字形名稱為鍵、字形ID為值的字典
font.getGlyphName(10000) # 輸入字形ID返回字形名稱
font.getGlyphID("uni70E0") # 輸入字形名稱返回字形ID
font.getGlyphSet() # 返回一個_TTGlyphSet物件,包含字形輪廓資料

上述方法中,最後一項與輪廓資料有關的方法是最重要的。可惜的是,官方文件似乎並沒有對這個物件做進一步解釋,故下文是我讀原始碼及其中註釋後的分析,如有錯漏,敬請指教。

Pen與_TTGlyphset

我認為,作者設計這一部分時的難點在於OpenType字型檔案標準存在兩種不同輪廓描述方式。Pen和_TTGlyphset的存在使得兩種不同的輪廓描述方式可以用同一套方法解析和顯示。

The Pen Protocol

基於TrueType輪廓的字型檔案和基於Postscript輪廓是兩種截然不同的資料格式。Pen是一個用於標準化的”畫"出輪廓的物件,或者是資料和實際輪廓間的媒介。

具體來說,Pen物件的子類包含將上述兩種輪廓資料轉化為畫線、移動等模擬實際輪廓的方法。在fontTools的pen庫中包含將輪廓資料轉化為qt、reportLab等第三方庫中例項的Pen子類。

_TTGlpyhset

_TTGlyphset是一個類似字典的,以字形名稱為鍵、_TTGlyph為值的物件。_TTGlyph中包含字形資料輪廓資料並可以通過draw方法“畫”出。_TTGlyph的兩個子類_TTGlyphGlyf和_TTGlyphCFF分別對應TrueType輪廓和Postscript輪廓。具體使用方法如下:

font = TTFont("Resources/simsun.ttf")
glyph = font.getGlyphSet()["uni70E0"]
glyph.draw(pen) # pen為例項化後的Pen子類

freetypePen

以freetype-py庫為例,使用freetypePen首先需要安裝freetype-py:

pip install freetype-py

以下程式碼修改自自fontTools的官方文件提供的範例程式:

from fontTools.ttLib import TTFont
from fontTools.pens.freetypePen import FreeTypePen
from fontTools.misc.transform import Offset

pen = FreeTypePen(None) # 例項化Pen子類
font = TTFont("Resources/simsun.ttf") # 例項化TTFont
glyph = font.getGlyphSet()["uni70E0"] # 通過字形名稱選擇某一字形物件
glyph.draw(pen) # “畫”出字形輪廓
width, ascender, descender = glyph.width, font['OS/2'].usWinAscent, -font['OS/2'].usWinDescent # 獲取字形的寬度和上沿以及下沿
height = ascender - descender # 利用上沿和下沿計算字形高度
pen.show(width=width, height=height, transform=Offset(0, -descender)) # 顯示以及矯正

執行結果如下:

注意,可能由於fontTools==4.28.5版本問題,通過pip安裝後freetypePen.py並沒有包含在pens資料夾下,需要使用的可以從fontTools的GitHub倉庫中下載,放到site-packages\fontTools\pens資料夾下,下載路徑為https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/pens/freetypePen.py。該問題可能在後續版本中修復。

ttx

最後是我個人認為fontTools中最為實用的一個元件ttx,其功能為將TTFont例項轉化為XML格式以及將XML檔案轉化為TTFont。基於這一元件,我們可以較為方便的實現對字型檔案內容的修改。

from fontTools.ttLib import TTFont

font = TTFont("Resources/simsun.ttf") # 例項化TTFont
font.saveXML("simsun.xml") # TTFont例項轉化為XML檔案,引數為XML檔案路徑
font.importXML("simsun1.xml") # XML檔案轉化為TTFont例項,引數為XML檔案路徑

除了將整個字型檔案轉化為XML檔案,ttx還可以將檔案中的單個錶轉化為XML檔案,避免多餘的儲存和時間消耗:

from fontTools.ttLib.ttFont import TTFont

font = TTFont("Resources/simsun.ttf")
font.saveXML("temp2.xml",tables=["glyf"]) # tables為需要轉化的表名組成列表

單個表的XML檔案直接匯入TTFont時,只會影響XML檔案存在的表,而其他表不變:

font.importXML("temp2.xml")

參考

https://fonttools.readthedocs.io/en/latest/index.html

https://github.com/fonttools/fonttools

相關文章