From: <lee...@us...> - 2009-09-15 19:28:19
|
Revision: 7764 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=7764&view=rev Author: leejjoon Date: 2009-09-15 19:28:09 +0000 (Tue, 15 Sep 2009) Log Message: ----------- implement draw_text and draw_tex method of backend_base using the textpath. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/backend_bases.py trunk/matplotlib/lib/matplotlib/backends/backend_svg.py trunk/matplotlib/lib/matplotlib/texmanager.py trunk/matplotlib/lib/matplotlib/text.py trunk/matplotlib/lib/matplotlib/textpath.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2009-09-15 12:37:55 UTC (rev 7763) +++ trunk/matplotlib/CHANGELOG 2009-09-15 19:28:09 UTC (rev 7764) @@ -1,3 +1,7 @@ +2009-09-15 Implement draw_text and draw_tex method of backend_base using + the textpath module. Implement draw_tex method of the svg + backend. - JJL + 2009-09-15 Don't fail on AFM files containing floating-point bounding boxes - JKS 2009-09-13 AxesGrid : add modified version of colorbar. Add colorbar Modified: trunk/matplotlib/lib/matplotlib/backend_bases.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backend_bases.py 2009-09-15 12:37:55 UTC (rev 7763) +++ trunk/matplotlib/lib/matplotlib/backend_bases.py 2009-09-15 19:28:09 UTC (rev 7764) @@ -30,13 +30,15 @@ import matplotlib.colors as colors import matplotlib.transforms as transforms import matplotlib.widgets as widgets -import matplotlib.path as path +#import matplotlib.path as path from matplotlib import rcParams from matplotlib.transforms import Bbox, TransformedBbox, Affine2D import cStringIO import matplotlib.tight_bbox as tight_bbox +import matplotlib.textpath as textpath +from matplotlib.path import Path class RendererBase: """An abstract base class to handle drawing/rendering operations. @@ -58,6 +60,8 @@ def __init__(self): self._texmanager = None + self._text2path = textpath.TextToPath() + def open_group(self, s, gid=None): """ Open a grouping element with label *s*. If *gid* is given, use @@ -337,7 +341,9 @@ return False def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!'): - raise NotImplementedError + """ + """ + self._draw_text_as_path(gc, x, y, s, prop, angle, ismath="TeX") def draw_text(self, gc, x, y, s, prop, angle, ismath=False): """ @@ -372,8 +378,85 @@ to if 1, and then the actual bounding box will be blotted along with your text. """ - raise NotImplementedError + self._draw_text_as_path(gc, x, y, s, prop, angle, ismath) + + def _draw_text_as_path(self, gc, x, y, s, prop, angle, ismath): + """ + draw the text by converting them to paths using textpath module. + + *prop* + font property + + *s* + text to be converted + + *usetex* + If True, use matplotlib usetex mode. + + *ismath* + If True, use mathtext parser. If "TeX", use *usetex* mode. + """ + + text2path = self._text2path + color = gc.get_rgb()[:3] + fontsize = self.points_to_pixels(prop.get_size_in_points()) + + if ismath == "TeX": + verts, codes = text2path.get_text_path(prop, s, ismath=False, usetex=True) + else: + verts, codes = text2path.get_text_path(prop, s, ismath=ismath, usetex=False) + + path = Path(verts, codes) + angle = angle/180.*3.141592 + if self.flipy(): + transform = Affine2D().scale(fontsize/text2path.FONT_SCALE, + fontsize/text2path.FONT_SCALE).\ + rotate(angle).translate(x, self.height-y) + else: + transform = Affine2D().scale(fontsize/text2path.FONT_SCALE, + fontsize/text2path.FONT_SCALE).\ + rotate(angle).translate(x, y) + + gc.set_linewidth(0.0) + self.draw_path(gc, path, transform, rgbFace=color) + + + def get_text_width_height_descent(self, s, prop, ismath): + """ + get the width and height, and the offset from the bottom to the + baseline (descent), in display coords of the string s with + :class:`~matplotlib.font_manager.FontProperties` prop + """ + if ismath=='TeX': + # todo: handle props + size = prop.get_size_in_points() + texmanager = self._text2path.get_texmanager() + fontsize = prop.get_size_in_points() + w, h, d = texmanager.get_text_width_height_descent(s, fontsize, + renderer=self) + return w, h, d + + dpi = self.points_to_pixels(72) + fontscale = self._text2path.FONT_SCALE + if ismath: + width, height, descent, glyphs, rects = \ + self._text2path.mathtext_parser.parse(s, dpi, prop) + return width, height, descent + + flags = self._text2path._get_hinting_flag() + font = self._text2path._get_font(prop) + size = prop.get_size_in_points() + font.set_size(size, dpi) + font.set_text(s, 0.0, flags=flags) # the width and height of unrotated string + w, h = font.get_width_height() + d = font.get_descent() + w /= 64.0 # convert from subpixels + h /= 64.0 + d /= 64.0 + return w, h, d + + def flipy(self): """ Return true if y small numbers are top for renderer Is used @@ -395,13 +478,6 @@ self._texmanager = TexManager() return self._texmanager - def get_text_width_height_descent(self, s, prop, ismath): - """ - get the width and height, and the offset from the bottom to the - baseline (descent), in display coords of the string s with - :class:`~matplotlib.font_manager.FontProperties` prop - """ - raise NotImplementedError def new_gc(self): """ @@ -741,7 +817,7 @@ """ if self._hatch is None: return None - return path.Path.hatch(self._hatch, density) + return Path.hatch(self._hatch, density) class Event: """ Modified: trunk/matplotlib/lib/matplotlib/backends/backend_svg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2009-09-15 12:37:55 UTC (rev 7763) +++ trunk/matplotlib/lib/matplotlib/backends/backend_svg.py 2009-09-15 19:28:09 UTC (rev 7764) @@ -58,6 +58,10 @@ self._hatchd = {} self._n_gradients = 0 self.mathtext_parser = MathTextParser('SVG') + + RendererBase.__init__(self) + self._glyph_map = dict() + svgwriter.write(svgProlog%(width,height,width,height)) def _draw_svg_element(self, element, details, gc, rgbFace): @@ -405,7 +409,147 @@ if url is not None: self._svgwriter.write('</a>') + def _adjust_char_id(self, char_id): + return char_id.replace("%20","_") + + def draw_text_as_path(self, gc, x, y, s, prop, angle, ismath): + """ + draw the text by converting them to paths using textpath module. + + *prop* + font property + + *s* + text to be converted + + *usetex* + If True, use matplotlib usetex mode. + + *ismath* + If True, use mathtext parser. If "TeX", use *usetex* mode. + + + """ + # this method works for normal text, mathtext and usetex mode. + # But currently only utilized by draw_tex method. + + glyph_map=self._glyph_map + + text2path = self._text2path + color = rgb2hex(gc.get_rgb()[:3]) + fontsize = prop.get_size_in_points() + + write = self._svgwriter.write + + if ismath == False: + font = text2path._get_font(prop) + _glyphs = text2path.get_glyphs_with_font(font, s, glyph_map=glyph_map, + return_new_glyphs_only=True) + glyph_info, glyph_map_new, rects = _glyphs + + _flip = Affine2D().scale(1.0, -1.0) + + if glyph_map_new: + write('<defs>\n') + for char_id, glyph_path in glyph_map_new.iteritems(): + path = Path(*glyph_path) + path_data = self._convert_path(path, _flip) + path_element = '<path id="%s" d="%s"/>\n' % (char_id, ''.join(path_data)) + write(path_element) + write('</defs>\n') + + glyph_map.update(glyph_map_new) + + svg = [] + clipid = self._get_gc_clip_svg(gc) + if clipid is not None: + svg.append('<g clip-path="url(#%s)">\n' % clipid) + + svg.append('<g style="fill: %s; opacity: %f" transform="' % (color, gc.get_alpha())) + if angle != 0: + svg.append('translate(%f,%f)rotate(%1.1f)' % (x,y,-angle)) + elif x != 0 or y != 0: + svg.append('translate(%f,%f)' % (x, y)) + svg.append('scale(%f)">\n' % (fontsize / text2path.FONT_SCALE)) + + for glyph_id, xposition, yposition, scale in glyph_info: + svg.append('<use xlink:href="#%s"' % glyph_id) + svg.append(' x="%f" y="%f"' % (xposition, yposition)) + #(currx * (self.FONT_SCALE / fontsize))) + svg.append('/>\n') + + svg.append('</g>\n') + if clipid is not None: + svg.append('</g>\n') + svg = ''.join(svg) + + + + else: + if ismath == "TeX": + _glyphs = text2path.get_glyphs_tex(prop, s, glyph_map=glyph_map) + else: + _glyphs = text2path.get_glyphs_mathtext(prop, s, glyph_map=glyph_map) + + glyph_info, glyph_map_new, rects = _glyphs + + # we store the character glyphs w/o flipping. Instead, the + # coordinate will be flipped when this characters are + # used. + if glyph_map_new: + write('<defs>\n') + for char_id, glyph_path in glyph_map_new.iteritems(): + char_id = self._adjust_char_id(char_id) + path = Path(*glyph_path) + path_data = self._convert_path(path, None) #_flip) + path_element = '<path id="%s" d="%s"/>\n' % (char_id, ''.join(path_data)) + write(path_element) + write('</defs>\n') + + glyph_map.update(glyph_map_new) + + svg = [] + clipid = self._get_gc_clip_svg(gc) + if clipid is not None: + svg.append('<g clip-path="url(#%s)">\n' % clipid) + + svg.append('<g style="fill: %s; opacity: %f" transform="' % (color, gc.get_alpha())) + if angle != 0: + svg.append('translate(%f,%f)rotate(%1.1f)' % (x,y,-angle)) + elif x != 0 or y != 0: + svg.append('translate(%f,%f)' % (x, y)) + svg.append('scale(%f,-%f)">\n' % (fontsize / text2path.FONT_SCALE, + fontsize / text2path.FONT_SCALE)) + + for char_id, xposition, yposition, scale in glyph_info: + char_id = self._adjust_char_id(char_id) + svg.append('<use xlink:href="#%s"' % char_id) + svg.append(' x="%f" y="%f" transform="scale(%f)"' % (xposition/scale, + yposition/scale, + scale)) + svg.append('/>\n') + + + for verts, codes in rects: + path = Path(verts, codes) + path_data = self._convert_path(path, None) + path_element = '<path d="%s"/>\n' % (''.join(path_data)) + svg.append(path_element) + + + svg.append('</g><!-- style -->\n') + if clipid is not None: + svg.append('</g><!-- clipid -->\n') + svg = ''.join(svg) + + write(svg) + + + def draw_tex(self, gc, x, y, s, prop, angle): + self.draw_text_as_path(gc, x, y, s, prop, angle, ismath="TeX") + def draw_text(self, gc, x, y, s, prop, angle, ismath): + if ismath: self._draw_mathtext(gc, x, y, s, prop, angle) return @@ -648,6 +792,14 @@ return self.width, self.height def get_text_width_height_descent(self, s, prop, ismath): + if ismath == "TeX": + size = prop.get_size_in_points() + texmanager = self._text2path.get_texmanager() + fontsize = prop.get_size_in_points() + w, h, d = texmanager.get_text_width_height_descent(s, fontsize, + renderer=self) + return w, h, d + if ismath: width, height, descent, trash, used_characters = \ self.mathtext_parser.parse(s, 72, prop) Modified: trunk/matplotlib/lib/matplotlib/texmanager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/texmanager.py 2009-09-15 12:37:55 UTC (rev 7763) +++ trunk/matplotlib/lib/matplotlib/texmanager.py 2009-09-15 19:28:09 UTC (rev 7764) @@ -304,7 +304,7 @@ %s %s \usepackage[active,showbox,tightpage]{preview} -%%\usepackage[papersize={72in,72in}, body={70in,70in}, margin={1in,1in}]{geometry} +\usepackage[papersize={72in,72in}, body={70in,70in}, margin={1in,1in}]{geometry} %% we override the default showbox as it is treated as an error and makes %% the exit status not zero Modified: trunk/matplotlib/lib/matplotlib/text.py =================================================================== --- trunk/matplotlib/lib/matplotlib/text.py 2009-09-15 12:37:55 UTC (rev 7763) +++ trunk/matplotlib/lib/matplotlib/text.py 2009-09-15 19:28:09 UTC (rev 7764) @@ -560,6 +560,7 @@ renderer.draw_tex(gc, x, y, clean_line, self._fontproperties, angle) + renderer.close_group('text') return for line, wh, x, y in info: Modified: trunk/matplotlib/lib/matplotlib/textpath.py =================================================================== --- trunk/matplotlib/lib/matplotlib/textpath.py 2009-09-15 12:37:55 UTC (rev 7763) +++ trunk/matplotlib/lib/matplotlib/textpath.py 2009-09-15 19:28:09 UTC (rev 7764) @@ -42,6 +42,8 @@ return font + def _get_hinting_flag(self): + return LOAD_NO_HINTING def _get_char_id(self, font, ccode): """ @@ -134,7 +136,8 @@ return verts, codes - def get_glyphs_with_font(self, font, s, glyph_map=None): + def get_glyphs_with_font(self, font, s, glyph_map=None, + return_new_glyphs_only=False): """ convert the string *s* to vertices and codes using the provided ttf font. @@ -152,6 +155,11 @@ if glyph_map is None: glyph_map = dict() + if return_new_glyphs_only: + glyph_map_new = dict() + else: + glyph_map_new = glyph_map + # I'm not sure if I get kernings right. Needs to be verified. -JJL for c in s: @@ -174,7 +182,7 @@ char_id = self._get_char_id(font, ccode) if not char_id in glyph_map: - glyph_map[char_id] = self.glyph_to_path(glyph) + glyph_map_new[char_id] = self.glyph_to_path(glyph) currx += (kern / 64.0) @@ -190,12 +198,13 @@ rects = [] - return zip(glyph_ids, xpositions, ypositions, sizes), glyph_map, rects + return zip(glyph_ids, xpositions, ypositions, sizes), glyph_map_new, rects - def get_glyphs_mathtext(self, prop, s): + def get_glyphs_mathtext(self, prop, s, glyph_map=None, + return_new_glyphs_only=False): """ convert the string *s* to vertices and codes by parsing it with mathtext. """ @@ -207,8 +216,14 @@ s, self.DPI, prop) - glyph_map = dict() - + if glyph_map is None: + glyph_map = dict() + + if return_new_glyphs_only: + glyph_map_new = dict() + else: + glyph_map_new = glyph_map + xpositions = [] ypositions = [] glyph_ids = [] @@ -223,7 +238,7 @@ font.clear() font.set_size(self.FONT_SCALE, self.DPI) glyph = font.load_char(ccode, flags=LOAD_NO_HINTING) - glyph_map[char_id] = self.glyph_to_path(glyph) + glyph_map_new[char_id] = self.glyph_to_path(glyph) xpositions.append(ox) ypositions.append(oy) @@ -253,7 +268,8 @@ return self._texmanager - def get_glyphs_tex(self, prop, s): + def get_glyphs_tex(self, prop, s, glyph_map=None, + return_new_glyphs_only=False): """ convert the string *s* to vertices and codes using matplotlib's usetex mode. """ @@ -275,8 +291,17 @@ page = iter(dvi).next() dvi.close() + + if glyph_map is None: + glyph_map = dict() + + if return_new_glyphs_only: + glyph_map_new = dict() + else: + glyph_map_new = glyph_map + + glyph_ids, xpositions, ypositions, sizes = [], [], [], [] - glyph_map = dict() # Gather font information and do some setup for combining # characters into strings. @@ -316,7 +341,7 @@ else: glyph0 = font.load_glyph(ng, flags=ft2font_flag) - glyph_map[char_id] = self.glyph_to_path(glyph0) + glyph_map_new[char_id] = self.glyph_to_path(glyph0) glyph_ids.append(char_id) xpositions.append(x1) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |