备注
前往结尾 下载完整示例代码。
Matplotlib 中的字体#
Matplotlib 需要字体来支持其文本引擎的工作,其中一些字体随安装包一起提供。默认字体是 DejaVu Sans,它涵盖了大多数欧洲书写系统。然而,用户可以配置默认字体,并提供自己的自定义字体。详情请参阅 自定义文本属性,特别是对于 DejaVu Sans 不支持的字形,请参阅 带有非拉丁字形的文本。
Matplotlib 还提供了一个选项,可以将文本渲染卸载到 TeX 引擎(usetex=True),参见 使用 LaTeX 进行文本渲染。
PDF 和 PostScript 中的字体#
字体在计算中有着悠久(且有时不兼容)的历史,导致不同平台支持不同类型的字体。实际上,Matplotlib 支持三种字体规范(除了 pdf '核心字体',这些将在本指南后面解释):
类型 1 (PDF) |
类型 3 (PDF/PS) |
TrueType (PDF) |
|---|---|---|
其中一种最古老的类型,由Adobe引入 |
在介绍方面类似于类型1 |
比以前的类型更新的,今天常用的,由苹果公司引入的 |
PostScript 的受限子集,字符串是字节码 |
完整的 PostScript 语言,允许嵌入任意代码(理论上,甚至在光栅化时渲染分形!) |
包含一个可以执行代码的虚拟机! |
这些字体支持字体提示 |
不支持字体提示 |
支持提示(虚拟机处理“提示”) |
通过 Matplotlib 非子集化 |
通过外部模块 ttconv 进行子集化 |
通过外部模块 fontTools 进行子集化 |
备注
Adobe 在2023年1月禁用了使用 Type 1 字体的创作支持。
Matplotlib 支持的其他字体规范:
Type 42 字体 (PS):
TrueType 字体的 PostScript 封装
42 是 生命、宇宙以及一切的答案!
Matplotlib 使用外部库 fontTools 来子集化这些类型的字体
OpenType 字体:
OpenType 是一种数字字体的新标准,由 Adobe 和 Microsoft 联合开发。
通常包含一个更大的字符集!
对 Matplotlib 的有限支持
字体子集化#
PDF 和 PostScript 格式支持在文件中嵌入字体,使得显示程序能够正确渲染文本,不受查看者计算机上安装的字体影响,也不需要预先光栅化文本。这确保了如果输出被缩放或调整大小,文本不会变得像素化。然而,在文件中嵌入完整字体可能会导致输出文件变大,特别是对于支持 CJK(中文/日文/韩文)等包含许多字形的字体。
解决这个问题的方法是对文档中使用的字体进行子集化,并且只嵌入实际使用的字形。这样可以同时获得矢量文本和小文件大小。计算所需的字体子集并编写新的(缩减的)字体都是复杂的问题,因此 Matplotlib 依赖于 fontTools 和一个 ttconv 的供应商分支。
目前,Type 3、Type 42 和 TrueType 字体被子集化。Type 1 字体则不被子集化。
核心字体#
除了嵌入字体的能力外,作为 PostScript 和 PDF 规范 的一部分,有 14 种核心字体是符合规范的查看器必须确保可用的。如果你将文档限制为仅使用这些字体,你就不需要在文档中嵌入任何字体信息,但仍然可以获得矢量文本。
这对于生成 真正轻量级 的文档特别有帮助:
# trigger core fonts for PDF backend
plt.rcParams["pdf.use14corefonts"] = True
# trigger core fonts for PS backend
plt.rcParams["ps.useafm"] = True
chars = "AFM ftw!"
fig, ax = plt.subplots()
ax.text(0.5, 0.5, chars)
fig.savefig("AFM_PDF.pdf", format="pdf")
fig.savefig("AFM_PS.ps", format="ps")
SVG 中的字体#
文本可以通过 rcParams["svg.fonttype"] (default: 'path') 控制的两种方式输出为 SVG:
作为SVG中的路径 (
'path')在SVG中作为字符串,并在元素上应用字体样式(
'none')
当通过 'path' 保存时,Matplotlib 将计算用作矢量路径的字形路径,并将这些路径写入输出。这样做的好处是 SVG 在所有计算机上的外观将保持一致,无论安装了什么字体。然而,文本在保存后将无法编辑。相比之下,使用 'none' 保存将生成较小的文件,并且文本将直接显示在标记中。然而,外观可能会根据 SVG 查看器和可用字体而有所不同。
Agg 中的字体#
要通过 Agg 输出到光栅格式,Matplotlib 依赖于 FreeType。由于字形的确切渲染在不同 FreeType 版本之间有所变化,我们为图像比较测试固定了一个特定版本。
Matplotlib 如何选择字体#
在 Matplotlib 中,使用字体内部是一个三步过程:
创建了一个
FontProperties对象(显式或隐式)基于
FontProperties对象,FontManager的方法用于选择 Matplotlib 所知的“最佳”字体(除了 SVG 的'none'模式)。字体对象的 Python 代理被后端代码用于渲染文本 -- 具体细节取决于通过
font_manager.get_font的后端。
选择“最佳”字体的算法是根据 CSS1 规范 修改的版本,该规范被网络浏览器使用。该算法考虑了字体族名称(例如“Arial”、“Noto Sans CJK”、“Hack”等)、大小、样式和权重。除了直接映射到字体的族名称外,还有五个“通用字体族名称”(serif、monospace、fantasy、cursive 和 sans-serif),这些名称将在内部映射到一组字体中的任何一个。
目前,执行步骤2的公共API是`.FontManager.findfont`(该方法在全局`.FontManager`实例上被模块级别的`.font_manager.findfont`别名),它只会找到一个字体并返回该字体在文件系统中的绝对路径。
字体回退#
没有一种字体能够覆盖整个Unicode空间,因此用户可能需要混合使用无法从单一字体中满足的字形。虽然在图表中可以在不同的`.Text`实例中使用多种字体,但之前无法在同一个`.Text`实例中使用多种字体(就像网页浏览器那样)。从Matplotlib 3.6开始,Agg、SVG、PDF和PS后端将在单个`.Text`实例中通过多种字体进行“回退”:
fig, ax = plt.subplots()
ax.text(
.5, .5, "There are 几个汉字 in between!",
family=['DejaVu Sans', 'Noto Sans CJK JP', 'Noto Sans TC'],
ha='center'
)
在内部,这是通过将“字体族”设置在 FontProperties 对象上为字体族列表来实现的。一个(目前)私有的API提取所有找到的字体的路径列表,然后构建一个单一的 ft2font.FT2Font 对象,该对象知道所有字体。字符串的每个字形都使用列表中包含该字形的第一个字体进行渲染。
这项工作的大部分是由Aitik Gupta在2021年Google Summer of Code的支持下完成的。