前言
本文主要介紹如果使用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")