字的研究(2)fontTools-字体文件的解析

字的研究(2)fontTools-字体文件的解析

前言

本文主要介绍如果使用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-packagesfontToolspens文件夹下,下载路径为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

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » 字的研究(2)fontTools-字体文件的解析