""Aitik,你的 GSoC 项目进展如何?""

嗯,我上次写博客已经有一段时间了。但我并没有一直在看洛基!(当然不是真的。)

在这段时间里,我的项目经历了一些有趣(而且压力很大)的转折,我打算在这篇简短的文章中谈谈它们。

新导师!#

在编码阶段的第一周,我遇到了我的新导师之一,Jouni。如果没有他和Tom以及Antony,我的项目可能寸步难行。

最初是我的提案中第一个里程碑目标,字体子集的起点是 Jouni 的 PR

字体子集究竟是什么?#

正如 Tom 建议的那样,理解事物的最好方法是记录你的旅程!(好吧,这也是 GSoC 希望我们遵循的,对吧?)

摘录我在这里写的一段话这里

字体子集可以在生成文档之前使用,用于将文档中仅包含必需的字形嵌入其中。字体可以被视为这些字形的集合,因此子集的最终目标是找出哪些字形是特定字符数组所需要的,并且仅将这些字形嵌入输出中。

现在这似乎很简单,对吧?

错了。#

字形程序可以调用它们自己的子程序,例如,像ä这样的字符可以通过调用a¨的子程序来构成;或者可以由一个改变显示矩阵并调用子程序的程序来构成。

由于子集器必须找出每个字形调用的所有这样的子程序,因此这是一个普遍的难题!

我的导师中有一位说过一句话,对我影响很大

Matplotlib 不是一个字体库,也不应该试图成为一个字体库。

很容易陷入试图在自己的项目中做所有事情的陷阱,这最终会导致伤害项目本身。

由于这对于 Matplotlib 也是适用的,它使用外部依赖项,如FreeTypettconv以及新提出的fontTools来处理字体子集、嵌入、渲染以及相关操作。

PS:如果字体相关内容你不理解,我建议你阅读我写的关于Matplotlib 和字体的友好教程!

意外的复杂情况#

Matplotlib 使用一个外部依赖项ttconv,该依赖项最初于**2003 年**被分叉到 Matplotlib 的存储库中!

ttconv 是一个独立的命令行实用程序,用于将 TrueType 字体转换为子集的 Type 3 字体(以及其他功能),它于 1995 年编写,Matplotlib 分叉了它以便将其作为库使用。

随着时间的推移,它出现了许多问题,这些问题要么难以修复,要么没有引起太多关注。(参见上一段,了解一个合理的原因)

仍然在使用的一个主要实用程序是convert_ttf_to_ps,它接受一个字体路径作为输入,并将其转换为一个 Type 3 或 Type 42 PostScript 字体,该字体可以嵌入 PS/EPS 输出文档中。我写的指南(链接)包含这些字体类型的差异等内容的详细描述。

因此,我们需要将字体路径输入转换为字体缓冲区输入。#

为什么我们需要这样做?ttconv 并不真正支持 Type 42 子集,因此我们使用一个名为 fontTools 的新依赖项,它的“全职工作”就是为我们(以及其他功能)子集 Type 42 字体。

它为我们提供了一个字体缓冲区,但是 ttconv 预期的是一个字体路径,以便嵌入该字体。

很简单,这可以通过 Python 的tempfile.NamedTemporaryFile来实现。

with tempfile.NamedTemporaryFile(suffix=".ttf") as tmp:
    # fontdata is the subsetted buffer
    # returned from fontTools
    tmp.write(fontdata.getvalue())

    # TODO: allow convert_ttf_to_ps
    # to input file objects (BytesIO)
    convert_ttf_to_ps(
        os.fsencode(tmp.name),
        fh,
        fonttype,
        glyph_ids,
    )

但这远非一个干净的 API;在*读取*文件和*解析*数据的分离方面。

我们理想的情况是将缓冲区传递给convert_ttf_to_ps,并修改ttconv(用 C++ 编写)的嵌入代码。而在这里,我们遇到了很多未经探索的代码库,自从它被分叉以来,它几乎没有被触碰过

有趣的是,就在昨天,我和我的导师们花了很多时间,终于发现ttconv 的整个日志系统都坏了,这一切都是因为一个简单的调试函数。🥲


这仍然是一个持续存在的问题,我们需要在接下来的几周内解决它,希望在我下次写博客时,它能得到解决!

再次感谢你花时间阅读这些博客。:D

注意:这篇文章也发布在我的个人网站上。#