注意
转到末尾 下载完整的示例代码。
Matplotlib 中的字体#
Matplotlib 需要字体才能使用其文本引擎,其中一些字体与安装一起提供。默认字体是 DejaVu Sans,它涵盖了大多数欧洲书写系统。但是,用户可以配置默认字体,并提供自己的自定义字体。有关详细信息,请参见 自定义文本属性,特别是 带有非拉丁字符的文本,用于 DejaVu Sans 不支持的字符。
Matplotlib 还提供了一个选项,可以将文本渲染卸载到 TeX 引擎 (usetex=True
),请参阅 使用 LaTeX 进行文本渲染.
PDF 和 PostScript 中的字体#
字体在计算领域有着悠久(有时不兼容)的历史,导致不同的平台支持不同类型的字体。实际上,Matplotlib 支持三种字体规范(除了稍后在本指南中解释的 pdf“核心字体”)。
Type 1 (PDF) |
Type 3 (PDF/PS) |
TrueType (PDF) |
---|---|---|
最早的类型之一,由 Adobe 引入 |
与 Type 1 在引入方面类似 |
比以前的类型更新,如今广泛使用,由 Apple 引入 |
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 和一个 vendored 的 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 中的字体#
文本可以通过两种方式输出到 SVG,这由 rcParams["svg.fonttype"]
(默认值:'path'
)控制。
作为 SVG 中的路径 (
'path'
)作为 SVG 中的字符串,并在元素上进行字体样式设置 (
'none'
)
当使用 'path'
保存时,Matplotlib 会将用作矢量路径的字形路径计算出来并写入输出。这样做的好处是,SVG 在所有计算机上看起来都一样,与安装的字体无关。但是,文本在保存后将无法编辑。相反,使用 'none'
保存将导致文件更小,并且文本将直接出现在标记中。但是,外观可能会根据 SVG 查看器和可用的字体而有所不同。
Agg 中的字体#
为了通过 Agg 将文本输出到光栅格式,Matplotlib 依赖于 FreeType。由于字形的精确渲染在 FreeType 版本之间有所不同,因此我们在图像比较测试中固定到特定版本。
Matplotlib 如何选择字体#
在内部,在 Matplotlib 中使用字体是一个三步过程
a
FontProperties
对象被创建(显式或隐式)基于
FontProperties
对象,FontManager
上的方法用于选择 Matplotlib 知道的最近的“最佳”字体(除了 SVG 的'none'
模式)。字体的 Python 代理由后端代码用于渲染文本 - 确切的细节取决于后端,通过
font_manager.get_font
。
选择“最佳”字体的算法是 CSS1 规范中指定的算法的修改版本,该规范由 Web 浏览器使用。该算法考虑了字体系列名称(例如“Arial”、“Noto Sans CJK”、“Hack”等)、大小、样式和粗细。除了直接映射到字体的字体系列名称之外,还有五个“通用字体系列名称”(serif、monospace、fantasy、cursive 和 sans-serif),它们将在内部映射到一组字体中的任何一个。
目前,执行步骤 2 的公共 API 是 FontManager.findfont
(以及全局 FontManager
实例上的该方法在模块级别别名为 font_manager.findfont
),它只会找到一个字体并返回文件系统上该字体的绝对路径。
字体回退#
没有一种字体可以覆盖整个 Unicode 空间,因此用户可能需要混合使用无法从单个字体中满足的字形。虽然以前可以在一个 Figure 中使用多个字体,在不同的 Text
实例中,但以前无法在一个 Text
实例中使用多个字体(就像 Web 浏览器一样)。从 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'
)
(Source code
, 2x.png
, png
)
在内部,这是通过将 FontProperties
对象的 "font family" 设置为字体系列列表来实现的。一个(目前)私有的 API 会提取所有找到的字体的路径列表,然后构建一个包含所有字体的单个 ft2font.FT2Font
对象。字符串的每个字形都使用列表中包含该字形的第一个字体进行渲染。
这项工作的大部分是由 Aitik Gupta 完成的,并得到了 Google Summer of Code 2021 的支持。