matplotlib 中文字体配置指南

matplotlib 默认不支持中文,即使安装了 fonts-noto-cjk 也可能失败。本文记录了完整的排查过程,以及用 fonttools 提取 SC 字体的解决方案。

在使用 matplotlib 绘制包含中文的图表时,经常会遇到中文显示为方块或乱码的问题。本文记录了完整的排查过程和解决方案。

问题原因

matplotlib 默认字体 DejaVu Sans 不包含中文字形。即使系统已安装 fonts-noto-cjk,也可能无法正常显示中文。

根本原因在于:fonts-noto-cjk 安装的字体是 .ttc(TrueType Collection)格式,这种格式将多个字体打包在一个文件中(SC、TC、JP、KR 四个版本)。但 matplotlib 在解析 .ttc 文件时只能读取第一个子字体(JP),导致 SC(简体中文)无法被识别。

验证方式:

import matplotlib.font_manager as fm

cjk_fonts = [f.name for f in fm.fontManager.ttflist if 'noto' in f.name.lower()]
print(set(cjk_fonts))
# 输出:{'Noto Sans CJK JP', 'Noto Serif CJK JP'}  ← 只有 JP,没有 SC

解决方案:用 fonttools 提取 SC 字体

最彻底的解决方式是从 .ttc 文件中提取出独立的 SC 字体文件。

Step 1:安装 fonttools

pip install fonttools

Step 2:查看 ttc 中的子字体索引

from fontTools import ttLib

ttc = ttLib.TTCollection('/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc')
for i, font in enumerate(ttc.fonts):
    print(i, font['name'].getDebugName(4))

找到 Noto Sans CJK SC 对应的索引编号。

Step 3:提取并保存 SC 字体

from fontTools import ttLib

ttc = ttLib.TTCollection('/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc')
ttc.fonts[1].save('/usr/share/fonts/NotoSansSC.otf')  # index 按上一步结果修改

保存到 /usr/share/fonts/ 后,matplotlib 会在下次启动时自动扫描到该字体,无需手动 addfont()

Step 4:清除缓存并验证

import matplotlib
import matplotlib.font_manager as fm
import shutil

# 清除旧缓存
shutil.rmtree(matplotlib.get_cachedir(), ignore_errors=True)

# 重新加载字体列表
fm._load_fontmanager(try_read_cache=False)

# 确认 SC 字体已出现
fonts = sorted(set(f.name for f in fm.fontManager.ttflist if 'noto' in f.name.lower()))
print(fonts)  # 应包含 Noto Sans CJK SC

Step 5:在代码中设置字体

在每个 notebook 或脚本开头加入:

import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False  # 防止负号显示为方块

备选方案一:直接使用 JP 字体

如果不想折腾,JP 字体同样包含所有常用汉字(日语汉字与中文汉字共用相同的 Unicode 码位),在图表中显示中文完全没问题,只是字形风格略有差异。

plt.rcParams['font.sans-serif'] = ['Noto Sans CJK JP', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

备选方案二:从 Google Fonts 下载独立字体

前往 Google Fonts – Noto Sans SC 下载,选择 static/NotoSansSC-Regular.ttf(注意不要用 VariableFont 版本,matplotlib 对可变字体支持不佳)。

将文件放入 /usr/share/fonts/ 后清除缓存,字体名为 Noto Sans SC(无 CJK 后缀):

plt.rcParams['font.sans-serif'] = ['Noto Sans SC', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

小结

方案 优点 缺点
fonttools 提取 SC 利用已有字体,无需下载 需要安装 fonttools
直接用 JP 字体 零配置,开箱即用 字形为日式风格
Google Fonts 下载 字体最标准 需要手动下载上传

对于日常数据可视化,三种方案都可以正常使用。

Leave a Reply

Your email address will not be published. Required fields are marked *