def xpdf_distill(tmpfile, eps=False, ptype='letter', bbox=None, rotated=False): """ Use ghostscript's ps2pdf and xpdf's/poppler's pdftops to distill a file. This yields smaller files without illegal encapsulated postscript operators. This distiller is preferred, generating high-level postscript output that treats text as text. """ mpl._get_executable_info("gs") # Effectively checks for ps2pdf. mpl._get_executable_info("pdftops") pdffile = tmpfile + '.pdf' psfile = tmpfile + '.ps' # Pass options as `-foo#bar` instead of `-foo=bar` to keep Windows happy # (https://www.ghostscript.com/doc/9.22/Use.htm#MS_Windows). cbook._check_and_log_subprocess( ["ps2pdf", "-dAutoFilterColorImages#false", "-dAutoFilterGrayImages#false", "-sAutoRotatePages#None", "-sGrayImageFilter#FlateEncode", "-sColorImageFilter#FlateEncode", "-dEPSCrop" if eps else "-sPAPERSIZE#%s" % ptype, tmpfile, pdffile], _log) cbook._check_and_log_subprocess( ["pdftops", "-paper", "match", "-level2", pdffile, psfile], _log) os.remove(tmpfile) shutil.move(psfile, tmpfile) if eps: pstoeps(tmpfile) for fname in glob.glob(tmpfile+'.*'): os.remove(fname)
def print_pdf(self, fname_or_fh, *, metadata=None, **kwargs): """Use LaTeX to compile a pgf generated figure to pdf.""" w, h = self.figure.get_size_inches() info_dict = _create_pdf_info_dict('pgf', metadata or {}) pdfinfo = ','.join( _metadata_to_str(k, v) for k, v in info_dict.items()) # print figure to pgf and compile it with latex with TemporaryDirectory() as tmpdir: tmppath = pathlib.Path(tmpdir) self.print_pgf(tmppath / "figure.pgf", **kwargs) (tmppath / "figure.tex").write_text( "\n".join([ r"\PassOptionsToPackage{pdfinfo={%s}}{hyperref}" % pdfinfo, r"\RequirePackage{hyperref}", r"\documentclass[12pt]{minimal}", r"\usepackage[papersize={%fin,%fin}, margin=0in]{geometry}" % (w, h), get_preamble(), get_fontspec(), r"\usepackage{pgf}", r"\begin{document}", r"\centering", r"\input{figure.pgf}", r"\end{document}", ]), encoding="utf-8") texcommand = mpl.rcParams["pgf.texsystem"] cbook._check_and_log_subprocess( [texcommand, "-interaction=nonstopmode", "-halt-on-error", "figure.tex"], _log, cwd=tmpdir) with (tmppath / "figure.pdf").open("rb") as orig, \ cbook.open_file_cm(fname_or_fh, "wb") as dest: shutil.copyfileobj(orig, dest) # copy file contents to target
def gs_distill(tmpfile, eps=False, ptype='letter', bbox=None, rotated=False): """ Use ghostscript's pswrite or epswrite device to distill a file. This yields smaller files without illegal encapsulated postscript operators. The output is low-level, converting text to outlines. """ if eps: paper_option = "-dEPSCrop" else: paper_option = "-sPAPERSIZE=%s" % ptype psfile = tmpfile + '.ps' dpi = mpl.rcParams['ps.distiller.res'] cbook._check_and_log_subprocess( [mpl._get_executable_info("gs").executable, "-dBATCH", "-dNOPAUSE", "-r%d" % dpi, "-sDEVICE=ps2write", paper_option, "-sOutputFile=%s" % psfile, tmpfile], _log) os.remove(tmpfile) shutil.move(psfile, tmpfile) # While it is best if above steps preserve the original bounding # box, there seem to be cases when it is not. For those cases, # the original bbox can be restored during the pstoeps step. if eps: # For some versions of gs, above steps result in an ps file where the # original bbox is no more correct. Do not adjust bbox for now. pstoeps(tmpfile, bbox, rotated=rotated)
def _print_pdf_to_fh(self, fh, *args, metadata=None, **kwargs): w, h = self.figure.get_figwidth(), self.figure.get_figheight() info_dict = _create_pdf_info_dict('pgf', metadata or {}) hyperref_options = ','.join( _metadata_to_str(k, v) for k, v in info_dict.items()) with TemporaryDirectory() as tmpdir: tmppath = pathlib.Path(tmpdir) # print figure to pgf and compile it with latex self.print_pgf(tmppath / "figure.pgf", *args, **kwargs) latexcode = """ \\PassOptionsToPackage{pdfinfo={%s}}{hyperref} \\RequirePackage{hyperref} \\documentclass[12pt]{minimal} \\usepackage[paperwidth=%fin, paperheight=%fin, margin=0in]{geometry} %s %s \\usepackage{pgf} \\begin{document} \\centering \\input{figure.pgf} \\end{document}""" % (hyperref_options, w, h, get_preamble(), get_fontspec()) (tmppath / "figure.tex").write_text(latexcode, encoding="utf-8") texcommand = mpl.rcParams["pgf.texsystem"] cbook._check_and_log_subprocess( [texcommand, "-interaction=nonstopmode", "-halt-on-error", "figure.tex"], _log, cwd=tmpdir) with (tmppath / "figure.pdf").open("rb") as fh_src: shutil.copyfileobj(fh_src, fh) # copy file contents to target
def _run_latex(self): texcommand = mpl.rcParams["pgf.texsystem"] cbook._check_and_log_subprocess( [texcommand, "-interaction=nonstopmode", "-halt-on-error", os.path.basename(self._fname_tex)], _log, cwd=self._tmpdir) # copy file contents to target shutil.copyfile(self._fname_pdf, self._outputfile)
def _run_latex(self): texcommand = rcParams["pgf.texsystem"] cbook._check_and_log_subprocess( [texcommand, "-interaction=nonstopmode", "-halt-on-error", os.path.basename(self._fname_tex)], _log, cwd=self._tmpdir) # copy file contents to target shutil.copyfile(self._fname_pdf, self._outputfile)
def convert_psfrags(tmpfile, psfrags, font_preamble, custom_preamble, paper_width, paper_height, orientation): """ When we want to use the LaTeX backend with postscript, we write PSFrag tags to a temporary postscript file, each one marking a position for LaTeX to render some text. convert_psfrags generates a LaTeX document containing the commands to convert those tags to text. LaTeX/dvips produces the postscript file that includes the actual text. """ with mpl.rc_context({ "text.latex.preamble": mpl.rcParams["text.latex.preamble"] + # Only load these packages if they have not already been loaded, in # order not to clash with custom packages. r"\makeatletter" r"\@ifpackageloaded{color}{}{\usepackage{color}}" r"\@ifpackageloaded{graphicx}{}{\usepackage{graphicx}}" r"\@ifpackageloaded{psfrag}{}{\usepackage{psfrag}}" r"\makeatother" r"\geometry{papersize={%(width)sin,%(height)sin},margin=0in}" % { "width": paper_width, "height": paper_height } }): dvifile = TexManager().make_dvi( "\n" r"\begin{figure}" "\n" r" \centering\leavevmode" "\n" r" %(psfrags)s" "\n" r" \includegraphics*[angle=%(angle)s]{%(epsfile)s}" "\n" r"\end{figure}" % { "psfrags": "\n".join(psfrags), "angle": 90 if orientation == 'landscape' else 0, "epsfile": pathlib.Path(tmpfile).resolve().as_posix(), }, fontsize=10) # tex's default fontsize. with TemporaryDirectory() as tmpdir: psfile = os.path.join(tmpdir, "tmp.ps") cbook._check_and_log_subprocess( ['dvips', '-q', '-R0', '-o', psfile, dvifile], _log) shutil.move(psfile, tmpfile) # check if the dvips created a ps in landscape paper. Somehow, # above latex+dvips results in a ps file in a landscape mode for a # certain figure sizes (e.g., 8.3in, 5.8in which is a5). And the # bounding box of the final output got messed up. We check see if # the generated ps file is in landscape and return this # information. The return value is used in pstoeps step to recover # the correct bounding box. 2010-06-05 JJL with open(tmpfile) as fh: psfrag_rotated = "Landscape" in fh.read(1000) return psfrag_rotated
def _run_latex(self): texcommand = mpl.rcParams["pgf.texsystem"] with TemporaryDirectory() as tmpdir: tex_source = pathlib.Path(tmpdir, "pdf_pages.tex") tex_source.write_bytes(self._file.getvalue()) cbook._check_and_log_subprocess( [texcommand, "-interaction=nonstopmode", "-halt-on-error", tex_source], _log, cwd=tmpdir) shutil.move(tex_source.with_suffix(".pdf"), self._output_name)
def _print_pdf_to_fh(self, fh, *args, metadata=None, **kwargs): w, h = self.figure.get_figwidth(), self.figure.get_figheight() info_dict = _create_pdf_info_dict('pgf', metadata or {}) hyperref_options = ','.join( _metadata_to_str(k, v) for k, v in info_dict.items()) try: # create temporary directory for compiling the figure tmpdir = tempfile.mkdtemp(prefix="mpl_pgf_") fname_pgf = os.path.join(tmpdir, "figure.pgf") fname_tex = os.path.join(tmpdir, "figure.tex") fname_pdf = os.path.join(tmpdir, "figure.pdf") # print figure to pgf and compile it with latex self.print_pgf(fname_pgf, *args, **kwargs) latex_preamble = get_preamble() latex_fontspec = get_fontspec() latexcode = """ \\PassOptionsToPackage{pdfinfo={%s}}{hyperref} \\RequirePackage{hyperref} \\documentclass[12pt]{minimal} \\usepackage[paperwidth=%fin, paperheight=%fin, margin=0in]{geometry} %s %s \\usepackage{pgf} \\begin{document} \\centering \\input{figure.pgf} \\end{document}""" % (hyperref_options, w, h, latex_preamble, latex_fontspec) pathlib.Path(fname_tex).write_text(latexcode, encoding="utf-8") texcommand = mpl.rcParams["pgf.texsystem"] cbook._check_and_log_subprocess([ texcommand, "-interaction=nonstopmode", "-halt-on-error", "figure.tex" ], _log, cwd=tmpdir) # copy file contents to target with open(fname_pdf, "rb") as fh_src: shutil.copyfileobj(fh_src, fh) finally: try: shutil.rmtree(tmpdir) except: TmpDirCleaner.add(tmpdir)
def find_tex_file(filename, format=None): """ Find a file in the texmf tree. Calls :program:`kpsewhich` which is an interface to the kpathsea library [1]_. Most existing TeX distributions on Unix-like systems use kpathsea. It is also available as part of MikTeX, a popular distribution on Windows. *If the file is not found, an empty string is returned*. Parameters ---------- filename : str or path-like format : str or bytes Used as the value of the ``--format`` option to :program:`kpsewhich`. Could be e.g. 'tfm' or 'vf' to limit the search to that type of files. References ---------- .. [1] `Kpathsea documentation <http://www.tug.org/kpathsea/>`_ The library that :program:`kpsewhich` is part of. """ # we expect these to always be ascii encoded, but use utf-8 # out of caution if isinstance(filename, bytes): filename = filename.decode('utf-8', errors='replace') if isinstance(format, bytes): format = format.decode('utf-8', errors='replace') if os.name == 'nt': # On Windows only, kpathsea can use utf-8 for cmd args and output. # The `command_line_encoding` environment variable is set to force it # to always use utf-8 encoding. See Matplotlib issue #11848. kwargs = { 'env': { **os.environ, 'command_line_encoding': 'utf-8' }, 'encoding': 'utf-8' } else: # On POSIX, run through the equivalent of os.fsdecode(). kwargs = { 'encoding': sys.getfilesystemencoding(), 'errors': 'surrogatescape' } cmd = ['kpsewhich'] if format is not None: cmd += ['--format=' + format] cmd += [filename] try: result = cbook._check_and_log_subprocess(cmd, _log, **kwargs) except RuntimeError: return '' return result.rstrip('\n')
def _print_pdf_to_fh(self, fh, *args, **kwargs): w, h = self.figure.get_figwidth(), self.figure.get_figheight() try: # create temporary directory for compiling the figure tmpdir = tempfile.mkdtemp(prefix="mpl_pgf_") fname_pgf = os.path.join(tmpdir, "figure.pgf") fname_tex = os.path.join(tmpdir, "figure.tex") fname_pdf = os.path.join(tmpdir, "figure.pdf") # print figure to pgf and compile it with latex self.print_pgf(fname_pgf, *args, **kwargs) latex_preamble = get_preamble() latex_fontspec = get_fontspec() latexcode = """ \\documentclass[12pt]{minimal} \\usepackage[paperwidth=%fin, paperheight=%fin, margin=0in]{geometry} %s %s \\usepackage{pgf} \\begin{document} \\centering \\input{figure.pgf} \\end{document}""" % (w, h, latex_preamble, latex_fontspec) pathlib.Path(fname_tex).write_text(latexcode, encoding="utf-8") texcommand = rcParams["pgf.texsystem"] cbook._check_and_log_subprocess( [texcommand, "-interaction=nonstopmode", "-halt-on-error", "figure.tex"], _log, cwd=tmpdir) # copy file contents to target with open(fname_pdf, "rb") as fh_src: shutil.copyfileobj(fh_src, fh) finally: try: shutil.rmtree(tmpdir) except: TmpDirCleaner.add(tmpdir)
def find_tex_file(filename, format=None): """ Find a file in the texmf tree. Calls :program:`kpsewhich` which is an interface to the kpathsea library [1]_. Most existing TeX distributions on Unix-like systems use kpathsea. It is also available as part of MikTeX, a popular distribution on Windows. *If the file is not found, an empty string is returned*. Parameters ---------- filename : string or bytestring format : string or bytestring Used as the value of the `--format` option to :program:`kpsewhich`. Could be e.g. 'tfm' or 'vf' to limit the search to that type of files. References ---------- .. [1] `Kpathsea documentation <http://www.tug.org/kpathsea/>`_ The library that :program:`kpsewhich` is part of. """ # we expect these to always be ascii encoded, but use utf-8 # out of caution if isinstance(filename, bytes): filename = filename.decode('utf-8', errors='replace') if isinstance(format, bytes): format = format.decode('utf-8', errors='replace') if os.name == 'nt': # On Windows only, kpathsea can use utf-8 for cmd args and output. # The `command_line_encoding` environment variable is set to force it # to always use utf-8 encoding. See mpl issue #11848 for more info. kwargs = dict(env=dict(os.environ, command_line_encoding='utf-8')) else: kwargs = {} cmd = ['kpsewhich'] if format is not None: cmd += ['--format=' + format] cmd += [filename] try: result = cbook._check_and_log_subprocess(cmd, _log, **kwargs) except RuntimeError: return '' if os.name == 'nt': return result.decode('utf-8').rstrip('\r\n') else: return os.fsdecode(result).rstrip('\n')
def find_tex_file(filename, format=None): """ Find a file in the texmf tree. Calls :program:`kpsewhich` which is an interface to the kpathsea library [1]_. Most existing TeX distributions on Unix-like systems use kpathsea. It is also available as part of MikTeX, a popular distribution on Windows. *If the file is not found, an empty string is returned*. Parameters ---------- filename : string or bytestring format : string or bytestring Used as the value of the `--format` option to :program:`kpsewhich`. Could be e.g. 'tfm' or 'vf' to limit the search to that type of files. References ---------- .. [1] `Kpathsea documentation <http://www.tug.org/kpathsea/>`_ The library that :program:`kpsewhich` is part of. """ # we expect these to always be ascii encoded, but use utf-8 # out of caution if isinstance(filename, bytes): filename = filename.decode('utf-8', errors='replace') if isinstance(format, bytes): format = format.decode('utf-8', errors='replace') cmd = ['kpsewhich'] if format is not None: cmd += ['--format=' + format] cmd += [filename] try: # Below: strip final newline. result = cbook._check_and_log_subprocess(cmd, _log)[:-1] except RuntimeError: return '' if os.name == 'nt': # On Windows only, kpathsea appears to use utf-8 output(?); see # __win32_fputs in the kpathsea sources and mpl issue #11848. return result.decode('utf-8') else: return os.fsdecode(result)
def _find_tex_file(filename, format=None): """ Find a file in the texmf tree using kpathsea_. The kpathsea library, provided by most existing TeX distributions, both on Unix-like systems and on Windows (MikTeX), is invoked via a long-lived luatex process if luatex is installed, or via kpsewhich otherwise. .. _kpathsea: https://www.tug.org/kpathsea/ Parameters ---------- filename : str or path-like format : str or bytes Used as the value of the ``--format`` option to :program:`kpsewhich`. Could be e.g. 'tfm' or 'vf' to limit the search to that type of files. Deprecated. Raises ------ FileNotFoundError If the file is not found. """ # we expect these to always be ascii encoded, but use utf-8 # out of caution if isinstance(filename, bytes): filename = filename.decode('utf-8', errors='replace') if isinstance(format, bytes): format = format.decode('utf-8', errors='replace') try: lk = _LuatexKpsewhich() except FileNotFoundError: lk = None # Fallback to directly calling kpsewhich, as below. if lk and format is None: path = lk.search(filename) else: if os.name == 'nt': # On Windows only, kpathsea can use utf-8 for cmd args and output. # The `command_line_encoding` environment variable is set to force # it to always use utf-8 encoding. See Matplotlib issue #11848. kwargs = { 'env': { **os.environ, 'command_line_encoding': 'utf-8' }, 'encoding': 'utf-8' } else: # On POSIX, run through the equivalent of os.fsdecode(). kwargs = { 'encoding': sys.getfilesystemencoding(), 'errors': 'surrogateescape' } cmd = ['kpsewhich'] if format is not None: cmd += ['--format=' + format] cmd += [filename] try: path = (cbook._check_and_log_subprocess(cmd, _log, **kwargs).rstrip('\n')) except (FileNotFoundError, RuntimeError): path = None if path: return path else: raise FileNotFoundError( f"Matplotlib's TeX implementation searched for a file named " f"{filename!r} in your texmf tree, but could not find it")