8000 Simplify font_manager font enumeration logic. · matplotlib/matplotlib@fa7f42f · GitHub
[go: up one dir, main page]

Skip to content

Commit fa7f42f

Browse files
committed
Simplify font_manager font enumeration logic.
The font enumeration logic on Windows was relatively difficult to follow, being split over three different functions (win32FontDirectory, _win32RegistryFonts, and win32InstalledFonts), with variable names that did not clearly indicate whether they are directories (such as MSUserFontDirectories) or registry keys (such as MSFontDirectories). Inline most of the logic to _get_win32_installed_fonts, and also get rid of the extension filtering (both here and in get_fontconfig_fonts), as it can be done once for all at the call site (in `findSystemFonts`); also make the helpers return Paths, for simpler manipulation (we still convert to strs at the end).
1 parent 9177280 commit fa7f42f

File tree

3 files changed

+55
-14
lines changed

3 files changed

+55
-14
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
``font_manager`` helpers
2+
~~~~~~~~~~~~~~~~~~~~~~~~
3+
The following functions in `matplotlib.font_manager` have been deprecated:
4+
``win32InstalledFonts``, ``get_fontconfig_fonts``.

lib/matplotlib/font_manager.py

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
263263
return items
264264

265265

266+
# Also remove _win32RegistryFonts when this is removed.
267+
@_api.deprecated("3.5")
266268
def win32InstalledFonts(directory=None, fontext='ttf'):
267269
"""
268270
Search for fonts in the specified font directory, or use the
@@ -290,9 +292,41 @@ def win32InstalledFonts(directory=None, fontext='ttf'):
290292
return [str(path) for path in items if path.suffix.lower() in fontext]
291293

292294

295+
def _get_win32_installed_fonts():
296+
"""List the font paths known to the Windows registry."""
297+
import winreg
298+
items = set()
299+
# Search and resolve fonts listed in the registry.
300+
for domain, base_dirs in [
301+
(winreg.HKEY_LOCAL_MACHINE, [win32FontDirectory()]), # System.
302+
(winreg.HKEY_CURRENT_USER, MSUserFontDirectories), # User.
303+
]:
304+
for base_dir in base_dirs:
305+
for reg_path in MSFontDirectories:
306+
try:
307+
with winreg.OpenKey(domain, reg_path) as local:
308+
for j in range(winreg.QueryInfoKey(local)[1]):
309+
# value may contain the filename of the font or its
310+
# absolute path.
311+
key, value, tp = winreg.EnumValue(local, j)
312+
if not isinstance(value, str):
313+
continue
314+
try:
315+
# If value contains already an absolute path,
316+
# then it is not changed further.
317+
path = Path(base_dir, value).resolve()
318+
except RuntimeError:
319+
# Don't fail with invalid entries.
320+
continue
321+
items.add(path)
322+
except (OSError, MemoryError):
323+
continue
324+
return items
325+
326+
293327
@lru_cache()
294-
def _call_fc_list():
295-
"""Cache and list the font filenames known to `fc-list`."""
328+
def _get_fontconfig_fonts():
329+
"""Cache and list the font paths known to `fc-list`."""
296330
try:
297331
if b'--format' not in subprocess.check_output(['fc-list', '--help']):
298332
_log.warning( # fontconfig 2.7 implemented --format.
@@ -301,14 +335,15 @@ def _call_fc_list():
301335
out = subprocess.check_output(['fc-list', '--format=%{file}\\n'])
302336
except (OSError, subprocess.CalledProcessError):
303337
return []
304-
return [os.fsdecode(fname) for fname in out.split(b'\n')]
338+
return [Path(os.fsdecode(fname)) for fname in out.split(b'\n')]
305339

306340

341+
@_api.deprecated("3.5")
307342
def get_fontconfig_fonts(fontext='ttf'):
308343
"""List font filenames known to `fc-list` having the given extension."""
309344
fontext = ['.' + ext for ext in get_fontext_synonyms(fontext)]
310-
return [fname for fname in _call_fc_list()
311-
if Path(fname).suffix.lower() in fontext]
345+
return [str(path) for path in _get_fontconfig_fonts()
346+
if path.suffix.lower() in fontext]
312347

313348

314349
def findSystemFonts(fontpaths=None, fontext='ttf'):
@@ -324,14 +359,16 @@ def findSystemFonts(fontpaths=None, fontext='ttf'):
324359

325360
if fontpaths is None:
326361
if sys.platform == 'win32':
362+
installed_fonts = _get_win32_installed_fonts()
327363
fontpaths = MSUserFontDirectories + [win32FontDirectory()]
328-
# now get all installed fonts directly...
329-
fontfiles.update(win32InstalledFonts(fontext=fontext))
330364
else:
331-
fontpaths = X11FontDirectories
365+
installed_fonts = _get_fontconfig_fonts()
332366
if sys.platform == 'darwin':
333367
fontpaths = [*X11FontDirectories, *OSXFontDirectories]
334-
fontfiles.update(get_fontconfig_fonts(fontext))
368+
else:
369+
fontpaths = X11FontDirectories
370+
fontfiles.update(str(path) for path in installed_fonts
371+
if path.suffix.lower()[1:] in fontexts)
335372

336373
elif isinstance(fontpaths, str):
337374
fontpaths = [fontpaths]

lib/matplotlib/tests/test_font_manager.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212

1313
from matplotlib.font_manager import (
1414
findfont, findSystemFonts, FontProperties, fontManager, json_dump,
15-
json_load, get_font, get_fontconfig_fonts, is_opentype_cff_font,
16-
MSUserFontDirectories, _call_fc_list)
15+
json_load, get_font, is_opentype_cff_font, MSUserFontDirectories,
16+
_get_fontconfig_fonts)
1717
from matplotlib import pyplot as plt, rc_context
1818

1919
has_fclist = shutil.which('fc-list') is not None
@@ -73,7 +73,7 @@ def test_otf():
7373

7474
@pytest.mark.skipif(not has_fclist, reason='no fontconfig installed')
7575
def test_get_fontconfig_fonts():
76-
assert len(get_fontconfig_fonts()) > 1
76+
assert len(_get_fontconfig_fonts()) > 1
7777

7878

7979
@pytest.mark.parametrize('factor', [2, 4, 6, 8])
@@ -154,13 +154,13 @@ def test_user_fonts_linux(tmpdir, monkeypatch):
154154

155155
with monkeypatch.context() as m:
156156
m.setenv('XDG_DATA_HOME', str(tmpdir))
157-
_call_fc_list.cache_clear()
157+
_get_fontconfig_fonts.cache_clear()
158158
# Now, the font should be available
159159
fonts = findSystemFonts()
160160
assert any(font_test_file in font for font in fonts)
161161

162162
# Make sure the temporary directory is no longer cached.
163-
_call_fc_list.cache_clear()
163+
_get_fontconfig_fonts.cache_clear()
164164

165165

166166
@pytest.mark.skipif(sys.platform != 'win32', reason='Windows only')

0 commit comments

Comments
 (0)
0