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 @@ - - - + - + - + - + - + - + - - - - + + - + - + - +" id="BitstreamVeraSans-Roman-74"/> + - - - - - - + + + + + + - + + - + + - - - +" id="Cmex10-58"/> - - - - - - + + + + + + - - - + +" id="BitstreamVeraSans-Roman-62"/> - - - - - - - - - + + + + + + + + + - - - - - - + + + + + + - - + - - + - - + + - + +M52.2031 31.2031 +L52.2031 0 +L43.2188 0 +L43.2188 8.29688 +Q40.1406 3.32812 35.5469 0.953125 +Q30.9531 -1.42188 24.3125 -1.42188 +Q15.9219 -1.42188 10.9531 3.29688 +Q6 8.01562 6 15.9219 +Q6 25.1406 12.1719 29.8281 +Q18.3594 34.5156 30.6094 34.5156 +L43.2188 34.5156 +L43.2188 35.4062 +Q43.2188 41.6094 39.1406 45 +Q35.0625 48.3906 27.6875 48.3906 +Q23 48.3906 18.5469 47.2656 +Q14.1094 46.1406 10.0156 43.8906 +L10.0156 52.2031 +Q14.9375 54.1094 19.5781 55.0469 +Q24.2188 56 28.6094 56 +Q40.4844 56 46.3438 49.8438 +Q52.2031 43.7031 52.2031 31.2031" id="BitstreamVeraSans-Roman-61"/> + - - - - - - - - - - - + + + + + + + + + + + - - - - - + + + + + - - - +" id="BitstreamVeraSans-Roman-72"/> + - - - - - - - - - + + + + + + + + + - - - - - - + + + + + + - - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - - - - - + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - + + + + + + + + + - - - - - - + + + + + + - +