From 03052fa3ed90f2f9858beb7054e635612aa61559 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 18 Feb 2018 01:05:50 -0800 Subject: [PATCH] Py3fy font_manager. --- .../2018-02-18-AL-removals.rst | 5 + lib/matplotlib/font_manager.py | 133 +++++++----------- 2 files changed, 55 insertions(+), 83 deletions(-) create mode 100644 doc/api/next_api_changes/2018-02-18-AL-removals.rst diff --git a/doc/api/next_api_changes/2018-02-18-AL-removals.rst b/doc/api/next_api_changes/2018-02-18-AL-removals.rst new file mode 100644 index 000000000000..6b094027a927 --- /dev/null +++ b/doc/api/next_api_changes/2018-02-18-AL-removals.rst @@ -0,0 +1,5 @@ +Removal of deprecated functions +``````````````````````````````` +The following previously deprecated functions have been removed: +- ``matplotlib.font_manager.ttfdict_to_fnames`` +- ``matplotlib.font_manager.weight_as_number`` diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 7b7881c0b93d..c8b56ba279c1 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -20,28 +20,16 @@ found. """ -import six - -""" -KNOWN ISSUES - - - documentation - - font variant is untested - - font stretch is incomplete - - font size is incomplete - - default font algorithm needs improvement and testing - - setWeights function needs improvement - - 'light' is an invalid weight value, remove it. - - update_fonts not implemented - -Authors : John Hunter - Paul Barrett - Michael Droettboom -Copyright : John Hunter (2004,2005), Paul Barrett (2004,2005) -License : matplotlib license (PSF compatible) - The font directory code is from ttfquery, - see license/LICENSE_TTFQUERY. -""" +# KNOWN ISSUES +# +# - documentation +# - font variant is untested +# - font stretch is incomplete +# - font size is incomplete +# - default font algorithm needs improvement and testing +# - setWeights function needs improvement +# - 'light' is an invalid weight value, remove it. +# - update_fonts not implemented from collections import Iterable from functools import lru_cache @@ -179,22 +167,17 @@ def win32FontDirectory(): If the key is not found, $WINDIR/Fonts will be returned. """ + import winreg try: - from six.moves import winreg - except ImportError: - pass # Fall through to default - else: + user = winreg.OpenKey(winreg.HKEY_CURRENT_USER, MSFolders) try: - user = winreg.OpenKey(winreg.HKEY_CURRENT_USER, MSFolders) - try: - try: - return winreg.QueryValueEx(user, 'Fonts')[0] - except OSError: - pass # Fall through to default - finally: - winreg.CloseKey(user) + return winreg.QueryValueEx(user, 'Fonts')[0] except OSError: pass # Fall through to default + finally: + winreg.CloseKey(user) + except OSError: + pass # Fall through to default return os.path.join(os.environ['WINDIR'], 'Fonts') @@ -206,7 +189,8 @@ def win32InstalledFonts(directory=None, fontext='ttf'): 'afm'. """ - from six.moves import winreg + import winreg + if directory is None: directory = win32FontDirectory() @@ -224,7 +208,7 @@ def win32InstalledFonts(directory=None, fontext='ttf'): for j in range(winreg.QueryInfoKey(local)[1]): try: key, direc, tp = winreg.EnumValue(local, j) - if not isinstance(direc, six.string_types): + if not isinstance(direc, str): continue # Work around for https://bugs.python.org/issue25778, which # is fixed in Py>=3.6.1. @@ -274,19 +258,12 @@ def _call_fc_list(): 'This may take a moment.')) timer.start() try: - out = subprocess.check_output([str('fc-list'), '--format=%{file}\\n']) + out = subprocess.check_output(['fc-list', '--format=%{file}\\n']) except (OSError, subprocess.CalledProcessError): return [] finally: timer.cancel() - fnames = [] - for fname in out.split(b'\n'): - try: - fname = six.text_type(fname, sys.getfilesystemencoding()) - except UnicodeDecodeError: - continue - fnames.append(fname) - return fnames + return [os.fsdecode(fname) for fname in out.split(b'\n')] def get_fontconfig_fonts(fontext='ttf'): @@ -328,7 +305,7 @@ def findSystemFonts(fontpaths=None, fontext='ttf'): for f in get_fontconfig_fonts(fontext): fontfiles.add(f) - elif isinstance(fontpaths, six.string_types): + elif isinstance(fontpaths, str): fontpaths = [fontpaths] for path in fontpaths: @@ -478,9 +455,9 @@ def afmFontProperty(fontpath, font): # Styles are: italic, oblique, and normal (default) - if font.get_angle() != 0 or name.lower().find('italic') >= 0: + if font.get_angle() != 0 or 'italic' in name.lower(): style = 'italic' - elif name.lower().find('oblique') >= 0: + elif 'oblique' in name.lower(): style = 'oblique' else: style = 'normal' @@ -501,12 +478,11 @@ def afmFontProperty(fontpath, font): # and ultra-expanded. # Relative stretches are: wider, narrower # Child value is: inherit - if fontname.find('narrow') >= 0 or fontname.find('condensed') >= 0 or \ - fontname.find('cond') >= 0: - stretch = 'condensed' - elif fontname.find('demi cond') >= 0: + if 'demi cond' in fontname: stretch = 'semi-condensed' - elif fontname.find('wide') >= 0 or fontname.find('expanded') >= 0: + elif 'narrow' in fontname or 'cond' in fontname: + stretch = 'condensed' + elif 'wide' in fontname or 'expanded' in fontname: stretch = 'expanded' else: stretch = 'normal' @@ -568,7 +544,7 @@ def createFontList(fontfiles, fontext='ttf'): except UnicodeError: _log.info("Cannot handle unicode filenames") continue - except IOError: + except OSError: _log.info("IO error - cannot open font file %s", fpath) continue try: @@ -646,7 +622,7 @@ def __init__(self, weight = None, stretch= None, size = None, - fname = None, # if this is set, it's a hardcoded filename to use + fname = None, # if set, it's a hardcoded filename to use _init = None # used only by copy() ): self._family = _normalize_font_family(rcParams['font.family']) @@ -662,7 +638,7 @@ def __init__(self, self.__dict__.update(_init.__dict__) return - if isinstance(family, six.string_types): + if isinstance(family, str): # Treat family as a fontconfig pattern if it is the only # parameter provided. if (style is None and @@ -712,23 +688,20 @@ def get_family(self): def get_name(self): """ - Return the name of the font that best matches the font - properties. + Return the name of the font that best matches the font properties. """ return get_font(findfont(self)).family_name def get_style(self): """ - Return the font style. Values are: 'normal', 'italic' or - 'oblique'. + Return the font style. Values are: 'normal', 'italic' or 'oblique'. """ return self._slant get_slant = get_style def get_variant(self): """ - Return the font variant. Values are: 'normal' or - 'small-caps'. + Return the font variant. Values are: 'normal' or 'small-caps'. """ return self._variant @@ -793,8 +766,7 @@ def set_family(self, family): def set_style(self, style): """ - Set the font style. Values are: 'normal', 'italic' or - 'oblique'. + Set the font style. Values are: 'normal', 'italic' or 'oblique'. """ if style is None: style = rcParams['font.style'] @@ -892,7 +864,7 @@ def set_fontconfig_pattern(self, pattern): support for it to be enabled. We are merely borrowing its pattern syntax for use here. """ - for key, val in six.iteritems(self._parse_fontconfig_pattern(pattern)): + for key, val in self._parse_fontconfig_pattern(pattern).items(): if type(val) == list: getattr(self, "set_" + key)(val[0]) else: @@ -936,9 +908,10 @@ def json_dump(data, filename): with open(filename, 'w') as fh: try: json.dump(data, fh, cls=JSONEncoder, indent=2) - except IOError as e: + except OSError as e: warnings.warn('Could not save font_manager cache ', e) + def json_load(filename): """Loads a data structure as JSON from the named file. Handles FontManager and its fields.""" @@ -948,10 +921,8 @@ def json_load(filename): def _normalize_font_family(family): - if isinstance(family, six.string_types): - family = [six.text_type(family)] - elif isinstance(family, Iterable): - family = [six.text_type(f) for f in family] + if isinstance(family, str): + family = [family] return family @@ -1170,14 +1141,14 @@ def score_weight(self, weight1, weight2): The result is 0.0 if both weight1 and weight 2 are given as strings and have the same value. - Otherwise, the result is the absolute value of the difference between the - CSS numeric values of *weight1* and *weight2*, normalized - between 0.05 and 1.0. + Otherwise, the result is the absolute value of the difference between + the CSS numeric values of *weight1* and *weight2*, normalized between + 0.05 and 1.0. """ - # exact match of the weight names (e.g. weight1 == weight2 == "regular") - if (isinstance(weight1, six.string_types) and - isinstance(weight2, six.string_types) and + # exact match of the weight names, e.g. weight1 == weight2 == "regular" + if (isinstance(weight1, str) and + isinstance(weight2, str) and weight1 == weight2): return 0.0 try: @@ -1364,18 +1335,14 @@ def fc_match(pattern, fontext): stdout=subprocess.PIPE, stderr=subprocess.PIPE) output = pipe.communicate()[0] - except (OSError, IOError): + 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 output.split(b'\n'): - try: - fname = six.text_type(fname, sys.getfilesystemencoding()) - except UnicodeDecodeError: - continue + for fname in map(os.fsdecode, output.split(b'\n')): if os.path.splitext(fname)[1][1:] in fontexts: return fname return None @@ -1383,7 +1350,7 @@ def fc_match(pattern, fontext): _fc_match_cache = {} def findfont(prop, fontext='ttf'): - if not isinstance(prop, six.string_types): + if not isinstance(prop, str): prop = prop.get_fontconfig_pattern() cached = _fc_match_cache.get(prop) if cached is not None: