def print_svg(self, filename, *args, **kwargs): if is_string_like(filename): with io.open(filename, 'w', encoding='utf-8') as svgwriter: return self._print_svg(filename, svgwriter, **kwargs) if not is_writable_file_like(filename): raise ValueError("filename must be a path or a file-like object") svgwriter = filename filename = getattr(svgwriter, 'name', '') if not isinstance(filename, six.string_types): filename = '' if not isinstance(svgwriter, io.TextIOBase): if six.PY3: svgwriter = io.TextIOWrapper(svgwriter, 'utf-8') else: svgwriter = codecs.getwriter('utf-8')(svgwriter) detach = True else: detach = False result = self._print_svg(filename, svgwriter, **kwargs) # Detach underlying stream from wrapper so that it remains open in the # caller. if detach: if six.PY3: svgwriter.detach() else: svgwriter.reset() svgwriter.stream = io.BytesIO() return result
def _print_image(self, filename, format): if self.flags() & gtk.REALIZED == 0: # for self.window(for pixmap) and has a side effect of altering # figure width,height (via configure-event?) gtk.DrawingArea.realize(self) width, height = self.get_width_height() pixmap = gdk.Pixmap (self.window, width, height) self._renderer.set_pixmap (pixmap) self._render_figure(pixmap, width, height) # jpg colors don't match the display very well, png colors match # better pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height) pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, width, height) if is_string_like(filename): try: pixbuf.save(filename, format) except gobject.GError as exc: error_msg_gtk('Save figure failure:\n%s' % (exc,), parent=self) elif is_writable_file_like(filename): if hasattr(pixbuf, 'save_to_callback'): def save_callback(buf, data=None): data.write(buf) try: pixbuf.save_to_callback(save_callback, format, user_data=filename) except gobject.GError as exc: error_msg_gtk('Save figure failure:\n%s' % (exc,), parent=self) else: raise ValueError("Saving to a Python file-like object is only supported by PyGTK >= 2.8") else: raise ValueError("filename must be a path or a file-like object")
def print_svg(self, filename, *args, **kwargs): if is_string_like(filename): fh_to_close = svgwriter = codecs.open(filename, "w", "utf-8") elif is_writable_file_like(filename): svgwriter = codecs.EncodedFile(filename, "utf-8") fh_to_close = None else: raise ValueError("filename must be a path or a file-like object") return self._print_svg(filename, svgwriter, fh_to_close)
def print_svg(self, filename, *args, **kwargs): if is_string_like(filename): fh_to_close = svgwriter = io.open(filename, 'w', encoding='utf-8') elif is_writable_file_like(filename): svgwriter = io.TextIOWrapper(filename, encoding='utf-8') fh_to_close = None else: raise ValueError("filename must be a path or a file-like object") return self._print_svg(filename, svgwriter, fh_to_close, **kwargs)
def print_svgz(self, filename, *args, **kwargs): if is_string_like(filename): gzipwriter = gzip.GzipFile(filename, "w") fh_to_close = svgwriter = codecs.EncodedFile(gzipwriter, "utf-8") elif is_writable_file_like(filename): fh_to_close = gzipwriter = gzip.GzipFile(fileobj=filename, mode="w") svgwriter = codecs.EncodedFile(gzipwriter, "utf-8") else: raise ValueError("filename must be a path or a file-like object") return self._print_svg(filename, svgwriter, fh_to_close)
def print_svgz(self, filename, *args, **kwargs): if is_string_like(filename): options = dict(filename=filename) elif is_writable_file_like(filename): options = dict(fileobj=filename) else: raise ValueError("filename must be a path or a file-like object") with gzip.GzipFile(mode='w', **options) as gzipwriter: return self.print_svg(gzipwriter)
def print_svgz(self, filename, *args, **kwargs): if is_string_like(filename): fh_to_close = gzipwriter = gzip.GzipFile(filename, 'w') svgwriter = io.TextIOWrapper(gzipwriter, 'utf-8') elif is_writable_file_like(filename): fh_to_close = gzipwriter = gzip.GzipFile(fileobj=filename, mode='w') svgwriter = io.TextIOWrapper(gzipwriter, 'utf-8') else: raise ValueError("filename must be a path or a file-like object") return self._print_svg(filename, svgwriter, fh_to_close)
def print_png(self, fname_or_fh, *args, **kwargs): """ Use LaTeX to compile a pgf figure to pdf and convert it to png. """ if is_string_like(fname_or_fh): with open(fname_or_fh, "wb") as fh: self._print_png_to_fh(fh) elif is_writable_file_like(fname_or_fh): self._print_png_to_fh(fname_or_fh) else: raise ValueError("filename must be a path or a file-like object")
def print_pdf(self, fname_or_fh, *args, **kwargs): """ Use LaTeX to compile a Pgf generated figure to PDF. """ # figure out where the pdf is to be written to if is_string_like(fname_or_fh): with open(fname_or_fh, "wb") as fh: self._print_pdf_to_fh(fh) elif is_writable_file_like(fname_or_fh): self._print_pdf_to_fh(fname_or_fh) else: raise ValueError("filename must be a path or a file-like object")
def print_svg(self, filename, *args, **kwargs): if is_string_like(filename): fh_to_close = svgwriter = io.open(filename, "w", encoding="utf-8") elif is_writable_file_like(filename): if not isinstance(filename, io.TextIOBase): if sys.version_info[0] >= 3: svgwriter = io.TextIOWrapper(filename, "utf-8") else: svgwriter = codecs.getwriter("utf-8")(filename) fh_to_close = None else: raise ValueError("filename must be a path or a file-like object") return self._print_svg(filename, svgwriter, fh_to_close, **kwargs)
def print_svg(self, filename, *args, **kwargs): if is_string_like(filename): fh_to_close = svgwriter = io.open(filename, 'w', encoding='utf-8') elif is_writable_file_like(filename): if not isinstance(filename, io.TextIOBase): if six.PY3: svgwriter = io.TextIOWrapper(filename, 'utf-8') else: svgwriter = codecs.getwriter('utf-8')(filename) else: svgwriter = filename fh_to_close = None else: raise ValueError("filename must be a path or a file-like object") return self._print_svg(filename, svgwriter, fh_to_close, **kwargs)
def print_png(self, fname_or_fh, *args, **kwargs): """ Use LaTeX to compile a pgf figure to pdf and convert it to png. """ if kwargs.get("dryrun", False): self._print_pgf_to_fh(None, *args, **kwargs) return if isinstance(fname_or_fh, six.string_types): with open(fname_or_fh, "wb") as fh: self._print_png_to_fh(fh, *args, **kwargs) elif is_writable_file_like(fname_or_fh): self._print_png_to_fh(fname_or_fh, *args, **kwargs) else: raise ValueError("filename must be a path or a file-like object")
def print_ipe(self, filename, *args, **kwargs): if isinstance(filename, string_types): fh_to_close = ipewriter = io.open(filename, 'w', encoding='utf-8') elif is_writable_file_like(filename): if not isinstance(filename, io.TextIOBase): if sys.version_info[0] >= 3: ipewriter = io.TextIOWrapper(filename, 'utf-8') else: ipewriter = codecs.getwriter('utf-8')(filename) else: ipewriter = filename fh_to_close = None else: raise ValueError("filename must be a path or a file-like object") return self._print_ipe(filename, ipewriter, fh_to_close, **kwargs)
def print_pdf(self, fname_or_fh, *args, **kwargs): """ Use LaTeX to compile a Pgf generated figure to PDF. """ if kwargs.get("dryrun", False): self._print_pgf_to_fh(None, *args, **kwargs) return # figure out where the pdf is to be written to if isinstance(fname_or_fh, six.string_types): with open(fname_or_fh, "wb") as fh: self._print_pdf_to_fh(fh, *args, **kwargs) elif is_writable_file_like(fname_or_fh): self._print_pdf_to_fh(fname_or_fh, *args, **kwargs) else: raise ValueError("filename must be a path or a file-like object")
def print_pgf(self, fname_or_fh, *args, **kwargs): """ Output pgf commands for drawing the figure so it can be included and rendered in latex documents. """ if kwargs.get("dryrun", False): return # figure out where the pgf is to be written to if is_string_like(fname_or_fh): with codecs.open(fname_or_fh, "w", encoding="utf-8") as fh: self._print_pgf_to_fh(fh) elif is_writable_file_like(fname_or_fh): raise ValueError("saving pgf to a stream is not supported, " + \ "consider using the pdf option of the pgf-backend") else: raise ValueError("filename must be a path")
def print_pgf(self, fname_or_fh, *args, **kwargs): """ Output pgf commands for drawing the figure so it can be included and rendered in latex documents. """ if kwargs.get("dryrun", False): self._print_pgf_to_fh(None, *args, **kwargs) return # figure out where the pgf is to be written to if is_string_like(fname_or_fh): with codecs.open(fname_or_fh, "w", encoding="utf-8") as fh: self._print_pgf_to_fh(fh, *args, **kwargs) elif is_writable_file_like(fname_or_fh): fh = codecs.getwriter("utf-8")(fname_or_fh) self._print_pgf_to_fh(fh, *args, **kwargs) else: raise ValueError("filename must be a path")
def _print_image(self, filename, format, *args, **kwargs): if self.flags() & gtk.REALIZED == 0: # for self.window(for pixmap) and has a side effect of altering # figure width,height (via configure-event?) gtk.DrawingArea.realize(self) width, height = self.get_width_height() pixmap = gdk.Pixmap(self.window, width, height) self._renderer.set_pixmap(pixmap) self._render_figure(pixmap, width, height) # jpg colors don't match the display very well, png colors match # better pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height) pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, width, height) # set the default quality, if we are writing a JPEG. # http://www.pygtk.org/docs/pygtk/class-gdkpixbuf.html#method-gdkpixbuf--save options = cbook.restrict_dict(kwargs, ["quality"]) if format in ["jpg", "jpeg"]: if "quality" not in options: options["quality"] = rcParams["savefig.jpeg_quality"] options["quality"] = str(options["quality"]) if is_string_like(filename): try: pixbuf.save(filename, format, options=options) except gobject.GError as exc: error_msg_gtk("Save figure failure:\n%s" % (exc,), parent=self) elif is_writable_file_like(filename): if hasattr(pixbuf, "save_to_callback"): def save_callback(buf, data=None): data.write(buf) try: pixbuf.save_to_callback(save_callback, format, user_data=filename, options=options) except gobject.GError as exc: error_msg_gtk("Save figure failure:\n%s" % (exc,), parent=self) else: raise ValueError("Saving to a Python file-like object is only supported by PyGTK >= 2.8") else: raise ValueError("filename must be a path or a file-like object")
def _print_image(self, filename, format): if self.flags() & gtk.REALIZED == 0: # for self.window(for pixmap) and has a side effect of altering # figure width,height (via configure-event?) gtk.DrawingArea.realize(self) width, height = self.get_width_height() pixmap = gdk.Pixmap(self.window, width, height) self._renderer.set_pixmap(pixmap) self._render_figure(pixmap, width, height) # jpg colors don't match the display very well, png colors match # better pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height) pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, width, height) if is_string_like(filename): try: pixbuf.save(filename, format) except gobject.GError as exc: error_msg_gtk('Save figure failure:\n%s' % (exc, ), parent=self) elif is_writable_file_like(filename): if hasattr(pixbuf, 'save_to_callback'): def save_callback(buf, data=None): data.write(buf) try: pixbuf.save_to_callback(save_callback, format, user_data=filename) except gobject.GError as exc: error_msg_gtk('Save figure failure:\n%s' % (exc, ), parent=self) else: raise ValueError( "Saving to a Python file-like object is only supported by PyGTK >= 2.8" ) else: raise ValueError("filename must be a path or a file-like object")
def _print_image(self, filename, format, *args, **kwargs): if self.flags() & gtk.REALIZED == 0: # for self.window(for pixmap) and has a side effect of altering # figure width,height (via configure-event?) gtk.DrawingArea.realize(self) width, height = self.get_width_height() pixmap = gdk.Pixmap (self.window, width, height) self._renderer.set_pixmap (pixmap) self._render_figure(pixmap, width, height) # jpg colors don't match the display very well, png colors match # better pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height) pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, width, height) # set the default quality, if we are writing a JPEG. # http://www.pygtk.org/docs/pygtk/class-gdkpixbuf.html#method-gdkpixbuf--save options = {k: kwargs[k] for k in ['quality'] if k in kwargs} if format in ['jpg', 'jpeg']: options.setdefault('quality', rcParams['savefig.jpeg_quality']) options['quality'] = str(options['quality']) if isinstance(filename, six.string_types): try: pixbuf.save(filename, format, options=options) except gobject.GError as exc: error_msg_gtk('Save figure failure:\n%s' % (exc,), parent=self) elif is_writable_file_like(filename): if hasattr(pixbuf, 'save_to_callback'): def save_callback(buf, data=None): data.write(buf) try: pixbuf.save_to_callback(save_callback, format, user_data=filename, options=options) except gobject.GError as exc: error_msg_gtk('Save figure failure:\n%s' % (exc,), parent=self) else: raise ValueError("Saving to a Python file-like object is only supported by PyGTK >= 2.8") else: raise ValueError("filename must be a path or a file-like object")
def print_pgf(self, fname_or_fh, *args, **kwargs): """ Output pgf commands for drawing the figure so it can be included and rendered in latex documents. """ if kwargs.get("dryrun", False): self._print_pgf_to_fh(None, *args, **kwargs) return # figure out where the pgf is to be written to if is_string_like(fname_or_fh): with codecs.open(fname_or_fh, "w", encoding="utf-8") as fh: self._print_pgf_to_fh(fh, *args, **kwargs) elif is_writable_file_like(fname_or_fh): if not os.path.exists(fname_or_fh.name): warnings.warn("streamed pgf-code does not support raster " "graphics, consider using the pgf-to-pdf option", UserWarning) self._print_pgf_to_fh(fname_or_fh, *args, **kwargs) else: raise ValueError("filename must be a path")
def _print_figure( self, outfile, format, *, dpi, dsc_comments, orientation, papertype, dryrun=False, bbox_inches_restore=None): """ Render the figure to a filesystem path or a file-like object. Parameters are as for `.print_figure`, except that *dsc_comments* is a all string containing Document Structuring Convention comments, generated from the *metadata* parameter to `.print_figure`. """ is_eps = format == 'eps' if isinstance(outfile, (str, os.PathLike)): outfile = os.fspath(outfile) passed_in_file_object = False elif is_writable_file_like(outfile): passed_in_file_object = True else: raise ValueError("outfile must be a path or a file-like object") # find the appropriate papertype width, height = self.figure.get_size_inches() if papertype == 'auto': papertype = _get_papertype( *orientation.swap_if_landscape((width, height))) paper_width, paper_height = orientation.swap_if_landscape( papersize[papertype]) if mpl.rcParams['ps.usedistiller']: # distillers improperly clip eps files if pagesize is too small if width > paper_width or height > paper_height: papertype = _get_papertype( *orientation.swap_if_landscape(width, height)) paper_width, paper_height = orientation.swap_if_landscape( papersize[papertype]) # center the figure on the paper xo = 72 * 0.5 * (paper_width - width) yo = 72 * 0.5 * (paper_height - height) llx = xo lly = yo urx = llx + self.figure.bbox.width ury = lly + self.figure.bbox.height rotation = 0 if orientation is _Orientation.landscape: llx, lly, urx, ury = lly, llx, ury, urx xo, yo = 72 * paper_height - yo, xo rotation = 90 bbox = (llx, lly, urx, ury) if dryrun: class NullWriter: def write(self, *args, **kwargs): pass self._pswriter = NullWriter() else: self._pswriter = StringIO() # mixed mode rendering ps_renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi) renderer = MixedModeRenderer( self.figure, width, height, dpi, ps_renderer, bbox_inches_restore=bbox_inches_restore) self.figure.draw(renderer) if dryrun: # return immediately if dryrun (tightbbox=True) return def print_figure_impl(fh): # write the PostScript headers if is_eps: print("%!PS-Adobe-3.0 EPSF-3.0", file=fh) else: print(f"%!PS-Adobe-3.0\n" f"%%DocumentPaperSizes: {papertype}\n" f"%%Pages: 1\n", end="", file=fh) print(f"{dsc_comments}\n" f"%%Orientation: {orientation.name}\n" f"%%BoundingBox: {bbox[0]} {bbox[1]} {bbox[2]} {bbox[3]}\n" f"%%EndComments\n", end="", file=fh) Ndict = len(psDefs) print("%%BeginProlog", file=fh) if not mpl.rcParams['ps.useafm']: Ndict += len(ps_renderer._character_tracker.used) print("/mpldict %d dict def" % Ndict, file=fh) print("mpldict begin", file=fh) print("\n".join(psDefs), file=fh) if not mpl.rcParams['ps.useafm']: for font_path, chars \ in ps_renderer._character_tracker.used.items(): if not chars: continue font = get_font(font_path) glyph_ids = [font.get_char_index(c) for c in chars] fonttype = mpl.rcParams['ps.fonttype'] # Can't use more than 255 chars from a single Type 3 font. if len(glyph_ids) > 255: fonttype = 42 # The ttf to ps (subsetting) support doesn't work for # OpenType fonts that are Postscript inside (like the STIX # fonts). This will simply turn that off to avoid errors. if is_opentype_cff_font(font_path): raise RuntimeError( "OpenType CFF fonts can not be saved using " "the internal Postscript backend at this " "time; consider using the Cairo backend") fh.flush() try: convert_ttf_to_ps(os.fsencode(font_path), fh, fonttype, glyph_ids) except RuntimeError: _log.warning("The PostScript backend does not " "currently support the selected font.") raise print("end", file=fh) print("%%EndProlog", file=fh) if not is_eps: print("%%Page: 1 1", file=fh) print("mpldict begin", file=fh) print("%s translate" % _nums_to_str(xo, yo), file=fh) if rotation: print("%d rotate" % rotation, file=fh) print("%s clipbox" % _nums_to_str(width*72, height*72, 0, 0), file=fh) # write the figure print(self._pswriter.getvalue(), file=fh) # write the trailer print("end", file=fh) print("showpage", file=fh) if not is_eps: print("%%EOF", file=fh) fh.flush() if mpl.rcParams['ps.usedistiller']: # We are going to use an external program to process the output. # Write to a temporary file. with TemporaryDirectory() as tmpdir: tmpfile = os.path.join(tmpdir, "tmp.ps") with open(tmpfile, 'w', encoding='latin-1') as fh: print_figure_impl(fh) if mpl.rcParams['ps.usedistiller'] == 'ghostscript': _try_distill(gs_distill, tmpfile, is_eps, ptype=papertype, bbox=bbox) elif mpl.rcParams['ps.usedistiller'] == 'xpdf': _try_distill(xpdf_distill, tmpfile, is_eps, ptype=papertype, bbox=bbox) _move_path_to_path_or_stream(tmpfile, outfile) else: # Write directly to outfile. if passed_in_file_object: requires_unicode = file_requires_unicode(outfile) if not requires_unicode: fh = TextIOWrapper(outfile, encoding="latin-1") # Prevent the TextIOWrapper from closing the underlying # file. fh.close = lambda: None else: fh = outfile print_figure_impl(fh) else: with open(outfile, 'w', encoding='latin-1') as fh: print_figure_impl(fh)
def _print_figure(self, outfile, format, dpi=72, facecolor='w', edgecolor='w', orientation='portrait', isLandscape=False, papertype=None): """ Render the figure to hardcopy. Set the figure patch face and edge colors. This is useful because some of the GUIs have a gray figure face color background and you'll probably want to override this on hardcopy If outfile is a string, it is interpreted as a file name. If the extension matches .ep* write encapsulated postscript, otherwise write a stand-alone PostScript file. If outfile is a file object, a stand-alone PostScript file is written into this file object. """ isEPSF = format == 'eps' passed_in_file_object = False if is_string_like(outfile): title = outfile tmpfile = os.path.join(gettempdir(), md5(outfile).hexdigest()) elif is_writable_file_like(outfile): title = None tmpfile = os.path.join(gettempdir(), md5(str(hash(outfile))).hexdigest()) passed_in_file_object = True else: raise ValueError("outfile must be a path or a file-like object") fh = file(tmpfile, 'w') # find the appropriate papertype width, height = self.figure.get_size_inches() if papertype == 'auto': if isLandscape: papertype = _get_papertype(height, width) else: papertype = _get_papertype(width, height) if isLandscape: paperHeight, paperWidth = papersize[papertype] else: paperWidth, paperHeight = papersize[papertype] if rcParams['ps.usedistiller'] and not papertype == 'auto': # distillers will improperly clip eps files if the pagesize is # too small if width > paperWidth or height > paperHeight: if isLandscape: papertype = _get_papertype(height, width) paperHeight, paperWidth = papersize[papertype] else: papertype = _get_papertype(width, height) paperWidth, paperHeight = papersize[papertype] # center the figure on the paper xo = 72 * 0.5 * (paperWidth - width) yo = 72 * 0.5 * (paperHeight - height) l, b, w, h = self.figure.bbox.bounds llx = xo lly = yo urx = llx + w ury = lly + h rotation = 0 if isLandscape: llx, lly, urx, ury = lly, llx, ury, urx xo, yo = 72 * paperHeight - yo, xo rotation = 90 bbox = (llx, lly, urx, ury) # generate PostScript code for the figure and store it in a string origfacecolor = self.figure.get_facecolor() origedgecolor = self.figure.get_edgecolor() self.figure.set_facecolor(facecolor) self.figure.set_edgecolor(edgecolor) self._pswriter = StringIO() renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi) self.figure.draw(renderer) self.figure.set_facecolor(origfacecolor) self.figure.set_edgecolor(origedgecolor) # write the PostScript headers if isEPSF: print >> fh, "%!PS-Adobe-3.0 EPSF-3.0" else: print >> fh, "%!PS-Adobe-3.0" if title: print >> fh, "%%Title: " + title print >> fh, ("%%Creator: matplotlib version " + __version__ + ", http://matplotlib.sourceforge.net/") print >> fh, "%%CreationDate: " + time.ctime(time.time()) print >> fh, "%%Orientation: " + orientation if not isEPSF: print >> fh, "%%DocumentPaperSizes: " + papertype print >> fh, "%%%%BoundingBox: %d %d %d %d" % bbox if not isEPSF: print >> fh, "%%Pages: 1" print >> fh, "%%EndComments" Ndict = len(psDefs) print >> fh, "%%BeginProlog" if not rcParams['ps.useafm']: Ndict += len(renderer.used_characters) print >> fh, "/mpldict %d dict def" % Ndict print >> fh, "mpldict begin" for d in psDefs: d = d.strip() for l in d.split('\n'): print >> fh, l.strip() if not rcParams['ps.useafm']: for font_filename, chars in renderer.used_characters.values(): if len(chars): font = FT2Font(font_filename) cmap = font.get_charmap() glyph_ids = [] for c in chars: gind = cmap.get(c) or 0 glyph_ids.append(gind) # The ttf to ps (subsetting) support doesn't work for # OpenType fonts that are Postscript inside (like the # STIX fonts). This will simply turn that off to avoid # errors. if is_opentype_cff_font(font_filename): raise RuntimeError( "OpenType CFF fonts can not be saved using the internal Postscript backend at this time.\nConsider using the Cairo backend." ) else: fonttype = rcParams['ps.fonttype'] convert_ttf_to_ps(font_filename, fh, rcParams['ps.fonttype'], glyph_ids) print >> fh, "end" print >> fh, "%%EndProlog" if not isEPSF: print >> fh, "%%Page: 1 1" print >> fh, "mpldict begin" #print >>fh, "gsave" print >> fh, "%s translate" % _nums_to_str(xo, yo) if rotation: print >> fh, "%d rotate" % rotation print >> fh, "%s clipbox" % _nums_to_str(width * 72, height * 72, 0, 0) # write the figure print >> fh, self._pswriter.getvalue() # write the trailer #print >>fh, "grestore" print >> fh, "end" print >> fh, "showpage" if not isEPSF: print >> fh, "%%EOF" fh.close() if rcParams['ps.usedistiller'] == 'ghostscript': gs_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox) elif rcParams['ps.usedistiller'] == 'xpdf': xpdf_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox) if passed_in_file_object: fh = file(tmpfile) print >> outfile, fh.read() else: shutil.move(tmpfile, outfile)
def _print_figure(self, outfile, format, dpi=72, facecolor='w', edgecolor='w', orientation='portrait', isLandscape=False, papertype=None, **kwargs): """ Render the figure to hardcopy. Set the figure patch face and edge colors. This is useful because some of the GUIs have a gray figure face color background and you'll probably want to override this on hardcopy If outfile is a string, it is interpreted as a file name. If the extension matches .ep* write encapsulated postscript, otherwise write a stand-alone PostScript file. If outfile is a file object, a stand-alone PostScript file is written into this file object. """ isEPSF = format == 'eps' passed_in_file_object = False fd, tmpfile = mkstemp() if is_string_like(outfile): title = outfile elif is_writable_file_like(outfile): title = None passed_in_file_object = True else: raise ValueError("outfile must be a path or a file-like object") os.close(fd) fh = file(tmpfile, 'w') width, height = self.figure.get_size_inches() if papertype == 'auto': if isLandscape: papertype = _get_papertype(height, width) else: papertype = _get_papertype(width, height) if isLandscape: paperHeight, paperWidth = papersize[papertype] else: paperWidth, paperHeight = papersize[papertype] if rcParams['ps.usedistiller'] and not papertype == 'auto': if width>paperWidth or height>paperHeight: if isLandscape: papertype = _get_papertype(height, width) paperHeight, paperWidth = papersize[papertype] else: papertype = _get_papertype(width, height) paperWidth, paperHeight = papersize[papertype] xo = 72*0.5*(paperWidth - width) yo = 72*0.5*(paperHeight - height) l, b, w, h = self.figure.bbox.bounds llx = xo lly = yo urx = llx + w ury = lly + h rotation = 0 if isLandscape: llx, lly, urx, ury = lly, llx, ury, urx xo, yo = 72*paperHeight - yo, xo rotation = 90 bbox = (llx, lly, urx, ury) origfacecolor = self.figure.get_facecolor() origedgecolor = self.figure.get_edgecolor() self.figure.set_facecolor(facecolor) self.figure.set_edgecolor(edgecolor) dryrun = kwargs.get("dryrun", False) if dryrun: class NullWriter(object): def write(self, *kl, **kwargs): pass self._pswriter = NullWriter() else: self._pswriter = StringIO() _bbox_inches_restore = kwargs.pop("bbox_inches_restore", None) ps_renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi) renderer = MixedModeRenderer(self.figure, width, height, dpi, ps_renderer, bbox_inches_restore=_bbox_inches_restore) self.figure.draw(renderer) if dryrun: # return immediately if dryrun (tightbbox=True) return self.figure.set_facecolor(origfacecolor) self.figure.set_edgecolor(origedgecolor) if isEPSF: print >>fh, "%!PS-Adobe-3.0 EPSF-3.0" else: print >>fh, "%!PS-Adobe-3.0" if title: print >>fh, "%%Title: "+title print >>fh, ("%%Creator: matplotlib version " +__version__+", http://matplotlib.sourceforge.net/") print >>fh, "%%CreationDate: "+time.ctime(time.time()) print >>fh, "%%Orientation: " + orientation if not isEPSF: print >>fh, "%%DocumentPaperSizes: "+papertype print >>fh, "%%%%BoundingBox: %d %d %d %d" % bbox if not isEPSF: print >>fh, "%%Pages: 1" print >>fh, "%%EndComments" Ndict = len(psDefs) print >>fh, "%%BeginProlog" if not rcParams['ps.useafm']: Ndict += len(ps_renderer.used_characters) print >>fh, "/mpldict %d dict def"%Ndict print >>fh, "mpldict begin" for d in psDefs: d=d.strip() for l in d.split('\n'): print >>fh, l.strip() if not rcParams['ps.useafm']: for font_filename, chars in ps_renderer.used_characters.values(): if len(chars): font = FT2Font(font_filename) cmap = font.get_charmap() glyph_ids = [] for c in chars: gind = cmap.get(c) or 0 glyph_ids.append(gind) if is_opentype_cff_font(font_filename): raise RuntimeError("OpenType CFF fonts can not be saved using the internal Postscript backend at this time.\nConsider using the Cairo backend.") else: fonttype = rcParams['ps.fonttype'] convert_ttf_to_ps(font_filename, fh, rcParams['ps.fonttype'], glyph_ids) print >>fh, "end" print >>fh, "%%EndProlog" if not isEPSF: print >>fh, "%%Page: 1 1" print >>fh, "mpldict begin" print >>fh, "%s translate"%_nums_to_str(xo, yo) if rotation: print >>fh, "%d rotate"%rotation print >>fh, "%s clipbox"%_nums_to_str(width*72, height*72, 0, 0) print >>fh, self._pswriter.getvalue() print >>fh, "end" print >>fh, "showpage" if not isEPSF: print >>fh, "%%EOF" fh.close() if rcParams['ps.usedistiller'] == 'ghostscript': gs_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox) elif rcParams['ps.usedistiller'] == 'xpdf': xpdf_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox) if passed_in_file_object: fh = file(tmpfile) print >>outfile, fh.read() else: shutil.move(tmpfile, outfile)
class FigureCanvasGTK(gtk.DrawingArea, FigureCanvasBase): keyvald = { 65507: 'control', 65505: 'shift', 65513: 'alt', 65508: 'control', 65506: 'shift', 65514: 'alt', 65361: 'left', 65362: 'up', 65363: 'right', 65364: 'down', 65307: 'escape', 65470: 'f1', 65471: 'f2', 65472: 'f3', 65473: 'f4', 65474: 'f5', 65475: 'f6', 65476: 'f7', 65477: 'f8', 65478: 'f9', 65479: 'f10', 65480: 'f11', 65481: 'f12', 65300: 'scroll_lock', 65299: 'break', 65288: 'backspace', 65293: 'enter', 65379: 'insert', 65535: 'delete', 65360: 'home', 65367: 'end', 65365: 'pageup', 65366: 'pagedown', 65438: '0', 65436: '1', 65433: '2', 65435: '3', 65430: '4', 65437: '5', 65432: '6', 65429: '7', 65431: '8', 65434: '9', 65451: '+', 65453: '-', 65450: '*', 65455: '/', 65439: 'dec', 65421: 'enter', } # Setting this as a static constant prevents # this resulting expression from leaking event_mask = (gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK | gdk.EXPOSURE_MASK | gdk.KEY_PRESS_MASK | gdk.KEY_RELEASE_MASK | gdk.ENTER_NOTIFY_MASK | gdk.LEAVE_NOTIFY_MASK | gdk.POINTER_MOTION_MASK | gdk.POINTER_MOTION_HINT_MASK) def __init__(self, figure): if _debug: print 'FigureCanvasGTK.%s' % fn_name() FigureCanvasBase.__init__(self, figure) gtk.DrawingArea.__init__(self) self._idle_draw_id = 0 self._need_redraw = True self._pixmap_width = -1 self._pixmap_height = -1 self._lastCursor = None self.connect('scroll_event', self.scroll_event) self.connect('button_press_event', self.button_press_event) self.connect('button_release_event', self.button_release_event) self.connect('configure_event', self.configure_event) self.connect('expose_event', self.expose_event) self.connect('key_press_event', self.key_press_event) self.connect('key_release_event', self.key_release_event) self.connect('motion_notify_event', self.motion_notify_event) self.connect('leave_notify_event', self.leave_notify_event) self.connect('enter_notify_event', self.enter_notify_event) self.set_events(self.__class__.event_mask) self.set_double_buffered(False) self.set_flags(gtk.CAN_FOCUS) self._renderer_init() self._idle_event_id = gobject.idle_add(self.idle_event) def destroy(self): #gtk.DrawingArea.destroy(self) gobject.source_remove(self._idle_event_id) if self._idle_draw_id != 0: gobject.source_remove(self._idle_draw_id) def scroll_event(self, widget, event): if _debug: print 'FigureCanvasGTK.%s' % fn_name() x = event.x # flipy so y=0 is bottom of canvas y = self.allocation.height - event.y if event.direction == gdk.SCROLL_UP: step = 1 else: step = -1 FigureCanvasBase.scroll_event(self, x, y, step, guiEvent=event) return False # finish event propagation? def button_press_event(self, widget, event): if _debug: print 'FigureCanvasGTK.%s' % fn_name() x = event.x # flipy so y=0 is bottom of canvas y = self.allocation.height - event.y FigureCanvasBase.button_press_event(self, x, y, event.button, guiEvent=event) return False # finish event propagation? def button_release_event(self, widget, event): if _debug: print 'FigureCanvasGTK.%s' % fn_name() x = event.x # flipy so y=0 is bottom of canvas y = self.allocation.height - event.y FigureCanvasBase.button_release_event(self, x, y, event.button, guiEvent=event) return False # finish event propagation? def key_press_event(self, widget, event): if _debug: print 'FigureCanvasGTK.%s' % fn_name() key = self._get_key(event) if _debug: print "hit", key FigureCanvasBase.key_press_event(self, key, guiEvent=event) return False # finish event propagation? def key_release_event(self, widget, event): if _debug: print 'FigureCanvasGTK.%s' % fn_name() key = self._get_key(event) if _debug: print "release", key FigureCanvasBase.key_release_event(self, key, guiEvent=event) return False # finish event propagation? def motion_notify_event(self, widget, event): if _debug: print 'FigureCanvasGTK.%s' % fn_name() if event.is_hint: x, y, state = event.window.get_pointer() else: x, y, state = event.x, event.y, event.state # flipy so y=0 is bottom of canvas y = self.allocation.height - y FigureCanvasBase.motion_notify_event(self, x, y, guiEvent=event) return False # finish event propagation? def leave_notify_event(self, widget, event): FigureCanvasBase.leave_notify_event(self, event) def enter_notify_event(self, widget, event): FigureCanvasBase.enter_notify_event(self, event) def _get_key(self, event): if event.keyval in self.keyvald: key = self.keyvald[event.keyval] elif event.keyval < 256: key = chr(event.keyval) else: key = None ctrl = event.state & gdk.CONTROL_MASK shift = event.state & gdk.SHIFT_MASK return key def configure_event(self, widget, event): if _debug: print 'FigureCanvasGTK.%s' % fn_name() if widget.window is None: return w, h = event.width, event.height if w < 3 or h < 3: return # empty fig # resize the figure (in inches) dpi = self.figure.dpi self.figure.set_size_inches(w / dpi, h / dpi) self._need_redraw = True return False # finish event propagation? def draw(self): # Note: FigureCanvasBase.draw() is inconveniently named as it clashes # with the deprecated gtk.Widget.draw() self._need_redraw = True if GTK_WIDGET_DRAWABLE(self): self.queue_draw() # do a synchronous draw (its less efficient than an async draw, # but is required if/when animation is used) self.window.process_updates(False) def draw_idle(self): def idle_draw(*args): self.draw() self._idle_draw_id = 0 return False if self._idle_draw_id == 0: self._idle_draw_id = gobject.idle_add(idle_draw) def _renderer_init(self): """Override by GTK backends to select a different renderer Renderer should provide the methods: set_pixmap () set_width_height () that are used by _render_figure() / _pixmap_prepare() """ self._renderer = RendererGDK(self, self.figure.dpi) def _pixmap_prepare(self, width, height): """ Make sure _._pixmap is at least width, height, create new pixmap if necessary """ if _debug: print 'FigureCanvasGTK.%s' % fn_name() create_pixmap = False if width > self._pixmap_width: # increase the pixmap in 10%+ (rather than 1 pixel) steps self._pixmap_width = max(int(self._pixmap_width * 1.1), width) create_pixmap = True if height > self._pixmap_height: self._pixmap_height = max(int(self._pixmap_height * 1.1), height) create_pixmap = True if create_pixmap: self._pixmap = gdk.Pixmap(self.window, self._pixmap_width, self._pixmap_height) self._renderer.set_pixmap(self._pixmap) def _render_figure(self, pixmap, width, height): """used by GTK and GTKcairo. GTKAgg overrides """ self._renderer.set_width_height(width, height) self.figure.draw(self._renderer) def expose_event(self, widget, event): """Expose_event for all GTK backends. Should not be overridden. """ if _debug: print 'FigureCanvasGTK.%s' % fn_name() if GTK_WIDGET_DRAWABLE(self): if self._need_redraw: x, y, w, h = self.allocation self._pixmap_prepare(w, h) self._render_figure(self._pixmap, w, h) self._need_redraw = False x, y, w, h = event.area self.window.draw_drawable(self.style.fg_gc[self.state], self._pixmap, x, y, x, y, w, h) return False # finish event propagation? filetypes = FigureCanvasBase.filetypes.copy() filetypes['jpg'] = 'JPEG' filetypes['jpeg'] = 'JPEG' filetypes['png'] = 'Portable Network Graphics' def print_jpeg(self, filename, *args, **kwargs): return self._print_image(filename, 'jpeg') print_jpg = print_jpeg def print_png(self, filename, *args, **kwargs): return self._print_image(filename, 'png') def _print_image(self, filename, format): if self.flags() & gtk.REALIZED == 0: # for self.window(for pixmap) and has a side effect of altering # figure width,height (via configure-event?) gtk.DrawingArea.realize(self) width, height = self.get_width_height() pixmap = gdk.Pixmap(self.window, width, height) self._renderer.set_pixmap(pixmap) self._render_figure(pixmap, width, height) # jpg colors don't match the display very well, png colors match # better pixbuf = gdk.Pixbuf(gdk.COLORSPACE_RGB, 0, 8, width, height) pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, width, height) if is_string_like(filename): try: pixbuf.save(filename, format) except gobject.GError, exc: error_msg_gtk('Save figure failure:\n%s' % (exc, ), parent=self) elif is_writable_file_like(filename): if hasattr(pixbuf, 'save_to_callback'): def save_callback(buf, data=None): data.write(buf) try: pixbuf.save_to_callback(save_callback, format, user_data=filename) except gobject.GError, exc: error_msg_gtk('Save figure failure:\n%s' % (exc, ), parent=self)
def _print_figure(self, outfile, format, dpi=72, facecolor='w', edgecolor='w', orientation='portrait', isLandscape=False, papertype=None): """ Render the figure to hardcopy. Set the figure patch face and edge colors. This is useful because some of the GUIs have a gray figure face color background and you'll probably want to override this on hardcopy If outfile is a string, it is interpreted as a file name. If the extension matches .ep* write encapsulated postscript, otherwise write a stand-alone PostScript file. If outfile is a file object, a stand-alone PostScript file is written into this file object. """ isEPSF = format == 'eps' passed_in_file_object = False if is_string_like(outfile): title = outfile tmpfile = os.path.join(gettempdir(), md5(outfile).hexdigest()) elif is_writable_file_like(outfile): title = None tmpfile = os.path.join(gettempdir(), md5(str(hash(outfile))).hexdigest()) passed_in_file_object = True else: raise ValueError("outfile must be a path or a file-like object") fh = file(tmpfile, 'w') # find the appropriate papertype width, height = self.figure.get_size_inches() if papertype == 'auto': if isLandscape: papertype = _get_papertype(height, width) else: papertype = _get_papertype(width, height) if isLandscape: paperHeight, paperWidth = papersize[papertype] else: paperWidth, paperHeight = papersize[papertype] if rcParams['ps.usedistiller'] and not papertype == 'auto': # distillers will improperly clip eps files if the pagesize is # too small if width>paperWidth or height>paperHeight: if isLandscape: papertype = _get_papertype(height, width) paperHeight, paperWidth = papersize[papertype] else: papertype = _get_papertype(width, height) paperWidth, paperHeight = papersize[papertype] # center the figure on the paper xo = 72*0.5*(paperWidth - width) yo = 72*0.5*(paperHeight - height) l, b, w, h = self.figure.bbox.bounds llx = xo lly = yo urx = llx + w ury = lly + h rotation = 0 if isLandscape: llx, lly, urx, ury = lly, llx, ury, urx xo, yo = 72*paperHeight - yo, xo rotation = 90 bbox = (llx, lly, urx, ury) # generate PostScript code for the figure and store it in a string origfacecolor = self.figure.get_facecolor() origedgecolor = self.figure.get_edgecolor() self.figure.set_facecolor(facecolor) self.figure.set_edgecolor(edgecolor) self._pswriter = StringIO() renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi) self.figure.draw(renderer) self.figure.set_facecolor(origfacecolor) self.figure.set_edgecolor(origedgecolor) # write the PostScript headers if isEPSF: print >>fh, "%!PS-Adobe-3.0 EPSF-3.0" else: print >>fh, "%!PS-Adobe-3.0" if title: print >>fh, "%%Title: "+title print >>fh, ("%%Creator: matplotlib version " +__version__+", http://matplotlib.sourceforge.net/") print >>fh, "%%CreationDate: "+time.ctime(time.time()) print >>fh, "%%Orientation: " + orientation if not isEPSF: print >>fh, "%%DocumentPaperSizes: "+papertype print >>fh, "%%%%BoundingBox: %d %d %d %d" % bbox if not isEPSF: print >>fh, "%%Pages: 1" print >>fh, "%%EndComments" Ndict = len(psDefs) print >>fh, "%%BeginProlog" if not rcParams['ps.useafm']: Ndict += len(renderer.used_characters) print >>fh, "/mpldict %d dict def"%Ndict print >>fh, "mpldict begin" for d in psDefs: d=d.strip() for l in d.split('\n'): print >>fh, l.strip() if not rcParams['ps.useafm']: for font_filename, chars in renderer.used_characters.values(): if len(chars): font = FT2Font(font_filename) cmap = font.get_charmap() glyph_ids = [] for c in chars: gind = cmap.get(c) or 0 glyph_ids.append(gind) # The ttf to ps (subsetting) support doesn't work for # OpenType fonts that are Postscript inside (like the # STIX fonts). This will simply turn that off to avoid # errors. if is_opentype_cff_font(font_filename): raise RuntimeError("OpenType CFF fonts can not be saved using the internal Postscript backend at this time.\nConsider using the Cairo backend.") else: fonttype = rcParams['ps.fonttype'] convert_ttf_to_ps(font_filename, fh, rcParams['ps.fonttype'], glyph_ids) print >>fh, "end" print >>fh, "%%EndProlog" if not isEPSF: print >>fh, "%%Page: 1 1" print >>fh, "mpldict begin" #print >>fh, "gsave" print >>fh, "%s translate"%_nums_to_str(xo, yo) if rotation: print >>fh, "%d rotate"%rotation print >>fh, "%s clipbox"%_nums_to_str(width*72, height*72, 0, 0) # write the figure print >>fh, self._pswriter.getvalue() # write the trailer #print >>fh, "grestore" print >>fh, "end" print >>fh, "showpage" if not isEPSF: print >>fh, "%%EOF" fh.close() if rcParams['ps.usedistiller'] == 'ghostscript': gs_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox) elif rcParams['ps.usedistiller'] == 'xpdf': xpdf_distill(tmpfile, isEPSF, ptype=papertype, bbox=bbox) if passed_in_file_object: fh = file(tmpfile) print >>outfile, fh.read() else: shutil.move(tmpfile, outfile)
def _print_figure(self, fmt, outfile, *, dpi, dsc_comments, orientation, papertype, bbox_inches_restore=None): """ Render the figure to a filesystem path or a file-like object. Parameters are as for `.print_figure`, except that *dsc_comments* is a all string containing Document Structuring Convention comments, generated from the *metadata* parameter to `.print_figure`. """ is_eps = fmt == 'eps' if not (isinstance(outfile, (str, os.PathLike)) or is_writable_file_like(outfile)): raise ValueError("outfile must be a path or a file-like object") # find the appropriate papertype width, height = self.figure.get_size_inches() if papertype == 'auto': papertype = _get_papertype( *orientation.swap_if_landscape((width, height))) paper_width, paper_height = orientation.swap_if_landscape( papersize[papertype]) if mpl.rcParams['ps.usedistiller']: # distillers improperly clip eps files if pagesize is too small if width > paper_width or height > paper_height: papertype = _get_papertype( *orientation.swap_if_landscape((width, height))) paper_width, paper_height = orientation.swap_if_landscape( papersize[papertype]) # center the figure on the paper xo = 72 * 0.5 * (paper_width - width) yo = 72 * 0.5 * (paper_height - height) llx = xo lly = yo urx = llx + self.figure.bbox.width ury = lly + self.figure.bbox.height rotation = 0 if orientation is _Orientation.landscape: llx, lly, urx, ury = lly, llx, ury, urx xo, yo = 72 * paper_height - yo, xo rotation = 90 bbox = (llx, lly, urx, ury) self._pswriter = StringIO() # mixed mode rendering ps_renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi) renderer = MixedModeRenderer(self.figure, width, height, dpi, ps_renderer, bbox_inches_restore=bbox_inches_restore) self.figure.draw(renderer) def print_figure_impl(fh): # write the PostScript headers if is_eps: print("%!PS-Adobe-3.0 EPSF-3.0", file=fh) else: print( f"%!PS-Adobe-3.0\n" f"%%DocumentPaperSizes: {papertype}\n" f"%%Pages: 1\n", end="", file=fh) print( f"{dsc_comments}\n" f"%%Orientation: {orientation.name}\n" f"{get_bbox_header(bbox)[0]}\n" f"%%EndComments\n", end="", file=fh) Ndict = len(psDefs) print("%%BeginProlog", file=fh) if not mpl.rcParams['ps.useafm']: Ndict += len(ps_renderer._character_tracker.used) print("/mpldict %d dict def" % Ndict, file=fh) print("mpldict begin", file=fh) print("\n".join(psDefs), file=fh) if not mpl.rcParams['ps.useafm']: for font_path, chars \ in ps_renderer._character_tracker.used.items(): if not chars: continue fonttype = mpl.rcParams['ps.fonttype'] # Can't use more than 255 chars from a single Type 3 font. if len(chars) > 255: fonttype = 42 fh.flush() if fonttype == 3: fh.write(_font_to_ps_type3(font_path, chars)) else: # Type 42 only. _font_to_ps_type42(font_path, chars, fh) print("end", file=fh) print("%%EndProlog", file=fh) if not is_eps: print("%%Page: 1 1", file=fh) print("mpldict begin", file=fh) print("%s translate" % _nums_to_str(xo, yo), file=fh) if rotation: print("%d rotate" % rotation, file=fh) print("%s clipbox" % _nums_to_str(width * 72, height * 72, 0, 0), file=fh) # write the figure print(self._pswriter.getvalue(), file=fh) # write the trailer print("end", file=fh) print("showpage", file=fh) if not is_eps: print("%%EOF", file=fh) fh.flush() if mpl.rcParams['ps.usedistiller']: # We are going to use an external program to process the output. # Write to a temporary file. with TemporaryDirectory() as tmpdir: tmpfile = os.path.join(tmpdir, "tmp.ps") with open(tmpfile, 'w', encoding='latin-1') as fh: print_figure_impl(fh) if mpl.rcParams['ps.usedistiller'] == 'ghostscript': _try_distill(gs_distill, tmpfile, is_eps, ptype=papertype, bbox=bbox) elif mpl.rcParams['ps.usedistiller'] == 'xpdf': _try_distill(xpdf_distill, tmpfile, is_eps, ptype=papertype, bbox=bbox) _move_path_to_path_or_stream(tmpfile, outfile) else: # Write directly to outfile. with cbook.open_file_cm(outfile, "w", encoding="latin-1") as file: if not file_requires_unicode(file): file = codecs.getwriter("latin-1")(file) print_figure_impl(file)
def _print_figure_tex(self, outfile, format, dpi, facecolor, edgecolor, orientation, papertype, *, metadata=None, dryrun=False, bbox_inches_restore=None, **kwargs): """ If text.usetex is True in rc, a temporary pair of tex/eps files are created to allow tex to manage the text layout via the PSFrags package. These files are processed to yield the final ps or eps file. metadata must be a dictionary. Currently, only the value for the key 'Creator' is used. """ is_eps = format == 'eps' if is_writable_file_like(outfile): title = None else: try: title = os.fspath(outfile) except TypeError: raise ValueError( "outfile must be a path or a file-like object") self.figure.dpi = 72 # ignore the dpi kwarg width, height = self.figure.get_size_inches() xo = 0 yo = 0 l, b, w, h = self.figure.bbox.bounds llx = xo lly = yo urx = llx + w ury = lly + h bbox = (llx, lly, urx, ury) # generate PostScript code for the figure and store it in a string origfacecolor = self.figure.get_facecolor() origedgecolor = self.figure.get_edgecolor() self.figure.set_facecolor(facecolor) self.figure.set_edgecolor(edgecolor) if dryrun: class NullWriter: def write(self, *args, **kwargs): pass self._pswriter = NullWriter() else: self._pswriter = StringIO() # mixed mode rendering ps_renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi) renderer = MixedModeRenderer(self.figure, width, height, dpi, ps_renderer, bbox_inches_restore=bbox_inches_restore) self.figure.draw(renderer) if dryrun: # return immediately if dryrun (tightbbox=True) return self.figure.set_facecolor(origfacecolor) self.figure.set_edgecolor(origedgecolor) # check for custom metadata if metadata is not None and 'Creator' in metadata: creator_str = metadata['Creator'] else: creator_str = \ f"matplotlib version {mpl.__version__}, http://matplotlib.org/" # write to a temp file, we'll move it to outfile when done with TemporaryDirectory() as tmpdir: tmpfile = os.path.join(tmpdir, "tmp.ps") # get source date from SOURCE_DATE_EPOCH, if set # See https://reproducible-builds.org/specs/source-date-epoch/ source_date_epoch = os.getenv("SOURCE_DATE_EPOCH") if source_date_epoch: source_date = datetime.datetime.utcfromtimestamp( int(source_date_epoch)).strftime("%a %b %d %H:%M:%S %Y") else: source_date = time.ctime() pathlib.Path(tmpfile).write_text(f"""\ %!PS-Adobe-3.0 EPSF-3.0 {f'''%%Title: {title} ''' if title else ""}\ %%Creator: {creator_str} %%CreationDate: {source_date} %%BoundingBox: {bbox[0]} {bbox[1]} {bbox[2]} {bbox[3]} %%EndComments %%BeginProlog /mpldict {len(psDefs)} dict def mpldict begin {"".join(psDefs)} end %%EndProlog mpldict begin {_nums_to_str(xo, yo)} translate {_nums_to_str(width*72, height*72)} 0 0 clipbox {self._pswriter.getvalue()} end showpage """, encoding="latin-1") if orientation is _Orientation.landscape: # now, ready to rotate width, height = height, width bbox = (lly, llx, ury, urx) # set the paper size to the figure size if is_eps. The # resulting ps file has the given size with correct bounding # box so that there is no need to call 'pstoeps' if is_eps: paper_width, paper_height = orientation.swap_if_landscape( self.figure.get_size_inches()) else: temp_papertype = _get_papertype(width, height) if papertype == 'auto': papertype = temp_papertype paper_width, paper_height = papersize[temp_papertype] else: paper_width, paper_height = papersize[papertype] texmanager = ps_renderer.get_texmanager() font_preamble = texmanager.get_font_preamble() custom_preamble = texmanager.get_custom_preamble() psfrag_rotated = convert_psfrags(tmpfile, ps_renderer.psfrag, font_preamble, custom_preamble, paper_width, paper_height, orientation.name) if (mpl.rcParams['ps.usedistiller'] == 'ghostscript' or mpl.rcParams['text.usetex']): gs_distill(tmpfile, is_eps, ptype=papertype, bbox=bbox, rotated=psfrag_rotated) elif mpl.rcParams['ps.usedistiller'] == 'xpdf': xpdf_distill(tmpfile, is_eps, ptype=papertype, bbox=bbox, rotated=psfrag_rotated) _move_path_to_path_or_stream(tmpfile, outfile)
def _print_figure(self, outfile, format, dpi, facecolor, edgecolor, orientation, papertype, *, metadata=None, dryrun=False, bbox_inches_restore=None, **kwargs): """ Render the figure to hardcopy. Set the figure patch face and edge colors. This is useful because some of the GUIs have a gray figure face color background and you'll probably want to override this on hardcopy If outfile is a string, it is interpreted as a file name. If the extension matches .ep* write encapsulated postscript, otherwise write a stand-alone PostScript file. If outfile is a file object, a stand-alone PostScript file is written into this file object. metadata must be a dictionary. Currently, only the value for the key 'Creator' is used. """ is_eps = format == 'eps' if isinstance(outfile, (str, os.PathLike)): outfile = title = os.fspath(outfile) title = title.encode("ascii", "replace").decode("ascii") passed_in_file_object = False elif is_writable_file_like(outfile): title = None passed_in_file_object = True else: raise ValueError("outfile must be a path or a file-like object") # find the appropriate papertype width, height = self.figure.get_size_inches() if papertype == 'auto': papertype = _get_papertype( *orientation.swap_if_landscape((width, height))) paper_width, paper_height = orientation.swap_if_landscape( papersize[papertype]) if mpl.rcParams['ps.usedistiller']: # distillers improperly clip eps files if pagesize is too small if width > paper_width or height > paper_height: papertype = _get_papertype( *orientation.swap_if_landscape(width, height)) paper_width, paper_height = orientation.swap_if_landscape( papersize[papertype]) # center the figure on the paper xo = 72 * 0.5 * (paper_width - width) yo = 72 * 0.5 * (paper_height - height) l, b, w, h = self.figure.bbox.bounds llx = xo lly = yo urx = llx + w ury = lly + h rotation = 0 if orientation is _Orientation.landscape: llx, lly, urx, ury = lly, llx, ury, urx xo, yo = 72 * paper_height - yo, xo rotation = 90 bbox = (llx, lly, urx, ury) # generate PostScript code for the figure and store it in a string origfacecolor = self.figure.get_facecolor() origedgecolor = self.figure.get_edgecolor() self.figure.set_facecolor(facecolor) self.figure.set_edgecolor(edgecolor) if dryrun: class NullWriter: def write(self, *args, **kwargs): pass self._pswriter = NullWriter() else: self._pswriter = StringIO() # mixed mode rendering ps_renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi) renderer = MixedModeRenderer(self.figure, width, height, dpi, ps_renderer, bbox_inches_restore=bbox_inches_restore) self.figure.draw(renderer) if dryrun: # return immediately if dryrun (tightbbox=True) return self.figure.set_facecolor(origfacecolor) self.figure.set_edgecolor(origedgecolor) # check for custom metadata if metadata is not None and 'Creator' in metadata: creator_str = metadata['Creator'] else: creator_str = \ f"matplotlib version {mpl.__version__}, http://matplotlib.org/" def print_figure_impl(fh): # write the PostScript headers if is_eps: print("%!PS-Adobe-3.0 EPSF-3.0", file=fh) else: print( f"%!PS-Adobe-3.0\n" f"%%DocumentPaperSizes: {papertype}\n" f"%%Pages: 1\n", end="", file=fh) if title: print("%%Title: " + title, file=fh) # get source date from SOURCE_DATE_EPOCH, if set # See https://reproducible-builds.org/specs/source-date-epoch/ source_date_epoch = os.getenv("SOURCE_DATE_EPOCH") if source_date_epoch: source_date = datetime.datetime.utcfromtimestamp( int(source_date_epoch)).strftime("%a %b %d %H:%M:%S %Y") else: source_date = time.ctime() print( f"%%Creator: {creator_str}\n" f"%%CreationDate: {source_date}\n" f"%%Orientation: {orientation.name}\n" f"%%BoundingBox: {bbox[0]} {bbox[1]} {bbox[2]} {bbox[3]}\n" f"%%EndComments\n", end="", file=fh) Ndict = len(psDefs) print("%%BeginProlog", file=fh) if not mpl.rcParams['ps.useafm']: Ndict += len(ps_renderer._character_tracker.used) print("/mpldict %d dict def" % Ndict, file=fh) print("mpldict begin", file=fh) for d in psDefs: d = d.strip() for l in d.split('\n'): print(l.strip(), file=fh) if not mpl.rcParams['ps.useafm']: for font_path, chars \ in ps_renderer._character_tracker.used.items(): if not chars: continue font = get_font(font_path) glyph_ids = [font.get_char_index(c) for c in chars] fonttype = mpl.rcParams['ps.fonttype'] # Can't use more than 255 chars from a single Type 3 font. if len(glyph_ids) > 255: fonttype = 42 # The ttf to ps (subsetting) support doesn't work for # OpenType fonts that are Postscript inside (like the STIX # fonts). This will simply turn that off to avoid errors. if is_opentype_cff_font(font_path): raise RuntimeError( "OpenType CFF fonts can not be saved using " "the internal Postscript backend at this " "time; consider using the Cairo backend") fh.flush() try: convert_ttf_to_ps(os.fsencode(font_path), fh, fonttype, glyph_ids) except RuntimeError: _log.warning("The PostScript backend does not " "currently support the selected font.") raise print("end", file=fh) print("%%EndProlog", file=fh) if not is_eps: print("%%Page: 1 1", file=fh) print("mpldict begin", file=fh) print("%s translate" % _nums_to_str(xo, yo), file=fh) if rotation: print("%d rotate" % rotation, file=fh) print("%s clipbox" % _nums_to_str(width * 72, height * 72, 0, 0), file=fh) # write the figure content = self._pswriter.getvalue() if not isinstance(content, str): content = content.decode('ascii') print(content, file=fh) # write the trailer print("end", file=fh) print("showpage", file=fh) if not is_eps: print("%%EOF", file=fh) fh.flush() if mpl.rcParams['ps.usedistiller']: # We are going to use an external program to process the output. # Write to a temporary file. with TemporaryDirectory() as tmpdir: tmpfile = os.path.join(tmpdir, "tmp.ps") with open(tmpfile, 'w', encoding='latin-1') as fh: print_figure_impl(fh) if mpl.rcParams['ps.usedistiller'] == 'ghostscript': gs_distill(tmpfile, is_eps, ptype=papertype, bbox=bbox) elif mpl.rcParams['ps.usedistiller'] == 'xpdf': xpdf_distill(tmpfile, is_eps, ptype=papertype, bbox=bbox) _move_path_to_path_or_stream(tmpfile, outfile) else: # Write directly to outfile. if passed_in_file_object: requires_unicode = file_requires_unicode(outfile) if not requires_unicode: fh = TextIOWrapper(outfile, encoding="latin-1") # Prevent the TextIOWrapper from closing the underlying # file. fh.close = lambda: None else: fh = outfile print_figure_impl(fh) else: with open(outfile, 'w', encoding='latin-1') as fh: print_figure_impl(fh)