python精简font awesome字体

前言

之前优化博客加载速度的时候,看到font awesome字体还是占了155k左右的网络传输,这已经接近网络传输的四分之一了,所以这次打算自己研究一下如何精简woff2字体。
查了下资料,有一款开源的字体编辑软件叫FontForge,并支持python代码调用。接下来折腾一波在Windows上使用python调用FontForge,并精简woff2字体。

环境配置

首先自然是安装fontforge了,这个去官网下载即可。
在Windows上,安装完成后,系统的python环境里并没有fontforge的包。Windows上的fontforge安装后,自带了一份python环境,运行fontforge-console.bat后就会把自带的python添加到系统环境变量中,通过ffpython可以启动。
Font Forge自带的python
不过这个命令行的环境并不适合开发,下面把这个内置的python解释器添加到pycharm中。
打开FontForgeBuilds下的bin目录,把ffpython.exe复制一份,并改名为python.exe,因为其他名字在pycharm中无法识别。

pycharm更改python项目python解释器
不过会出现下面的情况,这需要我们把FontForge目录下的文件复制到venv
报错提示
把bin、etc、share、lib四个目录复制到pycharm生成的venv下,然后重启pycharm就会开始扫描这个解释器自带的包了。

试一下import fontforge,可以看到pycharm已经成功加载了。
成功加载font forge

小试牛刀

加载与统计图标数

先加载字体试试看,我选择了fa-brands-400.woff2,参照了一波官方api,打印所有图标名称并看看图标个数。

1
2
3
4
5
6
7
8
9
10
11
12
import fontforge
font = fontforge.open("D:/blog/fontawesome-free-5.15.4-web/webfonts/fa-brands-400.woff2")
assert isinstance(font, fontforge.font)
selection = font.selection
assert isinstance(selection, fontforge.selection)
selection.all()
all_fonts = []
for i in selection.byGlyphs:
    assert isinstance(i, fontforge.glyph)
    print(i.glyphname)
    all_fonts.append(i.glyphname)
print("num: " + str(len(all_fonts)))

脚本运行结果
可以说brands-400,果然名副其实,我下载的版本459个图标。

手动精简

看了下官方文档,调用font下的removeGlyph方法即可对glyph进行移除,再调用generate方法。想了想,我的博客上好像只有CreativeCommon的图标用到了这个brand的字库,我先手动精简试试。
对上面的for循环做修改

1
2
3
4
5
6
7
8
9
for i in selection.byGlyphs:
    assert isinstance(i, fontforge.glyph)
    print(i.glyphname)
    if "creative" not in i.glyphname:
        font.removeGlyph(i)
    else:
        all_fonts.append(i.glyphname)
print(len(all_fonts))
font.generate("D:/blog/fontawesome-free-5.15.4-web/webfonts/fa-brands-400.minify.woff2")

fa-brands-400精简效果

可以看到这个新生成的字体仅剩下不到3kb了,相比之前的小了70多kb

自动化精简

感觉靠手动精简肯定是行不通的,打算做个脚本扫描目录自动化精简,可以看到font awesome的图标命名都是fa加上n组-开头的单词,所以可以用fa-[-\w]+匹配一波站点的html文件以及js文件(以防有些东西是靠js渲染的),然后找出使用到的font awesome图标,再通过上面的脚本进行精简。
下面贴出python脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import glob
import re
import fontforge


extensions = [".html", ".js"]
pattern = re.compile("(fa-[-\w]+)")


# 获取使用到的图标
def get_used_icons(path):
    icon_dict = {}
    for filename in glob.glob(path, recursive=True):
        for extension in extensions:
            if extension in filename:
                for line in open(filename, encoding='utf-8'):
                    for match in re.finditer(pattern, line):
                        icon_dict[match.group()[3:]] = True
    return icon_dict


# 去除除了传入的字典的图标
def minify(path, used_dict):
    font = fontforge.open(path)
    assert isinstance(font, fontforge.font)
    selection = font.selection
    assert isinstance(selection, fontforge.selection)
    selection.all()
    for i in selection.byGlyphs:
        assert isinstance(i, fontforge.glyph)
        if i.glyphname not in used_dict:
            font.removeGlyph(i)
    font.generate(path)


if __name__ == '__main__':
    web_path = "D:/blog/hexo/public/**"
    font_path = "D:/blog/hexo/public/fontawesome-free-5.15.4-web/webfonts/"
    font_list = ["fa-brands-400.woff2", "fa-solid-900.woff2"]
    used_icon_dict = get_used_icons(web_path)
    for font_name in font_list:
        minify(font_path + font_name, used_icon_dict)

看一下精简前后的效果,从155.5k降到了3k
精简前大小

精简后大小

脱离pycharm运行

Windows下只能通过font forge自带的解释器了,打开这个FontForge interactive console。
开始菜单中的FontForge interactive console
输入ffpython加上上面的python脚本路径即可

在控制台中执行

作者

ZhongHuihong

发布于

2021-10-23

更新于

2021-10-24

许可协议