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.
------------------------------------------------------------------------------
Come build with us! The BlackBerry® Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9-12, 2009. Register now!
http://p.sf.net/sfu/devconf
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins