diff --git a/examples/misc/logos2.py b/examples/misc/logos2.py index 528f09e92c18..bae838be6ad0 100644 --- a/examples/misc/logos2.py +++ b/examples/misc/logos2.py @@ -20,10 +20,10 @@ def get_font_properties(): # The original font is Calibri, if that is not installed, we fall back # to Carlito, which is metrically equivalent. - if 'Calibri' in matplotlib.font_manager.findfont('Calibri:bold'): + if 'Calibri' in matplotlib.font_manager.findfont('Calibri:bold').keys(): return matplotlib.font_manager.FontProperties(family='Calibri', weight='bold') - if 'Carlito' in matplotlib.font_manager.findfont('Carlito:bold'): + if 'Carlito' in matplotlib.font_manager.findfont('Carlito:bold').keys(): print('Original font not found. Falling back to Carlito. ' 'The logo text will not be in the correct font.') return matplotlib.font_manager.FontProperties(family='Carlito', diff --git a/examples/text_labels_and_annotations/font_table.py b/examples/text_labels_and_annotations/font_table.py index e9296430ac13..0ef5877c0d42 100644 --- a/examples/text_labels_and_annotations/font_table.py +++ b/examples/text_labels_and_annotations/font_table.py @@ -34,6 +34,7 @@ def print_glyphs(path): """ if path is None: path = fm.findfont(fm.FontProperties()) # The default font. + path = next(iter(path.values())) # Get the first filepath font = FT2Font(path) @@ -60,6 +61,7 @@ def draw_font_table(path): """ if path is None: path = fm.findfont(fm.FontProperties()) # The default font. + path = next(iter(path.values())) # Get the first filepath font = FT2Font(path) # A charmap is a mapping of "character codes" (in the sense of a character diff --git a/lib/matplotlib/_mathtext.py b/lib/matplotlib/_mathtext.py index 3cc90e1f7501..e9306606d1e6 100644 --- a/lib/matplotlib/_mathtext.py +++ b/lib/matplotlib/_mathtext.py @@ -242,6 +242,9 @@ def destroy(self): def _get_font(self, font): if font in self.fontmap: basename = self.fontmap[font] + # TODO: allow multiple fonts + # for now settle with the first element + basename = next(iter(basename.values())) else: basename = font cached_font = self._fonts.get(basename) diff --git a/lib/matplotlib/backends/_backend_pdf_ps.py b/lib/matplotlib/backends/_backend_pdf_ps.py index 780e79bf71b8..a66c3d675972 100644 --- a/lib/matplotlib/backends/_backend_pdf_ps.py +++ b/lib/matplotlib/backends/_backend_pdf_ps.py @@ -108,6 +108,10 @@ def get_text_width_height_descent(self, s, prop, ismath): def _get_font_afm(self, prop): fname = font_manager.findfont( prop, fontext="afm", directory=self._afm_font_dir) + + # TODO: allow multiple font caching + # for now pass the first font + fname = next(iter(fname.values())) return _cached_get_afm_from_fname(fname) def _get_font_ttf(self, prop): diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 10063bd9a7b3..7b60257f90c3 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -829,11 +829,16 @@ def fontName(self, fontprop): if isinstance(fontprop, str): filename = fontprop - elif mpl.rcParams['pdf.use14corefonts']: - filename = findfont( - fontprop, fontext='afm', directory=RendererPdf._afm_font_dir) else: - filename = findfont(fontprop) + if mpl.rcParams["pdf.use14corefonts"]: + filename = findfont(fontprop, fontext="afm", + directory=RendererPdf._afm_font_dir) + else: + filename = findfont(fontprop) + + # TODO: allow multiple fonts for PDF backend + # for now settle with the first element + filename = next(iter(filename.values())) Fx = self.fontNames.get(filename) if Fx is None: diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 575d2d263496..457c3fa1a9d5 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -50,7 +50,13 @@ def get_fontspec(): for family, command in zip(families, commands): # 1) Forward slashes also work on Windows, so don't mess with # backslashes. 2) The dirname needs to include a separator. - path = pathlib.Path(fm.findfont(family)) + path = fm.findfont(family) + + # TODO: Allow multiple fonts + # for now stick with the first font + path = next(iter(path.values())) + + path = pathlib.Path(path) latex_fontspec.append(r"\%s{%s}[Path=\detokenize{%s}]" % ( command, path.name, path.parent.as_posix() + "/")) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 9d45575eb13d..2234bf29e37d 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -23,6 +23,7 @@ # - setWeights function needs improvement # - 'light' is an invalid weight value, remove it. +from collections import OrderedDict import dataclasses from functools import lru_cache import json @@ -1097,7 +1098,9 @@ def addfont(self, path): def defaultFont(self): # Lazily evaluated (findfont then caches the result) to avoid including # the venv path in the json serialization. - return {ext: self.findfont(family, fontext=ext) + + # TODO: allow embedding multiple fonts + return {ext: next(iter(self.findfont(family, fontext=ext).values())) for ext, family in self.defaultFamily.items()} def get_default_weight(self): @@ -1304,16 +1307,38 @@ def findfont(self, prop, fontext='ttf', directory=None, rc_params = tuple(tuple(rcParams[key]) for key in [ "font.serif", "font.sans-serif", "font.cursive", "font.fantasy", "font.monospace"]) - return self._findfont_cached( - prop, fontext, directory, fallback_to_default, rebuild_if_missing, - rc_params) + + prop = FontProperties._from_any(prop) + ffamily = prop.get_family() + + # maintain two dicts, one for available paths, + # the other for fallback paths + fpaths, fbpaths = OrderedDict(), OrderedDict() + for fidx in range(len(ffamily)): + cprop = prop.copy() + + # set current prop's family + cprop.set_family(ffamily[fidx]) + + fpath = self._findfont_cached( + FontProperties._from_any(cprop), fontext, directory, + fallback_to_default, rebuild_if_missing, rc_params) + + # if fontfile isn't found, fpath will be an OrderedDict + if isinstance(fpath, OrderedDict): + fbpaths.update(fpath) + else: + fpaths[ffamily[fidx]] = fpath + + # append fallback font(s) to the very end + fpaths.update(fbpaths) + + return fpaths @lru_cache() def _findfont_cached(self, prop, fontext, directory, fallback_to_default, rebuild_if_missing, rc_params): - prop = FontProperties._from_any(prop) - fname = prop.get_file() if fname is not None: return fname @@ -1401,7 +1426,10 @@ def is_opentype_cff_font(filename): @lru_cache(64) -def _get_font(filename, hinting_factor, *, _kerning_factor, thread_id): +def _get_font(filenames, hinting_factor, *, _kerning_factor, thread_id): + # TODO: allow multiple files (future PR) + # for now just pass the first element + filename = filenames[0] return ft2font.FT2Font( filename, hinting_factor, _kerning_factor=_kerning_factor) @@ -1417,11 +1445,20 @@ def _get_font(filename, hinting_factor, *, _kerning_factor, thread_id): def get_font(filename, hinting_factor=None): # Resolving the path avoids embedding the font twice in pdf/ps output if a # single font is selected using two different relative paths. - filename = _cached_realpath(filename) + if isinstance(filename, OrderedDict): + filenames = [] + for fname in filename.values(): + filenames.append(_cached_realpath(fname)) + else: + filenames = [_cached_realpath(filename)] if hinting_factor is None: hinting_factor = rcParams['text.hinting_factor'] + + # convert to tuple so its hashable + filenames = tuple(filenames) + # also key on the thread ID to prevent segfaults with multi-threading - return _get_font(filename, hinting_factor, + return _get_font(filenames, hinting_factor, _kerning_factor=rcParams['text.kerning_factor'], thread_id=threading.get_ident()) diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index 4cad797b3757..d5c9be39c47a 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -24,7 +24,8 @@ def test_font_priority(): 'font.sans-serif': ['cmmi10', 'Bitstream Vera Sans']}): font = findfont(FontProperties(family=["sans-serif"])) - assert Path(font).name == 'cmmi10.ttf' + # first font should be cmmi10.ttf + assert Path(next(iter(font.values()))).name == 'cmmi10.ttf' # Smoketest get_charmap, which isn't used internally anymore font = get_font(font) @@ -110,7 +111,7 @@ def test_utf16m_sfnt(): def test_find_ttc(): fp = FontProperties(family=["WenQuanYi Zen Hei"]) - if Path(findfont(fp)).name != "wqy-zenhei.ttc": + if "wqy-zenhei.ttc" not in map(lambda x: Path(x).name, findfont(fp)): pytest.skip("Font may be missing") fig, ax = plt.subplots() diff --git a/lib/matplotlib/tests/test_mathtext.py b/lib/matplotlib/tests/test_mathtext.py index 22079ccf9874..63d020963f6e 100644 --- a/lib/matplotlib/tests/test_mathtext.py +++ b/lib/matplotlib/tests/test_mathtext.py @@ -225,6 +225,8 @@ def test_mathfont_rendering(baseline_images, fontset, index, text): def test_fontinfo(): fontpath = mpl.font_manager.findfont("DejaVu Sans") + # get the first element + fontpath = next(iter(fontpath.values())) font = mpl.ft2font.FT2Font(fontpath) table = font.get_sfnt_table("head") assert table['version'] == (1, 0) diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py index dccb74ba0038..cb1c5d64e288 100644 --- a/lib/matplotlib/tests/test_text.py +++ b/lib/matplotlib/tests/test_text.py @@ -27,7 +27,7 @@ def test_font_styles(): def find_matplotlib_font(**kw): prop = FontProperties(**kw) path = findfont(prop, directory=mpl.get_data_path()) - return FontProperties(fname=path) + return FontProperties(fname=next(iter(path.values()))) from matplotlib.font_manager import FontProperties, findfont warnings.filterwarnings( @@ -198,6 +198,7 @@ def test_antialiasing(): def test_afm_kerning(): fn = mpl.font_manager.findfont("Helvetica", fontext="afm") + fn = next(iter(fn.values())) with open(fn, 'rb') as fh: afm = mpl.afm.AFM(fh) assert afm.string_width_height('VAVAVAVAVAVA') == (7174.0, 718) diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 2831069ab222..6e11a5dda818 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -483,10 +483,15 @@ def __init__(self, useOffset=None, useMathText=None, useLocale=None): ), fallback_to_default=False, ) + # visit all values + ufont = ufont.values() except ValueError: ufont = None - if ufont == str(cbook._get_data_path("fonts/ttf/cmr10.ttf")): + if ( + ufont is not None and + str(cbook._get_data_path("fonts/ttf/cmr10.ttf")) in ufont + ): _api.warn_external( "cmr10 font should ideally be used with " "mathtext, set axes.formatter.use_mathtext to True"
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: