Skip to content

Commit beec09b

Browse files
committed
ps/pdf: Override font height metrics to support AFM files
When outputting files using the "core 14 fonts" (e.g., `rcParams['pdf.use14corefonts'] = True`), text will be measured using our internal AFM files instead of any external files. While we don't have a full API decided for how to pass full `Text` objects through backends, add in a small internal helper to allow the PS/PDF backends to override font height metrics with the AFM files.
1 parent 1f064dc commit beec09b

File tree

4 files changed

+55
-16
lines changed

4 files changed

+55
-16
lines changed

lib/matplotlib/_afm.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,10 +478,18 @@ def get_angle(self) -> float:
478478
"""Return the fontangle as float."""
479479
return self._header['ItalicAngle']
480480

481+
def get_ascender(self) -> float:
482+
"""Return the ascent as float."""
483+
return self._header['Ascender']
484+
481485
def get_capheight(self) -> float:
482486
"""Return the cap height as float."""
483487
return self._header['CapHeight']
484488

489+
def get_descender(self) -> float:
490+
"""Return the descent as float."""
491+
return self._header['Descender']
492+
485493
def get_xheight(self) -> float:
486494
"""Return the xheight as float."""
487495
return self._header['XHeight']

lib/matplotlib/backends/_backend_pdf_ps.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,32 @@ def get_canvas_width_height(self):
348348
# docstring inherited
349349
return self.width * 72.0, self.height * 72.0
350350

351+
def _get_font_height_metrics(self, prop):
352+
"""
353+
Return the ascent, descent, and line gap for font described by *prop*.
354+
355+
TODO: This is a temporary method until we design a proper API for the backends.
356+
357+
Parameters
358+
----------
359+
prop : `.font_manager.FontProperties`
360+
The properties describing the font to measure.
361+
362+
Returns
363+
-------
364+
ascent, descent, line_gap : float or None
365+
The ascent, descent and line gap of the determined font, or None to fall
366+
back to normal measurements.
367+
"""
368+
if not mpl.rcParams[self._use_afm_rc_name]:
369+
return None, None, None
370+
font = self._get_font_afm(prop)
371+
scale = prop.get_size_in_points() / 1000
372+
a = font.get_ascender() * scale
373+
d = -font.get_descender() * scale
374+
g = (a + d) * 0.2 # Preserve previous line spacing of 1.2.
375+
return a, d, g
376+
351377
def get_text_width_height_descent(self, s, prop, ismath):
352378
# docstring inherited
353379
if ismath == "TeX":
Binary file not shown.

lib/matplotlib/text.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -437,22 +437,27 @@ def _get_layout(self, renderer):
437437
dpi = self.get_figure(root=True).dpi
438438
# Determine full vertical extent of font, including ascenders and descenders:
439439
if not self.get_usetex():
440-
font = get_font(fontManager._find_fonts_by_props(self._fontproperties))
441-
possible_metrics = [
442-
('OS/2', 'sTypoLineGap', 'sTypoAscender', 'sTypoDescender'),
443-
('hhea', 'lineGap', 'ascent', 'descent')
444-
]
445-
for table_name, linegap_key, ascent_key, descent_key in possible_metrics:
446-
table = font.get_sfnt_table(table_name)
447-
if table is None:
448-
continue
449-
# Rescale to font size/DPI if the metrics were available.
450-
fontsize = self._fontproperties.get_size_in_points()
451-
units_per_em = font.get_sfnt_table('head')['unitsPerEm']
452-
line_gap = table[linegap_key] / units_per_em * fontsize * dpi / 72
453-
min_ascent = table[ascent_key] / units_per_em * fontsize * dpi / 72
454-
min_descent = -table[descent_key] / units_per_em * fontsize * dpi / 72
455-
break
440+
if hasattr(renderer, '_get_font_height_metrics'):
441+
min_ascent, min_descent, line_gap = renderer._get_font_height_metrics(
442+
self._fontproperties)
443+
if min_ascent is None:
444+
font = get_font(fontManager._find_fonts_by_props(self._fontproperties))
445+
possible = [
446+
('OS/2', 'sTypoLineGap', 'sTypoAscender', 'sTypoDescender'),
447+
('hhea', 'lineGap', 'ascent', 'descent')
448+
]
449+
for table_name, linegap_key, ascent_key, descent_key in possible:
450+
table = font.get_sfnt_table(table_name)
451+
if table is None:
452+
continue
453+
# Rescale to font size/DPI if the metrics were available.
454+
fontsize = self._fontproperties.get_size_in_points()
455+
units_per_em = font.get_sfnt_table('head')['unitsPerEm']
456+
scale = 1 / units_per_em * fontsize * dpi / 72
457+
line_gap = table[linegap_key] * scale
458+
min_ascent = table[ascent_key] * scale
459+
min_descent = -table[descent_key] * scale
460+
break
456461
if None in (min_ascent, min_descent):
457462
# Fallback to font measurement.
458463
_, h, min_descent = _get_text_metrics_with_cache(

0 commit comments

Comments
 (0)