Matplotlib PDF 导出使用错误的字体

18 投票
4 回答
12438 浏览
提问于 2025-04-15 22:31

我想为一个演示文稿生成高质量的图表。我正在使用Python的matplotlib来制作这些图形。不过,导出为PDF时,似乎我的字体设置没有被识别。

我尝试过通过传递一个FontProperties对象给文本绘制函数来设置字体,也尝试过全局设置字体选项。为了说明问题,这里有一个简单的示例:

import scipy
import matplotlib
matplotlib.use('cairo')
import matplotlib.pylab as pylab
import matplotlib.font_manager as fm

data = scipy.arange(5)

for font in ['Helvetica', 'Gill Sans']:
    fig = pylab.figure()
    ax = fig.add_subplot(111)
    ax.bar(data, data)
    ax.set_xticks(data)
    ax.set_xticklabels(data, fontproperties = fm.FontProperties(family = font))
    pylab.savefig('foo-%s.pdf' % font)

在这两种情况下,生成的输出是一样的,都是使用Helvetica字体(而且我确实安装了这两种字体)。

为了确认,以下方法也没有帮助:

matplotlib.rc('font', family = 'Gill Sans')

最后,如果我更换后端,使用原生查看器:

matplotlib.use('MacOSX')

我确实能在查看器界面中看到正确的字体——但这仅限于查看器的界面。PDF输出又一次出错了。

为了确认——我确实可以设置其他字体——但只能是其他类型的字体系列:我可以设置serif字体、fantasy字体或monospace字体。但所有的无衬线字体似乎都默认使用Helvetica。

4 个回答

0

如果你是为了寻找非cairo后端的解决方案而来到这里,这里有一些补充信息。

matplotlib的pdf后端目前还不支持真正的字体集合(也就是保存为.ttc文件的字体)。你可以查看这个问题

现在推荐的解决办法是从.ttc文件中提取你需要的字体,然后把它保存为.ttf文件。接着按照Konrad Rudolph的方式使用这个字体。

你可以使用一个叫做fonttools的Python包来做到这一点:

font = TTFont("/System/Library/Fonts/Helvetica.ttc", fontNumber=0)
font.save("Helvetica-regular.ttf")

据我所知,无法通过将新生成的.ttf文件路径传递给rc来让这个设置变成“全局”的。如果你真的很着急,可以尝试将一个.ttc文件中的所有字体提取到单独的.ttf文件中,然后卸载.ttc文件,单独安装.ttf文件。为了让提取的字体和原始的.ttc字体并排使用,你需要使用像FontForge这样的工具来更改字体名称。不过我还没有测试过这个方法。

8

基本上,@Jouni 的回答是正确的,但因为我在实现时遇到了一些问题,所以这里分享一下我的最终解决方案:

#!/usr/bin/env python2.6

import scipy
import matplotlib
matplotlib.use('cairo')
import matplotlib.pylab as pylab
import matplotlib.font_manager as fm

font = fm.FontProperties(
        family = 'Gill Sans', fname = '/Library/Fonts/GillSans.ttc')

data = scipy.arange(5)
fig = pylab.figure()
ax = fig.add_subplot(111)
ax.bar(data, data)
ax.set_yticklabels(ax.get_yticks(), fontproperties = font)
ax.set_xticklabels(ax.get_xticks(), fontproperties = font)
pylab.savefig('foo.pdf')

注意,字体必须通过 fontproperties 这个键来明确设置。显然,fname 属性没有 rc 设置(至少我没有找到)。

在创建 font 的时候,给 family 这个键并不是绝对必要的,PDF 后端会忽略它。

这段代码只适用于 cairo 后端,使用 MacOSX 是不行的。

3

这里提到的“family”参数和相应的rc参数其实并不是用来指定字体名称的,但可以这样使用。它有一个(可以说是复杂的)类似CSS的字体选择系统,帮助同一个脚本在不同的电脑上运行时,选择最接近的可用字体。通常推荐的使用方法是,比如说要用Gill Sans字体,可以把它放在rc参数font.sans-serif的值前面(具体可以参考这个示例rc文件),然后把font.family设置为sans-serif。

不过,如果字体管理器因为某些不明的原因决定Gill Sans不是最接近你要求的字体,这就会让人感到烦恼。为了绕过这种字体选择的逻辑,可以使用FontProperties(fname='/path/to/font.ttf')(具体可以查看这个文档)。

在你的情况下,我猜测MacOSX的后端通过操作系统的机制来使用字体,因此自动支持各种字体,但pdf后端有自己的一套字体支持代码,可能不支持你版本的Gill Sans。

撰写回答