From 1571c92ff809c5deb755db7b09c9f60d1dc2a771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Tue, 20 Feb 2018 16:23:14 +0100 Subject: [PATCH 01/25] Implement PdfPages for backend pgf --- lib/matplotlib/backends/backend_pgf.py | 176 +++++++++++++++++++++++ lib/matplotlib/tests/test_backend_pgf.py | 28 ++++ 2 files changed, 204 insertions(+) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index cec6358452d7..ee03ad7c7518 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -25,6 +25,8 @@ from matplotlib.compat import subprocess from matplotlib.compat.subprocess import check_output from matplotlib.path import Path +from matplotlib.figure import Figure +from matplotlib._pylab_helpers import Gcf ############################################################################### @@ -987,4 +989,178 @@ def _cleanup_all(): LatexManager._cleanup_remaining_instances() TmpDirCleaner.cleanup_remaining_tmpdirs() + atexit.register(_cleanup_all) + + +class PdfPages(object): + """ + A multi-page PDF file using the pgf backend + + Examples + -------- + + >>> import matplotlib.pyplot as plt + >>> # Initialize: + >>> with PdfPages('foo.pdf') as pdf: + ... # As many times as you like, create a figure fig and save it: + ... fig = plt.figure() + ... pdf.savefig(fig) + ... # When no figure is specified the current figure is saved + ... pdf.savefig() + """ + __slots__ = ( + 'outputfile', + 'keep_empty', + 'tmpdir', + 'base_name', + 'fname_tex', + 'fname_pdf', + '_n_figures', + '_file', + ) + + def __init__(self, filename, keep_empty=True, metadata=None): + """ + Create a new PdfPages object. + + Parameters + ---------- + + filename : str + Plots using :meth:`PdfPages.savefig` will be written to a file at + this location. Any older file with the same name is overwritten. + keep_empty : bool, optional + If set to False, then empty pdf files will be deleted automatically + when closed. + metadata : dictionary, optional + Information dictionary object (see PDF reference section 10.2.1 + 'Document Information Dictionary'), e.g.: + `{'Creator': 'My software', 'Author': 'Me', + 'Title': 'Awesome fig'}` + + The standard keys are `'Title'`, `'Author'`, `'Subject'`, + `'Keywords'`, `'Creator'`, `'Producer'`, `'CreationDate'`, + `'ModDate'`, and `'Trapped'`. Values have been predefined + for `'Creator'`, `'Producer'` and `'CreationDate'`. They + can be removed by setting them to `None`. + """ + self.outputfile = filename + self._n_figures = 0 + self.keep_empty = keep_empty + + # create temporary directory for compiling the figure + self.tmpdir = tempfile.mkdtemp(prefix="mpl_pgf_pdfpages_") + self.base_name = 'pdf_pages' + self.fname_tex = os.path.join(self.tmpdir, self.base_name + ".tex") + self.fname_pdf = os.path.join(self.tmpdir, self.base_name + ".pdf") + self._file = open(self.fname_tex, 'wb') + + def _write_header(self, width_inches, height_inches): + latex_preamble = get_preamble() + latex_fontspec = get_fontspec() + latex_header = r"""\documentclass[12pt]{minimal} +\usepackage[paperwidth=%fin, paperheight=%fin, margin=0in]{geometry} +%s +%s +\usepackage{pgf} +\setlength{\parindent}{0pt} + +\begin{document}%% +""" % (width_inches, height_inches, latex_preamble, latex_fontspec) + self._file.write(latex_header.encode('utf-8')) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def close(self): + """ + Finalize this object, running LaTeX in a temporary directory + and moving the final pdf file to `filename`. + """ + self._file.write(r'\end{document}'.encode('utf-8') + b'\n') + self._file.close() + + if self._n_figures > 0: + try: + self._run_latex() + finally: + pass + # try: + # shutil.rmtree(self.tmpdir) + # except: + # TmpDirCleaner.add(self.tmpdir) + elif self.keep_empty: + open(self.outputfile, 'wb').close() + + def _run_latex(self): + texcommand = get_texcommand() + cmdargs = [ + str(texcommand), + "-interaction=nonstopmode", + "-halt-on-error", + os.path.basename(self.fname_tex), + ] + try: + check_output(cmdargs, stderr=subprocess.STDOUT, cwd=self.tmpdir) + except subprocess.CalledProcessError as e: + raise RuntimeError( + "%s was not able to process your file.\n\nFull log:\n%s" + % (texcommand, e.output.decode('utf-8'))) + + # copy file contents to target + with open(self.fname_pdf, "rb") as fh_src, open(self.outputfile, "wb") as fh: + shutil.copyfileobj(fh_src, fh) + + def savefig(self, figure=None, **kwargs): + """ + Saves a :class:`~matplotlib.figure.Figure` to this file as a new page. + + Any other keyword arguments are passed to + :meth:`~matplotlib.figure.Figure.savefig`. + + Parameters + ---------- + + figure : :class:`~matplotlib.figure.Figure` or int, optional + Specifies what figure is saved to file. If not specified, the + active figure is saved. If a :class:`~matplotlib.figure.Figure` + instance is provided, this figure is saved. If an int is specified, + the figure instance to save is looked up by number. + """ + if not isinstance(figure, Figure): + if figure is None: + manager = Gcf.get_active() + else: + manager = Gcf.get_fig_manager(figure) + if manager is None: + raise ValueError("No figure {}".format(figure)) + figure = manager.canvas.figure + + try: + orig_canvas = figure.canvas + figure.canvas = FigureCanvasPgf(figure) + + if self._n_figures == 0: + self._write_header(*figure.get_size_inches()) + else: + self._file.write( + r'\newpage\pdfpagewidth={}in\pdfpageheight={}in%'.format( + *figure.get_size_inches() + ).encode('utf-8') + b'\n' + ) + figure.savefig(self._file, format="pgf", **kwargs) + self._n_figures += 1 + except Exception as e: + print(e) + finally: + figure.canvas = orig_canvas + + def get_pagecount(self): + """ + Returns the current number of pages in the multipage pdf file. + """ + return self._n_figures diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index 8269808af9d6..ac57d42e68fe 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -12,6 +12,7 @@ from matplotlib.compat import subprocess from matplotlib.testing.compare import compare_images, ImageComparisonFailure from matplotlib.testing.decorators import image_comparison, _image_directories +from matplotlib.backends.backend_pgf import PdfPages baseline_dir, result_dir = _image_directories(lambda: 'dummy func') @@ -195,3 +196,30 @@ def test_bbox_inches(): bbox = ax1.get_window_extent().transformed(fig.dpi_scale_trans.inverted()) compare_figure('pgf_bbox_inches.pdf', savefig_kwargs={'bbox_inches': bbox}, tol=0) + + +@needs_pdflatex +@pytest.mark.style('default') +@pytest.mark.backend('pgf') +def test_pdf_pages(): + rc_pdflatex = { + 'font.family': 'serif', + 'pgf.rcfonts': False, + } + mpl.rcParams.update(rc_pdflatex) + + Y, X = np.ogrid[-1:1:40j, -1:1:40j] + + fig1 = plt.figure() + ax1 = fig1.add_subplot(1, 1, 1) + ax1.plot(range(5)) + fig1.tight_layout() + + fig2 = plt.figure(figsize=(3, 2)) + ax2 = fig2.add_subplot(1, 1, 1) + ax2.plot(range(5)) + fig2.tight_layout() + + with PdfPages(os.path.join(result_dir, 'pdfpages.pdf')) as pdf: + pdf.savefig(fig1) + pdf.savefig(fig2) From 1ce415cb866ff441e54ad23912093ba3ae14e6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Tue, 20 Feb 2018 16:28:49 +0100 Subject: [PATCH 02/25] Cleanup tmpdir --- lib/matplotlib/backends/backend_pgf.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index ee03ad7c7518..6507c262531e 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1088,11 +1088,10 @@ def close(self): try: self._run_latex() finally: - pass - # try: - # shutil.rmtree(self.tmpdir) - # except: - # TmpDirCleaner.add(self.tmpdir) + try: + shutil.rmtree(self.tmpdir) + except: + TmpDirCleaner.add(self.tmpdir) elif self.keep_empty: open(self.outputfile, 'wb').close() From 455fc0ca1121e820f96835e7e3434949d8be0f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Tue, 20 Feb 2018 17:04:33 +0100 Subject: [PATCH 03/25] Make members private --- lib/matplotlib/backends/backend_pgf.py | 32 +++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 6507c262531e..09108fa4b2bf 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1010,12 +1010,12 @@ class PdfPages(object): ... pdf.savefig() """ __slots__ = ( - 'outputfile', + '_outputfile', 'keep_empty', - 'tmpdir', - 'base_name', - 'fname_tex', - 'fname_pdf', + '_tmpdir', + '_basename', + '_fname_tex', + '_fname_pdf', '_n_figures', '_file', ) @@ -1045,16 +1045,16 @@ def __init__(self, filename, keep_empty=True, metadata=None): for `'Creator'`, `'Producer'` and `'CreationDate'`. They can be removed by setting them to `None`. """ - self.outputfile = filename + self._outputfile = filename self._n_figures = 0 self.keep_empty = keep_empty # create temporary directory for compiling the figure - self.tmpdir = tempfile.mkdtemp(prefix="mpl_pgf_pdfpages_") - self.base_name = 'pdf_pages' - self.fname_tex = os.path.join(self.tmpdir, self.base_name + ".tex") - self.fname_pdf = os.path.join(self.tmpdir, self.base_name + ".pdf") - self._file = open(self.fname_tex, 'wb') + self._tmpdir = tempfile.mkdtemp(prefix="mpl_pgf_pdfpages_") + self._basename = 'pdf_pages' + self._fname_tex = os.path.join(self._tmpdir, self._basename + ".tex") + self._fname_pdf = os.path.join(self._tmpdir, self._basename + ".pdf") + self._file = open(self._fname_tex, 'wb') def _write_header(self, width_inches, height_inches): latex_preamble = get_preamble() @@ -1089,11 +1089,11 @@ def close(self): self._run_latex() finally: try: - shutil.rmtree(self.tmpdir) + shutil.rmtree(self._tmpdir) except: - TmpDirCleaner.add(self.tmpdir) + TmpDirCleaner.add(self._tmpdir) elif self.keep_empty: - open(self.outputfile, 'wb').close() + open(self._outputfile, 'wb').close() def _run_latex(self): texcommand = get_texcommand() @@ -1104,14 +1104,14 @@ def _run_latex(self): os.path.basename(self.fname_tex), ] try: - check_output(cmdargs, stderr=subprocess.STDOUT, cwd=self.tmpdir) + check_output(cmdargs, stderr=subprocess.STDOUT, cwd=self._tmpdir) except subprocess.CalledProcessError as e: raise RuntimeError( "%s was not able to process your file.\n\nFull log:\n%s" % (texcommand, e.output.decode('utf-8'))) # copy file contents to target - with open(self.fname_pdf, "rb") as fh_src, open(self.outputfile, "wb") as fh: + with open(self.fname_pdf, "rb") as fh_src, open(self._outputfile, "wb") as fh: shutil.copyfileobj(fh_src, fh) def savefig(self, figure=None, **kwargs): From 7757bbc6fc2b7d939ba6b1013d45b2f3aec475d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Tue, 20 Feb 2018 17:04:47 +0100 Subject: [PATCH 04/25] Do not catch exception --- lib/matplotlib/backends/backend_pgf.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 09108fa4b2bf..e5909c1c1e78 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1153,8 +1153,6 @@ def savefig(self, figure=None, **kwargs): ) figure.savefig(self._file, format="pgf", **kwargs) self._n_figures += 1 - except Exception as e: - print(e) finally: figure.canvas = orig_canvas From b961af4a524bfaf261e1962d25a49d02e8045082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 21 Feb 2018 09:17:13 +0100 Subject: [PATCH 05/25] Fix refactor leftover --- lib/matplotlib/backends/backend_pgf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index e5909c1c1e78..43412cfa16e0 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1101,7 +1101,7 @@ def _run_latex(self): str(texcommand), "-interaction=nonstopmode", "-halt-on-error", - os.path.basename(self.fname_tex), + os.path.basename(self._fname_tex), ] try: check_output(cmdargs, stderr=subprocess.STDOUT, cwd=self._tmpdir) @@ -1111,7 +1111,7 @@ def _run_latex(self): % (texcommand, e.output.decode('utf-8'))) # copy file contents to target - with open(self.fname_pdf, "rb") as fh_src, open(self._outputfile, "wb") as fh: + with open(self._fname_pdf, "rb") as fh_src, open(self._outputfile, "wb") as fh: shutil.copyfileobj(fh_src, fh) def savefig(self, figure=None, **kwargs): From eb5a385b1238f2a14bf23d47575af8ee7bbf8d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 21 Feb 2018 09:50:25 +0100 Subject: [PATCH 06/25] Use hyperref to set pdf metadata --- lib/matplotlib/backends/backend_pgf.py | 55 +++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 43412cfa16e0..2fa7b559a393 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -16,7 +16,7 @@ import weakref import matplotlib as mpl -from matplotlib import _png, rcParams +from matplotlib import _png, rcParams, __version__ from matplotlib.backend_bases import ( _Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) @@ -1018,6 +1018,7 @@ class PdfPages(object): '_fname_pdf', '_n_figures', '_file', + 'metadata', ) def __init__(self, filename, keep_empty=True, metadata=None): @@ -1040,14 +1041,14 @@ def __init__(self, filename, keep_empty=True, metadata=None): 'Title': 'Awesome fig'}` The standard keys are `'Title'`, `'Author'`, `'Subject'`, - `'Keywords'`, `'Creator'`, `'Producer'`, `'CreationDate'`, - `'ModDate'`, and `'Trapped'`. Values have been predefined - for `'Creator'`, `'Producer'` and `'CreationDate'`. They - can be removed by setting them to `None`. + `'Keywords'`, `'Producer'`, `'Creator'` and `'Trapped'`. + Values have been predefined for `'Creator'` and `'Producer'`. + They can be removed by setting them to the empty string. """ self._outputfile = filename self._n_figures = 0 self.keep_empty = keep_empty + self.metadata = metadata or {} # create temporary directory for compiling the figure self._tmpdir = tempfile.mkdtemp(prefix="mpl_pgf_pdfpages_") @@ -1057,17 +1058,43 @@ def __init__(self, filename, keep_empty=True, metadata=None): self._file = open(self._fname_tex, 'wb') def _write_header(self, width_inches, height_inches): + supported_keys = { + 'title', 'author', 'subject', 'keywords', 'creator', + 'producer', 'trapped' + } + infoDict = { + 'creator': 'matplotlib %s, http://matplotlib.org' % __version__, + 'producer': 'matplotlib pgf backend %s' % __version__, + } + metadata = {k.lower(): v for k, v in self.metadata.items()} + infoDict.update(metadata) + hyperref_options = '' + for k, v in infoDict.items(): + if k not in supported_keys: + raise ValueError('Not a supported pdf metadata field: "{}"'.format(k)) + hyperref_options += 'pdf' + k + '={' + str(v) + '},' + latex_preamble = get_preamble() latex_fontspec = get_fontspec() - latex_header = r"""\documentclass[12pt]{minimal} -\usepackage[paperwidth=%fin, paperheight=%fin, margin=0in]{geometry} -%s -%s -\usepackage{pgf} -\setlength{\parindent}{0pt} - -\begin{document}%% -""" % (width_inches, height_inches, latex_preamble, latex_fontspec) + latex_header = r"""\PassOptionsToPackage{{ + {metadata} +}}{{hyperref}} +\RequirePackage{{hyperref}} +\documentclass[12pt]{{minimal}} +\usepackage[paperwidth={width}in, paperheight={height}in, margin=0in]{{geometry}} +{preamble} +{fontspec} +\usepackage{{pgf}} +\setlength{{\parindent}}{{0pt}} + +\begin{{document}}%% +""".format( + width=width_inches, + height=height_inches, + preamble=latex_preamble, + fontspec=latex_fontspec, + metadata=hyperref_options, + ) self._file.write(latex_header.encode('utf-8')) def __enter__(self): From 7c7c1359243fda28ece7eb1caa183cc8007d7aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 21 Feb 2018 09:54:03 +0100 Subject: [PATCH 07/25] Make arguments kwargs only --- lib/matplotlib/backends/backend_pgf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 2fa7b559a393..b51dcd16114a 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1021,7 +1021,7 @@ class PdfPages(object): 'metadata', ) - def __init__(self, filename, keep_empty=True, metadata=None): + def __init__(self, filename, *, keep_empty=True, metadata=None): """ Create a new PdfPages object. From 8bfe11e0c1bad09346fb41d73ed31b628b702749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 21 Feb 2018 09:54:15 +0100 Subject: [PATCH 08/25] Use single bytestring --- lib/matplotlib/backends/backend_pgf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index b51dcd16114a..fe26e57e74fe 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1108,7 +1108,7 @@ def close(self): Finalize this object, running LaTeX in a temporary directory and moving the final pdf file to `filename`. """ - self._file.write(r'\end{document}'.encode('utf-8') + b'\n') + self._file.write(rb'\end{document}\n') self._file.close() if self._n_figures > 0: From 4e55172cbf1f2962186cab08bc153f6d9774ba17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 21 Feb 2018 10:39:42 +0100 Subject: [PATCH 09/25] Add docs for backend_pgf.PdfPages --- tutorials/text/pgf.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tutorials/text/pgf.py b/tutorials/text/pgf.py index 162f74807135..136fb46dae8a 100644 --- a/tutorials/text/pgf.py +++ b/tutorials/text/pgf.py @@ -56,6 +56,30 @@ .. _pgf-rcfonts: + +Multi-Page PDF Files +==================== + +The pgf backend also supportes multipage pdf files using ``PdfPages`` + +.. code-block:: python + + from matplotlib.backends.backend_pgf import PdfPages + import matplotlib.pyplot as plt + + with PdfPages('multipage.pdf', metadata={'author': 'Me'}) as pdf: + + fig1 = plt.figure() + ax1 = fig1.add_subplot(1, 1, 1) + ax1.plot([1, 5, 3]) + pdf.savefig(fig1) + + fig2 = plt.figure() + ax2 = fig2.add_subplot(1, 1, 1) + ax2.plot([1, 5, 3]) + pdf.savefig(fig2) + + Font specification ================== From 3fa47014bbf7112388690189b565b2bab45c532f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 21 Feb 2018 10:45:22 +0100 Subject: [PATCH 10/25] Add whats new for backend_pgf.PdfPages --- doc/users/next_whats_new/pgf_pdfpages.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 doc/users/next_whats_new/pgf_pdfpages.rst diff --git a/doc/users/next_whats_new/pgf_pdfpages.rst b/doc/users/next_whats_new/pgf_pdfpages.rst new file mode 100644 index 000000000000..7398019505e6 --- /dev/null +++ b/doc/users/next_whats_new/pgf_pdfpages.rst @@ -0,0 +1,19 @@ +Multipage PDF support for pgf backend +------------------------------------- + +The pgf backend now also supports multipage PDF files. + +.. code-block:: python + + from matplotlib.backends.backend_pgf import PdfPages + import matplotlib.pyplot as plt + + with PdfPages('multipage.pdf') as pdf: + # page 1 + plt.plot([2, 1, 3]) + pdf.savefig() + + # page 2 + plt.cla() + plt.plot([3, 1, 2]) + pdf.savefig() From e9128a2db33bdae2e21ee4f108bfde0120ec0633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 21 Feb 2018 11:41:44 +0100 Subject: [PATCH 11/25] pep8 --- lib/matplotlib/backends/backend_pgf.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index fe26e57e74fe..acd9d664cbb4 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1071,7 +1071,9 @@ def _write_header(self, width_inches, height_inches): hyperref_options = '' for k, v in infoDict.items(): if k not in supported_keys: - raise ValueError('Not a supported pdf metadata field: "{}"'.format(k)) + raise ValueError( + 'Not a supported pdf metadata field: "{}"'.format(k) + ) hyperref_options += 'pdf' + k + '={' + str(v) + '},' latex_preamble = get_preamble() @@ -1138,8 +1140,9 @@ def _run_latex(self): % (texcommand, e.output.decode('utf-8'))) # copy file contents to target - with open(self._fname_pdf, "rb") as fh_src, open(self._outputfile, "wb") as fh: - shutil.copyfileobj(fh_src, fh) + with open(self._fname_pdf, "rb") as fh_src: + with open(self._outputfile, "wb") as fh: + shutil.copyfileobj(fh_src, fh) def savefig(self, figure=None, **kwargs): """ From 0354d65c4a3a42dad42981bc92ff8e99189eb458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 21 Feb 2018 11:44:54 +0100 Subject: [PATCH 12/25] Add test for metadata --- lib/matplotlib/tests/test_backend_pgf.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index ac57d42e68fe..f96462c241c9 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -208,8 +208,6 @@ def test_pdf_pages(): } mpl.rcParams.update(rc_pdflatex) - Y, X = np.ogrid[-1:1:40j, -1:1:40j] - fig1 = plt.figure() ax1 = fig1.add_subplot(1, 1, 1) ax1.plot(range(5)) @@ -223,3 +221,24 @@ def test_pdf_pages(): with PdfPages(os.path.join(result_dir, 'pdfpages.pdf')) as pdf: pdf.savefig(fig1) pdf.savefig(fig2) + + +@needs_xelatex +@pytest.mark.style('default') +@pytest.mark.backend('pgf') +def test_pdf_pages_metadata(): + rc_pdflatex = { + 'font.family': 'serif', + 'pgf.rcfonts': False, + } + mpl.rcParams.update(rc_pdflatex) + + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.plot(range(5)) + fig.tight_layout() + + md = {'author': 'me', 'title': 'Multipage PDF with pgf'} + with PdfPages(os.path.join(result_dir, 'pdfpages.pdf'), metadata=md) as pdf: + pdf.savefig(fig) + pdf.savefig(fig) From f27d41de7a8e511b3b75177a1662b14d06e56061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 21 Feb 2018 12:00:09 +0100 Subject: [PATCH 13/25] Support also lualatex in backend_pgf.PdfPages --- lib/matplotlib/backends/backend_pgf.py | 17 ++++++++++---- lib/matplotlib/tests/test_backend_pgf.py | 30 +++++++++++++++++++++++- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index acd9d664cbb4..2dc537bcfd4e 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1176,11 +1176,18 @@ def savefig(self, figure=None, **kwargs): if self._n_figures == 0: self._write_header(*figure.get_size_inches()) else: - self._file.write( - r'\newpage\pdfpagewidth={}in\pdfpageheight={}in%'.format( - *figure.get_size_inches() - ).encode('utf-8') + b'\n' - ) + if get_texcommand() == 'lualatex': + self._file.write( + r'\newpage\pagewidth={}in\pageheight={}in%'.format( + *figure.get_size_inches() + ).encode('utf-8') + b'\n' + ) + else: + self._file.write( + r'\newpage\pdfpagewidth={}in\pdfpageheight={}in%'.format( + *figure.get_size_inches() + ).encode('utf-8') + b'\n' + ) figure.savefig(self._file, format="pgf", **kwargs) self._n_figures += 1 finally: diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index f96462c241c9..162d66faa77a 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -41,6 +41,8 @@ def check_for(texsystem): reason='xelatex + pgf is required') needs_pdflatex = pytest.mark.skipif(not check_for('pdflatex'), reason='pdflatex + pgf is required') +needs_lualatex = pytest.mark.skipif(not check_for('lualatex'), + reason='lualatex + pgf is required') def compare_figure(fname, savefig_kwargs={}, tol=0): @@ -205,6 +207,7 @@ def test_pdf_pages(): rc_pdflatex = { 'font.family': 'serif', 'pgf.rcfonts': False, + 'pgf.texsystem': 'pdflatex', } mpl.rcParams.update(rc_pdflatex) @@ -230,6 +233,7 @@ def test_pdf_pages_metadata(): rc_pdflatex = { 'font.family': 'serif', 'pgf.rcfonts': False, + 'pgf.texsystem': 'xelatex', } mpl.rcParams.update(rc_pdflatex) @@ -239,6 +243,30 @@ def test_pdf_pages_metadata(): fig.tight_layout() md = {'author': 'me', 'title': 'Multipage PDF with pgf'} - with PdfPages(os.path.join(result_dir, 'pdfpages.pdf'), metadata=md) as pdf: + with PdfPages(os.path.join(result_dir, 'pdfpages_meta.pdf'), metadata=md) as pdf: pdf.savefig(fig) pdf.savefig(fig) + + +@needs_lualatex +@pytest.mark.style('default') +@pytest.mark.backend('pgf') +def test_pdf_pages_lualatex(): + rc_pdflatex = { + 'font.family': 'serif', + 'pgf.rcfonts': False, + 'pgf.texsystem': 'lualatex' + } + mpl.rcParams.update(rc_pdflatex) + + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.plot(range(5)) + fig.tight_layout() + + md = {'author': 'me', 'title': 'Multipage PDF with pgf'} + with PdfPages(os.path.join(result_dir, 'pdfpages_lua.pdf'), metadata=md) as pdf: + pdf.savefig(fig) + pdf.savefig(fig) + + raise Exception(result_dir) From 697f75be26a25ffdbbee6ac5df87449a78b71d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 21 Feb 2018 21:34:04 +0100 Subject: [PATCH 14/25] Include next_whats_new --- doc/users/whats_new.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index 648daabc7c5b..3c5fdc23af73 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -14,12 +14,12 @@ revision, see the :ref:`github-stats`. .. For a release, add a new section after this, then comment out the include and toctree below by indenting them. Uncomment them after the release. - .. include:: next_whats_new/README.rst - .. toctree:: - :glob: - :maxdepth: 1 +.. include:: next_whats_new/README.rst +.. toctree:: + :glob: + :maxdepth: 1 - next_whats_new/* + next_whats_new/* New in Matplotlib 2.2 From e410beaa181e01552183036af930580bc727ac35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 21 Feb 2018 22:28:17 +0100 Subject: [PATCH 15/25] Test for pagecount --- lib/matplotlib/tests/test_backend_pgf.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index 162d66faa77a..bded135a60f2 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -246,6 +246,9 @@ def test_pdf_pages_metadata(): with PdfPages(os.path.join(result_dir, 'pdfpages_meta.pdf'), metadata=md) as pdf: pdf.savefig(fig) pdf.savefig(fig) + pdf.savefig(fig) + + assert pdf.get_pagecount() == 3 @needs_lualatex @@ -269,4 +272,4 @@ def test_pdf_pages_lualatex(): pdf.savefig(fig) pdf.savefig(fig) - raise Exception(result_dir) + assert pdf.get_pagecount() == 2 From 871cd5bcc7ddf1ff99b001469bfc717b359a94e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Fri, 23 Feb 2018 09:58:07 +0100 Subject: [PATCH 16/25] PEP8 --- lib/matplotlib/backends/backend_pgf.py | 6 +++++- lib/matplotlib/tests/test_backend_pgf.py | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 2dc537bcfd4e..054583f6acc7 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1083,7 +1083,11 @@ def _write_header(self, width_inches, height_inches): }}{{hyperref}} \RequirePackage{{hyperref}} \documentclass[12pt]{{minimal}} -\usepackage[paperwidth={width}in, paperheight={height}in, margin=0in]{{geometry}} +\usepackage[ + paperwidth={width}in, + paperheight={height}in, + margin=0in +]{{geometry}} {preamble} {fontspec} \usepackage{{pgf}} diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index bded135a60f2..d7459cbd1048 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -243,7 +243,9 @@ def test_pdf_pages_metadata(): fig.tight_layout() md = {'author': 'me', 'title': 'Multipage PDF with pgf'} - with PdfPages(os.path.join(result_dir, 'pdfpages_meta.pdf'), metadata=md) as pdf: + path = os.path.join(result_dir, 'pdfpages_meta.pdf') + + with PdfPages(path, metadata=md) as pdf: pdf.savefig(fig) pdf.savefig(fig) pdf.savefig(fig) @@ -268,7 +270,8 @@ def test_pdf_pages_lualatex(): fig.tight_layout() md = {'author': 'me', 'title': 'Multipage PDF with pgf'} - with PdfPages(os.path.join(result_dir, 'pdfpages_lua.pdf'), metadata=md) as pdf: + path = os.path.join(result_dir, 'pdfpages_lua.pdf') + with PdfPages(path, metadata=md) as pdf: pdf.savefig(fig) pdf.savefig(fig) From 66db38e20bd0015828e4558982b47ddafa7c40dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Fri, 23 Feb 2018 09:58:17 +0100 Subject: [PATCH 17/25] Install texlive-luatex on travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index bb5e37167177..1aba1fe913f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,6 +37,7 @@ addons: - texlive-latex-extra - texlive-latex-recommended - texlive-xetex + - texlive-luatex env: global: From 3496bd4ce9ba0aef56747fa57c5b74397fb54212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Fri, 23 Feb 2018 11:30:28 +0100 Subject: [PATCH 18/25] Support lualatex < 0.85 --- lib/matplotlib/backends/backend_pgf.py | 37 +++++++++++++++++------- lib/matplotlib/tests/test_backend_pgf.py | 37 ++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 054583f6acc7..7fd79ef8089f 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -51,6 +51,12 @@ except: warnings.warn('error getting fonts from fc-list', UserWarning) + +luatex_version_re = re.compile( + 'This is LuaTeX, Version (?:beta-)?([0-9]+)\.([0-9]+)\.([0-9]+)' +) + + def get_texcommand(): """Get chosen TeX system from rc.""" texsystem_options = ["xelatex", "lualatex", "pdflatex"] @@ -58,6 +64,18 @@ def get_texcommand(): return texsystem if texsystem in texsystem_options else "xelatex" +def get_lualatex_version(): + """Get version of luatex""" + output = check_output(['lualatex', '--version']) + return parse_lualatex_version(output.decode()) + + +def parse_lualatex_version(output): + '''parse the lualatex version from the output of `lualatex --version`''' + match = luatex_version_re.match(output) + return tuple(map(int, match.groups())) + + def get_fontspec(): """Build fontspec preamble from rc.""" latex_fontspec = [] @@ -1181,17 +1199,16 @@ def savefig(self, figure=None, **kwargs): self._write_header(*figure.get_size_inches()) else: if get_texcommand() == 'lualatex': - self._file.write( - r'\newpage\pagewidth={}in\pageheight={}in%'.format( - *figure.get_size_inches() - ).encode('utf-8') + b'\n' - ) + if get_lualatex_version() > (0, 85, 0): + np = r'\newpage\pagewidth={}in\pageheight={}in%' + else: + np = r'\newpage\pdfpagewidth={}in\pdfpageheight={}in%' else: - self._file.write( - r'\newpage\pdfpagewidth={}in\pdfpageheight={}in%'.format( - *figure.get_size_inches() - ).encode('utf-8') + b'\n' - ) + np = r'\newpage\pdfpagewidth={}in\pdfpageheight={}in%' + self._file.write(np.format( + *figure.get_size_inches() + ).encode('utf-8') + b'\n' + ) figure.savefig(self._file, format="pgf", **kwargs) self._n_figures += 1 finally: diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index d7459cbd1048..c1360785b650 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -276,3 +276,40 @@ def test_pdf_pages_lualatex(): pdf.savefig(fig) assert pdf.get_pagecount() == 2 + + +@needs_lualatex +def test_luatex_version(): + from matplotlib.backends.backend_pgf import parse_lualatex_version + from matplotlib.backends.backend_pgf import get_lualatex_version + + v1 = '''This is LuaTeX, Version 1.0.4 (TeX Live 2017) + +Execute 'luatex --credits' for credits and version details. + +There is NO warranty. Redistribution of this software is covered by +the terms of the GNU General Public License, version 2 or (at your option) +any later version. For more information about these matters, see the file +named COPYING and the LuaTeX source. + +LuaTeX is Copyright 2017 Taco Hoekwater and the LuaTeX Team. +''' + + v2 = '''This is LuaTeX, Version beta-0.76.0-2015112019 (TeX Live 2013) (rev 4627) + +Execute 'luatex --credits' for credits and version details. + +There is NO warranty. Redistribution of this software is covered by +the terms of the GNU General Public License, version 2 or (at your option) +any later version. For more information about these matters, see the file +named COPYING and the LuaTeX source. + +Copyright 2013 Taco Hoekwater, the LuaTeX Team. +''' + + assert parse_lualatex_version(v1) == (1, 0, 4) + assert parse_lualatex_version(v2) == (0, 76, 0) + + # just test if it is successfull + version = get_lualatex_version() + assert len(version) == 3 From 97cb414e1bf1bd50477191c7c3b24a38d47c2c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Fri, 23 Feb 2018 13:28:18 +0100 Subject: [PATCH 19/25] Implement review comments of @jkseppan --- lib/matplotlib/backends/backend_pgf.py | 4 ++-- lib/matplotlib/tests/test_backend_pgf.py | 2 +- tutorials/text/pgf.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 7fd79ef8089f..f85a5a2d7eeb 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1011,7 +1011,7 @@ def _cleanup_all(): atexit.register(_cleanup_all) -class PdfPages(object): +class PdfPages: """ A multi-page PDF file using the pgf backend @@ -1081,7 +1081,7 @@ def _write_header(self, width_inches, height_inches): 'producer', 'trapped' } infoDict = { - 'creator': 'matplotlib %s, http://matplotlib.org' % __version__, + 'creator': 'matplotlib %s, https://matplotlib.org' % __version__, 'producer': 'matplotlib pgf backend %s' % __version__, } metadata = {k.lower(): v for k, v in self.metadata.items()} diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index c1360785b650..0fd51e594bde 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -310,6 +310,6 @@ def test_luatex_version(): assert parse_lualatex_version(v1) == (1, 0, 4) assert parse_lualatex_version(v2) == (0, 76, 0) - # just test if it is successfull + # just test if it is successful version = get_lualatex_version() assert len(version) == 3 diff --git a/tutorials/text/pgf.py b/tutorials/text/pgf.py index 136fb46dae8a..3b2682a723e8 100644 --- a/tutorials/text/pgf.py +++ b/tutorials/text/pgf.py @@ -60,7 +60,7 @@ Multi-Page PDF Files ==================== -The pgf backend also supportes multipage pdf files using ``PdfPages`` +The pgf backend also supports multipage pdf files using ``PdfPages`` .. code-block:: python From 476ff78a0ed22c75496271a7f20db4a9082a4d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Sat, 24 Feb 2018 10:10:57 +0100 Subject: [PATCH 20/25] Make lualatex version functions private --- lib/matplotlib/backends/backend_pgf.py | 12 ++++++------ lib/matplotlib/tests/test_backend_pgf.py | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index f85a5a2d7eeb..66c76ae1d04d 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -52,7 +52,7 @@ warnings.warn('error getting fonts from fc-list', UserWarning) -luatex_version_re = re.compile( +_luatex_version_re = re.compile( 'This is LuaTeX, Version (?:beta-)?([0-9]+)\.([0-9]+)\.([0-9]+)' ) @@ -64,15 +64,15 @@ def get_texcommand(): return texsystem if texsystem in texsystem_options else "xelatex" -def get_lualatex_version(): +def _get_lualatex_version(): """Get version of luatex""" output = check_output(['lualatex', '--version']) - return parse_lualatex_version(output.decode()) + return _parse_lualatex_version(output.decode()) -def parse_lualatex_version(output): +def _parse_lualatex_version(output): '''parse the lualatex version from the output of `lualatex --version`''' - match = luatex_version_re.match(output) + match = _luatex_version_re.match(output) return tuple(map(int, match.groups())) @@ -1199,7 +1199,7 @@ def savefig(self, figure=None, **kwargs): self._write_header(*figure.get_size_inches()) else: if get_texcommand() == 'lualatex': - if get_lualatex_version() > (0, 85, 0): + if _get_lualatex_version() > (0, 85, 0): np = r'\newpage\pagewidth={}in\pageheight={}in%' else: np = r'\newpage\pdfpagewidth={}in\pdfpageheight={}in%' diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index 0fd51e594bde..8885f8344b8e 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -280,8 +280,8 @@ def test_pdf_pages_lualatex(): @needs_lualatex def test_luatex_version(): - from matplotlib.backends.backend_pgf import parse_lualatex_version - from matplotlib.backends.backend_pgf import get_lualatex_version + from matplotlib.backends.backend_pgf import _parse_lualatex_version + from matplotlib.backends.backend_pgf import _get_lualatex_version v1 = '''This is LuaTeX, Version 1.0.4 (TeX Live 2017) @@ -307,9 +307,9 @@ def test_luatex_version(): Copyright 2013 Taco Hoekwater, the LuaTeX Team. ''' - assert parse_lualatex_version(v1) == (1, 0, 4) - assert parse_lualatex_version(v2) == (0, 76, 0) + assert _parse_lualatex_version(v1) == (1, 0, 4) + assert _parse_lualatex_version(v2) == (0, 76, 0) # just test if it is successful - version = get_lualatex_version() + version = _get_lualatex_version() assert len(version) == 3 From 8cb4d5129baee0fd483715bd92ee9451f68050ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Tue, 27 Feb 2018 15:48:50 +0100 Subject: [PATCH 21/25] Fix merge artifact --- lib/matplotlib/backends/backend_pgf.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 308d33aac481..7a7888a893b6 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -51,7 +51,7 @@ except: warnings.warn('error getting fonts from fc-list', UserWarning) - + _luatex_version_re = re.compile( 'This is LuaTeX, Version (?:beta-)?([0-9]+)\.([0-9]+)\.([0-9]+)' ) @@ -66,7 +66,7 @@ def get_texcommand(): def _get_lualatex_version(): """Get version of luatex""" - output = check_output(['lualatex', '--version']) + output = subprocess.check_output(['lualatex', '--version']) return _parse_lualatex_version(output.decode()) @@ -1157,7 +1157,9 @@ def _run_latex(self): os.path.basename(self._fname_tex), ] try: - check_output(cmdargs, stderr=subprocess.STDOUT, cwd=self._tmpdir) + subprocess.check_output( + cmdargs, stderr=subprocess.STDOUT, cwd=self._tmpdir + ) except subprocess.CalledProcessError as e: raise RuntimeError( "%s was not able to process your file.\n\nFull log:\n%s" From afc26d8ee0b0c34e652defd4df5ad69444def30d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 28 Feb 2018 09:07:17 +0100 Subject: [PATCH 22/25] Implement review comments of @anntzer --- lib/matplotlib/backends/backend_pgf.py | 34 +++++++++++++----------- lib/matplotlib/tests/test_backend_pgf.py | 2 +- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 7a7888a893b6..f8d98c409a4b 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -1166,9 +1166,7 @@ def _run_latex(self): % (texcommand, e.output.decode('utf-8'))) # copy file contents to target - with open(self._fname_pdf, "rb") as fh_src: - with open(self._outputfile, "wb") as fh: - shutil.copyfileobj(fh_src, fh) + shutil.copyfile(self._fname_pdf, self._outputfile) def savefig(self, figure=None, **kwargs): """ @@ -1199,25 +1197,31 @@ def savefig(self, figure=None, **kwargs): orig_canvas = figure.canvas figure.canvas = FigureCanvasPgf(figure) + width, height = figure.get_size_inches() if self._n_figures == 0: - self._write_header(*figure.get_size_inches()) + self._write_header(width, height) else: - if get_texcommand() == 'lualatex': - if _get_lualatex_version() > (0, 85, 0): - np = r'\newpage\pagewidth={}in\pageheight={}in%' - else: - np = r'\newpage\pdfpagewidth={}in\pdfpageheight={}in%' - else: - np = r'\newpage\pdfpagewidth={}in\pdfpageheight={}in%' - self._file.write(np.format( - *figure.get_size_inches() - ).encode('utf-8') + b'\n' - ) + self._file.write(self._build_newpage_command(width, height)) + figure.savefig(self._file, format="pgf", **kwargs) self._n_figures += 1 finally: figure.canvas = orig_canvas + def _build_newpage_command(self, width, height): + '''LuaLaTeX from version 0.85 removed the `\pdf*` primitives, + so we need to check the lualatex version and use `\pagewidth` if + the version is 0.85 or newer + ''' + texcommand = get_texcommand() + if texcommand == 'lualatex' and _get_lualatex_version() >= (0, 85, 0): + cmd = r'\page' + else: + cmd = r'\pdfpage' + + newpage = r'\newpage{cmd}width={w}in,{cmd}height={h}in%' + '\n' + return newpage.format(cmd=cmd, w=width, h=height).encode('utf-8') + def get_pagecount(self): """ Returns the current number of pages in the multipage pdf file. diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index f83f79bc21fd..b42d99e23a61 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -42,7 +42,7 @@ def check_for(texsystem): needs_pdflatex = pytest.mark.skipif(not check_for('pdflatex'), reason='pdflatex + pgf is required') needs_lualatex = pytest.mark.skipif(not check_for('lualatex'), - reason='lualatex + pgf is required') + reason='lualatex + pgf is required') def compare_figure(fname, savefig_kwargs={}, tol=0): From 0f835a8bdbae8c91c520b268388d49ccda604c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 28 Feb 2018 09:07:41 +0100 Subject: [PATCH 23/25] Add .pytest_cache to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 36d13934bcf0..faa897b4f1c9 100644 --- a/.gitignore +++ b/.gitignore @@ -80,6 +80,7 @@ result_images # Nose/Pytest generated files # ############################### +.pytest_cache/ .cache/ .coverage .coverage.* From bc164835db4631b437845c609e9349c4082d903e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 28 Feb 2018 09:12:23 +0100 Subject: [PATCH 24/25] More docs --- examples/misc/multipage_pdf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/misc/multipage_pdf.py b/examples/misc/multipage_pdf.py index 532d771849cb..9b49f1d8644f 100644 --- a/examples/misc/multipage_pdf.py +++ b/examples/misc/multipage_pdf.py @@ -5,6 +5,10 @@ This is a demo of creating a pdf file with several pages, as well as adding metadata and annotations to pdf files. + +If you want to use a multipage pdf file using LaTeX, you need +to use `from matplotlib.backends.backend_pgf import PdfPages`. +This version however does not support `attach_note`. """ import datetime From 05e7f77deb44670995c5906a39c55ba13f51b905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20N=C3=B6the?= Date: Wed, 28 Feb 2018 09:17:02 +0100 Subject: [PATCH 25/25] Add comment in faq --- doc/faq/howto_faq.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/faq/howto_faq.rst b/doc/faq/howto_faq.rst index ab42bd303d10..cbfe0842433f 100644 --- a/doc/faq/howto_faq.rst +++ b/doc/faq/howto_faq.rst @@ -136,6 +136,10 @@ Finally, the multipage pdf object has to be closed:: pp.close() +The same can be done using the pgf backend:: + + from matplotlib.backends.backend_pgf import PdfPages + .. _howto-subplots-adjust: