Matplotlib PDF导出使用了错误的字体。

18

我想为演示文稿生成高质量的图表。我正在使用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')

虽然我成功地在查看器 GUI 中显示了正确的字体,但是 PDF 输出仍然不正确。

确切地说,我可以设置其他字体,但只能设置其他类别的字体系列:我可以设置有衬线幻想等宽字体。但所有无衬线字体似乎都默认为 Helvetica。

4个回答

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')docstring)。
在您的情况下,我怀疑MacOSX后端通过操作系统的机制使用字体,因此自动支持各种字体,但pdf后端具有自己的字体支持代码,不支持您的Gill Sans版本。

你说,“'family'参数并不是用来指定字体名称的” - 抱歉,但是文档表明:“这些项可能包括通用字体族名称[...]. 在这种情况下,将查找要使用的实际字体…”(我强调)。此外,在GUI中它可以工作。无论如何,我会尝试您提出的解决方案。谢谢。 - Konrad Rudolph
1
通过使用 fname = … 我在 backend_pdf.py 的第776行中调用 embedTTFType3 函数并调用 savefig 时出现了一个 RuntimeError “TrueType font is missing table”。我对 TTF 和 PDF 不太熟悉,但是我已经通过其他方式将 Gill Sans 嵌入到 PDF 中,所以我不确定这里有什么问题。 - Konrad Rudolph
你关于使用真实字体名称作为family参数是正确的。错误信息听起来像是一个bug。请在Sourceforge跟踪器上提交它,提到确切的字体、matplotlib版本和完整的回溯信息。 - Jouni K. Seppänen
好的,我会这样做。再次感谢。 - Konrad Rudolph
好的,我刚刚注意到我只是在MacOSX后端中尝试了您的“fname”解决方案。使用cairo后端,一切正常。我会发布一个可工作的代码示例。 - Konrad Rudolph

0

如果您来到这里是为了非cairo后端,那么这是对上面答案的补充。

Matplotlib的pdf后端尚不支持真正的字体集(保存为.ttc文件)。请参见this issue

目前建议的解决方法是从.ttc文件中提取所需字体并将其保存为.ttf文件。然后按照Konrad Rudolph所描述的方式使用该字体。

您可以使用Python包fonttools来实现此操作:

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

据我所见,通过将路径传递给 rc 来使此设置“全局化”似乎是不可能的。如果你真的很绝望,可以尝试从 .ttc 中提取所有字体到单独的 .ttf 文件中,卸载 .ttc 并分别安装 ttf。要让提取的字体与 .ttc 中的原始字体并排显示,需要使用 FontForge 等工具更改字体名称。不过我还没有测试过这种方法。

0

检查一下您是否正在使用LaTeX渲染文本,即text.usetex是否设置为True。因为LaTeX渲染仅支持少数字体,它很大程度上会忽略/覆盖其他字体设置,这可能是原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接