Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cygwin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ jobs:
export PATH="/usr/local/bin:$PATH"
python -m pip install --no-build-isolation 'contourpy>=1.0.1'
python -m pip install --upgrade cycler fonttools \
packaging pyparsing python-dateutil setuptools-scm \
packaging pyparsing python-dateutil 'setuptools-scm<10' \
-r requirements_test.txt sphinx ipython
python -m pip install --upgrade pycairo 'cairocffi>=0.8' PyGObject &&
python -c 'import gi; gi.require_version("Gtk", "3.0"); from gi.repository import Gtk' &&
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ jobs:
# Preinstall build requirements to enable no-build-isolation builds.
python -m pip install --upgrade $PRE \
'contourpy>=1.0.1' cycler fonttools kiwisolver importlib_resources \
packaging pillow 'pyparsing!=3.1.0' python-dateutil setuptools-scm \
packaging pillow 'pyparsing!=3.1.0' python-dateutil 'setuptools-scm<10' \
'meson-python>=0.13.1' 'pybind11>=2.13.2' \
-r requirements/testing/all.txt \
${{ matrix.extra-requirements }}
Expand Down
8 changes: 8 additions & 0 deletions lib/matplotlib/_afm.py
Original file line number Diff line number Diff line change
Expand Up @@ -478,10 +478,18 @@ def get_angle(self) -> float:
"""Return the fontangle as float."""
return self._header['ItalicAngle']

def get_ascender(self) -> float:
"""Return the ascent as float."""
return self._header['Ascender']

def get_capheight(self) -> float:
"""Return the cap height as float."""
return self._header['CapHeight']

def get_descender(self) -> float:
"""Return the descent as float."""
return self._header['Descender']

def get_xheight(self) -> float:
"""Return the xheight as float."""
return self._header['XHeight']
Expand Down
26 changes: 26 additions & 0 deletions lib/matplotlib/backends/_backend_pdf_ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,32 @@ def get_canvas_width_height(self):
# docstring inherited
return self.width * 72.0, self.height * 72.0

def _get_font_height_metrics(self, prop):
"""
Return the ascent, descent, and line gap for font described by *prop*.

TODO: This is a temporary method until we design a proper API for the backends.

Parameters
----------
prop : `.font_manager.FontProperties`
The properties describing the font to measure.

Returns
-------
ascent, descent, line_gap : float or None
The ascent, descent and line gap of the determined font, or None to fall
back to normal measurements.
"""
if not mpl.rcParams[self._use_afm_rc_name]:
return None, None, None
font = self._get_font_afm(prop)
scale = prop.get_size_in_points() / 1000
a = font.get_ascender() * scale
d = -font.get_descender() * scale
g = (a + d) * 0.2 # Preserve previous line spacing of 1.2.
return a, d, g

def get_text_width_height_descent(self, s, prop, ismath):
# docstring inherited
if ismath == "TeX":
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion lib/matplotlib/tests/test_bbox_tight.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_bbox_inches_tight(text_placeholders):

@image_comparison(['bbox_inches_tight_suptile_legend'],
savefig_kwarg={'bbox_inches': 'tight'},
tol=0 if platform.machine() == 'x86_64' else 0.022)
tol=0 if platform.machine() == 'x86_64' else 0.024)
def test_bbox_inches_tight_suptile_legend():
plt.plot(np.arange(10), label='a straight line')
plt.legend(bbox_to_anchor=(0.9, 1), loc='upper left')
Expand Down
7 changes: 5 additions & 2 deletions lib/matplotlib/tests/test_mathtext.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,11 @@ def baseline_images(request, fontset, index, text):
@pytest.mark.parametrize(
'fontset', ['cm', 'stix', 'stixsans', 'dejavusans', 'dejavuserif'])
@pytest.mark.parametrize('baseline_images', ['mathtext'], indirect=True)
@image_comparison(baseline_images=None, style='mpl20',
tol=0.011 if platform.machine() in ('ppc64le', 's390x') else 0)
@image_comparison(
baseline_images=None, style='mpl20',
tol=(0.013
if platform.machine() in ('ppc64le', 's390x') or platform.system() == 'Windows'
else 0))
def test_mathtext_rendering(baseline_images, fontset, index, text):
mpl.rcParams['mathtext.fontset'] = fontset
fig = plt.figure(figsize=(5.25, 0.75))
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/tests/test_patheffects.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def test_SimplePatchShadow_offset():
assert pe._offset == (4, 5)


@image_comparison(['collection'], tol=0.03, style='mpl20')
@image_comparison(['collection'], tol=0.032, style='mpl20')
def test_collection():
x, y = np.meshgrid(np.linspace(0, 10, 150), np.linspace(-5, 5, 100))
data = np.sin(x) + np.cos(y)
Expand Down
39 changes: 23 additions & 16 deletions lib/matplotlib/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,22 +437,29 @@ def _get_layout(self, renderer):
dpi = self.get_figure(root=True).dpi
# Determine full vertical extent of font, including ascenders and descenders:
if not self.get_usetex():
font = get_font(fontManager._find_fonts_by_props(self._fontproperties))
possible_metrics = [
('OS/2', 'sTypoLineGap', 'sTypoAscender', 'sTypoDescender'),
('hhea', 'lineGap', 'ascent', 'descent')
]
for table_name, linegap_key, ascent_key, descent_key in possible_metrics:
table = font.get_sfnt_table(table_name)
if table is None:
continue
# Rescale to font size/DPI if the metrics were available.
fontsize = self._fontproperties.get_size_in_points()
units_per_em = font.get_sfnt_table('head')['unitsPerEm']
line_gap = table[linegap_key] / units_per_em * fontsize * dpi / 72
min_ascent = table[ascent_key] / units_per_em * fontsize * dpi / 72
min_descent = -table[descent_key] / units_per_em * fontsize * dpi / 72
break
if hasattr(renderer, '_get_font_height_metrics'):
# TODO: This is a temporary internal method call (for _backend_pdf_ps to
# support AFM files) until we design a proper API for the backends.
min_ascent, min_descent, line_gap = renderer._get_font_height_metrics(
self._fontproperties)
if min_ascent is None:
font = get_font(fontManager._find_fonts_by_props(self._fontproperties))
possible = [
('OS/2', 'sTypoLineGap', 'sTypoAscender', 'sTypoDescender'),
('hhea', 'lineGap', 'ascent', 'descent')
]
for table_name, linegap_key, ascent_key, descent_key in possible:
table = font.get_sfnt_table(table_name)
if table is None:
continue
# Rescale to font size/DPI if the metrics were available.
fontsize = self._fontproperties.get_size_in_points()
units_per_em = font.get_sfnt_table('head')['unitsPerEm']
scale = 1 / units_per_em * fontsize * dpi / 72
line_gap = table[linegap_key] * scale
min_ascent = table[ascent_key] * scale
min_descent = -table[descent_key] * scale
break
if None in (min_ascent, min_descent):
# Fallback to font measurement.
_, h, min_descent = _get_text_metrics_with_cache(
Expand Down
2 changes: 1 addition & 1 deletion lib/mpl_toolkits/axisartist/tests/test_floating_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def test_curvelinear3():
l.set_clip_path(ax1.patch)


@image_comparison(['curvelinear4.png'], style='mpl20')
@image_comparison(['curvelinear4.png'], style='mpl20', tol=0.04)
def test_curvelinear4():
fig = plt.figure(figsize=(5, 5))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def inverted(self):
ax1.grid(True)


@image_comparison(['polar_box.png'], style='mpl20')
@image_comparison(['polar_box.png'], style='mpl20', tol=0.04)
def test_polar_box():
plt.rcParams.update({"xtick.direction": "inout", "ytick.direction": "out"})
fig = plt.figure(figsize=(5, 5))
Expand Down Expand Up @@ -138,7 +138,7 @@ def test_polar_box():
ax1.grid(True)


@image_comparison(['axis_direction.png'], style='mpl20')
@image_comparison(['axis_direction.png'], style='mpl20', tol=0.04)
def test_axis_direction():
fig = plt.figure(figsize=(5, 5))

Expand Down
2 changes: 1 addition & 1 deletion lib/mpl_toolkits/mplot3d/tests/test_axes3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -2878,7 +2878,7 @@ def _make_triangulation_data():


@mpl3d_image_comparison(['scale3d_artists_log.png'], style='mpl20',
remove_text=False, tol=0.03)
remove_text=False, tol=0.032)
def test_scale3d_artists_log():
"""Test all 3D artist types with log scale."""
fig = plt.figure(figsize=(16, 12))
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ requires-python = ">=3.11"
dev = [
"meson-python>=0.13.2,!=0.17.*",
"pybind11>=2.13.2,!=2.13.3",
"setuptools_scm>=7",
"setuptools_scm>=7,<10",
# Not required by us but setuptools_scm without a version, cso _if_
# installed, then setuptools_scm 8 requires at least this version.
# Unfortunately, we can't do a sort of minimum-if-installed dependency, so
Expand All @@ -75,7 +75,7 @@ requires = [
# you really need it and aren't using an sdist.
"meson-python>=0.13.2,!=0.17.*",
"pybind11>=2.13.2,!=2.13.3",
"setuptools_scm>=7",
"setuptools_scm>=7,<10",
]

[tool.meson-python.args]
Expand Down
2 changes: 1 addition & 1 deletion requirements/dev/build-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pybind11>=2.13.2,!=2.13.3
meson-python
setuptools-scm
setuptools-scm<10
2 changes: 1 addition & 1 deletion requirements/testing/mypy.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ packaging>=20.0
pillow>=9
pyparsing>=3
python-dateutil>=2.7
setuptools_scm>=7
setuptools_scm>=7,<10
setuptools>=64
Loading