Skip to content

Fix usetex path optical sizing - #32392

Open
krishsinghhura wants to merge 2 commits into
matplotlib:mainfrom
krishsinghhura:fix/usetex-optical-sizing
Open

krishsinghhura wants to merge 2 commits into
matplotlib:mainfrom
krishsinghhura:fix/usetex-optical-sizing

Conversation

@krishsinghhura

@krishsinghhura krishsinghhura commented Sep 24, 2026 •

Copy link
Copy Markdown

PR summary

Closes #32389

Problem

In usetex mode, TextToPath.get_glyphs_tex hardcoded LaTeX compilation to a fixed FONT_SCALE = 100 pt. Because TeX fonts have size-specific optical designs, TeX selected the large 17 pt design (CMSS17) and scaled it down, while layout measurement (TexManager.get_text_width_height_descent) typeset at the actual text size (e.g. CMSS8 for 6–8 pt).

This caused:

  1. Font mismatch: SVG output used CMSS17 while PDF output used CMSS8.
  2. Alignment errors: Right-aligned text with path_effects was narrower than its layout box and ended short of the alignment boundary.
  3. TextPath(size=...) issue: TextPath accepted size separately without propagating it to prop.

Solution

  • Updated TextToPath.get_glyphs_tex to typeset at the requested font size (fontsize = prop.get_size_in_points()) and rescale DVI coordinates and boxes back to FONT_SCALE units via scale = self.FONT_SCALE / fontsize.
  • Normalizes prop via FontProperties._from_any(prop) for defensive input handling.
  • Forwards explicit size in TextPath.__init__ to a copy of prop.
  • Added unit tests in test_usetex.py covering optical font selection and TextPath size propagation.

Reproduction & Verification

Click to view reproduction code
import re
import matplotlib as mpl
import matplotlib.patheffects as pe
import matplotlib.pyplot as plt

mpl.rcParams["text.usetex"] = True
s = "Hamburgefonstiv"

# 1. 6 pt text saved as SVG and as PDF
fig = plt.figure(figsize=(2, 1))
fig.text(0.1, 0.5, s, fontsize=6)
fig.savefig("usetex.svg")
fig.savefig("usetex.pdf")
with open("usetex.svg", encoding="utf-8") as f:
    svg = f.read()
with open("usetex.pdf", "rb") as f:
    pdf = f.read()

print("SVG fonts:", sorted(set(re.findall(r'id="(CM[A-Z]+\d+)-', svg))))
print("PDF fonts:", sorted({m.decode() for m in re.findall(rb"/BaseFont\s*/(?:[A-Z]{6}\+)?(\w+)", pdf)}))

# 2. Right-aligned 8 pt text on Agg, without and with path effects
fig = plt.figure(figsize=(1.6, 0.5), dpi=600)
fig.add_artist(plt.Line2D([0.93, 0.93], [0, 1], lw=0.3, color="tab:red"))
fig.text(0.93, 0.62, s, fontsize=8, ha="right", va="center")
fig.text(0.93, 0.25, s, fontsize=8, ha="right", va="center", path_effects=[pe.Normal()])
fig.savefig("usetex_path_effects.png")

Expected Results with this PR:

  • Output:
    SVG fonts: ['CMSS8']
    PDF fonts: ['CMSS8']
    
    (Before: SVG used ['CMSS17'])
  • Image: In usetex_path_effects.png, both lines of text align flush with the red guide line.

Tests Run:

  • pytest lib/matplotlib/tests/test_usetex.py -k "optical_sizing or textpath_size" (Passed)
  • Regression suite: test_mathdefault, test_multiline_eqnarray, test_usetex_with_underscore, test_usetex_fallback, test_rotation, test_unicode_sizing (All passed)
  • Code checks: git diff --check passed, no trailing whitespace or lint issues.

AI Disclosure

I used AI assistance to investigate the issue, trace relevant rendering paths, draft the implementation and tests, and help analyze test output. I reviewed the resulting changes, ran verification steps, and am responsible for the final contribution.


PR quality check

  • Use an expressive title (e.g. Fix usetex path font optical sizing and alignment)
  • New and changed code is tested
  • [N/A] Plotting related features are demonstrated in an example
  • [N/A] New features and API changes have release notes
  • Documentation complies with general and docstring guidelines

@github-actions

Copy link
Copy Markdown

Thank you for opening your first PR into Matplotlib!

If you have not heard from us in a week or so, please leave a new comment below and that should bring it to our attention. Most of our reviewers are volunteers and sometimes things fall through the cracks. We also ask that you please finish addressing any review comments on this PR and wait for it to be merged (or closed) before opening a new one, as it can be a valuable learning experience to go through the review process.

You can also join us on discourse chat for real-time discussion.

For details on testing, writing docs, and our review process, please see the developer guide.
Please let us know if (and how) you use AI, it will help us give you better feedback on your PR.

We strive to be a welcoming and open project. Please follow our Code of Conduct.

@iccir

iccir commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

Hello, please review our AI policy and fill out the AI Disclosure section from the original Pull Request Template. Thank you!

@krishsinghhura

Copy link
Copy Markdown
Author

Hey @iccir I’ve added the AI disclosure.

I also investigated the CI failures: every failed matrix job is the same expected-image mismatch in test_rotation[svg]. The optical-sizing correction intentionally changes the SVG TeX glyph geometry, leaving the existing rotation.svg baseline stale. I regenerated and updated that SVG baseline; the Linux x64 and ARM outputs are pixel-identical.

@krishsinghhura

Copy link
Copy Markdown
Author

@iccir I updated the rotation.svg baseline for the corrected TeX sizing. CI is not fully green yet: the remaining AppVeyor job timed out at its 60-minute limit around 80% through the Windows suite, without reporting a failed test or traceback. Please let me know if you prefer a rerun or targeted Windows verification.

@iccir

iccir commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

The AppVeyor job frequently times out and should hopefully be fixed by #32395.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: usetex text drawn as paths (SVG, path effects) is narrower than its layout

2 participants