Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
 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()
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
 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()
Ejemplo n.º 6
0
    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')
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
 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
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
    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
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
 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)
Ejemplo n.º 14
0
    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
Ejemplo n.º 15
0
    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)
Ejemplo n.º 16
0
 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
Ejemplo n.º 17
0
 def get_texmanager(self):
     """Return the cached `~.texmanager.TexManager` instance."""
     if self._texmanager is None:
         self._texmanager = TexManager()
     return self._texmanager
Ejemplo n.º 18
0
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
Ejemplo n.º 19
0
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]
Ejemplo n.º 20
0
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
Ejemplo n.º 21
0
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")
Ejemplo n.º 22
0
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")        
Ejemplo n.º 23
0
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
Ejemplo n.º 24
0
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()
Ejemplo n.º 25
0
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()
Ejemplo n.º 26
0
 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()