From e0913d466ed4e8f5fb9b03195ecd8b0dab5f43ce Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 24 Mar 2026 18:41:07 -0400 Subject: [PATCH] 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. --- lib/matplotlib/_afm.py | 8 ++++ lib/matplotlib/backends/_backend_pdf_ps.py | 26 ++++++++++++ .../test_backend_pdf/pdf_use14corefonts.pdf | Bin 3589 -> 3257 bytes lib/matplotlib/text.py | 39 +++++++++++------- 4 files changed, 57 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/_afm.py b/lib/matplotlib/_afm.py index 3d7f7a44baca..af607b0374fc 100644 --- a/lib/matplotlib/_afm.py +++ b/lib/matplotlib/_afm.py @@ -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'] diff --git a/lib/matplotlib/backends/_backend_pdf_ps.py b/lib/matplotlib/backends/_backend_pdf_ps.py index 83a8566517a7..a06779b8efee 100644 --- a/lib/matplotlib/backends/_backend_pdf_ps.py +++ b/lib/matplotlib/backends/_backend_pdf_ps.py @@ -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": diff --git a/lib/matplotlib/tests/baseline_images/test_backend_pdf/pdf_use14corefonts.pdf b/lib/matplotlib/tests/baseline_images/test_backend_pdf/pdf_use14corefonts.pdf index 5cdc2e34e25d21c805df535b9e1795de50917e4b..b7dbb9adec70559ea8d3cc0a214e443db6e9e146 100644 GIT binary patch delta 999 zcmZuwOKTHR6s9&wlZjmvONtQiv``VXx%13qGz5~gsam9(2o*|^$>cUpXlEpITWe8T zg@Q}<;KG%<5cevG;BRs5%75VF%%rg~b>MQ(YtHNY?m_m;>1Wx!TMzDWU{zb#8-5$u z4ZF<)P(g+|lu8o!T7I)56-W>7Vi8!)_XKEB0K2{1>~Keb*;5yG#9~9(f`i^O+^~h< zT@Q>N^G8QyAH1}R-oD#)SN(4phKrk z|90ry$FC!AQeh?q>WhGzCq7TeaER(Q4rj)~dkUY7W$<+*XYkk96lRkZ{Fa=; zhsg}SN|s2TkDEEmwD4SNC9dW)%fy4!T0)g`ilyQA)XTka+Hi@gY+8K;qh*A~Rod7g zs!E9BzuS($WsR+-h-eW)pa84b0q=RP@QrOQT*sE!g6H@Vnry}O%3k314cP(W3|6Qq zplBFN&tM}xFN17J&TDFjc76yt`~G%AD`XT^T$&e^6^v!W1wBpwcQQgjb2^D-Q_eBu)6PuV~6jPUG>&| ZPDueGd$=m5$z# zL#481*{7S(XohT_HP}2ly(x9B zE?y&;?-?FbMKJLKE$&lmS5jdcmxJgwt`}iy1nl6r=fh=@Cd#-7s8B9R!9wp>PzoA2 zErjmla_|5RWg})PC5-f$QZS#G?#erf<*xiCai=Sv#PE^6$u9n=FVcV07wP|u;pt?A zXOatD+r1e6DLK={eBrM?m zqFQh)Js&T!HZ}$xuDG_~a>($qpmfBw7P<<43|u`dBFnhTyhYJi