diff --git a/lib/matplotlib/afm.py b/lib/matplotlib/afm.py
index 58f63ba0176a..71df90ab73ee 100644
--- a/lib/matplotlib/afm.py
+++ b/lib/matplotlib/afm.py
@@ -523,6 +523,10 @@ def get_familyname(self):
br'light|ultralight|extra|condensed))+$')
return re.sub(extras, '', name)
+ @property
+ def family_name(self):
+ return self.get_familyname()
+
def get_weight(self):
"Return the font weight, e.g., 'Bold' or 'Roman'"
return self._header[b'Weight']
diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py
index 2e17a7aef99f..46f4fb100454 100644
--- a/lib/matplotlib/mathtext.py
+++ b/lib/matplotlib/mathtext.py
@@ -1240,58 +1240,115 @@ def get_underline_thickness(self, font, fontsize, dpi):
# The number of different sizes of chars to use, beyond which they will not
# get any smaller
NUM_SIZE_LEVELS = 6
-# Percentage of x-height of additional horiz. space after sub/superscripts
-SCRIPT_SPACE = {'cm': 0.075,
- 'stix': 0.10,
- 'stixsans': 0.05,
- 'dejavuserif': 0.05,
- 'dejavusans': 0.05}
-## Percentage of x-height that sub/superscripts drop below the baseline
-SUBDROP = {'cm': 0.2,
- 'stix': 0.4,
- 'stixsans': 0.4,
- 'dejavuserif': 0.4,
- 'dejavusans': 0.4}
-# Percentage of x-height that superscripts are raised from the baseline
-SUP1 = {'cm': 0.45,
- 'stix': 0.8,
- 'stixsans': 0.8,
- 'dejavuserif': 0.7,
- 'dejavusans': 0.7}
-# Percentage of x-height that subscripts drop below the baseline
-SUB1 = {'cm': 0.2,
- 'stix': 0.3,
- 'stixsans': 0.3,
- 'dejavuserif': 0.3,
- 'dejavusans': 0.3}
-# Percentage of x-height that subscripts drop below the baseline when a
-# superscript is present
-SUB2 = {'cm': 0.3,
- 'stix': 0.6,
- 'stixsans': 0.5,
- 'dejavuserif': 0.5,
- 'dejavusans': 0.5}
-# Percentage of x-height that sub/supercripts are offset relative to the
-# nucleus edge for non-slanted nuclei
-DELTA = {'cm': 0.075,
- 'stix': 0.05,
- 'stixsans': 0.025,
- 'dejavuserif': 0.025,
- 'dejavusans': 0.025}
-# Additional percentage of last character height above 2/3 of the x-height that
-# supercripts are offset relative to the subscript for slanted nuclei
-DELTASLANTED = {'cm': 0.3,
- 'stix': 0.3,
- 'stixsans': 0.6,
- 'dejavuserif': 0.2,
- 'dejavusans': 0.2}
-# Percentage of x-height that supercripts and subscripts are offset for
-# integrals
-DELTAINTEGRAL = {'cm': 0.3,
- 'stix': 0.3,
- 'stixsans': 0.3,
- 'dejavuserif': 0.1,
- 'dejavusans': 0.1}
+
+
+class FontConstantsBase(object):
+ """
+ A set of constants that controls how certain things, such as sub-
+ and superscripts are laid out. These are all metrics that can't
+ be reliably retrieved from the font metrics in the font itself.
+ """
+ # Percentage of x-height of additional horiz. space after sub/superscripts
+ script_space = 0.05
+
+ # Percentage of x-height that sub/superscripts drop below the baseline
+ subdrop = 0.4
+
+ # Percentage of x-height that superscripts are raised from the baseline
+ sup1 = 0.7
+
+ # Percentage of x-height that subscripts drop below the baseline
+ sub1 = 0.3
+
+ # Percentage of x-height that subscripts drop below the baseline when a
+ # superscript is present
+ sub2 = 0.5
+
+ # Percentage of x-height that sub/supercripts are offset relative to the
+ # nucleus edge for non-slanted nuclei
+ delta = 0.025
+
+ # Additional percentage of last character height above 2/3 of the
+ # x-height that supercripts are offset relative to the subscript
+ # for slanted nuclei
+ delta_slanted = 0.2
+
+ # Percentage of x-height that supercripts and subscripts are offset for
+ # integrals
+ delta_integral = 0.1
+
+
+class ComputerModernFontConstants(FontConstantsBase):
+ script_space = 0.075
+ subdrop = 0.2
+ sup1 = 0.45
+ sub1 = 0.2
+ sub2 = 0.3
+ delta = 0.075
+ delta_slanted = 0.3
+ delta_integral = 0.3
+
+
+class STIXFontConstants(FontConstantsBase):
+ script_space = 0.1
+ sup1 = 0.8
+ sub2 = 0.6
+ delta = 0.05
+ delta_slanted = 0.3
+ delta_integral = 0.3
+
+
+class STIXSansFontConstants(FontConstantsBase):
+ script_space = 0.05
+ sup1 = 0.8
+ delta_slanted = 0.6
+ delta_integral = 0.3
+
+
+class DejaVuSerifFontConstants(FontConstantsBase):
+ pass
+
+
+class DejaVuSansFontConstants(FontConstantsBase):
+ pass
+
+
+# Maps font family names to the FontConstantBase subclass to use
+_font_constant_mapping = {
+ 'DejaVu Sans': DejaVuSansFontConstants,
+ 'DejaVu Sans Mono': DejaVuSansFontConstants,
+ 'DejaVu Serif': DejaVuSerifFontConstants,
+ 'cmb10': ComputerModernFontConstants,
+ 'cmex10': ComputerModernFontConstants,
+ 'cmmi10': ComputerModernFontConstants,
+ 'cmr10': ComputerModernFontConstants,
+ 'cmss10': ComputerModernFontConstants,
+ 'cmsy10': ComputerModernFontConstants,
+ 'cmtt10': ComputerModernFontConstants,
+ 'STIXGeneral': STIXFontConstants,
+ 'STIXNonUnicode': STIXFontConstants,
+ 'STIXSizeFiveSym': STIXFontConstants,
+ 'STIXSizeFourSym': STIXFontConstants,
+ 'STIXSizeThreeSym': STIXFontConstants,
+ 'STIXSizeTwoSym': STIXFontConstants,
+ 'STIXSizeOneSym': STIXFontConstants,
+ # Map the fonts we used to ship, just for good measure
+ 'Bitstream Vera Sans': DejaVuSansFontConstants,
+ 'Bitstream Vera': DejaVuSansFontConstants,
+ }
+
+
+def _get_font_constant_set(state):
+ constants = _font_constant_mapping.get(
+ state.font_output._get_font(state.font).family_name,
+ FontConstantsBase)
+ # STIX sans isn't really its own fonts, just different code points
+ # in the STIX fonts, so we have to detect this one separately.
+ if (constants is STIXFontConstants and
+ isinstance(state.font_output, StixSansFonts)):
+ return STIXSansFontConstants
+ return constants
+
class MathTextWarning(Warning):
pass
@@ -2873,25 +2930,24 @@ def subsuper(self, s, loc, toks):
nucleus = Hlist([nucleus])
# Handle regular sub/superscripts
-
- fs = rcParams['mathtext.fontset']
- if fs == 'custom':
- fs = 'dejavusans'
-
+ constants = _get_font_constant_set(state)
lc_height = last_char.height
lc_baseline = 0
if self.is_dropsub(last_char):
lc_baseline = last_char.depth
# Compute kerning for sub and super
- superkern = DELTA[fs] * xHeight
- subkern = DELTA[fs] * xHeight
+ superkern = constants.delta * xHeight
+ subkern = constants.delta * xHeight
if self.is_slanted(last_char):
- superkern += DELTA[fs] * xHeight
- superkern += DELTASLANTED[fs] * (lc_height - xHeight * 2. / 3.)
+ superkern += constants.delta * xHeight
+ superkern += (constants.delta_slanted *
+ (lc_height - xHeight * 2. / 3.))
if self.is_dropsub(last_char):
- subkern = (3 * DELTA[fs] - DELTAINTEGRAL[fs]) * lc_height
- superkern = (3 * DELTA[fs] + DELTAINTEGRAL[fs]) * lc_height
+ subkern = (3 * constants.delta -
+ constants.delta_integral) * lc_height
+ superkern = (3 * constants.delta +
+ constants.delta_integral) * lc_height
else:
subkern = 0
@@ -2900,26 +2956,26 @@ def subsuper(self, s, loc, toks):
x = Hlist([Kern(subkern), sub])
x.shrink()
if self.is_dropsub(last_char):
- shift_down = lc_baseline + SUBDROP[fs] * xHeight
+ shift_down = lc_baseline + constants.subdrop * xHeight
else:
- shift_down = SUB1[fs] * xHeight
+ shift_down = constants.sub1 * xHeight
x.shift_amount = shift_down
else:
x = Hlist([Kern(superkern), super])
x.shrink()
if self.is_dropsub(last_char):
- shift_up = lc_height - SUBDROP[fs] * xHeight
+ shift_up = lc_height - constants.subdrop * xHeight
else:
- shift_up = SUP1[fs] * xHeight
+ shift_up = constants.sup1 * xHeight
if sub is None:
x.shift_amount = -shift_up
else: # Both sub and superscript
y = Hlist([Kern(subkern),sub])
y.shrink()
if self.is_dropsub(last_char):
- shift_down = lc_baseline + SUBDROP[fs] * xHeight
+ shift_down = lc_baseline + constants.subdrop * xHeight
else:
- shift_down = SUB2[fs] * xHeight
+ shift_down = constants.sub2 * xHeight
# If sub and superscript collide, move super up
clr = (2.0 * rule_thickness -
((shift_up - x.depth) - (y.height - shift_down)))
@@ -2931,8 +2987,9 @@ def subsuper(self, s, loc, toks):
x.shift_amount = shift_down
if not self.is_dropsub(last_char):
- x.width += SCRIPT_SPACE[fs] * xHeight
+ x.width += constants.script_space * xHeight
result = Hlist([nucleus, x])
+
return [result]
def _genfrac(self, ldelim, rdelim, rule, style, num, den):
diff --git a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle
index e30f95341f19..2429f2eb8fea 100644
--- a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle
+++ b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle
@@ -76,11 +76,11 @@ font.stretch : normal
# relative to font.size, using the following values: xx-small, x-small,
# small, medium, large, x-large, xx-large, larger, or smaller
font.size : 12.0
-font.serif : Bitstream Vera Serif, DejaVu Serif, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif
-font.sans-serif: Bitstream Vera Sans, DejaVu Sans, Lucida Grande, Verdana, Geneva, Lucid, Arial, Helvetica, Avant Garde, sans-serif
+font.serif : DejaVu Serif, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif
+font.sans-serif: DejaVu Sans, Lucida Grande, Verdana, Geneva, Lucid, Arial, Helvetica, Avant Garde, sans-serif
font.cursive : Apple Chancery, Textile, Zapf Chancery, Sand, Script MT, Felipa, cursive
font.fantasy : Comic Sans MS, Chicago, Charcoal, ImpactWestern, Humor Sans, fantasy
-font.monospace : Bitstream Vera Sans Mono, DejaVu Sans Mono, Andale Mono, Nimbus Mono L, Courier New, Courier, Fixed, Terminal, monospace
+font.monospace : DejaVu Sans Mono, Andale Mono, Nimbus Mono L, Courier New, Courier, Fixed, Terminal, monospace
### TEXT
# text properties used by text.Text. See
diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py
index f9a39fb6c500..8d8f2a27ca33 100644
--- a/lib/matplotlib/testing/decorators.py
+++ b/lib/matplotlib/testing/decorators.py
@@ -22,6 +22,7 @@
from matplotlib import ticker
from matplotlib import pyplot as plt
from matplotlib import ft2font
+from matplotlib import rcParams
from matplotlib.testing.noseclasses import KnownFailureTest, \
KnownFailureDidNotFailTest, ImageComparisonFailure
from matplotlib.testing.compare import comparable_formats, compare_images, \
@@ -109,18 +110,44 @@ def tearDownClass(cls):
cls.original_settings)
-def cleanup(func):
- @functools.wraps(func)
- def wrapped_function(*args, **kwargs):
- original_units_registry = matplotlib.units.registry.copy()
- original_settings = mpl.rcParams.copy()
- try:
- func(*args, **kwargs)
- finally:
- _do_cleanup(original_units_registry,
- original_settings)
+def cleanup(style=None):
+ """
+ A decorator to ensure that any global state is reset before
+ running a test.
+
+ Parameters
+ ----------
+ style : str, optional
+ The name of the style to apply.
+ """
- return wrapped_function
+ # If cleanup is used without arguments, `style` will be a
+ # callable, and we pass it directly to the wrapper generator. If
+ # cleanup if called with an argument, it is a string naming a
+ # style, and the function will be passed as an argument to what we
+ # return. This is a confusing, but somewhat standard, pattern for
+ # writing a decorator with optional arguments.
+
+ def make_cleanup(func):
+ @functools.wraps(func)
+ def wrapped_function(*args, **kwargs):
+ original_units_registry = matplotlib.units.registry.copy()
+ original_settings = mpl.rcParams.copy()
+ matplotlib.style.use(style)
+ try:
+ func(*args, **kwargs)
+ finally:
+ _do_cleanup(original_units_registry,
+ original_settings)
+
+ return wrapped_function
+
+ if isinstance(style, six.string_types):
+ return make_cleanup
+ else:
+ result = make_cleanup(style)
+ style = 'classic'
+ return result
def check_freetype_version(ver):
@@ -138,6 +165,7 @@ def check_freetype_version(ver):
class ImageComparisonTest(CleanupTest):
@classmethod
def setup_class(cls):
+ CleanupTest.setup_class()
cls._initial_settings = mpl.rcParams.copy()
try:
matplotlib.style.use(cls._style)
@@ -146,11 +174,8 @@ def setup_class(cls):
mpl.rcParams.clear()
mpl.rcParams.update(cls._initial_settings)
raise
- # Because the setup of a CleanupTest might involve
- # modifying a few rcparams, this setup should come
- # last prior to running the image test.
- CleanupTest.setup_class()
cls.original_settings = cls._initial_settings
+ matplotlib.tests.set_font_settings_for_testing()
cls._func()
@classmethod
diff --git a/lib/matplotlib/tests/__init__.py b/lib/matplotlib/tests/__init__.py
index c2900b57a76d..e3293907951b 100644
--- a/lib/matplotlib/tests/__init__.py
+++ b/lib/matplotlib/tests/__init__.py
@@ -22,6 +22,12 @@
'test data.')
+def set_font_settings_for_testing():
+ rcParams['font.family'] = 'DejaVu Sans'
+ rcParams['text.hinting'] = False
+ rcParams['text.hinting_factor'] = 8
+
+
def setup():
# The baseline images are created in this locale, so we should use
# it during all of the tests.
@@ -45,9 +51,8 @@ def setup():
# tests and are not necessarily the default values as specified in
# rcsetup.py
rcdefaults() # Start with all defaults
- rcParams['font.family'] = 'DejaVu Sans'
- rcParams['text.hinting'] = False
- rcParams['text.hinting_factor'] = 8
+
+ set_font_settings_for_testing()
def assert_str_equal(reference_str, test_str,
diff --git a/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf b/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf
index 92d378e13e62..91c923885675 100644
Binary files a/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf differ
diff --git a/lib/matplotlib/tests/baseline_images/test_axes/symlog.png b/lib/matplotlib/tests/baseline_images/test_axes/symlog.png
index 03557d68d10c..ffde731e9e7c 100644
Binary files a/lib/matplotlib/tests/baseline_images/test_axes/symlog.png and b/lib/matplotlib/tests/baseline_images/test_axes/symlog.png differ
diff --git a/lib/matplotlib/tests/baseline_images/test_axes/symlog.svg b/lib/matplotlib/tests/baseline_images/test_axes/symlog.svg
index 9ef7004e5d17..209dcb768852 100644
--- a/lib/matplotlib/tests/baseline_images/test_axes/symlog.svg
+++ b/lib/matplotlib/tests/baseline_images/test_axes/symlog.svg
@@ -27,7 +27,7 @@ z
" style="fill:#ffffff;"/>
-
-
-
-
-
@@ -63,20 +63,20 @@ L 518.4 43.2
+" id="m6c27b91e56" style="stroke:#000000;stroke-width:0.5;"/>
-
+
+" id="m193bcf3f41" style="stroke:#000000;stroke-width:0.5;"/>
-
+
@@ -100,22 +100,22 @@ Q 19.53125 -1.421875 13.0625 8.265625
Q 6.59375 17.96875 6.59375 36.375
Q 6.59375 54.828125 13.0625 64.515625
Q 19.53125 74.21875 31.78125 74.21875
-" id="BitstreamVeraSans-Roman-30"/>
+" id="DejaVuSans-30"/>
-
+
-
+
-
+
@@ -144,22 +144,22 @@ Q 35.15625 39.890625 26.703125 39.890625
Q 22.75 39.890625 18.8125 39.015625
Q 14.890625 38.140625 10.796875 36.28125
z
-" id="BitstreamVeraSans-Roman-35"/>
+" id="DejaVuSans-35"/>
-
+
-
+
-
+
@@ -177,42 +177,42 @@ L 54.390625 8.296875
L 54.390625 0
L 12.40625 0
z
-" id="BitstreamVeraSans-Roman-31"/>
+" id="DejaVuSans-31"/>
-
-
+
+
-
+
-
+
-
-
+
+
-
+
-
+
@@ -240,30 +240,30 @@ Q 53.21875 48.921875 51.53125 44.890625
Q 49.859375 40.875 45.40625 35.40625
Q 44.1875 33.984375 37.640625 27.21875
Q 31.109375 20.453125 19.1875 8.296875
-" id="BitstreamVeraSans-Roman-32"/>
+" id="DejaVuSans-32"/>
-
-
+
+
-
+
-
+
-
-
+
+
@@ -274,98 +274,98 @@ Q 31.109375 20.453125 19.1875 8.296875
+" id="maee9fb9c95" style="stroke:#000000;stroke-width:0.5;"/>
-
+
+" id="m9b957b9e3b" style="stroke:#000000;stroke-width:0.5;"/>
-
+
-
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
+
@@ -401,24 +401,24 @@ Q 40.828125 74.21875 47.359375 69.109375
Q 53.90625 64.015625 53.90625 55.328125
Q 53.90625 49.265625 50.4375 45.09375
Q 46.96875 40.921875 40.578125 39.3125
-" id="BitstreamVeraSans-Roman-33"/>
+" id="DejaVuSans-33"/>
-
-
-
-
+
+
+
+
-
+
-
+
@@ -440,44 +440,44 @@ L 37.796875 17.1875
L 4.890625 17.1875
L 4.890625 26.703125
z
-" id="BitstreamVeraSans-Roman-34"/>
+" id="DejaVuSans-34"/>
-
-
-
-
+
+
+
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
+
@@ -510,24 +510,24 @@ Q 6.984375 53.65625 15.1875 63.9375
Q 23.390625 74.21875 37.203125 74.21875
Q 40.921875 74.21875 44.703125 73.484375
Q 48.484375 72.75 52.59375 71.296875
-" id="BitstreamVeraSans-Roman-36"/>
+" id="DejaVuSans-36"/>
-
-
-
-
+
+
+
+
-
+
-
+
@@ -541,12 +541,12 @@ L 18.3125 0
L 43.21875 64.59375
L 8.203125 64.59375
z
-" id="BitstreamVeraSans-Roman-37"/>
+" id="DejaVuSans-37"/>
-
-
-
-
+
+
+
+
@@ -555,116 +555,116 @@ z
+" id="mf4ae001ace" style="stroke:#000000;stroke-width:0.5;"/>
-
+
+" id="m340d5bdc7f" style="stroke:#000000;stroke-width:0.5;"/>
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -672,7 +672,7 @@ L -2 0
-
+
diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf
index 8652c71c2ed5..e6199559b9da 100644
Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf and b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.pdf differ
diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png
index 9ebb4c578593..a4f11d9fac9d 100644
Binary files a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png and b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.png differ
diff --git a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg
index 902801f3962d..2e1ab2a7c384 100644
--- a/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg
+++ b/lib/matplotlib/tests/baseline_images/test_text/text_alignment.svg
@@ -5,740 +5,853 @@