好了?你搞定了吗?!

在回答这个问题之前,如果你错过了上下文,请查看我之前的博客的最后几行…保证你只需要不到30秒就能了解整个问题!

通过这篇简短的文章,我打算谈谈我们做了什么以及为什么我们做了这些。XD

鸵鸟算法#

听起来熟悉吗?还记得操作系统(OS)吗?这是我当时逃课现在后悔的几门核心计算机科学课程之一。(╥﹏╥)

如果你不知道什么是鸵鸟算法,可以查看维基百科页面,上面有2行解释…但我猜你们大多数人不会点击它XD,所以这里就简单解释一下

鸵鸟算法是一种通过“把头埋在沙子里假装没有问题”来忽略潜在问题的策略。

需要注意的一点是:当允许问题发生比试图防止它更具成本效益时,就会使用它。

正如你现在可能猜到的那样,我们最终得到了一个不太干净的 API(稍后详细介绍)。

问题是什么?#

问题最高级别的概述是

❌ fontTools -> buffer -> ttconv_with_buffer
✅ fontTools -> buffer -> tempfile -> ttconv_with_file

第一种方法产生了损坏的输出,但第二种方法运行良好。这里需要注意的一点是,方法1在将文件读取解析数据分离方面更好。

  1. fontTools 为我们处理 Type42 子集,而 ttconv 处理嵌入。
  2. ttconv_with_buffer是对原始ttconv_with_file的修改;它允许输入文件缓冲区而不是文件路径。

你可能会想说

“好吧,ttconv_with_buffer肯定修改错误了,这很明显。”

从逻辑上讲,是的。ttconv被设计为使用文件路径而不是文件对象(缓冲区),并且修改一个编写于1998年的代码库比我们预期的要痛苦得多。

到了某个时刻,我的一个导师决定用 Python 实现所有内容!#

他确实这么做了,但是将它投入生产/或修复ttconv嵌入的工作量⋙ 仅仅继续使用第二种方法。那个该死的鸵鸟确实帮助我们摆脱了调试地狱。🙃

字体回退 - 初始步骤#

最后,我们开始着手夏季的第二个子目标:字体回退

为了说明目前的情况

  1. 用户要求 Matplotlib 使用某些字体系列,由
matplotlib.rcParams["font-family"] = ["list", "of", "font", "families"]
  1. 此列表用于搜索用户系统上可用的字体。
  2. 但是,在 Matplotlib 的当前(和先前)版本中

    一旦通过迭代字体系列找到字体,所有文本都将由且仅由该字体呈现。

你可以立即看到这种方法的问题;对每个字符使用相同的字体将不会呈现该字体中不存在的任何字形,而是会输出一个称为“豆腐”的方形矩形(阅读第一行此处)。

这正是第一个里程碑!也就是说,解析字体系列的整个列表以获得多字体界面的中间表示。

不要崩溃,有很多东西岌岌可危!#

想象一下,如果你有能力更改 Python 标准库的内部函数,而无需咨询任何人。假设你想通过挂钩和更改来编写一个解决方案,例如通过返回更改str("dumb")的实现

>>> str("dumb")
["d", "u", "m", "b"]

非常“愚蠢”,对吧?xD

对于你的用例,它可能运行良好,但它也意味着破坏整个 Python 用户群的工作流程,更不用说依赖于原始功能的 1000000+ 库了。

同样,Matplotlib 有一个称为findfont(prop: str)的公共 API,它在给定字符串(或FontProperties)时,会找到与系统中给定属性最匹配的字体。

它在整个库中使用,以及在其他多个地方使用,包括下游库。由于我当时很天真,所以我更改了此函数签名并提交了PR。🥲

与我的导师就此进行了深入的讨论,很快提出了另一个 PR,它根本没有触及findfont API。


最后需要指出的一点是:即使我们完成了第一个里程碑,我们也还没有完成,因为这仅仅是解析整个列表以获取多个字体…

我们仍然需要将库的内部实现从字体优先迁移到文本优先

但那是以后的事情,现在:伯尼·桑德斯配上“我再次感谢您阅读。”的文字

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