Skip to content

[Bug]: Saving as an SVG and PDF produce different outputs with Latex characters, with wrong character sizing #29791

Closed

Description

@jamesmeiklejohn

Bug summary

When saving certain outputs using Latex rendering as an SVG, it produces wrong outputs. Using plt.show() it renders correctly, and it works for saving as a PDF, but saving the output as a SVG math environment characters can render in the wrong size (see attached screenshots.)

Code for reproduction

from pathlib import Path
import pandas as pd
import matplotlib.pyplot as plt
import scienceplots


BASE_PATH = Path(__file__).parent
DATA_PATH = BASE_PATH / "ion_ranges.csv"


plt.style.use(['science', 'latex-siunitx'])

dat = pd.read_csv(DATA_PATH)

plt.plot(dat["energy"], dat["projected_range_um"], marker="o", linestyle="-")
plt.xlabel("Energy (keV)")
plt.ylabel(r"Projected Range (\si{\um})")
plt.tight_layout()
plt.savefig(BASE_PATH / "ion_ranges_plot.pdf")

Actual outcome

Image

Expected outcome

ion_ranges_plot.pdf

Additional information

No response

Operating system

No response

Matplotlib Version

3.10.1

Matplotlib Backend

No response

Python version

No response

Jupyter version

No response

Installation

None

Activity

  1. jklymak commented on Mar 21, 2025

    @jklymak
    Member

    We don;'t provide the styles science or siunitx. Without those files it is hard to know if this is a Matplotlib bug or a bug in wherever you got those style files. Can you make a reproducible, self contained example that just uses Matplotlib and numpy?

  2. anntzer commented on Mar 22, 2025

    @anntzer
    Contributor

    I am almost certain that this is the same issue as the glyph sizing issue reported in #12928 (which that PR didn't fix). Probably need to revive #14159...

    Repro:

    rcParams["text.latex.preamble"] = r"\usepackage{siunitx}"; figtext(.5, .5, r"\si{\um}", usetex=True); savefig("/tmp/test.svg")
    # or, without siunitx
    figtext(.5, .5, r"ff\textwon", usetex=True); savefig("/tmp/test.svg")
  3. anntzer commented on Mar 22, 2025

    @anntzer
    Contributor

    Figured this out (after 6 years...). The following patch fixes the issue (it is derived from #27654 (comment) but not loading the incompatible type1cm anymore, which may have been the cause of the test failures reported in the linked comment)

    diff --git i/lib/matplotlib/mpl-data/matplotlibrc w/lib/matplotlib/mpl-data/matplotlibrc
    index ab412302f3..3dc0a4e2e3 100644
    --- i/lib/matplotlib/mpl-data/matplotlibrc
    +++ w/lib/matplotlib/mpl-data/matplotlibrc
    @@ -334,7 +334,7 @@
                             # become quite long.
                             # The following packages are always loaded with usetex,
                             # so beware of package collisions:
    -                        #   geometry, inputenc, type1cm.
    +                        #   color, fix-cm, geometry, graphicx, textcomp.
                             # PostScript (PSNFSS) font packages may also be
                             # loaded, depending on your font settings.
     
    diff --git i/lib/matplotlib/mpl-data/stylelib/classic.mplstyle w/lib/matplotlib/mpl-data/stylelib/classic.mplstyle
    index 6cba66076a..ca9b04a2cb 100644
    --- i/lib/matplotlib/mpl-data/stylelib/classic.mplstyle
    +++ w/lib/matplotlib/mpl-data/stylelib/classic.mplstyle
    @@ -122,8 +122,8 @@ text.latex.preamble :  # IMPROPER USE OF THIS FEATURE WILL LEAD TO LATEX FAILURE
                            # Note that it has to be put on a single line, which may
                            # become quite long.
                            # The following packages are always loaded with usetex, so
    -                       # beware of package collisions: color, geometry, graphicx,
    -                       # type1cm, textcomp.
    +                       # beware of package collisions:
    +                       #   color, fix-cm, geometry, graphicx, textcomp.
                            # Adobe Postscript (PSSNFS) font packages may also be
                            # loaded, depending on your font settings.
     
    diff --git i/lib/matplotlib/texmanager.py w/lib/matplotlib/texmanager.py
    index 94fc94e9e8..c10f90a796 100644
    --- i/lib/matplotlib/texmanager.py
    +++ w/lib/matplotlib/texmanager.py
    @@ -67,6 +67,13 @@ class TexManager:
         _grey_arrayd = {}
     
         _font_families = ('serif', 'sans-serif', 'cursive', 'monospace')
    +    # Check for the cm-super package (which registers unicode computer modern
    +    # support just by being installed) without actually loading any package
    +    # (because we already load the incompatible fix-cm).
    +    _check_cmsuper_installed = (
    +        r'\IfFileExists{type1ec.sty}{}{\PackageError{matplotlib-support}{'
    +        r'Missing cm-super package, required by Matplotlib}{}}'
    +    )
         _font_preambles = {
             'new century schoolbook': r'\renewcommand{\rmdefault}{pnc}',
             'bookman': r'\renewcommand{\rmdefault}{pbk}',
    @@ -83,10 +90,10 @@ class TexManager:
             # Loading the type1ec package ensures that cm-super is installed, which
             # is necessary for Unicode computer modern.  (It also allows the use of
             # computer modern at arbitrary sizes, but that's just a side effect.)
    -        'monospace': r'\usepackage{type1ec}',
    -        'computer modern roman': r'\usepackage{type1ec}',
    -        'computer modern sans serif': r'\usepackage{type1ec}',
    -        'computer modern typewriter': r'\usepackage{type1ec}',
    +        'monospace': _check_cmsuper_installed,
    +        'computer modern roman': _check_cmsuper_installed,
    +        'computer modern sans serif': _check_cmsuper_installed,
    +        'computer modern typewriter': _check_cmsuper_installed,
         }
         _font_types = {
             'new century schoolbook': 'serif',
    @@ -200,6 +207,7 @@ class TexManager:
             font_preamble, fontcmd = cls._get_font_preamble_and_command()
             baselineskip = 1.25 * fontsize
             return "\n".join([
    +            r"\RequirePackage{fix-cm}",
                 r"\documentclass{article}",
                 r"% Pass-through \mathdefault, which is used in non-usetex mode",
                 r"% to use the default text font but was historically suppressed",

    It also supersedes #14159 which turns out to not have been the right approach after all. This would also close #27654 and #28675.

    The fundamental underlying issue is the following: for glyph to path conversion (used by svg), textpath renders the text at fontsize FONT_SCALE = 100, and scales it appropriately after. But type1ec, despite its name, doesn't actually allow rendering glyphs such as the micro-sign or the won sign at that size; manually compiling the tex file generated by texmanager.py shows the warning

    LaTeX Font Warning: Font shape `TS1/cmr/m/n' in size <100> not available
    (Font)              size <35.83> substituted on input line 31.
    

    i.e. the micro sign is actually rendered ~3x smaller than the rest of the text (and then the ratio is kept when scaling down the text). Why greater sizes are now allowed remains a bit a mystery to me, as there's clearly an entry for bigger sizes in https://mirrors.ircam.fr/pub/CTAN/fonts/ps-type1/cm-super/type1ec.sty (<35.83->#53583%) but heh. In any case, using fix-cm allows rendering these glyphs correctly at size 100 and fixes the issue.

    More links:

    A side question that remains is whether textpath should really generate the glyphs at fontsize 100 and scale them (usually down) later. Indeed, size 100 computer modern is not the same as size 5 computer modern scaled up 20 times (see e.g. https://tex.stackexchange.com/questions/18721/). FWIW I don't see why textpath couldn't just do the rendering at the "real" requested size indicated in fontproperties...

  4. added this to the v3.11.0 milestone on May 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions