
“Matplotlib,我希望在文本之间插入多个汉字。”
假设您要求 Matplotlib 渲染一个包含在英文文本之间插入多个汉字的标签的绘图。
或者反过来说,假设您使用 Matplotlib 的中文字体,但在其间有英文文本(这很常见)。
假设:中文字体没有这些英文字形,反之亦然。
通过这篇简短的文章,我将讨论 Matplotlib 中从字体优先到文本优先方法的迁移过程,理想情况下,这可以解决上述问题。
拥有字体?#
逻辑上,解决这个问题的第一步是询问您是否拥有多种字体,对吧?
Matplotlib 不附带CJK(中日韩)字体,理想情况下,这些字体包含这些汉字字形。但是,它确实尝试使用它附带的默认字体涵盖大多数情况。
因此,如果您没有字体来渲染您的汉字,请继续安装一个!Matplotlib 将找到您安装的字体(在重建缓存之后)。
解析字体#
这就是事情变得有趣的地方,也是我的上一篇文章所要讨论的…
解析整个字体族以获取给定字体属性的多个字体
FT2Font 魔法!#
让您了解 Matplotlib 的工作原理
- 在绘制时选择单个字体(已修复:参考上一篇文章)
- 文档中显示的每个字符仅由该字体渲染(部分修复:参考本文)
FT2Font 是一个 matplotlib 到字体的模块,它提供高级 Python API 来与单个字体的操作(如读取/绘制/提取等)进行交互。
该模块是用 C++ 编写的,需要在其周围添加包装器才能使用 Python 的 C-API 转换为Python 扩展。
它允许我们直接从 Python 使用 C++ 函数!
因此,无论您在库中看到哪里使用字体(库是指可读的 Python 代码库 XD),您都可以推导出这一点
FT2Font === SingleFont
但是,现在情况有点不同了…
设计多字体系统#
FT2Font 本身基本上是围绕一个名为FreeType的库的包装器,它是一个免费提供的用于渲染字体的软件库。

在我的初始提案中…在查看 FT2Font 的结构时,我发现
Oh, looks like all we need are Faces!
如果您不知道面/字形/连字是什么,请查看为什么文本讨厌你。我可以保证您一定会喜欢一些关于文本渲染为何困难的真实案例。🥲
无论如何,如果您已经知道什么是面,您可能会想到
如果我们已经拥有来自多个字体所需的所有面(假设我们创建了 FT2Font 的子类…它只跟踪其字体的面),我们应该能够从该父 FT2Font 渲染所有内容,对吧?
正如我后来在实现此设计时发现段错误时发现的那样
Each FT2Font is linked to a single FT_Library object!
如果您尝试从不同的 FT2Font 对象加载面/字形/字符(基本上任何内容)…您将遇到严重的段错误。(因为链接到FT_Library
的一个对象无法真正访问具有自身FT_Library
的另一个对象)
// face is linked to FT2Font; which is
// linked to a single FT_Library object
FT_Face face = this->get_face();
FT_Get_Glyph(face->glyph, &placeholder); // works like a charm
// somehow get another FT2Font's face
FT_Face family_face = this->get_family_member()->get_face();
FT_Get_Glyph(family_face->glyph, &placeholder); // segfaults!
意识到这一点花费了相当长的时间!之后,我很快想出了一个递归方法,其中我们
- 在 Python 中创建一个 FT2Font 对象列表,并将其传递给 FT2Font
- FT2Font 将通过以下方式保存其字体的指针:
std::vector<FT2Font *> fallback_list
- 查找我们想要的字符是否在当前字体中可用
- 如果字符可用,则使用该 FT2Font 渲染该字符
- 如果找不到字符,则再次转到步骤 3,但现在遍历
fallback_list
- 就是这样!
上面代码片段的快速修改^
bool ft_get_glyph(FT_Glyph &placeholder) {
FT_Error not_found = FT_Get_Glyph(this->get_face(), &placeholder);
if (not_found) return False;
else return True;
}
// within driver code
for (uint i=0; i<fallback_list.size(); i++) {
// iterate through all FT2Font objects
bool was_found = fallback_list[i]->ft_get_glyph(placeholder);
if (was_found) break;
}
有了围绕此实现的想法,Agg 后端能够使用多个字体渲染文档(通过 GUI 或 PNG)!

Python C-API 起初很难!#
我在 Python C-API 的参数文档上花费了几天时间,一开始很难获得您需要的内容,说实话。
但是,在 GSoC 社区中一些很棒的人(@srijan-paul,@atharvaraykar)和很棒的导师的帮助下,障碍消失了!
那么我们完成了?#
哦,不。XD
Agg 后端工作正常,但是使用多个字体生成 PDF/PS/SVG 则是另一回事!我想我会留到以后再处理。
