Repository navigation
[without findfont diff] Parsing all families in font_manager #20549
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
e877533
dc2fb2b
778de1f
8f3f410
6ffc56e
aaf0b0a
42fcf01
7b5ea77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
|
@@ -1308,6 +1309,100 @@ def findfont(self, prop, fontext='ttf', directory=None, | |
| prop, fontext, directory, fallback_to_default, rebuild_if_missing, | ||
| rc_params) | ||
|
|
||
| def find_fontsprop(self, prop, fontext='ttf', directory=None, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We talked on the phone about renaming this. The should invoke that is is going at a) find multiple fonts b) that the order of the returned fonts matters.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some suggestions:
|
||
| fallback_to_default=True, rebuild_if_missing=True): | ||
|
aitikgupta marked this conversation as resolved.
|
||
| """ | ||
| Find font families that most closely matches the given properties. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| prop : str or `~matplotlib.font_manager.FontProperties` | ||
| The font properties to search for. This can be either a | ||
| `.FontProperties` object or a string defining a | ||
| `fontconfig patterns`_. | ||
|
|
||
| fontext : {'ttf', 'afm'}, default: 'ttf' | ||
| The extension of the font file: | ||
|
|
||
| - 'ttf': TrueType and OpenType fonts (.ttf, .ttc, .otf) | ||
| - 'afm': Adobe Font Metrics (.afm) | ||
|
|
||
| directory : str, optional | ||
| If given, only search this directory and its subdirectories. | ||
|
|
||
| fallback_to_default : bool | ||
| If True, will fallback to the default font family (usually | ||
| "DejaVu Sans" or "Helvetica") if none of the families were found. | ||
|
|
||
| rebuild_if_missing : bool | ||
| Whether to rebuild the font cache and search again if the first | ||
| match appears to point to a nonexisting font (i.e., the font cache | ||
| contains outdated entries). | ||
|
|
||
| Returns | ||
| ------- | ||
| OrderedDict | ||
| key, value pair of families and their corresponding filepaths. | ||
|
|
||
| Notes | ||
| ----- | ||
| This is a plugin to original findfont API, which only returns a | ||
| single font for given font properties. Instead, this API returns | ||
| an OrderedDict containing multiple fonts and their filepaths which | ||
| closely match the given font properties. | ||
| Since this internally uses original API, there's no change | ||
| to the logic of performing the nearest neighbor search. | ||
| See `findfont` for more details. | ||
|
|
||
| """ | ||
|
|
||
| rc_params = tuple(tuple(rcParams[key]) for key in [ | ||
| "font.serif", "font.sans-serif", "font.cursive", "font.fantasy", | ||
| "font.monospace"]) | ||
|
|
||
|
aitikgupta marked this conversation as resolved.
|
||
| prop = FontProperties._from_any(prop) | ||
| ffamily = prop.get_family() | ||
|
|
||
| fpaths = OrderedDict() | ||
| for fidx in range(len(ffamily)): | ||
| cprop = prop.copy() | ||
|
|
||
| # set current prop's family | ||
| cprop.set_family(ffamily[fidx]) | ||
|
|
||
| try: | ||
| fpath = self._findfont_cached( | ||
| cprop, fontext, directory, | ||
| False, rebuild_if_missing, rc_params | ||
| ) | ||
| fpaths[ffamily[fidx]] = fpath | ||
| except ValueError: | ||
| _log.warning( | ||
| 'findfont: Font family [\'%s\'] not found.', ffamily[fidx] | ||
| ) | ||
| if ffamily[fidx].lower() in font_family_aliases: | ||
| _log.warning( | ||
| "findfont: Generic family [%r] not found because " | ||
| "none of the following families were found: %s", | ||
| ffamily[fidx], | ||
| ", ".join(self._expand_aliases(ffamily[fidx])) | ||
| ) | ||
|
|
||
| if not fpaths: | ||
| if fallback_to_default: | ||
| dfamily = self.defaultFamily[fontext] | ||
| cprop = prop.copy().set_family(dfamily) | ||
| fpath = self._findfont_cached( | ||
| cprop, fontext, directory, | ||
| True, rebuild_if_missing, rc_params | ||
| ) | ||
| fpaths[dfamily] = fpath | ||
| else: | ||
| raise ValueError("Failed to find any font, and fallback " | ||
| "to the default font was disabled.") | ||
|
|
||
| return fpaths | ||
|
|
||
| @lru_cache() | ||
| def _findfont_cached(self, prop, fontext, directory, fallback_to_default, | ||
| rebuild_if_missing, rc_params): | ||
|
|
@@ -1401,9 +1496,11 @@ def is_opentype_cff_font(filename): | |
|
|
||
|
|
||
| @lru_cache(64) | ||
| def _get_font(filename, hinting_factor, *, _kerning_factor, thread_id): | ||
| def _get_font(fpaths, hinting_factor, *, _kerning_factor, thread_id): | ||
| # multiple paths, take first one | ||
| # for now, which is always guaranteed | ||
| return ft2font.FT2Font( | ||
| filename, hinting_factor, _kerning_factor=_kerning_factor) | ||
| fpaths[0], hinting_factor, _kerning_factor=_kerning_factor) | ||
|
|
||
|
|
||
| # FT2Font objects cannot be used across fork()s because they reference the same | ||
|
|
@@ -1417,11 +1514,14 @@ 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): | ||
| paths = tuple(_cached_realpath(fname) for fname in filename.values()) | ||
| else: | ||
| paths = (_cached_realpath(filename),) | ||
| if hinting_factor is None: | ||
| hinting_factor = rcParams['text.hinting_factor'] | ||
| # also key on the thread ID to prevent segfaults with multi-threading | ||
| return _get_font(filename, hinting_factor, | ||
| return _get_font(paths, hinting_factor, | ||
| _kerning_factor=rcParams['text.kerning_factor'], | ||
| thread_id=threading.get_ident()) | ||
|
|
||
|
|
@@ -1446,3 +1546,4 @@ def _load_fontmanager(*, try_read_cache=True): | |
|
|
||
| fontManager = _load_fontmanager() | ||
| findfont = fontManager.findfont | ||
| find_fontsprop = fontManager.find_fontsprop | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We aim for the writing in the docs to be more formal.
[We also discussed this on the phone]