【小记】 Matplotlib 中设置自定义中文字体的正确姿势

最近做实验涉及到用 Matplotlib 绘制图表,我希望相同的代码能不仅在本地运行,且还能在 Google Colab, Binder 这些线上平台运行。

问题就来了,为了在各个平台上都能绘制中文字符,该如何在 Matplotlib 中使用自定义的字体?(毕竟国外的一些平台不会预装支持中文字符的字体)

网上查了一下,很多文章都是复制粘贴来的,十分误导人,遂决定写下此笔记。

studyAttitude-2024-11-23

1. 开门见山

import matplotlib.pyplot as plt
import matplotlib.font_manager as fm# 自定义字体(OTF, TTF)的路径
FONT_PATH = './SourceHanSerifCN-Regular.otf'# 💡 利用 fontManager 的方法添加字体到内部的字体列表中
fm.fontManager.addfont(FONT_PATH)
# 获得字体属性对象
font_props=fm.FontProperties(fname=FONT_PATH)# 获得字体名
font_name=font_props.get_name()# 优先使用自定义的字体,不满足的则 fallback 到 sans-serif
plt.rcParams['font.family']=[font_name, 'sans-serif']
# (可选)还可以单独设置数学公式字体,这里用 matplotlib 默认的字体
plt.rcParams["mathtext.fontset"]='cm'
# unicode_minus 即采用 Unicode 中的 '−' 字符(U+2212),而不是 ASCII 中的 '-' 字符(U+002D)
# 如果你用的字体没有 U+2212 对应的字形,就需要把这一项设定为 False,让减号用 ASCII 编码。
# plt.rcParams['axes.unicode_minus'] = False

这样配置后,Matplotlib 就可以用自定义字体来渲染文本了。

  • 在线运行测试代码:Binder
  • unicode_minus 选项的文档:Unicode minus — Matplotlib 3.9.2 documentation
  • 测试采用的 Matplotlib 版本:3.8.0, 3.9.2

2. 顺藤摸瓜

2.1. 让 Matplotlib 能找到字体

在渲染图像时,如果涉及到文字部分,Matplotlib 会在内部调用字体管理模块 FontManager 的方法 matplotlib.font_manager.FontManager.findfont 来找到合适的字体对应的路径

  • findfont 源码:matplotlib.font_manager.py#L1238

调用 findfont 时实际上是调用了同模块的 _findfont_cached 方法,从这个方法的源码中可以看到,查找 TTF/OTF 字体时,依赖于 FontManager 对象本身的一个列表 ttflist (#L1417)。

FontManager 对象初始化的时候,程序实际上是扫描了所有的系统字体,把它们添加到 ttflist 中,采用了 FontManager.addfont 方法(#L1048)。

  • 相关文档:How Matplotlib selects fonts — Matplotlib 3.9.2 documentation

💡 因此为了让 Matplotlib 能找到我们自定义的中文字体,要做的事就是调用 FontManager.addfont 这个方法,其把自定义 TTF/OTF 字体路径包装为 FontEntry 对象后添加到 ttflist 这个列表中(#L1057)。

fm.fontManager.addfont(FONT_PATH)

2.2. 获得字体族名

在用字体属性对象 FontProperties 包装了自定义字体后,可以用其 get_name 方法来获得字体名:

# 获得字体属性对象
font_props=fm.FontProperties(fname=FONT_PATH)
# 获得字体名
font_name=font_props.get_name()

get_name (#L672)的方法调用链如下:

FontProperties.get_name -> FontManager.get_font -> FontManager.find_font -> FontManager._findfont_cached

因为已经通过 FontPropertiesfname 参数指定了字体路径,FontManager.find_font 会直接返回这个路径给 get_font (#L1531)方法,get_font 则将字体载入后取得字体的 family_name

因此上面代码片段中 font_name 存储的是自定义字体的 family name(这里是 Source Han Serif CN)。

2.3. 修改 Matplotlib 的字体配置

# 设定字体 family name 
plt.rcParams['font.family']=[font_name, 'sans-serif']
# (可选)单独设定数学字体
plt.rcParams["mathtext.fontset"]='cm'

注:plt.rcParamsmatplotlib.rcParams 是一样的,前者只不过是在 pyplot 模块内导入了 rcParams

这里修改了 Matplotlib 的运行时配置(Runtime Configuration,即 rc)中的相关配置,相关文档已有说明:

  • Configuring the font family — Matplotlib 3.9.2 documentation

3. 拓展:字体回退(Fallback)

Matplotlib 支持字体回退,借此我可以让中文和英文字符在被渲染时分别基于不同的字体:

# 英文字符用 Monospace,中文字符用自定义字体
plt.rcParams['font.family']=['monospace', font_name, 'sans-serif']fig, axe = plt.subplots(figsize=(1, 1))
axe.axis("off")
axe.text(0, 0.5, "I 有 some 水 in that 瓶子.")

遇到 monospace 不支持的 CJK 字符时,会回退(fallback)到第二个字体,即我们自定义的中文字体。字体渲染效果如下:

font_fallback_render_output-2024-11-23

  • 相关文档:Font fallback — Matplotlib 3.9.2 documentation

3.1. 存在的问题

写这篇笔记的过程中我意外发现,文本中包含有数学公式时字体无法正常回退:

axe.text(0, 0.5, "I 有 some 水 in that $bottle$. 哦看哪,这里有一个数学公式:$sin(x)$")

font_cannot_fallback-2024-11-23

注意,这里英文字符仍然是用 monospace 字体渲染的,但是遇到中文字符时却没有回退,因而找不到对应的字形。

火速去 Github 提了个 issue,目前已经被确认为 BUG。期待维护者们能在后续版本中修复,加油!

  • https://github.com/matplotlib/matplotlib/issues/29173

3.2. 权宜之计

既然没法回退,那么只好把我们自定义的字体放在首个位置上了:

# 中文和英文字符都基于自定义字体来渲染
plt.rcParams['font.family']=[font_name, 'sans-serif']

output_with_math_formulas-2024-11-23

4. 写在最后

以上的方式可以直接让当前运行环境中的 Matplotlib 支持中文字符的渲染。

如果你要为少数几条字体单独配置字体,可以在相关的绘制语句上配置相应参数:

font_props=fm.FontProperties(fname=FONT_PATH)
# fontproperties 配置后会覆盖默认的配置
plt.text(0, 0.5, "为什么是 SomeBottle 而不是 SomeBottles ?", fontproperties=font_props)  

那么这篇笔记就是这样,咱们下次再会~ (∠・ω< )⌒★

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/839940.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

南昌航空大学-22207107-胡优乐-Java第二次Blog大作业

前言在这段时间里,我们总共进行了三次大作业练习,基于这三次大作业的体量及设计的知识点,难度分布,我做了以下的总结:1.第四次大作业总共有三道题,分别是:1.校园角色类设计-1;2.设计一个学生类和它的子类-本科生类;3.答题判断程序-4;实际做下来的体会是:(1)第一题难…

团队作业5——测试与发布

团队作业5——测试与发布这个作业属于哪个课程 <计科22级34班>这个作业要求在哪里 <作业要求>这个作业的目标 完成连续七天的项目冲刺GitHub 链接 https://github.com/tangliweiwww/ChatGpt团队 1.团队名称:Elegance 2.团队成员姓名 班级 学号唐立伟(组长) 计科…

Java中定时任务实现方式及源码剖析

概述 在企业级应用开发场景中,定时任务占据着至关重要的地位。比如以下这些场景:用户4个小时以内没有进行任何操作,就自动清除用户会话。 每天晚上凌晨自动拉取另一个业务系统的某部分数据。 每隔15分钟,自动执行一段逻辑,更新某部分数据。类似的场景会频繁出现在我们的日…

001 增肌锻炼

三分化https://www.bilibili.com/video/BV1mY411Y7FR 推拉腿休 推拉腿休...推力日:胸 肩 三头拉力日:背 二头每周同一部位练习2次动作:腹肌https://www.bilibili.com/video/BV1Vh4y197aD https://www.bilibili.com/video/BV1hd4y1f76Z 腹肌激活30S上腹拉伸来自为知笔记(Wiz)…

docker 存储卷实验

需求:创建1个myvolume1的空卷,将其挂载给web1的容器,挂载目录/usr/local/apache2/htdocs 运行两个web2 web3的容器,更新web2中容器内容为 This is a test! 通过宿主机访问web3查看输出内容。docker volume create myvolume1 docker run -d --name web2 -p 82:80 -v myvolum…

让何同学翻车的项目是什么来头?

‍ 背景 最近, B 站知名 UP 主何同学(1207 万粉丝)因涉嫌抄袭开源项目 ASCII generator​ 而引发争议。 视频《我用 36 万行备忘录做了个动画…》从 11 月 15 号发布,获得几百万播放,热度相当高。 他提到团队专门写了一个软件,但实际上该软件基于越南开发者 vietnh1009 在…

selenium模块,web自动化,元素定位

1. 元素定位 查看网页元素 右键-->检查from selenium.webdriver.common.by import By # 元素定位包# 使用 test.find_element(By.XXX) 1)定位元素ID--对应浏览器id# 定位一个元素 a = test.find_element(By.ID, value="wrapper") print(a) # 定位多个元素(返回列…

22207321-王郅坚-BLOG2

前言 这三次题目集涉及了不同的知识点、编程技巧及而算法逻辑,从简单的基础题目逐步过渡到复杂的业务逻辑模拟。三次题目集不仅是单单考核独立的编程任务,其实它们有明确的迭代关系,逐步递进并且不断添加复杂度。题目集1是针对前三次的题目再进行迭代升级,题目集2开始了一个…

ffmpeg 时基转换

1:av_q2d(AVRational a)函数av_q2d(AVRational);该函数负责把AVRational结构转换成double,通过这个函数可以计算出某一帧在视频中的时间位置 timestamp(秒) = pts * av_q2d(st->time_base); 计算视频长度的方法: time(秒) = st->duration * av_q2d(st->…

2024-2025-1学号20241309《计算机基础与程序设计》第九周学习总结

作业信息这个作业属于哪个课程 2024-2025-1-计算机基础与程序设计这个作业要求在哪里 2024-2025-1计算机基础与程序设计第九周作业这个作业的目标|作业正文|2024-2025-1学号20241309《计算机基础与程序设计》第九周学习总结 教材学习内容总结 《计算机科学概论》第十章: (一)…

docker网络互通实验

需求:创建两个自定义容器,分别使用自定义网络,使其互通1. 创建容器 docker run -d --name web1 -p 80:80 httpd 2. 创建网络 docker network create --driver bridge --subnet 192.168.1.0/24 net1 docker network create --driver bridge --subnet 171.16.1.0/24 net2 …

Windows下命令行及Java+Tesseract-OCR对图像进行(字母+数字+中文)识别,亲测可行

第一步:下载Tesseract OCR引擎安装包 访问Tesseract的GitHub发布页面(https://github.com/tesseract-ocr/tesseract)或第三方下载站点(https://digi.bib.uni-mannheim.de/tesseract/),下载适合你操作系统的版本(最新版本)。 推荐使用第三方下载:第二步:详细阐述一下第…