无法在 matplotlib 中使用自定义字体

Vib*_*bex 2 python fonts matplotlib python-3.x

我在 Windows 上的 Python 3.7.3 上获取与 Matplotlib(版本 3.1.1)一起使用的自定义字体时遇到问题。标准方式使用

import matplotlib as mpl
mpl.rcParams['font.family'] = 'sans-serif'
mpl.rcParams['font.sans-serif'] = [FONTNAME] 
Run Code Online (Sandbox Code Playgroud)

适用于系统上预安装的一系列字体。我最近手动安装了 Lato 系列字体。但是,当我使用“Lato”作为 FONTNAME 时,Matplotlib 默认返回 Deja Vu Sans,甚至不会抛出任何错误。我还使用重建字体缓存

mpl.font_manager._rebuild()
Run Code Online (Sandbox Code Playgroud)

当我运行时,现在会出现几种名为“Lato”的字体

mpl.font_manager.fontManager.ttflist
Run Code Online (Sandbox Code Playgroud)

例如

 <Font 'Lato' (Lato-Semibold.ttf) normal normal semibold normal>,
 <Font 'Lato' (Lato-Thin.ttf) normal normal 400 normal>,
...
Run Code Online (Sandbox Code Playgroud)

然而这些情节看起来仍然像是使用 Deja Vu Sans 一样。我已经查看了所有内容,但找不到解决此问题的方法。

ama*_*anb 8

matplotlib 绘图样式中的字体属性由FontManager类管理,并使用FontProperties类指定。为了获取这些字体属性,matplotlib 在内部使用 FontManager 类的实例来调用一个函数findfont(),该函数搜索字体并返回本地或系统字体路径中与 FontProperties 实例中的字体规范匹配的最佳 TrueType (TTF) 字体文件。规范中的默认后备字体是 DejaVu Sans。字体系列可以设置为以下参数之一:“serif”、“sans-serif”、“cursive”、“fantasy”或“monospace”。可以找到任何字体系列的 TTF 文件位置,如下所示:

In [1]: from matplotlib.font_manager import findfont, FontProperties

In [2]: font = findfont(FontProperties(family=['sans-serif']))

In [3]: font
Out[3]: 'C:\\Users\\xxxxxx\\Anaconda3\\envs\\py3.7.4\\lib\\site-packages\\matplotlib\\mpl-data\\fonts\\ttf\\DejaVuSans.ttf'
Run Code Online (Sandbox Code Playgroud)

“等宽”系列的另一个例子:

In [7]: font = findfont(FontProperties(family=['monospace']))

In [8]: font
Out[8]: 'C:\\Users\\xxxxxx\\Anaconda3\\envs\\py3.7.4\\lib\\site-packages\\matplotlib\\mpl-data\\fonts\\ttf\\DejaVuSansMono.ttf'
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,上面的 sans-serif 系列指向默认的 DejaVuSans TTF 文件,因为我尚未将其设置FONTNAME为其他字体,例如也属于 sans-serif 系列的“Lato”字体。

在更改 之前FONTNAME,首先了解字体搜索是如何发生的非常重要。现在,字体搜索是一项昂贵的任务,为了使后续请求高效,字体信息缓存在 JSON 文件中。您可以在该类的源代码中找到证据FontManager。对于 Windows,此文件位于:%userprofile%\.matplotlib。有关更多详细信息,请参阅类文档的注释部分FontManager

这将执行最近邻搜索。每种字体都会被赋予与目标字体属性的相似度分数。返回得分最高的第一个字体。如果未找到低于特定阈值的匹配项,则返回默认字体(通常为 DejaVu Sans)。

结果被缓存,因此后续查找不必执行 O(n) 最近邻搜索。

在我的计算机(Windows 10)上,我有两个缓存文件:fontlist-v300fontlist-v310。如果您检查任何这些文件的内容,它会显示字体及其属性的列表,例如 TTF 文件位置、样式、粗细等。观察默认的系列键:

"defaultFamily": {
    "ttf": "DejaVu Sans",
    "afm": "Helvetica"
  }
Run Code Online (Sandbox Code Playgroud)

此时,我们了解到该字体将以 DejaVu Sans 格式显示。这在情节标题中最为明显:

In [1]: import matplotlib as mpl
   ...: mpl.rcParams['font.family'] = 'sans-serif'
   ...: import matplotlib.pyplot as plt
   ...: plt.plot(range(0,50,10))
   ...: plt.title('Font test', size=32)
   ...: plt.show()
Run Code Online (Sandbox Code Playgroud)

绘图(默认字体)

默认字体后备

findfont()函数将始终查找缓存文件(如果不存在则创建一个),如果我在计算机上安装一种新字体,更新该缓存文件很重要,否则它将继续显示后备字体(与默认值相同)。在继续下一步之前,请确保 Lato 字体安装正确。该字体应该在控制面板的字体下可用。

现在正确安装了 Lato 字体,删除缓存文件并将 sans-serif 字体设置为 Lato:

In [4]: import matplotlib as mpl^M
   ...: mpl.rcParams['font.family'] = 'sans-serif'
   ...: mpl.rcParams['font.sans-serif'] = 'Lato'
   ...: import matplotlib.pyplot as plt
   ...: plt.plot(range(0,50,10))
   ...: plt.title('Font test', size=32)
   ...: plt.show()
Run Code Online (Sandbox Code Playgroud)

情节(Lato无衬线字体)

在此输入图像描述

您还将观察到已创建一个新的缓存文件。上面的代码片段重新构建了缓存文件,该文件现在还包含 Lato 字体的信息。同样,您可以在文本编辑器中打开此缓存文件以验证其存在。现在让我们验证 sans-serif 系列的 TTF 文件路径:

In [4]: from matplotlib.font_manager import findfont, FontProperties

In [5]: font = findfont(FontProperties(family=['sans-serif']))

In [6]: font
Out[6]: 'C:\\Users\\xxxxx\\AppData\\Local\\Microsoft\\Windows\\Fonts\\Lato-Thin.ttf'
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,sans-serif 系列现在指向 Lato-Thin TTF 文件。

将字体样式更改为斜体还需要先删除缓存文件:

In [3]: In [4]: import matplotlib as mpl
   ...:    ...: mpl.rcParams['font.family'] = 'sans-serif'
   ...:    ...: mpl.rcParams['font.sans-serif'] = 'Lato'
   ...:    ...: mpl.rcParams['font.style'] = 'italic'
   ...:    ...: import matplotlib.pyplot as plt
   ...:    ...: plt.plot(range(0,50,10))
   ...:    ...: plt.title('Font test', size=32)
   ...:    ...: plt.show()

In [4]: from matplotlib.font_manager import findfont, FontProperties

In [5]: font = findfont(FontProperties(family=['sans-serif']))

In [6]: font
Out[6]: 'C:\\Users\\xxxxxx\\AppData\\Local\\Microsoft\\Windows\\Fonts\\Lato-HairlineItalic.ttf'
Run Code Online (Sandbox Code Playgroud)

阴谋

在此输入图像描述

注意:所有步骤都是在 IPython 控制台上执行的,可能需要重新启动 IPython 会话才能使更改生效。