问题来源
如何在 matplotlib
中使用中文字体是老问题了,相关文章非常多。
前几天有人问我 如何知道中文字体名称和实际文件的对应关系
时,才想起来原来没思考过这个问题,只能让他记住字体与文件的对应关系或者去 fonts
目录查看。
**难道真的就没有稍微自动化、智能化的查看支持matplotlib
的字体名称与文件的对应关系的方法? **
问题解决步骤
matplotlib
与字体相关的模块是 font_manager
,观察源码 font_manager.py
可知:
1. matplotlib支持哪种类型的字体?
def get_fontext_synonyms(fontext):
"""
Return a list of file extensions extensions that are synonyms for
the given file extension *fileext*.
"""
return {
'afm': ['afm'],
'otf': ['otf', 'ttc', 'ttf'],
'ttc': ['otf', 'ttc', 'ttf'],
'ttf': ['otf', 'ttc', 'ttf'],
}[fontext]
由此可知 matplotlib
只支持 ttf
和 afm
字体。
- ‘ttf’: TrueType and OpenType fonts (.ttf, .ttc, .otf)
- ‘afm’: Adobe Font Metrics (.afm)
2. matplotlib如何查找系统字体?
findSystemFonts
模块函数的作用是查找系统字体。
def findSystemFonts(fontpaths=None, fontext='ttf'): -->list
参数 fontext
默认值为 'ttf'
,另外还支持值 'afm'
。
参数 fontpaths
默认值是 None
。
对于 fontpaths
有两种种情况。
fontpaths
为 None
函数会判断操作系统,如果是Windows,调用函数 win32InstalledFonts
,如果不是Windows,调用函数 get_fontconfig_fonts
。
对于Windows,函数 win32InstalledFonts
到以下路径查找。
# OS Font paths
MSFolders = \
r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
MSFontDirectories = [
r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts',
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Fonts']
MSUserFontDirectories = [
str(Path.home() / 'AppData/Local/Microsoft/Windows/Fonts'),
str(Path.home() / 'AppData/Roaming/Microsoft/Windows/Fonts'),
]
对于 非Windows
操作系统,函数 get_fontconfig_fonts
主要依托 fontconfig
包(即 fc- list
命令)查找字体,要求 fontconfig>=2.7
。
fontpaths
不为 None
当 fontpaths
不为 None
时,通过 list_fonts()
函数查找字体。
**list_fonts()
函数其实一个通用的按路径、扩展名递归遍历文件路径的函数。 **
def list_fonts(directory, extensions):
"""
Return a list of all fonts matching any of the extensions, found
recursively under the directory.
"""
extensions = ["." + ext for ext in extensions]
return [os.path.join(dirpath, filename)
# os.walk ignores access errors, unlike Path.glob.
for dirpath, _, filenames in os.walk(directory)
for filename in filenames
if Path(filename).suffix.lower() in extensions]
3.matplotlib如何查找matplotlib自带字体?
安装 matplotlib
时,会在 site-packages\matplotlib\mpl-data\fonts
目录放置一系列字体。
通过源码可知 fonts
目录下有 'ttf', 'afm', 'pdfcorefonts'
3个子目录。
paths = [cbook._get_data_path('fonts', subdir)
for subdir in ['ttf', 'afm', 'pdfcorefonts']]
In [1]: import matplotlib.cbook as cbook
In [2]: paths = [cbook._get_data_path('fonts', subdir) for subdir in ['ttf', 'afm', 'pdfcorefonts']]
In [3]: paths
Out[4]:
[WindowsPath('c:/users/administrator/appdata/local/programs/python/python37/lib/site-packages/matplotlib/mpl-data/fonts/ttf'),
WindowsPath('c:/users/administrator/appdata/local/programs/python/python37/lib/site-packages/matplotlib/mpl-data/fonts/afm'),
WindowsPath('c:/users/administrator/appdata/local/programs/python/python37/lib/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts')]
4.matplotlib如何生成字体列表?
FontManager
类是 matplotlib
管理字体的重要类,是一个单例类。 FontManager
实例在构造后会创建一个
ttf
字体列表和一个 afm
字体列表,并会缓存他们的字体属性。
下面简单看看 ttflist
、 afmlist
列表的元素的属性。
In [1]: import matplotlib.font_manager as mf
In [2]: ttflist=mf.FontManager().ttflist
In [3]: vars(ttflist[0])
Out[3]:
{
'fname': 'c:\\users\\administrator\\appdata\\local\\programs\\python\\python37\\lib\\site-packages\\matplotlib\\mpl-data\\fonts\\ttf\\DejaVuSansMono-BoldOblique.ttf',
'name': 'DejaVu Sans Mono',
'style': 'oblique',
'variant': 'normal',
'weight': 700,
'stretch': 'normal',
'size': 'scalable'}
In [4]: len(ttflist)
Out[4]: 252
In [5]: afmlist=mf.FontManager().afmlist
In [6]: vars(afmlist[0])
Out[6]:
{
'fname': 'c:\\users\\administrator\\appdata\\local\\programs\\python\\python37\\lib\\site-packages\\matplotlib\\mpl-data\\fonts\\afm\\pagko8a.afm',
'name': 'ITC Avant Garde Gothic',
'style': 'italic',
'variant': 'normal',
'weight': 'book',
'stretch': 'normal',
'size': 'scalable'}
In [7]: len(afmlist)
Out[7]: 60
5.matplotlib的字体属性缓存在哪里?
前面了解到 matplotlib
会缓存字体属性那在什么位置呢?
根据源码可知,字体缓存的所在目录是 .matplotlib
,缓存文件是 fontlist-v330.json
(文件名与版本有关)。在某些老版本的 matplotlib
中字体缓存文件名是 fontList.cache
。
_fmcache = os.path.join(
mpl.get_cachedir(), 'fontlist-v{}.json'.format(FontManager.__version__))
In [1]: import matplotlib
In [2]: matplotlib.get_cachedir()
Out[2]: 'C:\\Users\\Administrator\\.matplotlib'
6. 怎么知道哪些字体是中文字体?
虽然在前面已经知道 matplotlib
会把系统字体和自带字体信息存放在 ttflist
和 afmlist
中,但是在这些字体信息中也不容易确定哪些是中文字体。
通过资料查找有3种方法:
查看Windows的 fonts
系统目录中显示的字体名称和文件名属性。 这种方法效率太低!
通过注册表项 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts]
查看字体名和字体文件名称映射。这种方法一些系统字体仍然看不到汉字名称。
根据第1种方法想到了去查字体文件的元数据,Python相关的库有两个: fonttools
,作者是老爹Guido的弟弟 Just van Rossum
; TTFQuery
,基于 fonttools
的TTF包,只能查看 TTF
文件而且最后更新日期是2012年。
TTFQery
项目地址 [ https://pypi.org/project/TTFQuery/
](https://pypi.org/project/TTFQuery/)
源码中有些小问题,源码不算复杂,直接修改。
import sys
from fontTools.ttLib import TTFont
from fontTools.ttLib.ttCollection import TTCollection
UNICODE_ENCODINGS = {
0: 'Unicode 1.0 semantics',
1: 'Unicode 1.1 semantics',
2: 'Unicode 1.1 semantics',
3: 'Unicode 2.0 and onwards semantics, Unicode BMP only (cmap subtable formats 0, 4, 6).',
4: 'Unicode 2.0 and onwards semantics, Unicode full repertoire (cmap subtable formats 0, 4, 6, 10, 12).',
5: 'Unicode Variation Sequences (cmap subtable format 14).',
6: 'Unicode Variation Sequences (cmap subtable format 14).'}
WINDOWS_ENCODINGS = {
0