diff --git a/doc/api/next_api_changes/behavior/31486-ES.rst b/doc/api/next_api_changes/behavior/31486-ES.rst new file mode 100644 index 000000000000..a04c1d025d90 --- /dev/null +++ b/doc/api/next_api_changes/behavior/31486-ES.rst @@ -0,0 +1,6 @@ +New environment variable to ignore system fonts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +System fonts may be ignored by setting the :envvar:`MPL_IGNORE_SYSTEM_FONTS`; this +suppresses searching for system fonts (in known directories or via some +platform-specific subprocess) as well as limiting the results from `.FontManager.findfont`. diff --git a/doc/install/environment_variables_faq.rst b/doc/install/environment_variables_faq.rst index 38e0d0ef0c63..033350f6e5e8 100644 --- a/doc/install/environment_variables_faq.rst +++ b/doc/install/environment_variables_faq.rst @@ -29,6 +29,11 @@ Environment variables used to find a base directory in which the :file:`matplotlib` subdirectory is created. +.. envvar:: MPL_IGNORE_SYSTEM_FONTS + + When this variable is set, Matplotlib will update its font cache only with its own + directories. This can be used to limit subprocess usage for querying system fonts. + .. envvar:: PATH The list of directories searched to find executable programs. diff --git a/doc/release/next_whats_new/ignore_system_fonts.rst b/doc/release/next_whats_new/ignore_system_fonts.rst new file mode 100644 index 000000000000..a04c1d025d90 --- /dev/null +++ b/doc/release/next_whats_new/ignore_system_fonts.rst @@ -0,0 +1,6 @@ +New environment variable to ignore system fonts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +System fonts may be ignored by setting the :envvar:`MPL_IGNORE_SYSTEM_FONTS`; this +suppresses searching for system fonts (in known directories or via some +platform-specific subprocess) as well as limiting the results from `.FontManager.findfont`. diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 90aa778cb292..2f2a258d568c 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -277,17 +277,31 @@ def _get_macos_fonts(): def findSystemFonts(fontpaths=None, fontext='ttf'): """ - Search for fonts in the specified font paths. If no paths are - given, will use a standard set of system paths, as well as the - list of fonts tracked by fontconfig if fontconfig is installed and - available. A list of TrueType fonts are returned by default with - AFM fonts as an option. + Find fonts in a search path, system paths, or some other platform-specific method. + + Parameters + ---------- + fontpaths : list of str, optional + Search for fonts in these specified font paths. If no paths are given and the + :envvar:`MPL_IGNORE_SYSTEM_FONTS` is not set, use a standard set of system + paths, as well as the list of fonts tracked by fontconfig if fontconfig is + installed and available. + fontext : {'ttf', 'afm'}, default: 'ttf' + If 'ttf', search for TrueType fonts; if 'afm', search for with AFM fonts. + + Returns + ------- + list of str + A list of file paths with fonts of the given type. """ fontfiles = set() fontexts = get_fontext_synonyms(fontext) if fontpaths is None: - if sys.platform == 'win32': + if os.getenv('MPL_IGNORE_SYSTEM_FONTS'): + installed_fonts = [] + fontpaths = [] + elif sys.platform == 'win32': installed_fonts = _get_win32_installed_fonts() fontpaths = [] elif sys.platform == 'emscripten': @@ -1465,6 +1479,8 @@ def findfont(self, prop, fontext='ttf', directory=None, directory : str, optional If given, only search this directory and its subdirectories. + If :envvar:`MPL_IGNORE_SYSTEM_FONTS` is set, then this defaults to + Matplotlib's internal font directory. fallback_to_default : bool If True, will fall back to the default font family (usually @@ -1506,6 +1522,8 @@ def findfont(self, prop, fontext='ttf', directory=None, "serif", "sans-serif", "cursive", "fantasy", "monospace"]] rc_params = tuple(tuple(e) if isinstance(e, list) else e for e in rc_params) # Make this hashable. + if directory is None and os.getenv('MPL_IGNORE_SYSTEM_FONTS'): + directory = cbook._get_data_path('fonts') ret = self._findfont_cached( prop, fontext, directory, fallback_to_default, rebuild_if_missing, rc_params) diff --git a/lib/matplotlib/font_manager.pyi b/lib/matplotlib/font_manager.pyi index 22d925ea9273..faaa028635fd 100644 --- a/lib/matplotlib/font_manager.pyi +++ b/lib/matplotlib/font_manager.pyi @@ -27,7 +27,7 @@ def _get_font_alt_names( font: ft2font.FT2Font, primary_name: str ) -> list[tuple[str, int]]: ... def findSystemFonts( - fontpaths: Iterable[str | os.PathLike] | None = ..., fontext: str = ... + fontpaths: Iterable[str | os.PathLike] | None = ..., fontext: Literal['ttf', 'afm'] = ... ) -> list[str]: ... class FontPath(str): diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index ab6c1d91b106..6ba9f338f6d7 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -248,12 +248,16 @@ def test_user_fonts_linux(tmpdir, monkeypatch): _get_fontconfig_fonts.cache_clear() -def test_addfont_as_path(): +def test_addfont_as_path(monkeypatch): """Smoke test that addfont() accepts pathlib.Path.""" font_test_file = 'mpltest.ttf' path = Path(__file__).parent / 'data' / font_test_file try: fontManager.addfont(path) + assert fontManager.findfont('mpltest:weight=500') == FontPath(path, 0) + with monkeypatch.context() as m, pytest.raises(ValueError): + m.setenv('MPL_IGNORE_SYSTEM_FONTS', 'true') # Can only find internal fonts. + fontManager.findfont('mpltest:weight=500', fallback_to_default=False) added, = (font for font in fontManager.ttflist if font.fname.endswith(font_test_file)) fontManager.ttflist.remove(added)