From 7c933098fda95f8a637eeb802c309ac53137a279 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 31 Mar 2018 17:21:09 -0700 Subject: [PATCH] Remove "experimental" fontconfig font_manager backend. The "experimental" fontconfig backend for font_manager was never publicly accessible: even if one does `matplotlib.font_manager.USE_FONTCONFIG = True`, that didn't change the already existing `findfont`. The only way to access it was to edit the source, or use `reload()`, neither of which really count as public API... Note that our "use" of "fontconfig-like" patterns actually has subtly different semantics from actual fontconfig patterns, so it's not as if that backend was correctly working anyways. We don't need to set `fontManager.default_size` when loading it as we already check that fontManager has the correct `__version__` (and thus must have a correct `default_size`; moreover the only use of `default_size` is in `sizeval1 = self.default_size * font_scalings[size1]` so it's not as if a value of `None` (if it had somehow been missing) was going to be helpful anyways... `get_cachedir()` always returns a real directory (creating a temporary one if necessary), so we can drop the code paths handling `get_cachedir() == None`. Note that we still rely on fontconfig to *list* fonts; the backend only added an (non-publicly-accessible, per above) option to *match* fonts using fontconfig. --- .../2018-07-16-AL-removals.rst | 5 + lib/matplotlib/__init__.py | 6 +- lib/matplotlib/font_manager.py | 125 ++++-------------- 3 files changed, 33 insertions(+), 103 deletions(-) create mode 100644 doc/api/next_api_changes/2018-07-16-AL-removals.rst diff --git a/doc/api/next_api_changes/2018-07-16-AL-removals.rst b/doc/api/next_api_changes/2018-07-16-AL-removals.rst new file mode 100644 index 000000000000..46a060c73eff --- /dev/null +++ b/doc/api/next_api_changes/2018-07-16-AL-removals.rst @@ -0,0 +1,5 @@ +API removals +```````````` +The following API elements have been removed: + +- ``font_manager.USE_FONTCONFIG``, ``font_manager.cachedir``, diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index f504673776af..1ddb7e4bec7d 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -555,9 +555,9 @@ def get_home(): return None -def _create_tmp_config_dir(): +def _create_tmp_config_or_cache_dir(): """ - If the config directory can not be created, create a temporary directory. + If the config or cache directory cannot be created, create a temporary one. """ configdir = os.environ['MPLCONFIGDIR'] = ( tempfile.mkdtemp(prefix='matplotlib-')) @@ -609,7 +609,7 @@ def _get_config_or_cache_dir(xdg_base): if os.access(str(configdir), os.W_OK) and configdir.is_dir(): return str(configdir) - return _create_tmp_config_dir() + return _create_tmp_config_or_cache_dir() @_logged_cached('CONFIGDIR=%s') diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 620a41620d58..3b01bf2c4b48 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -11,13 +11,6 @@ The design is based on the `W3C Cascading Style Sheet, Level 1 (CSS1) font specification `_. Future versions may implement the Level 2 or 2.1 specifications. - -Experimental support is included for using `fontconfig` on Unix -variant platforms (Linux, OS X, Solaris). To enable it, set the -constant ``USE_FONTCONFIG`` in this file to ``True``. Fontconfig has -the advantage that it is the standard way to look up fonts on X11 -platforms, so if a font is installed, it is much more likely to be -found. """ # KNOWN ISSUES @@ -44,14 +37,12 @@ import warnings import matplotlib as mpl -from matplotlib import afm, cbook, ft2font, rcParams, get_cachedir +from matplotlib import afm, cbook, ft2font, rcParams from matplotlib.fontconfig_pattern import ( parse_fontconfig_pattern, generate_fontconfig_pattern) _log = logging.getLogger(__name__) -USE_FONTCONFIG = False - font_scalings = { 'xx-small' : 0.579, 'x-small' : 0.694, @@ -104,12 +95,10 @@ MSFolders = \ r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders' - MSFontDirectories = [ r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts', r'SOFTWARE\Microsoft\Windows\CurrentVersion\Fonts'] - X11FontDirectories = [ # an old standard installation point "/usr/X11R6/lib/X11/fonts/TTF/", @@ -120,7 +109,9 @@ "/usr/local/share/fonts/", # common application, not really useful "/usr/lib/openoffice/share/fonts/truetype/", - ] + # user fonts + str(Path.home() / ".fonts"), +] OSXFontDirectories = [ "/Library/Fonts/", @@ -128,12 +119,10 @@ "/System/Library/Fonts/", # fonts installed via MacPorts "/opt/local/share/fonts", + # user fonts + str(Path.home() / "Library/Fonts"), ] -if not USE_FONTCONFIG and sys.platform != 'win32': - OSXFontDirectories.append(str(Path.home() / "Library/Fonts")) - X11FontDirectories.append(str(Path.home() / ".fonts")) - def get_fontext_synonyms(fontext): """ @@ -1149,7 +1138,7 @@ def score_size(self, size1, size2): sizeval2 = float(size2) except ValueError: return 1.0 - return abs(sizeval1 - sizeval2) / 72.0 + return abs(sizeval1 - sizeval2) / 72 def findfont(self, prop, fontext='ttf', directory=None, fallback_to_default=True, rebuild_if_missing=True): @@ -1273,11 +1262,10 @@ def is_opentype_cff_font(filename): return False -fontManager = None -_fmcache = None - - _get_font = lru_cache(64)(ft2font.FT2Font) +_fmcache = os.path.join( + mpl.get_cachedir(), 'fontlist-v{}.json'.format(FontManager.__version__)) +fontManager = None def get_font(filename, hinting_factor=None): @@ -1286,86 +1274,23 @@ def get_font(filename, hinting_factor=None): return _get_font(filename, hinting_factor) -# The experimental fontconfig-based backend. -if USE_FONTCONFIG and sys.platform != 'win32': +def _rebuild(): + global fontManager + fontManager = FontManager() + with cbook._lock_path(_fmcache): + json_dump(fontManager, _fmcache) + _log.info("generated new fontManager") - def fc_match(pattern, fontext): - fontexts = get_fontext_synonyms(fontext) - ext = "." + fontext - try: - pipe = subprocess.Popen( - ['fc-match', '-s', '--format=%{file}\\n', pattern], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - output = pipe.communicate()[0] - except OSError: - return None - - # The bulk of the output from fc-list is ascii, so we keep the - # result in bytes and parse it as bytes, until we extract the - # filename, which is in sys.filesystemencoding(). - if pipe.returncode == 0: - for fname in map(os.fsdecode, output.split(b'\n')): - if os.path.splitext(fname)[1][1:] in fontexts: - return fname - return None - - _fc_match_cache = {} - - def findfont(prop, fontext='ttf'): - if not isinstance(prop, str): - prop = prop.get_fontconfig_pattern() - cached = _fc_match_cache.get(prop) - if cached is not None: - return cached - - result = fc_match(prop, fontext) - if result is None: - result = fc_match(':', fontext) - - _fc_match_cache[prop] = result - return result +try: + fontManager = json_load(_fmcache) +except Exception: + _rebuild() else: - _fmcache = None - - cachedir = get_cachedir() - if cachedir is not None: - _fmcache = os.path.join( - cachedir, 'fontlist-v{}.json'.format(FontManager.__version__)) - - fontManager = None - - def _rebuild(): - global fontManager - - fontManager = FontManager() - - if _fmcache: - with cbook._lock_path(_fmcache): - json_dump(fontManager, _fmcache) - _log.debug("generated new fontManager") - - if _fmcache: - try: - fontManager = json_load(_fmcache) - except FileNotFoundError: - _log.debug("No font cache found %s", _fmcache) - except json.JSONDecodeError: - _log.warning("Font cache parsing failed %s", _fmcache) - else: - if (not hasattr(fontManager, '_version') or - fontManager._version != FontManager.__version__): - _log.debug("Font cache needs rebuild (version mismatch)") - fontManager = None - else: - fontManager.default_size = None - _log.debug("Using fontManager instance from %s", _fmcache) - - if fontManager is None: + if getattr(fontManager, '_version', object()) != FontManager.__version__: _rebuild() + else: + _log.debug("Using fontManager instance from %s", _fmcache) + - def findfont(prop, **kw): - global fontManager - font = fontManager.findfont(prop, **kw) - return font +findfont = fontManager.findfont