字的研究(2)Fonttools-字体文件的解析
前言
本文主要介绍如果使用Python第三方库fontTools对TrueType字体文件(指使用TrueType描述轮廓的OpenType字体文件)的解析、修改和创建等操作。
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.saveXML("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为需要转化的表名组成列表
参考
https://fonttools.readthedocs.io/en/latest/index.html
https://github.com/fonttools/fonttools
字的研究(2)Fonttools-字体文件的解析的更多相关文章
- js分析 天_眼_查 字体文件
0. 参考 js分析 猫_眼_电_影 字体文件 @font-face 1. 分析 1.1 定位目标元素 1.2 查看网页源代码 1.3 requests 请求提取得到大量错误信息 对比猫_眼_电_影抓 ...
- BMFont使用图片自定义字体(无需字体文件)
网上搜BMFont做字体,很多都是从一个字体文件读取,然后选择需要的字,然后保存成图片文字,这个对于一般的文字的确很实用,因为Unity本身不支持中文,所以只好这样了. 但是做过游戏的都知道,策划总是 ...
- 字的研究(3)fontTools-TrueType轮廓坐标的获取以及基于TrueType的Glyph实例的构建
前言 本文主要介绍如果使用Python第三方库fontTools提取OpenType字体文件中的TrueType轮廓坐标以及如何构建基于TrueType的Glyph实例 TrueType轮廓坐标的获取 ...
- iOS使用自定义字体的方法(内置和任意下载ttf\otf\ttc字体文件)
最近做了个有关阅读的应用,使用了自定义字体,学习了一下这方面的知识. 1.首先是最简单也普遍的做法,打包内置字符库文件: 把字体库文件添加到工程,如font1.ttf添加到工程,然后在工程plist添 ...
- js分析 猫_眼_电_影 字体文件 @font-face
0. 参考 https://developer.mozilla.org/zh-CN/docs/Web/CSS/@font-face 这是一个叫做@font-face 的CSS @规则 ,它允许网页开发 ...
- Delphi XE5开发Android程序使用自定义字体文件.
万事大吉,只欠根据字体文件(.ttf文件)切换阅读字体,通常Android系统只带三种以下字体.一般用Java/Eclipse开发的话比较简单,typeface的createFromAsset,cre ...
- [转]TrueType(TTF)字体文件裁剪(支持简体中文,繁体中文TTF字体裁剪)
原文入口: TTF字体文件裁剪(支持简体中文,繁体中文TTF字体裁剪) 对于TrueType(TTF)字体格式的介绍可以看: https://www.cnblogs.com/slysky/p/1131 ...
- @font-face字体文件用法
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- JVM-class文件完全解析-字段表集合
字段表集合 这个class文件的解析,分析得有点太久了.前面介绍类魔数,次版本号,主板本号,常量池入口,常量池,访问标志,类索引,父类索引和接口索引集合.下面就应该到字段表集合了. 紧接着接口索引 ...
随机推荐
- 【LeetCode】163. Missing Ranges 解题报告 (C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 遍历 日期 题目地址:https://leetcode ...
- 【LeetCode】409. Longest Palindrome 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:字典统计次数 方法二:HashSet 方法三 ...
- 【LeetCode】506. Relative Ranks 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 排序 argsort 堆 日期 题目地址:https ...
- AOP 日志切面
AOP把软件的功能模块分为两个部分:核心关注点和横切关注点.业务处理的主要功能为核心关注点,而非核心.需要拓展的功能为横切关注点.AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点进行分 ...
- Spurious Local Minima are Common in Two-Layer ReLU Neural Networks
目录 引 主要内容 定理1 推论1 引理1 引理2 Safran I, Shamir O. Spurious Local Minima are Common in Two-Layer ReLU Neu ...
- Mysql溯源-任意文件读取👻
Mysql溯源-任意文件读取 前言 读了<MySQL蜜罐获取攻击者微信ID>的文章,文中说明了通过mysql蜜罐读取攻击者微信ID的过程,抱着学习的态度尝试了一下 原理 mysql中有一个 ...
- [c++]对vector<T>容器求交集,并集,去重
#include "iostream" #include "vector" #include "algorithm" //sort函数.交并 ...
- [opencv]二维码识别率提升方案-resize调整
这里采用循环resize的方式,对二维码图像进行放缩. 识别到name(二维码结果)不为空,则立即退出循环 //循环识别 for (int i = 1;name.empty(); i++){ resi ...
- 编写Java程序,实现多线程操作同一个实例变量的操作会引发多线程并发的安全问题。
查看本章节 查看作业目录 需求说明: 多线程操作同一个实例变量的操作会引发多线程并发的安全问题.现有 3 个线程代表 3 只猴子,对类中的一个整型变量 count(代表花的总数,共 20 朵花)进行操 ...
- 编写Java程序,演练静态内部类应用
返回本章节 返回作业目录 需求说明: 创建一个Person类,在该类中定义一个Home静态内部类,并在这个Home类中定义一个显示Home相关信息的方法. 在Person类中设置一个Home类型属性对 ...