本文主要介紹如果使用Python第三方庫(kù)fontTools對(duì)OpenType字體文件(包括TrueType輪廓和Postscript輪廓)的解析操作。
創(chuàng)新互聯(lián)-云計(jì)算及IDC服務(wù)提供商,涵蓋公有云、IDC機(jī)房租用、眉山服務(wù)器托管、等保安全、私有云建設(shè)等企業(yè)級(jí)互聯(lián)網(wǎng)基礎(chǔ)服務(wù),聯(lián)系電話:18980820575
fontTools是由一組操作字體的庫(kù)和組件組成的Python第三方庫(kù),要求Python3.6以及更高的版本。其中包括merge(字體合并)、subset(取字體子集)以及ttx(將OpenType轉(zhuǎn)化為XML)等。
pip install fontTools
本文中使用的版本為4.28.5
如前文所述,OpenType字體文件標(biāo)準(zhǔn)是有sfnt結(jié)構(gòu)封裝的,基于sfnt的表結(jié)構(gòu),OpenType格式的字體文件可以分為多個(gè)表結(jié)構(gòu)。
創(chuàng)建TTFont實(shí)例,通過(guò)keys()可以查看字體文件的所有表名:
from fontTools.ttLib.ttFont import TTFont
font = TTFont("Resources/simsun.ttf")
print(font.keys())
運(yùn)行結(jié)果如下:
['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是一個(gè)使用TrueType輪廓的字體文件。
換成使用Postscript輪廓的字體文件:
font = TTFont("Resources/AdobeSongStd-Light.otf")
print(font.keys())
運(yùn)行結(jié)果如下:
['GlyphOrder', 'head', 'hhea', 'maxp', 'OS/2', 'name', 'cmap', 'post', 'CFF ', 'BASE', 'GPOS', 'GSUB', 'VORG', 'hmtx', 'vhea', 'vmtx', 'DSIG']
表名中沒有'glyf'而存在'CFF ',后者是存儲(chǔ)Postscript信息的表格。
對(duì)于TrueType Collection文件則可以使用如下方法讀取,返回一個(gè)TTFont實(shí)例的列表
from fontTools.ttLib.ttCollection import TTCollection
collection = TTCollection("Resources/simsun.ttc")
print(list(collection))
運(yùn)行結(jié)果如下:
[, ]
直接從這些表格提取到具體信息是復(fù)雜的,但TTFont提供了一些方法以方便地獲取信息:
font.getGlyphOrder() # 返回一個(gè)字形名稱列表,以其在文件中的順序排序
font.getGlyphNames() # 返回一個(gè)字形名稱列表,以字母順序排序
font.getBestCmap() # 返回一個(gè)字形ID為鍵、字形名稱為值的字典
font.getReverseGlyphMap() # 返回一個(gè)字形名稱為鍵、字形ID為值的字典
font.getGlyphName() # 輸入字形ID返回字形名稱
font.getGlyphID("uni70E0") # 輸入字形名稱返回字形ID
font.getGlyphSet() # 返回一個(gè)_TTGlyphSet對(duì)象,包含字形輪廓數(shù)據(jù)
上述方法中,最后一項(xiàng)與輪廓數(shù)據(jù)有關(guān)的方法是最重要的??上У氖?,官方文檔似乎并沒有對(duì)這個(gè)對(duì)象做進(jìn)一步解釋,故下文是我讀源碼及其中注釋后的分析,如有錯(cuò)漏,敬請(qǐng)指教。
我認(rèn)為,作者設(shè)計(jì)這一部分時(shí)的難點(diǎn)在于OpenType字體文件標(biāo)準(zhǔn)存在兩種不同輪廓描述方式。Pen和_TTGlyphset的存在使得兩種不同的輪廓描述方式可以用同一套方法解析和顯示。
基于TrueType輪廓的字體文件和基于Postscript輪廓是兩種截然不同的數(shù)據(jù)格式。Pen是一個(gè)用于標(biāo)準(zhǔn)化的”畫"出輪廓的對(duì)象,或者是數(shù)據(jù)和實(shí)際輪廓間的媒介。
具體來(lái)說(shuō),Pen對(duì)象的子類包含將上述兩種輪廓數(shù)據(jù)轉(zhuǎn)化為畫線、移動(dòng)等模擬實(shí)際輪廓的方法。在fontTools的pen庫(kù)中包含將輪廓數(shù)據(jù)轉(zhuǎn)化為qt、reportLab等第三方庫(kù)中實(shí)例的Pen子類。
_TTGlyphset是一個(gè)類似字典的,以字形名稱為鍵、_TTGlyph為值的對(duì)象。_TTGlyph中包含字形數(shù)據(jù)輪廓數(shù)據(jù)并可以通過(guò)draw方法“畫”出。_TTGlyph的兩個(gè)子類_TTGlyphGlyf和_TTGlyphCFF分別對(duì)應(yīng)TrueType輪廓和Postscript輪廓。具體使用方法如下:
font = TTFont("Resources/simsun.ttf")
glyph = font.getGlyphSet()["uni70E0"]
glyph.draw(pen) # pen為實(shí)例化后的Pen子類
以freetype-py庫(kù)為例,使用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) # 實(shí)例化Pen子類
font = TTFont("Resources/simsun.ttf") # 實(shí)例化TTFont
glyph = font.getGlyphSet()["uni70E0"] # 通過(guò)字形名稱選擇某一字形對(duì)象
glyph.draw(pen) # “畫”出字形輪廓
width, ascender, descender = glyph.width, font['OS/2'].usWinAscent, -font['OS/2'].usWinDescent # 獲取字形的寬度和上沿以及下沿
height = ascender - descender # 利用上沿和下沿計(jì)算字形高度
pen.show(width=width, height=height, transform=Offset(0, -descender)) # 顯示以及矯正
運(yùn)行結(jié)果如下:
注意,可能由于fontTools==4.28.5版本問(wèn)題,通過(guò)pip安裝后freetypePen.py并沒有包含在pens文件夾下,需要使用的可以從fontTools的GitHub倉(cāng)庫(kù)中下載,放到site-packages\fontTools\pens文件夾下,下載路徑為https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/pens/freetypePen.py。該問(wèn)題可能在后續(xù)版本中修復(fù)。
最后是我個(gè)人認(rèn)為fontTools中最為實(shí)用的一個(gè)組件ttx,其功能為將TTFont實(shí)例轉(zhuǎn)化為XML格式以及將XML文件轉(zhuǎn)化為TTFont?;谶@一組件,我們可以較為方便的實(shí)現(xiàn)對(duì)字體文件內(nèi)容的修改。
from fontTools.ttLib import TTFont
font = TTFont("Resources/simsun.ttf") # 實(shí)例化TTFont
font.saveXML("simsun.xml") # TTFont實(shí)例轉(zhuǎn)化為XML文件,參數(shù)為XML文件路徑
font.importXML("simsun1.xml") # XML文件轉(zhuǎn)化為TTFont實(shí)例,參數(shù)為XML文件路徑
除了將整個(gè)字體文件轉(zhuǎn)化為XML文件,ttx還可以將文件中的單個(gè)表轉(zhuǎn)化為XML文件,避免多余的存儲(chǔ)和時(shí)間消耗:
from fontTools.ttLib.ttFont import TTFont
font = TTFont("Resources/simsun.ttf")
font.saveXML("temp2.xml",tables=["glyf"]) # tables為需要轉(zhuǎn)化的表名組成列表
單個(gè)表的XML文件直接導(dǎo)入TTFont時(shí),只會(huì)影響XML文件存在的表,而其他表不變:
font.importXML("temp2.xml")
https://fonttools.readthedocs.io/en/latest/index.html
https://github.com/fonttools/fonttools