From 06dce269968b13af2b3c6370974f1ba331aa588f Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 29 Aug 2016 23:35:59 -0400 Subject: [PATCH 1/9] API: convert string fontsize to points immediately This bakes into the text artist (via the FontProperties) the default fontsize at the time of creation. --- lib/matplotlib/font_manager.py | 8 ++++++-- lib/matplotlib/tests/test_text.py | 12 ++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 51920abd2781..39b1068f57f8 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -910,7 +910,10 @@ def set_size(self, size): try: size = float(size) except ValueError: - if size is not None and size not in font_scalings: + try: + scale = font_scalings[size] + size = scale * FontManager.get_default_size() + except KeyError: raise ValueError( "Size is invalid. Valid font size are " + ", ".join( str(i) for i in font_scalings.keys())) @@ -942,7 +945,8 @@ def set_fontconfig_pattern(self, pattern): def copy(self): """Return a deep copy of self""" - return FontProperties(_init = self) + return FontProperties(_init=self) + def ttfdict_to_fnames(d): """ diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py index 88bbc4ad0349..44d2abb38aee 100644 --- a/lib/matplotlib/tests/test_text.py +++ b/lib/matplotlib/tests/test_text.py @@ -399,3 +399,15 @@ def test_agg_text_clip(): ax1.text(x, y, "foo", clip_on=True) ax2.text(x, y, "foo") plt.show() + + +@cleanup +def test_text_size_binding(): + from matplotlib.font_manager import FontProperties + + matplotlib.rcParams['font.size'] = 10 + fp = FontProperties(size='large') + sz1 = fp.get_size_in_points() + matplotlib.rcParams['font.size'] = 100 + + assert sz1 == fp.get_size_in_points() From 2ddcc68c7de0cfef089e5b09bcad0c4a3d04afab Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 3 Dec 2016 16:57:12 -0500 Subject: [PATCH 2/9] API: cache usetex value at text creation time --- lib/matplotlib/text.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 029cbb65f098..8f853133ce0d 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -1222,7 +1222,7 @@ def is_math_text(s): """ # Did we find an even number of non-escaped dollar signs? # If so, treat is as math text. - if rcParams['text.usetex']: + if self.get_usetex(): if s == ' ': s = r'\ ' return s, 'TeX' @@ -1256,7 +1256,7 @@ def set_usetex(self, usetex): `rcParams['text.usetex']` """ if usetex is None: - self._usetex = None + self._usetex = rcParams['text.usetex'] else: self._usetex = bool(usetex) self.stale = True From 98e9fb20623645ae414e875bc8ce4c88ce16eb57 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 3 Dec 2016 17:28:01 -0500 Subject: [PATCH 3/9] STY: some whitespace cleanups --- lib/matplotlib/font_manager.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 39b1068f57f8..61926a6c095b 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -122,11 +122,13 @@ MSFolders = \ r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders' -MSFontDirectories = [ + +MSFontDirectories = [ r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts', r'SOFTWARE\Microsoft\Windows\CurrentVersion\Fonts'] -X11FontDirectories = [ + +X11FontDirectories = [ # an old standard installation point "/usr/X11R6/lib/X11/fonts/TTF/", "/usr/X11/lib/X11/fonts", @@ -156,6 +158,7 @@ path = os.path.join(home, '.fonts') X11FontDirectories.append(path) + def get_fontext_synonyms(fontext): """ Return a list of file extensions extensions that are synonyms for @@ -165,6 +168,7 @@ def get_fontext_synonyms(fontext): 'otf': ('ttf', 'otf'), 'afm': ('afm',)}[fontext] + def list_fonts(directory, extensions): """ Return a list of all fonts matching any of the extensions, @@ -174,6 +178,7 @@ def list_fonts(directory, extensions): for ext in extensions]) return cbook.listFiles(directory, pattern) + def win32FontDirectory(): """ Return the user-specified font directory for Win32. This is @@ -186,7 +191,7 @@ def win32FontDirectory(): try: from six.moves import winreg except ImportError: - pass # Fall through to default + pass # Fall through to default else: try: user = winreg.OpenKey(winreg.HKEY_CURRENT_USER, MSFolders) @@ -194,13 +199,14 @@ def win32FontDirectory(): try: return winreg.QueryValueEx(user, 'Fonts')[0] except OSError: - pass # Fall through to default + pass # Fall through to default finally: winreg.CloseKey(user) except OSError: - pass # Fall through to default + pass # Fall through to default return os.path.join(os.environ['WINDIR'], 'Fonts') + def win32InstalledFonts(directory=None, fontext='ttf'): """ Search for fonts in the specified font directory, or use the @@ -246,6 +252,7 @@ def win32InstalledFonts(directory=None, fontext='ttf'): winreg.CloseKey(local) return None + def OSXInstalledFonts(directories=None, fontext='ttf'): """ Get list of font files on OS X - ignores font suffix by default. @@ -297,6 +304,7 @@ def get_fontconfig_fonts(fontext='ttf'): if os.path.splitext(fname)[1][1:] in fontext] + def findSystemFonts(fontpaths=None, fontext='ttf'): """ Search for fonts in the specified font paths. If no paths are @@ -338,6 +346,7 @@ def findSystemFonts(fontpaths=None, fontext='ttf'): return [fname for fname in fontfiles if os.path.exists(fname)] + def weight_as_number(weight): """ Return the weight property as a numeric value. String values @@ -419,7 +428,6 @@ def ttfFontProperty(font): else: style = 'normal' - # Variants are: small-caps and normal (default) # !!!! Untested @@ -451,8 +459,8 @@ def ttfFontProperty(font): # Relative stretches are: wider, narrower # Child value is: inherit - if sfnt4.find('narrow') >= 0 or sfnt4.find('condensed') >= 0 or \ - sfnt4.find('cond') >= 0: + if (sfnt4.find('narrow') >= 0 or sfnt4.find('condensed') >= 0 or + sfnt4.find('cond') >= 0): stretch = 'condensed' elif sfnt4.find('demi cond') >= 0: stretch = 'semi-condensed' From bb37ab6fd1e2cbb9049feb2e66225701c36ba68e Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 3 Dec 2016 17:28:12 -0500 Subject: [PATCH 4/9] MNT: ensure that defaults are cached init This appears to already be the case for all of these properties, but remove the rcparams lookup from the get_* methods just to be sure. --- lib/matplotlib/font_manager.py | 33 ++++++--------------------------- lib/matplotlib/text.py | 10 ++++++---- 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 61926a6c095b..505f46094828 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -682,11 +682,11 @@ def __init__(self, _init = None # used only by copy() ): self._family = None - self._slant = None - self._variant = None - self._weight = None - self._stretch = None - self._size = None + self._slant = rcParams['font.style'] + self._variant = rcParams['font.variant'] + self._weight = rcParams['font.weight'] + self._stretch = rcParams['font.stretch'] + self._size = rcParams['font.size'] self._file = None # This is used only by copy() @@ -740,11 +740,6 @@ def get_family(self): """ Return a list of font names that comprise the font family. """ - if self._family is None: - family = rcParams['font.family'] - if is_string_like(family): - return [family] - return family return self._family def get_name(self): @@ -759,8 +754,6 @@ def get_style(self): Return the font style. Values are: 'normal', 'italic' or 'oblique'. """ - if self._slant is None: - return rcParams['font.style'] return self._slant get_slant = get_style @@ -769,8 +762,6 @@ def get_variant(self): Return the font variant. Values are: 'normal' or 'small-caps'. """ - if self._variant is None: - return rcParams['font.variant'] return self._variant def get_weight(self): @@ -780,8 +771,6 @@ def get_weight(self): 'medium', 'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy', 'extra bold', 'black' """ - if self._weight is None: - return rcParams['font.weight'] return self._weight def get_stretch(self): @@ -790,26 +779,16 @@ def get_stretch(self): 'extra-condensed', 'condensed', 'semi-condensed', 'normal', 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded'. """ - if self._stretch is None: - return rcParams['font.stretch'] return self._stretch def get_size(self): """ Return the font size. """ - if self._size is None: - return rcParams['font.size'] return self._size def get_size_in_points(self): - if self._size is not None: - try: - return float(self._size) - except ValueError: - pass - default_size = FontManager.get_default_size() - return default_size * font_scalings.get(self._size) + return self._size def get_file(self): """ diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 8f853133ce0d..a37e9e575df8 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -355,7 +355,7 @@ def _get_layout(self, renderer): baseline = 0 for i, line in enumerate(lines): - clean_line, ismath = self.is_math_text(line) + clean_line, ismath = self.is_math_text(line, self.get_usetex()) if clean_line: w, h, d = renderer.get_text_width_height_descent(clean_line, self._fontproperties, @@ -782,7 +782,7 @@ def draw(self, renderer): y = y + posy if renderer.flipy(): y = canvash - y - clean_line, ismath = textobj.is_math_text(line) + clean_line, ismath = textobj.is_math_text(line, self.get_usetex()) if textobj.get_path_effects(): from matplotlib.patheffects import PathEffectRenderer @@ -1212,7 +1212,7 @@ def set_text(self, s): self.stale = True @staticmethod - def is_math_text(s): + def is_math_text(s, usetex=None): """ Returns a cleaned string and a boolean flag. The flag indicates if the given string *s* contains any mathtext, @@ -1222,7 +1222,9 @@ def is_math_text(s): """ # Did we find an even number of non-escaped dollar signs? # If so, treat is as math text. - if self.get_usetex(): + if usetex is None: + usetex = rcParams['text.usetex'] + if usetex: if s == ' ': s = r'\ ' return s, 'TeX' From a5786285092e880e6a44bf8bcf142f9e4aed6b75 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 4 Dec 2016 16:32:49 -0500 Subject: [PATCH 5/9] STY: wrap long line --- lib/matplotlib/text.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index a37e9e575df8..2fad1ee19caf 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -782,7 +782,8 @@ def draw(self, renderer): y = y + posy if renderer.flipy(): y = canvash - y - clean_line, ismath = textobj.is_math_text(line, self.get_usetex()) + clean_line, ismath = textobj.is_math_text(line, + self.get_usetex()) if textobj.get_path_effects(): from matplotlib.patheffects import PathEffectRenderer From b079583f32723951c122066fe38ebaa2d8335ed1 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 4 Dec 2016 16:40:05 -0500 Subject: [PATCH 6/9] MNT: remove None from value checks `None` used to be a valid value for the style/slant and variant, however in cc617006f7f0a18396cecf4a9f1e222f1ee5204e the behavior was changed to look up the defaults as set time (not get time) so there is no reason to allow invalid values to be set to the internal state. --- lib/matplotlib/font_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 505f46094828..e9c43530ea89 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -834,7 +834,7 @@ def set_style(self, style): """ if style is None: style = rcParams['font.style'] - if style not in ('normal', 'italic', 'oblique', None): + if style not in ('normal', 'italic', 'oblique'): raise ValueError("style must be normal, italic or oblique") self._slant = style set_slant = set_style @@ -845,7 +845,7 @@ def set_variant(self, variant): """ if variant is None: variant = rcParams['font.variant'] - if variant not in ('normal', 'small-caps', None): + if variant not in ('normal', 'small-caps'): raise ValueError("variant must be normal or small-caps") self._variant = variant From 55c266c97924b04cfa1843bac2ea1ce1ae663ed2 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 4 Dec 2016 20:43:46 -0500 Subject: [PATCH 7/9] MNT: set the family to rcParam value in init --- lib/matplotlib/font_manager.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index e9c43530ea89..e642e251c8cf 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -681,7 +681,7 @@ def __init__(self, fname = None, # if this is set, it's a hardcoded filename to use _init = None # used only by copy() ): - self._family = None + self._family = _normalize_font_family(rcParams['font.family']) self._slant = rcParams['font.style'] self._variant = rcParams['font.variant'] self._weight = rcParams['font.weight'] @@ -820,10 +820,7 @@ def set_family(self, family): """ if family is None: family = rcParams['font.family'] - if is_string_like(family): - family = [six.text_type(family)] - elif not is_string_like(family) and isinstance(family, Iterable): - family = [six.text_type(f) for f in family] + family = _normalize_font_family(family) self._family = family set_name = set_family @@ -967,6 +964,14 @@ def pickle_load(filename): return data +def _normalize_font_family(family): + if is_string_like(family): + family = [six.text_type(family)] + elif (not is_string_like(family) and isinstance(family, Iterable)): + family = [six.text_type(f) for f in family] + return family + + class TempCache(object): """ A class to store temporary caches that are (a) not saved to disk From b6278e3e9402852c2a6d8a8fb053482406ade8ee Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 4 Dec 2016 20:47:17 -0500 Subject: [PATCH 8/9] MNT: use else block to localize exceptions --- lib/matplotlib/font_manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index e642e251c8cf..f30c8697dcc2 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -896,11 +896,13 @@ def set_size(self, size): except ValueError: try: scale = font_scalings[size] - size = scale * FontManager.get_default_size() except KeyError: raise ValueError( "Size is invalid. Valid font size are " + ", ".join( str(i) for i in font_scalings.keys())) + else: + size = scale * FontManager.get_default_size() + self._size = size def set_file(self, file): From 5f4eeadbd3a9c9b2d395e33728092ceda87578ca Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 12 Dec 2016 15:16:59 -0500 Subject: [PATCH 9/9] MNT: minor simplification --- lib/matplotlib/font_manager.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index f30c8697dcc2..dc41207b0a18 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -820,8 +820,7 @@ def set_family(self, family): """ if family is None: family = rcParams['font.family'] - family = _normalize_font_family(family) - self._family = family + self._family = _normalize_font_family(family) set_name = set_family def set_style(self, style): @@ -948,6 +947,7 @@ def ttfdict_to_fnames(d): fnames.append(fname) return fnames + def pickle_dump(data, filename): """ Equivalent to pickle.dump(data, open(filename, 'w')) @@ -956,6 +956,7 @@ def pickle_dump(data, filename): with open(filename, 'wb') as fh: pickle.dump(data, fh) + def pickle_load(filename): """ Equivalent to pickle.load(open(filename, 'r')) @@ -969,7 +970,7 @@ def pickle_load(filename): def _normalize_font_family(family): if is_string_like(family): family = [six.text_type(family)] - elif (not is_string_like(family) and isinstance(family, Iterable)): + elif isinstance(family, Iterable): family = [six.text_type(f) for f in family] return family