def _print_svg(self, filename, svgwriter, fh_to_close=None): self.figure.set_dpi(72.0) width, height = self.figure.get_size_inches() w, h = width*72, height*72 renderer = MixedModeRenderer( width, height, 72.0, RendererSVG(w, h, svgwriter, filename)) self.figure.draw(renderer) renderer.finalize() if fh_to_close is not None: svgwriter.close()
def _print_svg( self, filename, fh, *, dpi=72, bbox_inches_restore=None, **kwargs): self.figure.set_dpi(72.0) width, height = self.figure.get_size_inches() w, h = width * 72, height * 72 renderer = MixedModeRenderer( self.figure, width, height, dpi, RendererSVG(w, h, fh, filename, dpi), bbox_inches_restore=bbox_inches_restore) self.figure.draw(renderer) renderer.finalize()
def _print_svg(self, filename, svgwriter, **kwargs): image_dpi = kwargs.pop("dpi", 72) self.figure.set_dpi(72.0) width, height = self.figure.get_size_inches() w, h = width*72, height*72 _bbox_inches_restore = kwargs.pop("bbox_inches_restore", None) renderer = MixedModeRenderer( self.figure, width, height, image_dpi, RendererSVG(w, h, svgwriter, filename, image_dpi), bbox_inches_restore=_bbox_inches_restore) self.figure.draw(renderer) renderer.finalize()
method_annotations = [ "@OVERRIDE: " if any(c for c in this_cls.mro()[1:] if hasattr(c, name)) else "" for name in method_names ] method_docs = [ prefix + doc.document(getattr(this_cls, name)) for prefix, name in zip(method_annotations, method_names) ] content = doc.indent("".join(method_docs)) mro = " <- ".join([cls.__name__ for cls in this_cls.mro()]) return "\n".join([mro, content]) def grep_by_indent(s, level, rx=re.compile("^\s+")): for line in s.split("\n"): m = rx.search(line) if m is None or len(m.group(0)) <= level: yield line # from collections import ChainMap as cls # from wsgiref.simple_server import WSGIServer as cls # from matplotlib.figure import Figure as cls from matplotlib.backends.backend_mixed import MixedModeRenderer as cls for cls in cls.mro(): if cls == object: break text = shape_text(cls) print("\n".join(grep_by_indent(text, 4)))
def _print_pgf_to_fh(self, fh, *args, dryrun=False, bbox_inches_restore=None, **kwargs): if dryrun: renderer = RendererPgf(self.figure, None, dummy=True) self.figure.draw(renderer) return header_text = """%% Creator: Matplotlib, PGF backend %% %% To include the figure in your LaTeX document, write %% \\input{<filename>.pgf} %% %% Make sure the required packages are loaded in your preamble %% \\usepackage{pgf} %% %% Figures using additional raster images can only be included by \\input if %% they are in the same directory as the main LaTeX file. For loading figures %% from other directories you can use the `import` package %% \\usepackage{import} %% and then include the figures with %% \\import{<path to file>}{<filename>.pgf} %% """ # append the preamble used by the backend as a comment for debugging header_info_preamble = ["%% Matplotlib used the following preamble"] for line in get_preamble().splitlines(): header_info_preamble.append("%% " + line) for line in get_fontspec().splitlines(): header_info_preamble.append("%% " + line) header_info_preamble.append("%%") header_info_preamble = "\n".join(header_info_preamble) # get figure size in inch w, h = self.figure.get_figwidth(), self.figure.get_figheight() dpi = self.figure.get_dpi() # create pgfpicture environment and write the pgf code fh.write(header_text) fh.write(header_info_preamble) fh.write("\n") writeln(fh, r"\begingroup") writeln(fh, r"\makeatletter") writeln(fh, r"\begin{pgfpicture}") writeln( fh, r"\pgfpathrectangle{\pgfpointorigin}{\pgfqpoint{%fin}{%fin}}" % (w, h)) writeln(fh, r"\pgfusepath{use as bounding box, clip}") renderer = MixedModeRenderer(self.figure, w, h, dpi, RendererPgf(self.figure, fh), bbox_inches_restore=bbox_inches_restore) self.figure.draw(renderer) # end the pgfpicture environment writeln(fh, r"\end{pgfpicture}") writeln(fh, r"\makeatother") writeln(fh, r"\endgroup")
def _print_figure(self, outfile, format, *, dpi, dsc_comments, orientation, papertype, dryrun=False, bbox_inches_restore=None, **kwargs): """ 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_tex(self, outfile, format, *, dpi, dsc_comments, orientation, papertype, dryrun=False, bbox_inches_restore=None, **kwargs): """ If :rc:`text.usetex` is True, 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. The rest of the behavior is as for `._print_figure`. """ is_eps = format == 'eps' width, height = self.figure.get_size_inches() xo = 0 yo = 0 llx = xo lly = yo urx = llx + self.figure.bbox.width ury = lly + self.figure.bbox.height 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 # write to a temp file, we'll move it to outfile when done with TemporaryDirectory() as tmpdir: tmpfile = os.path.join(tmpdir, "tmp.ps") pathlib.Path(tmpfile).write_text(f"""\ %!PS-Adobe-3.0 EPSF-3.0 {dsc_comments} %%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: if papertype == 'auto': papertype = _get_papertype(width, height) 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']): _try_distill(gs_distill, tmpfile, is_eps, ptype=papertype, bbox=bbox, rotated=psfrag_rotated) elif mpl.rcParams['ps.usedistiller'] == 'xpdf': _try_distill(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 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 = "matplotlib version " + __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 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 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 = 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 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 rcParams['ps.usedistiller'] == 'ghostscript': gs_distill(tmpfile, is_eps, ptype=papertype, bbox=bbox) elif 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)
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 = "matplotlib version " + __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 (rcParams['ps.usedistiller'] == 'ghostscript' or rcParams['text.usetex']): gs_distill(tmpfile, is_eps, ptype=papertype, bbox=bbox, rotated=psfrag_rotated) elif 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, 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 = format == '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)