diff --git a/doc/users/next_whats_new/pgf-documentclass.rst b/doc/users/next_whats_new/pgf-documentclass.rst new file mode 100644 index 000000000000..ecc9d93fb1cb --- /dev/null +++ b/doc/users/next_whats_new/pgf-documentclass.rst @@ -0,0 +1,9 @@ +PGF backend: new rcParam `pgf.documentclass` +============================================ + +A new rcParam `pgf.documentclass` has been added to allow users to override +the default LaTeX document class (`article`) used by the PGF backend. + +This enables better compatibility when including PGF figures in documents that +use custom LaTeX classes like `IEEEtran` or others, avoiding layout +issues like incorrect font sizes or spacing mismatches. diff --git a/galleries/users_explain/text/pgf.py b/galleries/users_explain/text/pgf.py index c5fa16f35ce7..6b5b0eacb63a 100644 --- a/galleries/users_explain/text/pgf.py +++ b/galleries/users_explain/text/pgf.py @@ -48,6 +48,7 @@ pgf.preamble Lines to be included in the LaTeX preamble pgf.rcfonts Setup fonts from rc params using the fontspec package pgf.texsystem Either "xelatex" (default), "lualatex" or "pdflatex" +pgf.documentclass The LaTeX document class to use (e.g., "article", "IEEEtran") ================= ===================================================== .. note:: @@ -144,6 +145,7 @@ if you want to do the font configuration yourself instead of using the fonts specified in the rc parameters, make sure to disable :rc:`pgf.rcfonts`. + .. code-block:: python import matplotlib as mpl @@ -170,6 +172,17 @@ ax.set_ylabel(r"\url{https://matplotlib.org}") ax.legend(["unicode math: $λ=∑_i^∞ μ_i^2$"]) +You can also change the LaTeX document class used when generating PGF figures. +By default, Matplotlib uses ``article``, but you can override this using +the ``pgf.documentclass`` rcParam:: + + import matplotlib.pyplot as plt + plt.rcParams["pgf.documentclass"] = "IEEEtran" + +This is useful when including PGF figures into LaTeX documents using +custom classes such as ``IEEEtran``, to avoid layout +mismatches in font size or spacing. + .. redirect-from:: /gallery/userdemo/pgf_texsystem .. _pgf-texsystem: diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 9abc6c5a84dd..f52baec79cbf 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -958,6 +958,7 @@ def rc_params_from_file(fname, fail_on_error=False, use_default_template=True): # Strip leading comment. transform=lambda line: line[1:] if line.startswith("#") else line, fail_on_error=True) +rcParamsDefault["pgf.documentclass"] = "article" rcParamsDefault._update_raw(rcsetup._hardcoded_defaults) rcParamsDefault._ensure_has_backend() diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 48b6e8ac152c..fc047f6c4b27 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -29,9 +29,6 @@ _log = logging.getLogger(__name__) -_DOCUMENTCLASS = r"\documentclass{article}" - - # Note: When formatting floating point values, it is important to use the # %f/{:f} format rather than %s/{} to avoid triggering scientific notation, # which is not recognized by TeX. @@ -205,7 +202,7 @@ class LatexManager: @staticmethod def _build_latex_header(): latex_header = [ - _DOCUMENTCLASS, + rf"\documentclass{{{mpl.rcParams.get('pgf.documentclass', 'article')}}}", # Include TeX program name as a comment for cache invalidation. # TeX does not allow this to be the first line. rf"% !TeX program = {mpl.rcParams['pgf.texsystem']}", @@ -834,7 +831,8 @@ def print_pdf(self, fname_or_fh, *, metadata=None, **kwargs): self.print_pgf(tmppath / "figure.pgf", **kwargs) (tmppath / "figure.tex").write_text( "\n".join([ - _DOCUMENTCLASS, + rf"\documentclass{{{mpl.rcParams.get( + 'pgf.documentclass', 'article')}}}", r"\usepackage[pdfinfo={%s}]{hyperref}" % pdfinfo, r"\usepackage[papersize={%fin,%fin}, margin=0in]{geometry}" % (w, h), @@ -931,7 +929,7 @@ def _write_header(self, width_inches, height_inches): pdfinfo = ','.join( _metadata_to_str(k, v) for k, v in self._info_dict.items()) latex_header = "\n".join([ - _DOCUMENTCLASS, + rf"\documentclass{{{mpl.rcParams.get('pgf.documentclass', 'article')}}}", r"\usepackage[pdfinfo={%s}]{hyperref}" % pdfinfo, r"\usepackage[papersize={%fin,%fin}, margin=0in]{geometry}" % (width_inches, height_inches), diff --git a/lib/matplotlib/mpl-data/matplotlibrc b/lib/matplotlib/mpl-data/matplotlibrc index acb131c82e6c..3317c7a3ff0b 100644 --- a/lib/matplotlib/mpl-data/matplotlibrc +++ b/lib/matplotlib/mpl-data/matplotlibrc @@ -762,6 +762,7 @@ #pgf.rcfonts: True #pgf.preamble: # See text.latex.preamble for documentation #pgf.texsystem: xelatex +#pgf.documentclass: article ### docstring params #docstring.hardcopy: False # set this when you want to generate hardcopy docstring diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index ce29c5076100..30caac62a872 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -1336,6 +1336,7 @@ def _convert_validator_spec(key, conv): "pgf.texsystem": ["xelatex", "lualatex", "pdflatex"], # latex variant used "pgf.rcfonts": validate_bool, # use mpl's rc settings for font config "pgf.preamble": validate_string, # custom LaTeX preamble + "pgf.documentclass": validate_string, # LaTeX document class used by PGF backend # write raster image data into the svg file "svg.image_inline": validate_bool, diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index e218a81cdceb..661ae2d3a59e 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -400,3 +400,12 @@ def test_document_font_size(): label=r'\normalsize the document font size is \the\fontdimen6\font' ) plt.legend() + + +@pytest.mark.backend("pgf") +def test_pgf_documentclass_written(): + from matplotlib.backends.backend_pgf import LatexManager + + mpl.rcParams["pgf.documentclass"] = "IEEEtran" + header = LatexManager._build_latex_header() + assert r"\documentclass{IEEEtran}" in header