def __init__(self, width, height, dpi): if __debug__: verbose.report('RendererAgg.__init__', 'debug-annoying') self.dpi = dpi self.width = width self.height = height self._renderer = _RendererAgg(int(width), int(height), dpi.get(), debug=False) self.draw_polygon = self._renderer.draw_polygon self.draw_rectangle = self._renderer.draw_rectangle self.draw_path = self._renderer.draw_path self.draw_lines = self._renderer.draw_lines self.draw_markers = self._renderer.draw_markers self.draw_image = self._renderer.draw_image self.draw_line_collection = self._renderer.draw_line_collection self.draw_quad_mesh = self._renderer.draw_quad_mesh self.draw_poly_collection = self._renderer.draw_poly_collection self.draw_regpoly_collection = self._renderer.draw_regpoly_collection self.copy_from_bbox = self._renderer.copy_from_bbox self.restore_region = self._renderer.restore_region self.texmanager = TexManager() self.bbox = lbwh_to_bbox(0, 0, self.width, self.height)
def test_fontconfig_preamble(): """Test that the preamble is included in the source.""" plt.rcParams['text.usetex'] = True src1 = TexManager()._get_tex_source("", fontsize=12) plt.rcParams['text.latex.preamble'] = '\\usepackage{txfonts}' src2 = TexManager()._get_tex_source("", fontsize=12) assert src1 != src2
def __init__(self, dpi, width, height): self.dpi = dpi if __version__[0] >= '2': self.nominal_fontsize = 0.001625 default_dpi = 100 else: self.nominal_fontsize = 0.0013 default_dpi = 80 self.width = float(width) * dpi / default_dpi self.height = float(height) * dpi / default_dpi self.mathtext_parser = MathTextParser('agg') self.texmanager = TexManager()
def test_fontconfig_preamble(): """ Test that the preamble is included in _fontconfig """ plt.rcParams['text.usetex'] = True tm1 = TexManager() font_config1 = tm1.get_font_config() plt.rcParams['text.latex.preamble'] = [r'\usepackage{txfonts}'] tm2 = TexManager() font_config2 = tm2.get_font_config() assert font_config1 != font_config2
def __init__(self, dpi): self.dpi = dpi self.width = 640.0 * dpi / 80 self.height = 480.0 * dpi / 80 mwidth, mheight, width, height = gr.inqdspsize() if (width / (mwidth / 0.0256) < 200): mwidth *= self.width / width gr.setwsviewport(0, mwidth, 0, mwidth * 0.75) else: gr.setwsviewport(0, 0.192, 0, 0.144) gr.setwswindow(0, 1, 0, 0.75) gr.setviewport(0, 1, 0, 0.75) gr.setwindow(0, self.width, 0, self.height) self.mathtext_parser = MathTextParser('agg') self.texmanager = TexManager()
def __init__(self, width, height, dpi): if __debug__: verbose.report('RendererAgg.__init__', 'debug-annoying') self.dpi = dpi self.width = width self.height = height if __debug__: verbose.report('RendererAgg.__init__ width=%s, height=%s'%(width, height), 'debug-annoying') self._renderer = _RendererAgg(int(width), int(height), dpi.get(), debug=False) if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done', 'debug-annoying') self.draw_polygon = self._renderer.draw_polygon self.draw_rectangle = self._renderer.draw_rectangle self.draw_path = self._renderer.draw_path self.draw_lines = self._renderer.draw_lines self.draw_markers = self._renderer.draw_markers self.draw_image = self._renderer.draw_image self.draw_line_collection = self._renderer.draw_line_collection self.draw_quad_mesh = self._renderer.draw_quad_mesh self.draw_poly_collection = self._renderer.draw_poly_collection self.draw_regpoly_collection = self._renderer.draw_regpoly_collection self.copy_from_bbox = self._renderer.copy_from_bbox self.restore_region = self._renderer.restore_region self.texmanager = TexManager() self.bbox = lbwh_to_bbox(0,0, self.width, self.height) if __debug__: verbose.report('RendererAgg.__init__ done', 'debug-annoying')
def __init__(self, width, height, pswriter): self.width = width self.height = height self._pswriter = pswriter if rcParams['text.usetex']: self.textcnt = 0 self.psfrag = [] self.texmanager = TexManager() # current renderer state (None=uninitialised) self.color = None self.linewidth = None self.linejoin = None self.linecap = None self.linedash = None self.fontname = None self.fontsize = None
def get_texmanager(self): """ return the :class:`matplotlib.texmanager.TexManager` instance """ if self._texmanager is None: from matplotlib.texmanager import TexManager self._texmanager = TexManager() return self._texmanager
def get_glyphs_tex(self, prop, s, glyph_map=None, return_new_glyphs_only=False): """Convert the string *s* to vertices and codes using usetex mode.""" # Mostly borrowed from pdf backend. dvifile = TexManager().make_dvi(s, self.FONT_SCALE) with dviread.Dvi(dvifile, self.DPI) as dvi: page, = dvi if glyph_map is None: glyph_map = OrderedDict() if return_new_glyphs_only: glyph_map_new = OrderedDict() else: glyph_map_new = glyph_map glyph_ids, xpositions, ypositions, sizes = [], [], [], [] # Gather font information and do some setup for combining # characters into strings. for x1, y1, dvifont, glyph, width in page.text: font, enc = self._get_ps_font_and_encoding(dvifont.texname) char_id = self._get_char_id(font, glyph) if char_id not in glyph_map: font.clear() font.set_size(self.FONT_SCALE, self.DPI) # See comments in _get_ps_font_and_encoding. if enc is not None: index = font.get_name_index(enc[glyph]) font.load_glyph(index, flags=LOAD_TARGET_LIGHT) else: font.load_char(glyph, flags=LOAD_TARGET_LIGHT) glyph_map_new[char_id] = font.get_path() glyph_ids.append(char_id) xpositions.append(x1) ypositions.append(y1) sizes.append(dvifont.size / self.FONT_SCALE) myrects = [] for ox, oy, h, w in page.boxes: vert1 = [(ox, oy), (ox + w, oy), (ox + w, oy + h), (ox, oy + h), (ox, oy), (0, 0)] code1 = [ Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY ] myrects.append((vert1, code1)) return (list(zip(glyph_ids, xpositions, ypositions, sizes)), glyph_map_new, myrects)
def convert_psfrags(tmpfile, psfrags, font_preamble, custom_preamble, paper_width, paper_height, orientation): """ When we want to use the LaTeX backend with postscript, we write PSFrag tags to a temporary postscript file, each one marking a position for LaTeX to render some text. convert_psfrags generates a LaTeX document containing the commands to convert those tags to text. LaTeX/dvips produces the postscript file that includes the actual text. """ with mpl.rc_context({ "text.latex.preamble": mpl.rcParams["text.latex.preamble"] + # Only load these packages if they have not already been loaded, in # order not to clash with custom packages. r"\makeatletter" r"\@ifpackageloaded{color}{}{\usepackage{color}}" r"\@ifpackageloaded{graphicx}{}{\usepackage{graphicx}}" r"\@ifpackageloaded{psfrag}{}{\usepackage{psfrag}}" r"\makeatother" r"\geometry{papersize={%(width)sin,%(height)sin},margin=0in}" % { "width": paper_width, "height": paper_height } }): dvifile = TexManager().make_dvi( "\n" r"\begin{figure}" "\n" r" \centering\leavevmode" "\n" r" %(psfrags)s" "\n" r" \includegraphics*[angle=%(angle)s]{%(epsfile)s}" "\n" r"\end{figure}" % { "psfrags": "\n".join(psfrags), "angle": 90 if orientation == 'landscape' else 0, "epsfile": pathlib.Path(tmpfile).resolve().as_posix(), }, fontsize=10) # tex's default fontsize. with TemporaryDirectory() as tmpdir: psfile = os.path.join(tmpdir, "tmp.ps") cbook._check_and_log_subprocess( ['dvips', '-q', '-R0', '-o', psfile, dvifile], _log) shutil.move(psfile, tmpfile) # check if the dvips created a ps in landscape paper. Somehow, # above latex+dvips results in a ps file in a landscape mode for a # certain figure sizes (e.g., 8.3in, 5.8in which is a5). And the # bounding box of the final output got messed up. We check see if # the generated ps file is in landscape and return this # information. The return value is used in pstoeps step to recover # the correct bounding box. 2010-06-05 JJL with open(tmpfile) as fh: psfrag_rotated = "Landscape" in fh.read(1000) return psfrag_rotated
def test_fontconfig_preamble(): """Test that the preamble is included in _fontconfig.""" plt.rcParams['text.usetex'] = True tm1 = TexManager() font_config1 = tm1.get_font_config() plt.rcParams['text.latex.preamble'] = '\\usepackage{txfonts}' tm2 = TexManager() font_config2 = tm2.get_font_config() assert font_config1 != font_config2
def __init__(self, width, height, dpi): if __debug__: verbose.report('RendererAgg.__init__', 'debug-annoying') self.dpi = dpi self.width = width self.height = height self._renderer = _RendererAgg(int(width), int(height), dpi.get(), debug=False) self.draw_polygon = self._renderer.draw_polygon self.draw_rectangle = self._renderer.draw_rectangle self.draw_path = self._renderer.draw_path self.draw_lines = self._renderer.draw_lines self.draw_markers = self._renderer.draw_markers self.draw_image = self._renderer.draw_image self.draw_line_collection = self._renderer.draw_line_collection self.draw_poly_collection = self._renderer.draw_poly_collection self.draw_regpoly_collection = self._renderer.draw_regpoly_collection self.cache = self._renderer.cache self.blit = self._renderer.blit self.texmanager = TexManager() self.texd = {} # a cache of tex image rasters self.bbox = lbwh_to_bbox(0,0, self.width, self.height)
def get_text_width_height_descent(self, s, prop, ismath): fontsize = prop.get_size_in_points() if ismath == "TeX": return TexManager().get_text_width_height_descent(s, fontsize) scale = fontsize / self.FONT_SCALE if ismath: prop = prop.copy() prop.set_size(self.FONT_SCALE) width, height, descent, *_ = \ self.mathtext_parser.parse(s, 72, prop) return width * scale, height * scale, descent * scale font = self._get_font(prop) font.set_text(s, 0.0, flags=LOAD_NO_HINTING) w, h = font.get_width_height() w /= 64.0 # convert from subpixels h /= 64.0 d = font.get_descent() d /= 64.0 return w * scale, h * scale, d * scale
def get_glyphs_tex(self, prop, s, glyph_map=None, return_new_glyphs_only=False): """Convert the string *s* to vertices and codes using usetex mode.""" # Mostly borrowed from pdf backend. dvifile = TexManager().make_dvi(s, self.FONT_SCALE) with dviread.Dvi(dvifile, self.DPI) as dvi: page, = dvi if glyph_map is None: glyph_map = OrderedDict() if return_new_glyphs_only: glyph_map_new = OrderedDict() else: glyph_map_new = glyph_map glyph_ids, xpositions, ypositions, sizes = [], [], [], [] # Gather font information and do some setup for combining # characters into strings. for text in page.text: font = get_font(text.font_path) char_id = self._get_char_id(font, text.glyph) if char_id not in glyph_map: font.clear() font.set_size(self.FONT_SCALE, self.DPI) glyph_name_or_index = text.glyph_name_or_index if isinstance(glyph_name_or_index, str): index = font.get_name_index(glyph_name_or_index) font.load_glyph(index, flags=LOAD_TARGET_LIGHT) elif isinstance(glyph_name_or_index, int): self._select_native_charmap(font) font.load_char(glyph_name_or_index, flags=LOAD_TARGET_LIGHT) else: # Should not occur. raise TypeError(f"Glyph spec of unexpected type: " f"{glyph_name_or_index!r}") glyph_map_new[char_id] = font.get_path() glyph_ids.append(char_id) xpositions.append(text.x) ypositions.append(text.y) sizes.append(text.font_size / self.FONT_SCALE) myrects = [] for ox, oy, h, w in page.boxes: vert1 = [(ox, oy), (ox + w, oy), (ox + w, oy + h), (ox, oy + h), (ox, oy), (0, 0)] code1 = [ Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY ] myrects.append((vert1, code1)) return (list(zip(glyph_ids, xpositions, ypositions, sizes)), glyph_map_new, myrects)
def get_texmanager(self): """Return the cached `~.texmanager.TexManager` instance.""" if self._texmanager is None: from matplotlib.texmanager import TexManager self._texmanager = TexManager() return self._texmanager
def get_texmanager(self): """Return the cached `~.texmanager.TexManager` instance.""" if self._texmanager is None: self._texmanager = TexManager() return self._texmanager
class RendererGR(RendererBase): """ Handles drawing/rendering operations using GR """ texd = maxdict(50) # a cache of tex image rasters def __init__(self, dpi): self.dpi = dpi self.width = 640.0 * dpi / 80 self.height = 480.0 * dpi / 80 mwidth, mheight, width, height = gr.inqdspsize() if (width / (mwidth / 0.0256) < 200): mwidth *= self.width / width gr.setwsviewport(0, mwidth, 0, mwidth * 0.75) else: gr.setwsviewport(0, 0.192, 0, 0.144) gr.setwswindow(0, 1, 0, 0.75) gr.setviewport(0, 1, 0, 0.75) gr.setwindow(0, self.width, 0, self.height) self.mathtext_parser = MathTextParser('agg') self.texmanager = TexManager() def draw_path(self, gc, path, transform, rgbFace=None): path = transform.transform_path(path) points = path.vertices codes = path.codes bbox = gc.get_clip_rectangle() if bbox is not None: x, y, w, h = bbox.bounds clrt = np.array([x, x + w, y, y + h]) else: clrt = np.array([0, self.width, 0, self.height]) gr.setviewport(*clrt/self.width) gr.setwindow(*clrt) if rgbFace is not None and len(points) > 2: color = gr.inqcolorfromrgb(rgbFace[0], rgbFace[1], rgbFace[2]) gr.settransparency(rgbFace[3]) gr.setcolorrep(color, rgbFace[0], rgbFace[1], rgbFace[2]) gr.setfillintstyle(gr.INTSTYLE_SOLID) gr.setfillcolorind(color) gr.drawpath(points, codes, fill=True) lw = gc.get_linewidth() if lw != 0: rgba = gc.get_rgb()[:4] color = gr.inqcolorfromrgb(rgba[0], rgba[1], rgba[2]) gr.settransparency(rgba[3]) gr.setcolorrep(color, rgba[0], rgba[1], rgba[2]) if type(gc._linestyle) is unicode: gr.setlinetype(linetype[gc._linestyle]) gr.setlinewidth(lw) gr.setlinecolorind(color) gr.drawpath(points, codes, fill=False) def draw_image(self, gc, x, y, im): h, w, s = im.as_rgba_str() img = np.fromstring(s, np.uint32) img.shape = (h, w) gr.drawimage(x, x + w, y + h, y, w, h, img) def draw_mathtext(self, x, y, angle, Z): h, w = Z.shape img = np.zeros((h, w), np.uint32) for i in range(h): for j in range(w): img[i, j] = (255 - Z[i, j]) << 24 a = int(angle) if a == 90: gr.drawimage(x - h, x, y, y + w, h, w, np.resize(np.rot90(img, 1), (h, w))) elif a == 180: gr.drawimage(x - w, x, y - h, y, w, h, np.rot90(img, 2)) elif a == 270: gr.drawimage(x, x + h, y - w, y, h, w, np.resize(np.rot90(img, 3), (h, w))) else: gr.drawimage(x, x + w, y, y + h, w, h, img) def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): size = prop.get_size_in_points() key = s, size, self.dpi, angle, self.texmanager.get_font_config() im = self.texd.get(key) if im is None: Z = self.texmanager.get_grey(s, size, self.dpi) Z = np.array(255.0 - Z * 255.0, np.uint8) self.draw_mathtext(x, y, angle, Z) def _draw_mathtext(self, gc, x, y, s, prop, angle): ox, oy, width, height, descent, image, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) self.draw_mathtext(x, y, angle, 255 - image.as_array()) def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): if ismath: self._draw_mathtext(gc, x, y, s, prop, angle) else: x, y = gr.wctondc(x, y) s = s.replace(u'\u2212', '-') fontsize = prop.get_size_in_points() rgba = gc.get_rgb()[:4] color = gr.inqcolorfromrgb(rgba[0], rgba[1], rgba[2]) gr.settransparency(rgba[3]) gr.setcolorrep(color, rgba[0], rgba[1], rgba[2]) gr.setcharheight(fontsize * 0.0013) gr.settextcolorind(color) if angle != 0: gr.setcharup(-np.sin(angle * np.pi/180), np.cos(angle * np.pi/180)) else: gr.setcharup(0, 1) gr.text(x, y, s.encode("latin-1")) def flipy(self): return False def get_canvas_width_height(self): return self.width, self.height def get_text_width_height_descent(self, s, prop, ismath): if ismath == 'TeX': fontsize = prop.get_size_in_points() w, h, d = self.texmanager.get_text_width_height_descent( s, fontsize, renderer=self) return w, h, d if ismath: ox, oy, width, height, descent, fonts, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) return width, height, descent # family = prop.get_family() # weight = prop.get_weight() # style = prop.get_style() s = s.replace(u'\u2212', '-').encode("latin-1") fontsize = prop.get_size_in_points() gr.setcharheight(fontsize * 0.0013) gr.setcharup(0, 1) (tbx, tby) = gr.inqtextext(0, 0, s) width, height, descent = tbx[1], tby[2], 0 return width, height, descent def new_gc(self): return GraphicsContextGR() def points_to_pixels(self, points): return points
def test_font_selection(rc, preamble, family): plt.rcParams.update(rc) tm = TexManager() src = Path(tm.make_tex("hello, world", fontsize=12)).read_text() assert preamble in src assert [*re.findall(r"\\\w+family", src)] == [family]
class RendererGR(RendererBase): """ Handles drawing/rendering operations using GR """ texd = maxdict(50) # a cache of tex image rasters def __init__(self, dpi, width, height): self.dpi = dpi if __version__[0] >= '2': self.nominal_fontsize = 0.001625 default_dpi = 100 else: self.nominal_fontsize = 0.0013 default_dpi = 80 self.width = float(width) * dpi / default_dpi self.height = float(height) * dpi / default_dpi self.mathtext_parser = MathTextParser('agg') self.texmanager = TexManager() def configure(self): aspect_ratio = self.width / self.height if aspect_ratio > 1: rect = np.array([0, 1, 0, 1.0 / aspect_ratio]) self.size = self.width else: rect = np.array([0, aspect_ratio, 0, 1]) self.size = self.height mwidth, mheight, width, height = gr.inqdspsize() if width / (mwidth / 0.0256) < 200: mwidth *= self.width / width gr.setwsviewport(*rect * mwidth) else: gr.setwsviewport(*rect * 0.192) gr.setwswindow(*rect) gr.setviewport(*rect) gr.setwindow(0, self.width, 0, self.height) def draw_path(self, gc, path, transform, rgbFace=None): path = transform.transform_path(path) points = path.vertices codes = path.codes bbox = gc.get_clip_rectangle() if bbox is not None and not np.any(np.isnan(bbox.bounds)): x, y, w, h = bbox.bounds clrt = np.array([x, x + w, y, y + h]) else: clrt = np.array([0, self.width, 0, self.height]) gr.setviewport(*clrt / self.size) gr.setwindow(*clrt) if rgbFace is not None and len(points) > 2: color = gr.inqcolorfromrgb(rgbFace[0], rgbFace[1], rgbFace[2]) gr.settransparency(rgbFace[3]) gr.setcolorrep(color, rgbFace[0], rgbFace[1], rgbFace[2]) gr.setfillintstyle(gr.INTSTYLE_SOLID) gr.setfillcolorind(color) gr.drawpath(points, codes, fill=True) lw = gc.get_linewidth() if lw != 0: rgba = gc.get_rgb()[:4] color = gr.inqcolorfromrgb(rgba[0], rgba[1], rgba[2]) gr.settransparency(rgba[3]) gr.setcolorrep(color, rgba[0], rgba[1], rgba[2]) if isinstance(gc._linestyle, str): gr.setlinetype(linetype[gc._linestyle]) gr.setlinewidth(lw) gr.setlinecolorind(color) gr.drawpath(points, codes, fill=False) def draw_image(self, gc, x, y, im): if hasattr(im, 'as_rgba_str'): h, w, s = im.as_rgba_str() img = np.fromstring(s, np.uint32) img.shape = (h, w) elif len(im.shape) == 3 and im.shape[2] == 4 and im.dtype == np.uint8: img = im.view(np.uint32) img.shape = im.shape[:2] h, w = img.shape else: type_info = repr(type(im)) if hasattr(im, 'shape'): type_info += ' shape=' + repr(im.shape) if hasattr(im, 'dtype'): type_info += ' dtype=' + repr(im.dtype) warnings.warn('Unsupported image type ({}). Please report this at https://github.com/sciapp/python-gr/issues'.format(type_info)) return gr.drawimage(x, x + w, y + h, y, w, h, img) def draw_mathtext(self, x, y, angle, Z): h, w = Z.shape img = np.zeros((h, w), np.uint32) for i in range(h): for j in range(w): img[i, j] = (255 - Z[i, j]) << 24 a = int(angle) if a == 90: gr.drawimage(x - h, x, y, y + w, h, w, np.resize(np.rot90(img, 1), (h, w))) elif a == 180: gr.drawimage(x - w, x, y - h, y, w, h, np.rot90(img, 2)) elif a == 270: gr.drawimage(x, x + h, y - w, y, h, w, np.resize(np.rot90(img, 3), (h, w))) else: gr.drawimage(x, x + w, y, y + h, w, h, img) def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): size = prop.get_size_in_points() key = s, size, self.dpi, angle, self.texmanager.get_font_config() im = self.texd.get(key) if im is None: Z = self.texmanager.get_grey(s, size, self.dpi) Z = np.array(255.0 - Z * 255.0, np.uint8) self.draw_mathtext(x, y, angle, Z) def _draw_mathtext(self, gc, x, y, s, prop, angle): ox, oy, width, height, descent, image, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) self.draw_mathtext(x, y, angle, 255 - np.asarray(image)) def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): if ismath: self._draw_mathtext(gc, x, y, s, prop, angle) else: x, y = gr.wctondc(x, y) fontsize = prop.get_size_in_points() rgba = gc.get_rgb()[:4] color = gr.inqcolorfromrgb(rgba[0], rgba[1], rgba[2]) gr.settransparency(rgba[3]) gr.setcolorrep(color, rgba[0], rgba[1], rgba[2]) gr.setcharheight(fontsize * self.nominal_fontsize) gr.settextcolorind(color) if angle != 0: gr.setcharup(-np.sin(angle * np.pi/180), np.cos(angle * np.pi/180)) else: gr.setcharup(0, 1) gr.text(x, y, s) def flipy(self): return False def get_canvas_width_height(self): return self.width, self.height def get_text_width_height_descent(self, s, prop, ismath): if ismath == 'TeX': fontsize = prop.get_size_in_points() w, h, d = self.texmanager.get_text_width_height_descent( s, fontsize, renderer=self) return w, h, d if ismath: ox, oy, width, height, descent, fonts, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) return width, height, descent # family = prop.get_family() # weight = prop.get_weight() # style = prop.get_style() fontsize = prop.get_size_in_points() gr.setcharheight(fontsize * self.nominal_fontsize) gr.setcharup(0, 1) (tbx, tby) = gr.inqtextext(0, 0, s) width, height, descent = tbx[1], tby[2], 0.2 * tby[2] return width, height, descent def new_gc(self): return GraphicsContextGR() def points_to_pixels(self, points): return points
class RendererPS(RendererBase): """ The renderer handles all the drawing primitives using a graphics context instance that controls the colors/styles. """ def __init__(self, width, height, pswriter): self.width = width self.height = height self._pswriter = pswriter if rcParams['text.usetex']: self.textcnt = 0 self.psfrag = [] self.texmanager = TexManager() # current renderer state (None=uninitialised) self.color = None self.linewidth = None self.linejoin = None self.linecap = None self.linedash = None self.fontname = None self.fontsize = None def set_color(self, r, g, b, store=1): if (r, g, b) != self.color: if r == g and r == b: self._pswriter.write("%1.3f setgray\n" % r) else: self._pswriter.write("%1.3f %1.3f %1.3f setrgbcolor\n" % (r, g, b)) if store: self.color = (r, g, b) def set_linewidth(self, linewidth): if linewidth != self.linewidth: self._pswriter.write("%1.3f setlinewidth\n" % linewidth) self.linewidth = linewidth def set_linejoin(self, linejoin): if linejoin != self.linejoin: self._pswriter.write("%d setlinejoin\n" % linejoin) self.linejoin = linejoin def set_linecap(self, linecap): if linecap != self.linecap: self._pswriter.write("%d setlinecap\n" % linecap) self.linecap = linecap def set_linedash(self, offset, seq): if self.linedash is not None: oldo, oldseq = self.linedash if seq_allequal(seq, oldseq): return if seq is not None and len(seq): s = "[%s] %d setdash\n" % (_nums_to_str(*seq), offset) self._pswriter.write(s) else: self._pswriter.write("[] 0 setdash\n") self.linedash = (offset, seq) def set_font(self, fontname, fontsize): if rcParams['ps.useafm']: return if (fontname, fontsize) != (self.fontname, self.fontsize): out = ("/%s findfont\n" "%1.3f scalefont\n" "setfont\n" % (fontname, fontsize)) self._pswriter.write(out) self.fontname = fontname self.fontsize = fontsize def get_canvas_width_height(self): 'return the canvas width and height in display coords' return self.width, self.height def get_text_width_height(self, s, prop, ismath): """ get the width and height in display coords of the string s with FontPropertry prop """ if rcParams['text.usetex']: fontsize = prop.get_size_in_points() l, b, r, t = self.texmanager.get_ps_bbox(s) w = (r - l) * fontsize / 10. h = (t - b) * fontsize / 10. #print s, w, h return w, h if rcParams['ps.useafm']: if ismath: s = s[1:-1] font = self._get_font_afm(prop) l, b, w, h = font.get_str_bbox(s) fontsize = prop.get_size_in_points() w *= 0.001 * fontsize h *= 0.001 * fontsize return w, h if ismath: width, height, pswriter = math_parse_s_ps( s, 72, prop.get_size_in_points()) return width, height font = self._get_font_ttf(prop) font.set_text(s, 0.0) w, h = font.get_width_height() w /= 64.0 # convert from subpixels h /= 64.0 #print s, w, h return w, h def flipy(self): 'return true if small y numbers are top for renderer' return False def _get_font_afm(self, prop): key = hash(prop) font = _afmfontd.get(key) if font is None: font = AFM(file(fontManager.findfont(prop, fontext='afm'))) _afmfontd[key] = font return font def _get_font_ttf(self, prop): key = hash(prop) font = _fontd.get(key) if font is None: fname = fontManager.findfont(prop) font = FT2Font(str(fname)) _fontd[key] = font if fname not in _type42: _type42.append(fname) font.clear() size = prop.get_size_in_points() font.set_size(size, 72.0) return font def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2): """ Draw an arc centered at x,y with width and height and angles from 0.0 to 360.0 If gcFace is not None, fill the arc slice with it. gcEdge is a GraphicsContext instance """ ps = '%s ellipse' % _nums_to_str(angle1, angle2, 0.5 * width, 0.5 * height, x, y) self._draw_ps(ps, gc, rgbFace, "arc") def _rgba(self, im, flipud): return im.as_str(fliud) def _rgb(self, im, flipud): rgbat = im.as_str(flipud) rgba = fromstring(rgbat[2], UInt8) rgba.shape = (rgbat[0], rgbat[1], 4) rgb = rgba[:, :, :3] return rgbat[0], rgbat[1], rgb.tostring() def _gray(self, im, flipud, rc=0.3, gc=0.59, bc=0.11): rgbat = im.as_str(flipud) rgba = fromstring(rgbat[2], UInt8) rgba.shape = (rgbat[0], rgbat[1], 4) rgba_f = rgba.astype(Float32) r = rgba_f[:, :, 0] g = rgba_f[:, :, 1] b = rgba_f[:, :, 2] gray = (r * rc + g * gc + b * bc).astype(UInt8) return rgbat[0], rgbat[1], gray.tostring() def _hex_lines(self, s, chars_per_line=128): s = binascii.b2a_hex(s) nhex = len(s) lines = [] for i in range(0, nhex, chars_per_line): limit = min(i + chars_per_line, nhex) lines.append(s[i:limit]) return lines def draw_image(self, x, y, im, origin, bbox): """ Draw the Image instance into the current axes; x is the distance in pixels from the left hand side of the canvas. y is the distance from the origin. That is, if origin is upper, y is the distance from top. If origin is lower, y is the distance from bottom origin is 'upper' or 'lower' bbox is a matplotlib.transforms.BBox instance for clipping, or None """ flipud = origin == 'lower' if im.is_grayscale: h, w, bits = self._gray(im, flipud) imagecmd = "image" else: h, w, bits = self._rgb(im, flipud) imagecmd = "false 3 colorimage" hexlines = '\n'.join(self._hex_lines(bits)) xscale, yscale = w, h figh = self.height * 72 #print 'values', origin, flipud, figh, h, y if bbox is not None: clipx, clipy, clipw, cliph = bbox.get_bounds() clip = '%s clipbox' % _nums_to_str(clipw, cliph, clipx, clipy) if not flipud: y = figh - (y + h) ps = """gsave %(clip)s %(x)s %(y)s translate %(xscale)s %(yscale)s scale /DataString %(w)s string def %(w)s %(h)s 8 [ %(w)s 0 0 -%(h)s 0 %(h)s ] { currentfile DataString readhexstring pop } bind %(imagecmd)s %(hexlines)s grestore """ % locals() self._pswriter.write(ps) def draw_line(self, gc, x0, y0, x1, y1): """ Draw a single line from x0,y0 to x1,y1 """ ps = '%1.3f %1.3f m %1.3f %1.3f l' % (x0, y0, x1, y1) self._draw_ps(ps, gc, None, "line") def _draw_markers(self, gc, path, rgbFace, x, y, transform): """ Draw the markers defined by path at each of the positions in x and y. path coordinates are points, x and y coords will be transformed by the transform """ if debugPS: self._pswriter.write('% draw_markers \n') return if rgbFace: if rgbFace[0] == rgbFace[0] and rgbFace[0] == rgbFace[2]: ps_color = '%1.3f setgray' % rgbFace[0] else: ps_color = '%1.3f %1.3f %1.3f setrgbcolor' % rgbFace #if transform.need_nonlinear(): # x,y,mask = transform.nonlinear_only_numerix(x, y, returnMask=1) #else: # mask = ones(x.shape) x, y = transform.numerix_x_y(x, y) # the a,b,c,d,tx,ty affine which transforms x and y #vec6 = transform.as_vec6_val() #theta = (180 / pi) * math.atan2 (vec6[1], src[0]) # this defines a single vertex. We need to define this as ps # function, properly stroked and filled with linewidth etc, # and then simply iterate over the x and y and call this # function at each position. Eg, this is the path that is # relative to each x and y offset. # construct the generic marker command: ps_cmd = ['gsave'] ps_cmd.append('newpath') ps_cmd.append('translate') while 1: code, xp, yp = path.vertex() if code == agg.path_cmd_stop: ps_cmd.append('closepath') # Hack, path_cmd_end_poly not found break elif code == agg.path_cmd_move_to: ps_cmd.append('%1.3f %1.3f m' % (xp, yp)) elif code == agg.path_cmd_line_to: ps_cmd.append('%1.3f %1.3f l' % (xp, yp)) elif code == agg.path_cmd_curve3: pass elif code == agg.path_cmd_curve4: pass elif code == agg.path_cmd_end_poly: pass ps_cmd.append('closepath') elif code == agg.path_cmd_mask: pass else: pass #print code if rgbFace: ps_cmd.append('gsave') ps_cmd.append(ps_color) ps_cmd.append('fill') ps_cmd.append('grestore') ps_cmd.append('stroke') ps_cmd.append('grestore') # undo translate() ps_cmd = '\n'.join(ps_cmd) #self._pswriter.write(' '.join(['/marker {', ps_cmd, '} bind def\n'])) #self._pswriter.write('[%s]' % ';'.join([float(val) for val in vec6])) # Now evaluate the marker command at each marker location: start = 0 end = 1000 while start < len(x): to_draw = izip(x[start:end], y[start:end]) ps = ['%1.3f %1.3f marker' % point for point in to_draw] self._draw_ps("\n".join(ps), gc, None) start = end end += 1000 def draw_path(self, gc, rgbFace, path, trans): pass def _draw_lines(self, gc, points): """ Draw many lines. 'points' is a list of point coordinates. """ # inline this for performance ps = ["%1.3f %1.3f m" % points[0]] ps.extend(["%1.3f %1.3f l" % point for point in points[1:]]) self._draw_ps("\n".join(ps), gc, None) def draw_lines(self, gc, x, y, transform=None): """ x and y are equal length arrays, draw lines connecting each point in x, y """ if debugPS: self._pswriter.write('% draw_lines \n') if transform: # this won't be called if draw_markers is hidden #if transform.need_nonlinear(): # x, y = transform.nonlinear_only_numerix(x, y) x, y = transform.numerix_x_y(x, y) start = 0 end = 1000 points = zip(x, y) while start < len(x): to_draw = izip(x[start:end], y[start:end]) ps = ["%1.3f %1.3f m" % to_draw.next()] ps.extend(["%1.3f %1.3f l" % point for point in to_draw]) self._draw_ps("\n".join(ps), gc, None) start = end end += 1000 def __draw_lines_hide(self, gc, x, y, transform=None): """ x and y are equal length arrays, draw lines connecting each point in x, y """ if debugPS: self._pswriter.write('% draw_lines \n') if transform: if transform.need_nonlinear(): x, y, mask = transform.nonlinear_only_numerix(x, y, returnMask=1) else: mask = ones(x.shape) vec6 = transform.as_vec6_val() a, b, c, d, tx, ty = vec6 sx, sy = get_vec6_scales(vec6) start = 0 end = 1000 points = zip(x, y) write = self._pswriter.write write('gsave\n') self.push_gc(gc) write('[%f %f %f %f %f %f] concat\n' % (a, b, c, d, tx, ty)) while start < len(x): # put moveto on all the bad data and on the first good # point after the bad data codes = where(mask[start:end + 1], 'l', 'm') ind = nonzero(mask[start:end + 1] == 0) + 1 if ind[-1] >= len(codes): ind = ind[:-1] put(codes, ind, 'm') thisx = x[start:end + 1] thisy = y[start:end + 1] to_draw = izip(thisx, thisy, codes) if not to_draw: break ps = ['%1.3f %1.3f m' % to_draw.next()[:2]] ps.extend(["%1.3f %1.3f %c" % tup for tup in to_draw]) # we don't want to scale the line width, etc so invert the # scale for the stroke ps.append('\ngsave %f %f scale stroke grestore\n' % (1. / sx, 1. / sy)) write('\n'.join(ps)) start = end end += 1000 write("grestore\n") def draw_lines_old(self, gc, x, y): """ x and y are equal length arrays, draw lines connecting each point in x, y """ if debugPS: self._pswriter.write("% lines\n") start = 0 end = 1000 points = zip(x, y) while 1: to_draw = points[start:end] if not to_draw: break self._draw_lines(gc, to_draw) start = end end += 1000 def draw_point(self, gc, x, y): """ Draw a single point at x,y """ # TODO: is there a better way to draw points in postscript? # (use a small circle?) self.draw_line(gc, x, y, x + 1, y + 1) def draw_polygon(self, gc, rgbFace, points): """ Draw a polygon. points is a len vertices tuple, each element giving the x,y coords a vertex If rgbFace is not None, fill the poly with it. gc is a GraphicsContext instance """ ps = ["%s m\n" % _nums_to_str(*points[0])] ps.extend(["%s l\n" % _nums_to_str(x, y) for x, y in points[1:]]) ps.append("closepath") self._draw_ps(''.join(ps), gc, rgbFace, "polygon") def draw_rectangle(self, gc, rgbFace, x, y, width, height): """ Draw a rectangle with lower left at x,y with width and height. If gcFace is not None, fill the rectangle with it. gcEdge is a GraphicsContext instance """ # TODO: use rectstroke ps = '%s box' % _nums_to_str(width, height, x, y) self._draw_ps(ps, gc, rgbFace, "rectangle") def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!'): """ draw a Text instance """ w, h = self.get_text_width_height(s, prop, ismath) fontsize = prop.get_size_in_points() corr = 0 #w/2*(fontsize-10)/10 pos = _nums_to_str(x - corr, y) thetext = 'psmarker%d' % self.textcnt scale = float(fontsize / 10.0) color = '%1.3f,%1.3f,%1.3f' % gc.get_rgb() tex = r'\color[rgb]{%s} %s' % (color, s) self.psfrag.append(r'\psfrag{%s}[bl][bl][%f][%f]{%s}' % (thetext, scale, angle, tex)) ps = """\ gsave %(pos)s moveto (%(thetext)s) show grestore """ % locals() self._pswriter.write(ps) self.textcnt += 1 def draw_text(self, gc, x, y, s, prop, angle, ismath): """ draw a Text instance """ # local to avoid repeated attribute lookups write = self._pswriter.write if debugPS: write("% text\n") if rcParams['ps.useafm']: if ismath: s = s[1:-1] font = self._get_font_afm(prop) l, b, w, h = font.get_str_bbox(s) fontsize = prop.get_size_in_points() l *= 0.001 * fontsize b *= 0.001 * fontsize w *= 0.001 * fontsize h *= 0.001 * fontsize if angle == 90: l, b = -b, l # todo generalize for arb rotations pos = _nums_to_str(x - l, y - b) thetext = '(%s)' % s fontname = font.get_fontname() fontsize = prop.get_size_in_points() rotate = '%1.1f rotate' % angle setcolor = '%1.3f %1.3f %1.3f setrgbcolor' % gc.get_rgb() #h = 0 ps = """\ gsave /%(fontname)s findfont %(fontsize)s scalefont setfont %(pos)s moveto %(rotate)s %(thetext)s %(setcolor)s show grestore """ % locals() self._draw_ps(ps, gc, None) elif ismath == 'TeX': return self.tex(gc, x, y, s, prop, angle) elif ismath: return self.draw_mathtext(gc, x, y, s, prop, angle) elif isinstance(s, unicode): return self.draw_unicode(gc, x, y, s, prop, angle) else: font = self._get_font_ttf(prop) font.set_text(s, 0) self.set_color(*gc.get_rgb()) self.set_font(font.get_sfnt()[(1, 0, 0, 6)], prop.get_size_in_points()) write("%s m\n" % _nums_to_str(x, y)) if angle: write("gsave\n") write("%s rotate\n" % _num_to_str(angle)) descent = font.get_descent() / 64.0 if descent: write("0 %s rmoveto\n" % _num_to_str(descent)) write("(%s) show\n" % quote_ps_string(s)) if angle: write("grestore\n") def new_gc(self): return GraphicsContextPS() def draw_unicode(self, gc, x, y, s, prop, angle): """draw a unicode string. ps doesn't have unicode support, so we have to do this the hard way """ font = self._get_font_ttf(prop) self.set_color(*gc.get_rgb()) self.set_font(font.get_sfnt()[(1, 0, 0, 6)], prop.get_size_in_points()) cmap = font.get_charmap() glyphd = reverse_dict(cmap) lastgind = None #print 'text', s lines = [] thisx, thisy = 0, 0 for c in s: ccode = ord(c) gind = glyphd.get(ccode) if gind is None: ccode = ord('?') name = '.notdef' gind = 0 else: name = font.get_glyph_name(gind) glyph = font.load_char(ccode) if lastgind is not None: kern = font.get_kerning(lastgind, gind, KERNING_UNFITTED) else: kern = 0 lastgind = gind thisx += kern / 64.0 lines.append('%f %f m /%s glyphshow' % (thisx, thisy, name)) thisx += glyph.linearHoriAdvance / 65536.0 thetext = '\n'.join(lines) ps = """gsave %(x)f %(y)f translate %(angle)f rotate %(thetext)s grestore """ % locals() self._pswriter.write(ps) def draw_mathtext(self, gc, x, y, s, prop, angle): """ Draw the math text using matplotlib.mathtext """ if debugPS: self._pswriter.write("% mathtext\n") fontsize = prop.get_size_in_points() width, height, pswriter = math_parse_s_ps(s, 72, fontsize) self.set_color(*gc.get_rgb()) thetext = pswriter.getvalue() ps = """gsave %(x)f %(y)f translate %(angle)f rotate %(thetext)s grestore """ % locals() self._pswriter.write(ps) def _draw_ps(self, ps, gc, rgbFace, command=None): """ Emit the PostScript sniplet 'ps' with all the attributes from 'gc' applied. 'ps' must consist of PostScript commands to construct a path. """ # local variable eliminates all repeated attribute lookups write = self._pswriter.write if debugPS and command: write("% " + command + "\n") cliprect = gc.get_clip_rectangle() self.set_color(*gc.get_rgb()) self.set_linewidth(gc.get_linewidth()) # TODO: move the lookup into GraphicsContextPS jint = {'miter': 0, 'round': 1, 'bevel': 2}[gc.get_joinstyle()] self.set_linejoin(jint) # TODO: move the lookup into GraphicsContextPS cint = {'butt': 0, 'round': 1, 'projecting': 2}[gc.get_capstyle()] self.set_linecap(cint) self.set_linedash(*gc.get_dashes()) if cliprect: x, y, w, h = cliprect write('gsave\n%1.3f %1.3f %1.3f %1.3f clipbox\n' % (w, h, x, y)) # Jochen, is the strip necessary? - this could be a honking big string write(ps.strip()) write("\n") if rgbFace: #print 'rgbface', rgbFace write("gsave\n") self.set_color(store=0, *rgbFace) write("fill\ngrestore\n") write("stroke\n") if cliprect: write("grestore\n") def push_gc(self, gc): """ Push the current onto stack """ # local variable eliminates all repeated attribute lookups write = self._pswriter.write cliprect = gc.get_clip_rectangle() self.set_color(*gc.get_rgb()) self.set_linewidth(gc.get_linewidth()) # TODO: move the lookup into GraphicsContextPS jint = {'miter': 0, 'round': 1, 'bevel': 2}[gc.get_joinstyle()] self.set_linejoin(jint) # TODO: move the lookup into GraphicsContextPS cint = {'butt': 0, 'round': 1, 'projecting': 2}[gc.get_capstyle()] self.set_linecap(cint) self.set_linedash(*gc.get_dashes()) if cliprect: x, y, w, h = cliprect write('%1.3f %1.3f %1.3f %1.3f clipbox\n' % (w, h, x, y)) write("\n")
class RendererPS(RendererBase): """ The renderer handles all the drawing primitives using a graphics context instance that controls the colors/styles. """ def __init__(self, width, height, pswriter): self.width = width self.height = height self._pswriter = pswriter if rcParams['text.usetex']: self.textcnt = 0 self.psfrag = [] self.texmanager = TexManager() # current renderer state (None=uninitialised) self.color = None self.linewidth = None self.linejoin = None self.linecap = None self.linedash = None self.fontname = None self.fontsize = None def set_color(self, r, g, b, store=1): if (r,g,b) != self.color: if r==g and r==b: self._pswriter.write("%1.3f setgray\n"%r) else: self._pswriter.write("%1.3f %1.3f %1.3f setrgbcolor\n"%(r,g,b)) if store: self.color = (r,g,b) def set_linewidth(self, linewidth): if linewidth != self.linewidth: self._pswriter.write("%1.3f setlinewidth\n"%linewidth) self.linewidth = linewidth def set_linejoin(self, linejoin): if linejoin != self.linejoin: self._pswriter.write("%d setlinejoin\n"%linejoin) self.linejoin = linejoin def set_linecap(self, linecap): if linecap != self.linecap: self._pswriter.write("%d setlinecap\n"%linecap) self.linecap = linecap def set_linedash(self, offset, seq): if self.linedash is not None: oldo, oldseq = self.linedash if seq_allequal(seq, oldseq): return if seq is not None and len(seq): s="[%s] %d setdash\n"%(_nums_to_str(*seq), offset) self._pswriter.write(s) else: self._pswriter.write("[] 0 setdash\n") self.linedash = (offset,seq) def set_font(self, fontname, fontsize): if rcParams['ps.useafm']: return if (fontname,fontsize) != (self.fontname,self.fontsize): out = ("/%s findfont\n" "%1.3f scalefont\n" "setfont\n" % (fontname,fontsize)) self._pswriter.write(out) self.fontname = fontname self.fontsize = fontsize def get_canvas_width_height(self): 'return the canvas width and height in display coords' return self.width, self.height def get_text_width_height(self, s, prop, ismath): """ get the width and height in display coords of the string s with FontPropertry prop """ if rcParams['text.usetex']: fontsize = prop.get_size_in_points() l,b,r,t = self.texmanager.get_ps_bbox(s) w = (r-l)*fontsize/10. h = (t-b)*fontsize/10. #print s, w, h return w, h if rcParams['ps.useafm']: if ismath: s = s[1:-1] font = self._get_font_afm(prop) l,b,w,h = font.get_str_bbox(s) fontsize = prop.get_size_in_points() w *= 0.001*fontsize h *= 0.001*fontsize return w, h if ismath: width, height, pswriter = math_parse_s_ps( s, 72, prop.get_size_in_points()) return width, height font = self._get_font_ttf(prop) font.set_text(s, 0.0) w, h = font.get_width_height() w /= 64.0 # convert from subpixels h /= 64.0 #print s, w, h return w, h def flipy(self): 'return true if small y numbers are top for renderer' return False def _get_font_afm(self, prop): key = hash(prop) font = _afmfontd.get(key) if font is None: font = AFM(file(fontManager.findfont(prop, fontext='afm'))) _afmfontd[key] = font return font def _get_font_ttf(self, prop): key = hash(prop) font = _fontd.get(key) if font is None: fname = fontManager.findfont(prop) font = FT2Font(str(fname)) _fontd[key] = font if fname not in _type42: _type42.append(fname) font.clear() size = prop.get_size_in_points() font.set_size(size, 72.0) return font def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2): """ Draw an arc centered at x,y with width and height and angles from 0.0 to 360.0 If gcFace is not None, fill the arc slice with it. gcEdge is a GraphicsContext instance """ ps = '%s ellipse' % _nums_to_str(angle1, angle2, 0.5*width, 0.5*height, x, y) self._draw_ps(ps, gc, rgbFace, "arc") def _rgba(self, im, flipud): return im.as_str(fliud) def _rgb(self, im, flipud): rgbat = im.as_str(flipud) rgba = fromstring(rgbat[2], UInt8) rgba.shape = (rgbat[0], rgbat[1], 4) rgb = rgba[:,:,:3] return rgbat[0], rgbat[1], rgb.tostring() def _gray(self, im, flipud, rc=0.3, gc=0.59, bc=0.11): rgbat = im.as_str(flipud) rgba = fromstring(rgbat[2], UInt8) rgba.shape = (rgbat[0], rgbat[1], 4) rgba_f = rgba.astype(Float32) r = rgba_f[:,:,0] g = rgba_f[:,:,1] b = rgba_f[:,:,2] gray = (r*rc + g*gc + b*bc).astype(UInt8) return rgbat[0], rgbat[1], gray.tostring() def _hex_lines(self, s, chars_per_line=128): s = binascii.b2a_hex(s) nhex = len(s) lines = [] for i in range(0,nhex,chars_per_line): limit = min(i+chars_per_line, nhex) lines.append(s[i:limit]) return lines def draw_image(self, x, y, im, origin, bbox): """ Draw the Image instance into the current axes; x is the distance in pixels from the left hand side of the canvas. y is the distance from the origin. That is, if origin is upper, y is the distance from top. If origin is lower, y is the distance from bottom origin is 'upper' or 'lower' bbox is a matplotlib.transforms.BBox instance for clipping, or None """ flipud = origin=='lower' if im.is_grayscale: h, w, bits = self._gray(im, flipud) imagecmd = "image" else: h, w, bits = self._rgb(im, flipud) imagecmd = "false 3 colorimage" hexlines = '\n'.join(self._hex_lines(bits)) xscale, yscale = w, h figh = self.height*72 #print 'values', origin, flipud, figh, h, y if bbox is not None: clipx,clipy,clipw,cliph = bbox.get_bounds() clip = '%s clipbox' % _nums_to_str(clipw, cliph, clipx, clipy) if not flipud: y = figh-(y+h) ps = """gsave %(clip)s %(x)s %(y)s translate %(xscale)s %(yscale)s scale /DataString %(w)s string def %(w)s %(h)s 8 [ %(w)s 0 0 -%(h)s 0 %(h)s ] { currentfile DataString readhexstring pop } bind %(imagecmd)s %(hexlines)s grestore """ % locals() self._pswriter.write(ps) def draw_line(self, gc, x0, y0, x1, y1): """ Draw a single line from x0,y0 to x1,y1 """ ps = '%1.3f %1.3f m %1.3f %1.3f l'%(x0, y0, x1, y1) self._draw_ps(ps, gc, None, "line") def _draw_markers(self, gc, path, rgbFace, x, y, transform): """ Draw the markers defined by path at each of the positions in x and y. path coordinates are points, x and y coords will be transformed by the transform """ if debugPS: self._pswriter.write('% draw_markers \n') return if rgbFace: if rgbFace[0]==rgbFace[0] and rgbFace[0]==rgbFace[2]: ps_color = '%1.3f setgray' % rgbFace[0] else: ps_color = '%1.3f %1.3f %1.3f setrgbcolor' % rgbFace #if transform.need_nonlinear(): # x,y,mask = transform.nonlinear_only_numerix(x, y, returnMask=1) #else: # mask = ones(x.shape) x, y = transform.numerix_x_y(x, y) # the a,b,c,d,tx,ty affine which transforms x and y #vec6 = transform.as_vec6_val() #theta = (180 / pi) * math.atan2 (vec6[1], src[0]) # this defines a single vertex. We need to define this as ps # function, properly stroked and filled with linewidth etc, # and then simply iterate over the x and y and call this # function at each position. Eg, this is the path that is # relative to each x and y offset. # construct the generic marker command: ps_cmd = ['gsave'] ps_cmd.append('newpath') ps_cmd.append('translate') while 1: code, xp, yp = path.vertex() if code == agg.path_cmd_stop: ps_cmd.append('closepath') # Hack, path_cmd_end_poly not found break elif code == agg.path_cmd_move_to: ps_cmd.append('%1.3f %1.3f m' % (xp,yp)) elif code == agg.path_cmd_line_to: ps_cmd.append('%1.3f %1.3f l' % (xp,yp)) elif code == agg.path_cmd_curve3: pass elif code == agg.path_cmd_curve4: pass elif code == agg.path_cmd_end_poly: pass ps_cmd.append('closepath') elif code == agg.path_cmd_mask: pass else: pass #print code if rgbFace: ps_cmd.append('gsave') ps_cmd.append(ps_color) ps_cmd.append('fill') ps_cmd.append('grestore') ps_cmd.append('stroke') ps_cmd.append('grestore') # undo translate() ps_cmd = '\n'.join(ps_cmd) #self._pswriter.write(' '.join(['/marker {', ps_cmd, '} bind def\n'])) #self._pswriter.write('[%s]' % ';'.join([float(val) for val in vec6])) # Now evaluate the marker command at each marker location: start = 0 end = 1000 while start < len(x): to_draw = izip(x[start:end],y[start:end]) ps = ['%1.3f %1.3f marker' % point for point in to_draw] self._draw_ps("\n".join(ps), gc, None) start = end end += 1000 def draw_path(self,gc,rgbFace,path,trans): pass def _draw_lines(self, gc, points): """ Draw many lines. 'points' is a list of point coordinates. """ # inline this for performance ps = ["%1.3f %1.3f m" % points[0]] ps.extend(["%1.3f %1.3f l" % point for point in points[1:] ]) self._draw_ps("\n".join(ps), gc, None) def draw_lines(self, gc, x, y, transform=None): """ x and y are equal length arrays, draw lines connecting each point in x, y """ if debugPS: self._pswriter.write('% draw_lines \n') if transform: # this won't be called if draw_markers is hidden #if transform.need_nonlinear(): # x, y = transform.nonlinear_only_numerix(x, y) x, y = transform.numerix_x_y(x, y) start = 0 end = 1000 points = zip(x,y) while start < len(x): to_draw = izip(x[start:end],y[start:end]) ps = ["%1.3f %1.3f m" % to_draw.next()] ps.extend(["%1.3f %1.3f l" % point for point in to_draw]) self._draw_ps("\n".join(ps), gc, None) start = end end += 1000 def __draw_lines_hide(self, gc, x, y, transform=None): """ x and y are equal length arrays, draw lines connecting each point in x, y """ if debugPS: self._pswriter.write('% draw_lines \n') if transform: if transform.need_nonlinear(): x, y, mask = transform.nonlinear_only_numerix(x, y, returnMask=1) else: mask = ones(x.shape) vec6 = transform.as_vec6_val() a,b,c,d,tx,ty = vec6 sx, sy = get_vec6_scales(vec6) start = 0 end = 1000 points = zip(x,y) write = self._pswriter.write write('gsave\n') self.push_gc(gc) write('[%f %f %f %f %f %f] concat\n'%(a,b,c,d,tx,ty)) while start < len(x): # put moveto on all the bad data and on the first good # point after the bad data codes = where(mask[start:end+1], 'l', 'm') ind = nonzero(mask[start:end+1]==0)+1 if ind[-1]>=len(codes): ind = ind[:-1] put(codes, ind, 'm') thisx = x[start:end+1] thisy = y[start:end+1] to_draw = izip(thisx, thisy, codes) if not to_draw: break ps = ['%1.3f %1.3f m' % to_draw.next()[:2]] ps.extend(["%1.3f %1.3f %c" % tup for tup in to_draw]) # we don't want to scale the line width, etc so invert the # scale for the stroke ps.append('\ngsave %f %f scale stroke grestore\n'%(1./sx,1./sy)) write('\n'.join(ps)) start = end end += 1000 write("grestore\n") def draw_lines_old(self, gc, x, y): """ x and y are equal length arrays, draw lines connecting each point in x, y """ if debugPS: self._pswriter.write("% lines\n") start = 0 end = 1000 points = zip(x,y) while 1: to_draw = points[start:end] if not to_draw: break self._draw_lines(gc,to_draw) start = end end += 1000 def draw_point(self, gc, x, y): """ Draw a single point at x,y """ # TODO: is there a better way to draw points in postscript? # (use a small circle?) self.draw_line(gc, x, y, x+1, y+1) def draw_polygon(self, gc, rgbFace, points): """ Draw a polygon. points is a len vertices tuple, each element giving the x,y coords a vertex If rgbFace is not None, fill the poly with it. gc is a GraphicsContext instance """ ps = ["%s m\n" % _nums_to_str(*points[0])] ps.extend([ "%s l\n" % _nums_to_str(x, y) for x,y in points[1:] ]) ps.append("closepath") self._draw_ps(''.join(ps), gc, rgbFace, "polygon") def draw_rectangle(self, gc, rgbFace, x, y, width, height): """ Draw a rectangle with lower left at x,y with width and height. If gcFace is not None, fill the rectangle with it. gcEdge is a GraphicsContext instance """ # TODO: use rectstroke ps = '%s box' % _nums_to_str(width, height, x, y) self._draw_ps(ps, gc, rgbFace, "rectangle") def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!'): """ draw a Text instance """ w, h = self.get_text_width_height(s, prop, ismath) fontsize = prop.get_size_in_points() corr = 0#w/2*(fontsize-10)/10 pos = _nums_to_str(x-corr, y) thetext = 'psmarker%d' % self.textcnt scale = float(fontsize/10.0) color = '%1.3f,%1.3f,%1.3f'% gc.get_rgb() tex = r'\color[rgb]{%s} %s' % (color, s) self.psfrag.append(r'\psfrag{%s}[bl][bl][%f][%f]{%s}'%(thetext, scale, angle, tex)) ps = """\ gsave %(pos)s moveto (%(thetext)s) show grestore """ % locals() self._pswriter.write(ps) self.textcnt += 1 def draw_text(self, gc, x, y, s, prop, angle, ismath): """ draw a Text instance """ # local to avoid repeated attribute lookups write = self._pswriter.write if debugPS: write("% text\n") if rcParams['ps.useafm']: if ismath: s = s[1:-1] font = self._get_font_afm(prop) l,b,w,h = font.get_str_bbox(s) fontsize = prop.get_size_in_points() l *= 0.001*fontsize b *= 0.001*fontsize w *= 0.001*fontsize h *= 0.001*fontsize if angle==90: l,b = -b, l # todo generalize for arb rotations pos = _nums_to_str(x-l, y-b) thetext = '(%s)' % s fontname = font.get_fontname() fontsize = prop.get_size_in_points() rotate = '%1.1f rotate' % angle setcolor = '%1.3f %1.3f %1.3f setrgbcolor' % gc.get_rgb() #h = 0 ps = """\ gsave /%(fontname)s findfont %(fontsize)s scalefont setfont %(pos)s moveto %(rotate)s %(thetext)s %(setcolor)s show grestore """ % locals() self._draw_ps(ps, gc, None) elif ismath=='TeX': return self.tex(gc, x, y, s, prop, angle) elif ismath: return self.draw_mathtext(gc, x, y, s, prop, angle) elif isinstance(s, unicode): return self.draw_unicode(gc, x, y, s, prop, angle) else: font = self._get_font_ttf(prop) font.set_text(s,0) self.set_color(*gc.get_rgb()) self.set_font(font.get_sfnt()[(1,0,0,6)], prop.get_size_in_points()) write("%s m\n"%_nums_to_str(x,y)) if angle: write("gsave\n") write("%s rotate\n"%_num_to_str(angle)) descent = font.get_descent() / 64.0 if descent: write("0 %s rmoveto\n"%_num_to_str(descent)) write("(%s) show\n"%quote_ps_string(s)) if angle: write("grestore\n") def new_gc(self): return GraphicsContextPS() def draw_unicode(self, gc, x, y, s, prop, angle): """draw a unicode string. ps doesn't have unicode support, so we have to do this the hard way """ font = self._get_font_ttf(prop) self.set_color(*gc.get_rgb()) self.set_font(font.get_sfnt()[(1,0,0,6)], prop.get_size_in_points()) cmap = font.get_charmap() glyphd = reverse_dict(cmap) lastgind = None #print 'text', s lines = [] thisx, thisy = 0,0 for c in s: ccode = ord(c) gind = glyphd.get(ccode) if gind is None: ccode = ord('?') name = '.notdef' gind = 0 else: name = font.get_glyph_name(gind) glyph = font.load_char(ccode) if lastgind is not None: kern = font.get_kerning(lastgind, gind, KERNING_UNFITTED) else: kern = 0 lastgind = gind thisx += kern/64.0 lines.append('%f %f m /%s glyphshow'%(thisx, thisy, name)) thisx += glyph.linearHoriAdvance/65536.0 thetext = '\n'.join(lines) ps = """gsave %(x)f %(y)f translate %(angle)f rotate %(thetext)s grestore """ % locals() self._pswriter.write(ps) def draw_mathtext(self, gc, x, y, s, prop, angle): """ Draw the math text using matplotlib.mathtext """ if debugPS: self._pswriter.write("% mathtext\n") fontsize = prop.get_size_in_points() width, height, pswriter = math_parse_s_ps(s, 72, fontsize) self.set_color(*gc.get_rgb()) thetext = pswriter.getvalue() ps = """gsave %(x)f %(y)f translate %(angle)f rotate %(thetext)s grestore """ % locals() self._pswriter.write(ps) def _draw_ps(self, ps, gc, rgbFace, command=None): """ Emit the PostScript sniplet 'ps' with all the attributes from 'gc' applied. 'ps' must consist of PostScript commands to construct a path. """ # local variable eliminates all repeated attribute lookups write = self._pswriter.write if debugPS and command: write("% "+command+"\n") cliprect = gc.get_clip_rectangle() self.set_color(*gc.get_rgb()) self.set_linewidth(gc.get_linewidth()) # TODO: move the lookup into GraphicsContextPS jint = {'miter':0, 'round':1, 'bevel':2}[gc.get_joinstyle()] self.set_linejoin(jint) # TODO: move the lookup into GraphicsContextPS cint = {'butt':0, 'round':1, 'projecting':2}[gc.get_capstyle()] self.set_linecap(cint) self.set_linedash(*gc.get_dashes()) if cliprect: x,y,w,h=cliprect write('gsave\n%1.3f %1.3f %1.3f %1.3f clipbox\n' % (w,h,x,y)) # Jochen, is the strip necessary? - this could be a honking big string write(ps.strip()) write("\n") if rgbFace: #print 'rgbface', rgbFace write("gsave\n") self.set_color(store=0, *rgbFace) write("fill\ngrestore\n") write("stroke\n") if cliprect: write("grestore\n") def push_gc(self, gc): """ Push the current onto stack """ # local variable eliminates all repeated attribute lookups write = self._pswriter.write cliprect = gc.get_clip_rectangle() self.set_color(*gc.get_rgb()) self.set_linewidth(gc.get_linewidth()) # TODO: move the lookup into GraphicsContextPS jint = {'miter':0, 'round':1, 'bevel':2}[gc.get_joinstyle()] self.set_linejoin(jint) # TODO: move the lookup into GraphicsContextPS cint = {'butt':0, 'round':1, 'projecting':2}[gc.get_capstyle()] self.set_linecap(cint) self.set_linedash(*gc.get_dashes()) if cliprect: x,y,w,h=cliprect write('%1.3f %1.3f %1.3f %1.3f clipbox\n' % (w,h,x,y)) write("\n")
class RendererGR(RendererBase): """ Handles drawing/rendering operations using GR """ texd = maxdict(50) # a cache of tex image rasters def __init__(self, dpi, width, height): self.dpi = dpi self.width = float(width) * dpi / 80 self.height = float(height) * dpi / 80 self.mathtext_parser = MathTextParser('agg') self.texmanager = TexManager() def configure(self): aspect_ratio = self.width / self.height if aspect_ratio > 1: rect = np.array([0, 1, 0, 1.0 / aspect_ratio]) self.size = self.width else: rect = np.array([0, aspect_ratio, 0, 1]) self.size = self.height mwidth, mheight, width, height = gr.inqdspsize() if width / (mwidth / 0.0256) < 200: mwidth *= self.width / width gr.setwsviewport(*rect * mwidth) else: gr.setwsviewport(*rect * 0.192) gr.setwswindow(*rect) gr.setviewport(*rect) gr.setwindow(0, self.width, 0, self.height) def draw_path(self, gc, path, transform, rgbFace=None): path = transform.transform_path(path) points = path.vertices codes = path.codes bbox = gc.get_clip_rectangle() if bbox is not None: x, y, w, h = bbox.bounds clrt = np.array([x, x + w, y, y + h]) else: clrt = np.array([0, self.width, 0, self.height]) gr.setviewport(*clrt / self.size) gr.setwindow(*clrt) if rgbFace is not None and len(points) > 2: color = gr.inqcolorfromrgb(rgbFace[0], rgbFace[1], rgbFace[2]) gr.settransparency(rgbFace[3]) gr.setcolorrep(color, rgbFace[0], rgbFace[1], rgbFace[2]) gr.setfillintstyle(gr.INTSTYLE_SOLID) gr.setfillcolorind(color) gr.drawpath(points, codes, fill=True) lw = gc.get_linewidth() if lw != 0: rgba = gc.get_rgb()[:4] color = gr.inqcolorfromrgb(rgba[0], rgba[1], rgba[2]) gr.settransparency(rgba[3]) gr.setcolorrep(color, rgba[0], rgba[1], rgba[2]) if isinstance(gc._linestyle, str): gr.setlinetype(linetype[gc._linestyle]) gr.setlinewidth(lw) gr.setlinecolorind(color) gr.drawpath(points, codes, fill=False) def draw_image(self, gc, x, y, im): h, w, s = im.as_rgba_str() img = np.fromstring(s, np.uint32) img.shape = (h, w) gr.drawimage(x, x + w, y + h, y, w, h, img) def draw_mathtext(self, x, y, angle, Z): h, w = Z.shape img = np.zeros((h, w), np.uint32) for i in range(h): for j in range(w): img[i, j] = (255 - Z[i, j]) << 24 a = int(angle) if a == 90: gr.drawimage(x - h, x, y, y + w, h, w, np.resize(np.rot90(img, 1), (h, w))) elif a == 180: gr.drawimage(x - w, x, y - h, y, w, h, np.rot90(img, 2)) elif a == 270: gr.drawimage(x, x + h, y - w, y, h, w, np.resize(np.rot90(img, 3), (h, w))) else: gr.drawimage(x, x + w, y, y + h, w, h, img) def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None): size = prop.get_size_in_points() key = s, size, self.dpi, angle, self.texmanager.get_font_config() im = self.texd.get(key) if im is None: Z = self.texmanager.get_grey(s, size, self.dpi) Z = np.array(255.0 - Z * 255.0, np.uint8) self.draw_mathtext(x, y, angle, Z) def _draw_mathtext(self, gc, x, y, s, prop, angle): ox, oy, width, height, descent, image, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) self.draw_mathtext(x, y, angle, 255 - image.as_array()) def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): if ismath: self._draw_mathtext(gc, x, y, s, prop, angle) else: x, y = gr.wctondc(x, y) s = s.replace(u'\u2212', '-') fontsize = prop.get_size_in_points() rgba = gc.get_rgb()[:4] color = gr.inqcolorfromrgb(rgba[0], rgba[1], rgba[2]) gr.settransparency(rgba[3]) gr.setcolorrep(color, rgba[0], rgba[1], rgba[2]) gr.setcharheight(fontsize * 0.0013) gr.settextcolorind(color) if angle != 0: gr.setcharup(-np.sin(angle * np.pi/180), np.cos(angle * np.pi/180)) else: gr.setcharup(0, 1) gr.text(x, y, s) def flipy(self): return False def get_canvas_width_height(self): return self.width, self.height def get_text_width_height_descent(self, s, prop, ismath): if ismath == 'TeX': fontsize = prop.get_size_in_points() w, h, d = self.texmanager.get_text_width_height_descent( s, fontsize, renderer=self) return w, h, d if ismath: ox, oy, width, height, descent, fonts, used_characters = \ self.mathtext_parser.parse(s, self.dpi, prop) return width, height, descent # family = prop.get_family() # weight = prop.get_weight() # style = prop.get_style() fontsize = prop.get_size_in_points() gr.setcharheight(fontsize * 0.0013) gr.setcharup(0, 1) (tbx, tby) = gr.inqtextext(0, 0, s) width, height, descent = tbx[1], tby[2], 0 return width, height, descent def new_gc(self): return GraphicsContextGR() def points_to_pixels(self, points): return points
class RendererAgg(RendererBase): """ The renderer handles all the drawing primitives using a graphics context instance that controls the colors/styles """ debug = 1 texd = {} # a cache of tex image rasters def __init__(self, width, height, dpi): if __debug__: verbose.report('RendererAgg.__init__', 'debug-annoying') self.dpi = dpi self.width = width self.height = height self._renderer = _RendererAgg(int(width), int(height), dpi.get(), debug=False) self.draw_polygon = self._renderer.draw_polygon self.draw_rectangle = self._renderer.draw_rectangle self.draw_path = self._renderer.draw_path self.draw_lines = self._renderer.draw_lines self.draw_markers = self._renderer.draw_markers self.draw_image = self._renderer.draw_image self.draw_line_collection = self._renderer.draw_line_collection self.draw_quad_mesh = self._renderer.draw_quad_mesh self.draw_poly_collection = self._renderer.draw_poly_collection self.draw_regpoly_collection = self._renderer.draw_regpoly_collection self.copy_from_bbox = self._renderer.copy_from_bbox self.restore_region = self._renderer.restore_region self.texmanager = TexManager() self.bbox = lbwh_to_bbox(0, 0, self.width, self.height) def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2): """ Draw an arc centered at x,y with width and height and angles from 0.0 to 360.0 If rgbFace is not None, fill the rectangle with that color. gcEdge is a GraphicsContext instance Currently, I'm only supporting ellipses, ie angle args are ignored """ if __debug__: verbose.report('RendererAgg.draw_arc', 'debug-annoying') self._renderer.draw_ellipse(gcEdge, rgbFace, x, y, width / 2, height / 2) # ellipse takes radius def _draw_image(self, x, y, im): """ Draw the Image instance into the current axes; x, y is the upper left hand corner of the image """ if __debug__: verbose.report('RendererAgg.draw_image', 'debug-annoying') #self._renderer.draw_image(int(x), int(self.height-y), im) self._renderer.draw_image(int(x), int(y), im) def draw_line(self, gc, x1, y1, x2, y2): """ x and y are equal length arrays, draw lines connecting each point in x, y """ if __debug__: verbose.report('RendererAgg.draw_line', 'debug-annoying') x = array([x1, x2], typecode=Float) y = array([y1, y2], typecode=Float) self._renderer.draw_lines(gc, x, y) def draw_point(self, gc, x, y): """ Draw a single point at x,y """ if __debug__: verbose.report('RendererAgg.draw_point', 'debug-annoying') rgbFace = gc.get_rgb() self._renderer.draw_ellipse(gc, rgbFace, x, y, 0.5, 0.5) def draw_mathtext(self, gc, x, y, s, prop, angle): """ Draw the math text using matplotlib.mathtext """ if __debug__: verbose.report('RendererAgg.draw_mathtext', 'debug-annoying') size = prop.get_size_in_points() width, height, fonts = math_parse_s_ft2font(s, self.dpi.get(), size, angle) if angle == 90: width, height = height, width for font in fonts: if angle == 90: font.horiz_image_to_vert_image() # <-- Rotate self._renderer.draw_text(font, int(x) - width, int(y) - height, gc) else: self._renderer.draw_text(font, int(x), int(y) - height, gc) if 0: self._renderer.draw_rectangle(gc, None, int(x), self.height - int(y), width, height) def draw_text(self, gc, x, y, s, prop, angle, ismath): """ Render the text """ if __debug__: verbose.report('RendererAgg.draw_text', 'debug-annoying') if ismath: return self.draw_mathtext(gc, x, y, s, prop, angle) font = self._get_agg_font(prop) if font is None: return None if len(s) == 1 and ord(s) > 127: font.load_char(ord(s)) else: font.set_text(s, angle) font.draw_glyphs_to_bitmap() #print x, y, int(x), int(y) self._renderer.draw_text(font, int(x), int(y), gc) def get_text_width_height(self, s, prop, ismath, rgb=(0, 0, 0)): """ get the width and height in display coords of the string s with FontPropertry prop # passing rgb is a little hack to make cacheing in the # texmanager more efficient. It is not meant to be used # outside the backend """ if ismath == 'TeX': # todo: handle props size = prop.get_size_in_points() Z = self.texmanager.get_rgba(s, size, rgb) m, n, tmp = Z.shape return n, m if ismath: width, height, fonts = math_parse_s_ft2font( s, self.dpi.get(), prop.get_size_in_points()) return width, height font = self._get_agg_font(prop) font.set_text(s, 0.0) # the width and height of unrotated string w, h = font.get_width_height() w /= 64.0 # convert from subpixels h /= 64.0 return w, h def draw_tex(self, gc, x, y, s, prop, angle): # todo, handle props, angle, origins rgb = gc.get_rgb() size = prop.get_size_in_points() flip = angle == 90 w, h = self.get_text_width_height(s, prop, 'TeX', rgb) if flip: w, h = h, w x -= w key = s, size, rgb, angle, self.texmanager.get_font_config() im = self.texd.get(key) if im is None: Z = self.texmanager.get_rgba(s, size, rgb) if flip: r = Z[:, :, 0] g = Z[:, :, 1] b = Z[:, :, 2] a = Z[:, :, 3] m, n, tmp = Z.shape def func(x): return transpose(fliplr(x)) Z = zeros((n, m, 4), typecode=Float) Z[:, :, 0] = func(r) Z[:, :, 1] = func(g) Z[:, :, 2] = func(b) Z[:, :, 3] = func(a) im = fromarray(Z, 1) im.flipud_out() self.texd[key] = im cliprect = gc.get_clip_rectangle() if cliprect is None: bbox = None else: bbox = lbwh_to_bbox(*cliprect) self.draw_image(x, self.height - y, im, bbox) def get_canvas_width_height(self): 'return the canvas width and height in display coords' return self.width, self.height def _get_agg_font(self, prop): """ Get the font for text instance t, cacheing for efficiency """ if __debug__: verbose.report('RendererAgg._get_agg_font', 'debug-annoying') key = hash(prop) font = _fontd.get(key) if font is None: fname = fontManager.findfont(prop) font = FT2Font(str(fname)) _fontd[key] = font font.clear() size = prop.get_size_in_points() font.set_size(size, self.dpi.get()) return font def points_to_pixels(self, points): """ convert point measures to pixes using dpi and the pixels per inch of the display """ if __debug__: verbose.report('RendererAgg.points_to_pixels', 'debug-annoying') return points * self.dpi.get() / 72.0 def tostring_rgb(self): if __debug__: verbose.report('RendererAgg.tostring_rgb', 'debug-annoying') return self._renderer.tostring_rgb() def tostring_argb(self): if __debug__: verbose.report('RendererAgg.tostring_argb', 'debug-annoying') return self._renderer.tostring_argb() def buffer_rgba(self, x, y): if __debug__: verbose.report('RendererAgg.buffer_rgba', 'debug-annoying') return self._renderer.buffer_rgba(x, y) def clear(self): self._renderer.clear()
class RendererAgg(RendererBase): """ The renderer handles all the drawing primitives using a graphics context instance that controls the colors/styles """ debug=1 def __init__(self, width, height, dpi): if __debug__: verbose.report('RendererAgg.__init__', 'debug-annoying') self.dpi = dpi self.width = width self.height = height self._renderer = _RendererAgg(int(width), int(height), dpi.get(), debug=False) self.draw_polygon = self._renderer.draw_polygon self.draw_rectangle = self._renderer.draw_rectangle self.draw_path = self._renderer.draw_path self.draw_lines = self._renderer.draw_lines self.draw_markers = self._renderer.draw_markers self.draw_image = self._renderer.draw_image self.draw_line_collection = self._renderer.draw_line_collection self.draw_poly_collection = self._renderer.draw_poly_collection self.draw_regpoly_collection = self._renderer.draw_regpoly_collection self.cache = self._renderer.cache self.blit = self._renderer.blit self.texmanager = TexManager() self.texd = {} # a cache of tex image rasters self.bbox = lbwh_to_bbox(0,0, self.width, self.height) def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2): """ Draw an arc centered at x,y with width and height and angles from 0.0 to 360.0 If rgbFace is not None, fill the rectangle with that color. gcEdge is a GraphicsContext instance Currently, I'm only supporting ellipses, ie angle args are ignored """ if __debug__: verbose.report('RendererAgg.draw_arc', 'debug-annoying') self._renderer.draw_ellipse( gcEdge, rgbFace, x, y, width/2, height/2) # ellipse takes radius def _draw_image(self, x, y, im): """ Draw the Image instance into the current axes; x, y is the upper left hand corner of the image """ if __debug__: verbose.report('RendererAgg.draw_image', 'debug-annoying') #self._renderer.draw_image(int(x), int(self.height-y), im) self._renderer.draw_image(int(x), int(y), im) def draw_line(self, gc, x1, y1, x2, y2): """ x and y are equal length arrays, draw lines connecting each point in x, y """ if __debug__: verbose.report('RendererAgg.draw_line', 'debug-annoying') x = array([x1,x2], typecode=Float) y = array([y1,y2], typecode=Float) self._renderer.draw_lines(gc, x, y) def draw_point(self, gc, x, y): """ Draw a single point at x,y """ if __debug__: verbose.report('RendererAgg.draw_point', 'debug-annoying') rgbFace = gc.get_rgb() self._renderer.draw_ellipse( gc, rgbFace, x, y, 0.5, 0.5) def draw_mathtext(self, gc, x, y, s, prop, angle): """ Draw the math text using matplotlib.mathtext """ if __debug__: verbose.report('RendererAgg.draw_mathtext', 'debug-annoying') size = prop.get_size_in_points() width, height, fonts = math_parse_s_ft2font( s, self.dpi.get(), size, angle) if angle == 90: width, height = height, width for font in fonts: if angle == 90: font.horiz_image_to_vert_image() # <-- Rotate self._renderer.draw_text( font, int(x)-width, int(y)-height, gc) else: self._renderer.draw_text( font, int(x), int(y)-height, gc) if 0: self._renderer.draw_rectangle(gc, None, int(x), self.height-int(y), width, height) def draw_text(self, gc, x, y, s, prop, angle, ismath): """ Render the text """ if __debug__: verbose.report('RendererAgg.draw_text', 'debug-annoying') if ismath: return self.draw_mathtext(gc, x, y, s, prop, angle) font = self._get_agg_font(prop) if font is None: return None if len(s)==1 and ord(s)>127: font.load_char(ord(s)) else: font.set_text(s, angle) font.draw_glyphs_to_bitmap() #print x, y, int(x), int(y) self._renderer.draw_text(font, int(x), int(y), gc) def get_text_width_height(self, s, prop, ismath, rgb=(0,0,0)): """ get the width and height in display coords of the string s with FontPropertry prop # passing rgb is a little hack to make cacheing in the # texmanager more efficient. It is not meant to be used # outside the backend """ if ismath=='TeX': # todo: handle props size = prop.get_size_in_points() dpi = self.dpi.get() Z = self.texmanager.get_rgba(s, size, dpi, rgb) m,n,tmp = Z.shape return n,m if ismath: width, height, fonts = math_parse_s_ft2font( s, self.dpi.get(), prop.get_size_in_points()) return width, height font = self._get_agg_font(prop) font.set_text(s, 0.0) # the width and height of unrotated string w, h = font.get_width_height() w /= 64.0 # convert from subpixels h /= 64.0 return w, h def draw_tex(self, gc, x, y, s, prop, angle): # todo, handle props, angle, origins rgb = gc.get_rgb() size = prop.get_size_in_points() dpi = self.dpi.get() flip = angle==90 w,h = self.get_text_width_height(s, prop, 'TeX', rgb) if flip: w,h = h,w x -= w key = s, size, dpi, rgb im = self.texd.get(key) if im is None: Z = self.texmanager.get_rgba(s, size, dpi, rgb) if flip: r = Z[:,:,0] g = Z[:,:,1] b = Z[:,:,2] a = Z[:,:,3] m,n,tmp = Z.shape def func(x): return transpose(fliplr(x)) Z = zeros((n,m,4), typecode=Float) Z[:,:,0] = func(r) Z[:,:,1] = func(g) Z[:,:,2] = func(b) Z[:,:,3] = func(a) im = fromarray(Z, 1) self.texd[key] = im self.draw_image(x, y-h, im, 'upper', self.bbox) def get_canvas_width_height(self): 'return the canvas width and height in display coords' return self.width, self.height def _get_agg_font(self, prop): """ Get the font for text instance t, cacheing for efficiency """ if __debug__: verbose.report('RendererAgg._get_agg_font', 'debug-annoying') key = hash(prop) font = _fontd.get(key) if font is None: fname = fontManager.findfont(prop) font = FT2Font(str(fname)) _fontd[key] = font font.clear() size = prop.get_size_in_points() font.set_size(size, self.dpi.get()) return font def points_to_pixels(self, points): """ convert point measures to pixes using dpi and the pixels per inch of the display """ if __debug__: verbose.report('RendererAgg.points_to_pixels', 'debug-annoying') return points*self.dpi.get()/72.0 def tostring_rgb(self): if __debug__: verbose.report('RendererAgg.tostring_rgb', 'debug-annoying') return self._renderer.tostring_rgb() def tostring_argb(self): if __debug__: verbose.report('RendererAgg.tostring_argb', 'debug-annoying') return self._renderer.tostring_argb() def buffer_rgba(self): if __debug__: verbose.report('RendererAgg.buffer_rgba', 'debug-annoying') return self._renderer.buffer_rgba() def clear(self): self._renderer.clear()
def __init__(self, dpi, width, height): self.dpi = dpi self.width = float(width) * dpi / 80 self.height = float(height) * dpi / 80 self.mathtext_parser = MathTextParser('agg') self.texmanager = TexManager()