def make_pdf_to_png_converter(): """Return a function that converts a pdf file to a png file.""" try: mpl._get_executable_info("pdftocairo") except mpl.ExecutableNotFoundError: pass else: return lambda pdffile, pngfile, dpi: subprocess.check_output( [ "pdftocairo", "-singlefile", "-transp", "-png", "-r", "%d" % dpi, pdffile, os.path.splitext(pngfile)[0] ], stderr=subprocess.STDOUT) try: gs_info = mpl._get_executable_info("gs") except mpl.ExecutableNotFoundError: pass else: return lambda pdffile, pngfile, dpi: subprocess.check_output([ gs_info.executable, '-dQUIET', '-dSAFER', '-dBATCH', '-dNOPAUSE', '-dNOPROMPT', '-dUseCIEColor', '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4', '-dDOINTERPOLATE', '-sDEVICE=pngalpha', '-sOutputFile=%s' % pngfile, '-r%d' % dpi, pdffile ], stderr= subprocess .STDOUT) raise RuntimeError("No suitable pdf to png renderer found.")
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 _update_converter(): try: mpl._get_executable_info("gs") except FileNotFoundError: pass else: converter['pdf'] = converter['eps'] = _GSConverter() try: mpl._get_executable_info("inkscape") except FileNotFoundError: pass else: converter['svg'] = _SVGConverter()
def _update_converter(): try: mpl._get_executable_info("gs") except FileNotFoundError: pass else: converter['pdf'] = converter['eps'] = _GSConverter() try: mpl._get_executable_info("inkscape") except FileNotFoundError: pass else: converter['svg'] = _SVGConverter()
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 get_file_hash(path, block_size=2**20): md5 = hashlib.md5() with open(path, 'rb') as fd: while True: data = fd.read(block_size) if not data: break md5.update(data) if path.endswith('.pdf'): md5.update(str(mpl._get_executable_info("gs").version).encode('utf-8')) elif path.endswith('.svg'): md5.update( str(mpl._get_executable_info("inkscape").version).encode('utf-8')) return md5.hexdigest()
def __call__(self, orig, dest): if not self._proc: self._stdout = TemporaryFile() self._proc = subprocess.Popen( [ mpl._get_executable_info("gs").executable, "-dNOPAUSE", "-sDEVICE=png16m" ], # As far as I can see, ghostscript never outputs to stderr. stdin=subprocess.PIPE, stdout=subprocess.PIPE) try: self._read_until(b"\nGS") except _ConverterError: raise OSError("Failed to start Ghostscript") def encode_and_escape(name): return (os.fsencode(name).replace(b"\\", b"\\\\").replace( b"(", br"\(").replace(b")", br"\)")) self._proc.stdin.write(b"<< /OutputFile (" + encode_and_escape(dest) + b") >> setpagedevice (" + encode_and_escape(orig) + b") run flush\n") self._proc.stdin.flush() # GS> if nothing left on the stack; GS<n> if n items left on the stack. err = self._read_until(b"GS") stack = self._read_until(b">") if stack or not os.path.exists(dest): stack_size = int(stack[1:]) if stack else 0 self._proc.stdin.write(b"pop\n" * stack_size) # Using the systemencoding should at least get the filenames right. raise ImageComparisonFailure((err + b"GS" + stack + b">").decode( sys.getfilesystemencoding(), "replace"))
def make_pdf_to_png_converter(): """Returns a function that converts a pdf file to a png file.""" if shutil.which("pdftocairo"): def cairo_convert(pdffile, pngfile, dpi): cmd = [ "pdftocairo", "-singlefile", "-png", "-r", "%d" % dpi, pdffile, os.path.splitext(pngfile)[0] ] subprocess.check_output(cmd, stderr=subprocess.STDOUT) return cairo_convert try: gs_info = mpl._get_executable_info("gs") except FileNotFoundError: pass else: def gs_convert(pdffile, pngfile, dpi): cmd = [ gs_info.executable, '-dQUIET', '-dSAFER', '-dBATCH', '-dNOPAUSE', '-dNOPROMPT', '-dUseCIEColor', '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4', '-dDOINTERPOLATE', '-sDEVICE=png16m', '-sOutputFile=%s' % pngfile, '-r%d' % dpi, pdffile ] subprocess.check_output(cmd, stderr=subprocess.STDOUT) return gs_convert raise RuntimeError("No suitable pdf to png renderer found.")
def make_png(self, tex, fontsize, dpi): """ Generate a png file containing latex's rendering of tex string. Return the file name. """ basefile = self.get_basefile(tex, fontsize, dpi) pngfile = '%s.png' % basefile # see get_rgba for a discussion of the background if not os.path.exists(pngfile): dvifile = self.make_dvi(tex, fontsize) cmd = [ "dvipng", "-bg", "Transparent", "-D", str(dpi), "-T", "tight", "-o", pngfile, dvifile ] # When testing, disable FreeType rendering for reproducibility; but # dvipng 1.16 has a bug (fixed in f3ff241) that breaks --freetype0 # mode, so for it we keep FreeType enabled; the image will be # slightly off. bad_ver = parse_version("1.16") if (getattr(mpl, "_called_from_pytest", False) and mpl._get_executable_info("dvipng").version != bad_ver): cmd.insert(1, "--freetype0") self._run_checked_subprocess(cmd, tex) return pngfile
def __call__(self, orig, dest): if not self._proc: self._proc = subprocess.Popen( [mpl._get_executable_info("gs").executable, "-dNOPAUSE", "-sDEVICE=png16m"], # As far as I can see, ghostscript never outputs to stderr. stdin=subprocess.PIPE, stdout=subprocess.PIPE) try: self._read_until(b"\nGS") except _ConverterError: raise OSError("Failed to start Ghostscript") def encode_and_escape(name): return (os.fsencode(name) .replace(b"\\", b"\\\\") .replace(b"(", br"\(") .replace(b")", br"\)")) self._proc.stdin.write( b"<< /OutputFile (" + encode_and_escape(dest) + b") >> setpagedevice (" + encode_and_escape(orig) + b") run flush\n") self._proc.stdin.flush() # GS> if nothing left on the stack; GS<n> if n items left on the stack. err = self._read_until(b"GS") stack = self._read_until(b">") if stack or not os.path.exists(dest): stack_size = int(stack[1:]) if stack else 0 self._proc.stdin.write(b"pop\n" * stack_size) # Using the systemencoding should at least get the filenames right. raise ImageComparisonFailure( (err + b"GS" + stack + b">") .decode(sys.getfilesystemencoding(), "replace"))
def get_file_hash(path, block_size=2 ** 20): md5 = hashlib.md5() with open(path, 'rb') as fd: while True: data = fd.read(block_size) if not data: break md5.update(data) if path.endswith('.pdf'): md5.update(str(mpl._get_executable_info("gs").version) .encode('utf-8')) elif path.endswith('.svg'): md5.update(str(mpl._get_executable_info("inkscape").version) .encode('utf-8')) return md5.hexdigest()
def make_pdf_to_png_converter(): """Returns a function that converts a pdf file to a png file.""" if shutil.which("pdftocairo"): def cairo_convert(pdffile, pngfile, dpi): cmd = ["pdftocairo", "-singlefile", "-png", "-r", "%d" % dpi, pdffile, os.path.splitext(pngfile)[0]] subprocess.check_output(cmd, stderr=subprocess.STDOUT) return cairo_convert try: gs_info = mpl._get_executable_info("gs") except FileNotFoundError: pass else: def gs_convert(pdffile, pngfile, dpi): cmd = [gs_info.executable, '-dQUIET', '-dSAFER', '-dBATCH', '-dNOPAUSE', '-dNOPROMPT', '-dUseCIEColor', '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4', '-dDOINTERPOLATE', '-sDEVICE=png16m', '-sOutputFile=%s' % pngfile, '-r%d' % dpi, pdffile] subprocess.check_output(cmd, stderr=subprocess.STDOUT) return gs_convert raise RuntimeError("No suitable pdf to png renderer found.")
assert common_texification(plain_text) == escaped_text # test compiling a figure to pdf with xelatex @needs_xelatex @pytest.mark.backend('pgf') @image_comparison(['pgf_xelatex.pdf'], style='default') def test_xelatex(): rc_xelatex = {'font.family': 'serif', 'pgf.rcfonts': False} mpl.rcParams.update(rc_xelatex) create_figure() try: _old_gs_version = \ mpl._get_executable_info('gs').version < parse_version('9.50') except mpl.ExecutableNotFoundError: _old_gs_version = True # test compiling a figure to pdf with pdflatex @needs_pdflatex @pytest.mark.skipif(not _has_tex_package('ucs'), reason='needs ucs.sty') @pytest.mark.backend('pgf') @image_comparison(['pgf_pdflatex.pdf'], style='default', tol=11.7 if _old_gs_version else 0) def test_pdflatex(): if os.environ.get('APPVEYOR'): pytest.xfail("pdflatex test does not work on appveyor due to missing " "LaTeX fonts")
def __call__(self, orig, dest): old_inkscape = mpl._get_executable_info("inkscape").version < "1" terminator = b"\n>" if old_inkscape else b"> " if not hasattr(self, "_tmpdir"): self._tmpdir = TemporaryDirectory() if (not self._proc # First run. or self._proc.poll() is not None): # Inkscape terminated. env = { **os.environ, # If one passes e.g. a png file to Inkscape, it will try to # query the user for conversion options via a GUI (even with # `--without-gui`). Unsetting `DISPLAY` prevents this (and # causes GTK to crash and Inkscape to terminate, but that'll # just be reported as a regular exception below). "DISPLAY": "", # Do not load any user options. "INKSCAPE_PROFILE_DIR": os.devnull, } # Old versions of Inkscape (e.g. 0.48.3.1) seem to sometimes # deadlock when stderr is redirected to a pipe, so we redirect it # to a temporary file instead. This is not necessary anymore as of # Inkscape 0.92.1. stderr = TemporaryFile() self._proc = subprocess.Popen( ["inkscape", "--without-gui", "--shell"] if old_inkscape else ["inkscape", "--shell"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=stderr, env=env, cwd=self._tmpdir.name) # Slight abuse, but makes shutdown handling easier. self._proc.stderr = stderr try: self._read_until(terminator) except _ConverterError as err: raise OSError("Failed to start Inkscape in interactive " "mode") from err # Inkscape's shell mode does not support escaping metacharacters in the # filename ("\n", and ":;" for inkscape>=1). Avoid any problems by # running from a temporary directory and using fixed filenames. inkscape_orig = Path(self._tmpdir.name, os.fsdecode(b"f.svg")) inkscape_dest = Path(self._tmpdir.name, os.fsdecode(b"f.png")) try: inkscape_orig.symlink_to(Path(orig).resolve()) except OSError: shutil.copyfile(orig, inkscape_orig) self._proc.stdin.write( b"f.svg --export-png=f.png\n" if old_inkscape else b"file-open:f.svg;export-filename:f.png;export-do;file-close\n") self._proc.stdin.flush() try: self._read_until(terminator) except _ConverterError as err: # Inkscape's output is not localized but gtk's is, so the output # stream probably has a mixed encoding. Using the filesystem # encoding should at least get the filenames right... self._proc.stderr.seek(0) raise ImageComparisonFailure(self._proc.stderr.read().decode( sys.getfilesystemencoding(), "replace")) from err os.remove(inkscape_orig) shutil.move(inkscape_dest, dest)
assert common_texification(plain_text) == escaped_text # test compiling a figure to pdf with xelatex @needs_xelatex @pytest.mark.backend('pgf') @image_comparison(['pgf_xelatex.pdf'], style='default') def test_xelatex(): rc_xelatex = {'font.family': 'serif', 'pgf.rcfonts': False} mpl.rcParams.update(rc_xelatex) create_figure() try: _old_gs_version = mpl._get_executable_info('gs').version < '9.50' except mpl.ExecutableNotFoundError: _old_gs_version = True # test compiling a figure to pdf with pdflatex @needs_pdflatex @pytest.mark.skipif(not _has_tex_package('ucs'), reason='needs ucs.sty') @pytest.mark.backend('pgf') @image_comparison(['pgf_pdflatex.pdf'], style='default', tol=11.7 if _old_gs_version else 0) def test_pdflatex(): if os.environ.get('APPVEYOR'): pytest.xfail("pdflatex test does not work on appveyor due to missing " "LaTeX fonts")