Skip to content

Commit 856e95f

Browse files
committed
Implement loading of any font in a collection
For backwards-compatibility, the path+index is passed around in a lightweight subclass of `str`.
1 parent 11a56ec commit 856e95f

File tree

3 files changed

+36
-8
lines changed

3 files changed

+36
-8
lines changed

lib/matplotlib/font_manager.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,19 @@ def findSystemFonts(fontpaths=None, fontext='ttf'):
310310
return [fname for fname in fontfiles if os.path.exists(fname)]
311311

312312

313+
class FontPath(str):
314+
__match_args__ = ('path', 'face_index')
315+
316+
def __new__(cls, path, face_index):
317+
ret = super().__new__(cls, path)
318+
ret.face_index = face_index
319+
return ret
320+
321+
@property
322+
def path(self):
323+
return str(self)
324+
325+
313326
@dataclasses.dataclass(frozen=True)
314327
class FontEntry:
315328
"""
@@ -1542,7 +1555,7 @@ def _findfont_cached(self, prop, fontext, directory, fallback_to_default,
15421555
# actually raised.
15431556
return cbook._ExceptionInfo(ValueError, "No valid font could be found")
15441557

1545-
return _cached_realpath(result)
1558+
return FontPath(_cached_realpath(result), best_font.index)
15461559

15471560

15481561
@_api.deprecated("3.11")
@@ -1618,8 +1631,9 @@ def get_font(font_filepaths, hinting_factor=None):
16181631
16191632
Parameters
16201633
----------
1621-
font_filepaths : Iterable[str, Path, bytes, tuple[str | Path | bytes, int]], \
1622-
str, Path, bytes, tuple[str | Path | bytes, int]
1634+
font_filepaths : Iterable[str, Path, bytes, FontPath, \
1635+
tuple[str | Path | bytes, int, FontPath]], \
1636+
str, Path, bytes, FontPath, tuple[str | Path | bytes, int]
16231637
Relative or absolute paths to the font files to be used.
16241638
16251639
If a single string, bytes, or `pathlib.Path`, then it will be treated
@@ -1635,14 +1649,17 @@ def get_font(font_filepaths, hinting_factor=None):
16351649
16361650
"""
16371651
match font_filepaths:
1652+
case FontPath(path, index):
1653+
paths = ((_cached_realpath(path), index), )
16381654
case str() | Path() | bytes() as path:
16391655
paths = ((_cached_realpath(path), 0), )
16401656
case (str() | Path() | bytes() as path, int() as index):
16411657
paths = ((_cached_realpath(path), index), )
16421658
case _:
16431659
paths = tuple(
16441660
(_cached_realpath(fname[0]), fname[1]) if isinstance(fname, tuple)
1645-
else (_cached_realpath(fname), 0)
1661+
else (_cached_realpath(fname.path), fname.face_index)
1662+
if isinstance(fname, FontPath) else (_cached_realpath(fname), 0)
16461663
for fname in font_filepaths)
16471664

16481665
hinting_factor = mpl._val_or_rc(hinting_factor, 'text.hinting_factor')

lib/matplotlib/font_manager.pyi

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ def _get_fontconfig_fonts() -> list[Path]: ...
2828
def findSystemFonts(
2929
fontpaths: Iterable[str | os.PathLike | Path] | None = ..., fontext: str = ...
3030
) -> list[str]: ...
31+
32+
class FontPath(str):
33+
face_index: int
34+
def __new__(cls: type[str], path: str, face_index: int) -> FontPath: ...
35+
@property
36+
def path(self) -> str: ...
37+
3138
@dataclass
3239
class FontEntry:
3340
fname: str = ...
@@ -118,12 +125,12 @@ class FontManager:
118125
directory: str | None = ...,
119126
fallback_to_default: bool = ...,
120127
rebuild_if_missing: bool = ...,
121-
) -> str: ...
128+
) -> FontPath: ...
122129
def get_font_names(self) -> list[str]: ...
123130

124131
def is_opentype_cff_font(filename: str) -> bool: ...
125132
def get_font(
126-
font_filepaths: Iterable[str | Path | bytes | FontFace] | str | Path | bytes | FontFace,
133+
font_filepaths: Iterable[str | Path | bytes | FontFace | FontPath] | str | Path | bytes | FontFace | FontPath,
127134
hinting_factor: int | None = ...,
128135
) -> ft2font.FT2Font: ...
129136

lib/matplotlib/tests/test_font_manager.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,12 @@ def test_find_ttc():
122122
pytest.skip("Font wqy-zenhei.ttc may be missing")
123123
# All fonts from this collection should have loaded as well.
124124
for name in ["WenQuanYi Zen Hei Mono", "WenQuanYi Zen Hei Sharp"]:
125-
assert findfont(FontProperties(family=[name]),
126-
fallback_to_default=False) == fontpath
125+
subfontpath = findfont(FontProperties(family=[name]), fallback_to_default=False)
126+
assert subfontpath.path == fontpath.path
127+
assert subfontpath.face_index != fontpath.face_index
128+
subfont = get_font(subfontpath)
129+
assert subfont.fname == subfontpath.path
130+
assert subfont.face_index == subfontpath.face_index
127131
fig, ax = plt.subplots()
128132
ax.text(.5, .5, "\N{KANGXI RADICAL DRAGON}", fontproperties=fp)
129133
for fmt in ["raw", "svg", "pdf", "ps"]:

0 commit comments

Comments
 (0)