2323from matplotlib .font_manager import findfont , FontProperties
2424from matplotlib .ft2font import FT2Font , KERNING_UNFITTED , KERNING_DEFAULT , KERNING_UNSCALED
2525
26+ from matplotlib .path import Path
27+ from matplotlib .transforms import Affine2D
28+ from matplotlib .mlab import quad2cubic
29+
2630# Font handling stuff snarfed from backend_ps, but only using TTF fonts
2731_fontd = {}
2832
@@ -81,7 +85,7 @@ def set_linestyle(self):
8185 'dashdot' :pyemf .PS_DASHDOT , 'dotted' :pyemf .PS_DOT }
8286 #style=styles.get(self.gc.get_linestyle('solid'))
8387 style = self .gc .get_linestyle ('solid' )
84- if debugHandle : print "EMFPen: style=%d " % style
88+ if debugHandle : print "EMFPen: style=%s " % style
8589 if style in styles :
8690 self .style = styles [style ]
8791 else :
@@ -116,6 +120,28 @@ class RendererEMF(RendererBase):
116120 The renderer handles drawing/rendering operations through a
117121 pyemf.EMF instance.
118122 """
123+
124+ fontweights = {
125+ 100 : pyemf .FW_NORMAL ,
126+ 200 : pyemf .FW_NORMAL ,
127+ 300 : pyemf .FW_NORMAL ,
128+ 400 : pyemf .FW_NORMAL ,
129+ 500 : pyemf .FW_NORMAL ,
130+ 600 : pyemf .FW_BOLD ,
131+ 700 : pyemf .FW_BOLD ,
132+ 800 : pyemf .FW_BOLD ,
133+ 900 : pyemf .FW_BOLD ,
134+ 'ultralight' : pyemf .FW_ULTRALIGHT ,
135+ 'light' : pyemf .FW_LIGHT ,
136+ 'normal' : pyemf .FW_NORMAL ,
137+ 'medium' : pyemf .FW_MEDIUM ,
138+ 'semibold' : pyemf .FW_SEMIBOLD ,
139+ 'bold' : pyemf .FW_BOLD ,
140+ 'heavy' : pyemf .FW_HEAVY ,
141+ 'ultrabold' : pyemf .FW_ULTRABOLD ,
142+ 'black' : pyemf .FW_BLACK ,
143+ }
144+
119145 def __init__ (self , outfile , width , height , dpi ):
120146 "Initialize the renderer with a gd image instance"
121147 self .outfile = outfile
@@ -141,6 +167,8 @@ def __init__(self, outfile, width, height, dpi):
141167 # set baseline for text to be bottom left corner
142168 self .emf .SetTextAlign ( pyemf .TA_BOTTOM | pyemf .TA_LEFT )
143169
170+ self ._lastClipRect = None
171+
144172 if debugPrint : print "RendererEMF: (%f,%f) %s dpi=%f" % (self .width ,self .height ,outfile ,dpi )
145173
146174
@@ -176,7 +204,65 @@ def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotatio
176204 self .emf .Arc (int (x - hw ),int (self .height - (y - hh )),int (x + hw ),int (self .height - (y + hh )),int (x + math .cos (angle1 * math .pi / 180.0 )* hw ),int (self .height - (y + math .sin (angle1 * math .pi / 180.0 )* hh )),int (x + math .cos (angle2 * math .pi / 180.0 )* hw ),int (self .height - (y + math .sin (angle2 * math .pi / 180.0 )* hh )))
177205
178206
179- def draw_image (self , x , y , im , bbox ):
207+ def handle_clip_rectangle (self , gc ):
208+ new_bounds = gc .get_clip_rectangle ()
209+ if new_bounds is not None :
210+ new_bounds = new_bounds .bounds
211+ if self ._lastClipRect != new_bounds :
212+ self ._lastClipRect = new_bounds
213+ if new_bounds is None :
214+ # use the maximum rectangle to disable clipping
215+ x , y , width , height = (0 , 0 , self .width , self .height )
216+ else :
217+ x , y , width , height = new_bounds
218+ self .emf .BeginPath ()
219+ self .emf .MoveTo (int (x ), int (self .height - y ))
220+ self .emf .LineTo (int (x ) + int (width ), int (self .height - y ))
221+ self .emf .LineTo (int (x ) + int (width ), int (self .height - y ) - int (height ))
222+ self .emf .LineTo (int (x ), int (self .height - y ) - int (height ))
223+ self .emf .CloseFigure ()
224+ self .emf .EndPath ()
225+ self .emf .SelectClipPath ()
226+
227+
228+ def convert_path (self , tpath ):
229+ self .emf .BeginPath ()
230+ last_points = None
231+ for points , code in tpath .iter_segments ():
232+ if code == Path .MOVETO :
233+ self .emf .MoveTo (* points )
234+ elif code == Path .LINETO :
235+ self .emf .LineTo (* points )
236+ elif code == Path .CURVE3 :
237+ points = quad2cubic (* (list (last_points [- 2 :]) + list (points )))
238+ self .emf .PolyBezierTo (zip (points [2 ::2 ], points [3 ::2 ]))
239+ elif code == Path .CURVE4 :
240+ self .emf .PolyBezierTo (zip (points [::2 ], points [1 ::2 ]))
241+ elif code == Path .CLOSEPOLY :
242+ self .emf .CloseFigure ()
243+ last_points = points
244+ self .emf .EndPath ()
245+
246+
247+ def draw_path (self , gc , path , transform , rgbFace = None ):
248+ """
249+ Draws a :class:`~matplotlib.path.Path` instance using the
250+ given affine transform.
251+ """
252+ self .handle_clip_rectangle (gc )
253+ gc ._rgb = gc ._rgb [:3 ]
254+ self .select_pen (gc )
255+ self .select_brush (rgbFace )
256+ transform = transform + Affine2D ().scale (1.0 , - 1.0 ).translate (0.0 , self .height )
257+ tpath = transform .transform_path (path )
258+ self .convert_path (tpath )
259+ if rgbFace is None :
260+ self .emf .StrokePath ()
261+ else :
262+ self .emf .StrokeAndFillPath ()
263+
264+
265+ def draw_image (self , x , y , im , bbox , clippath = None , clippath_trans = None ):
180266 """
181267 Draw the Image instance into the current axes; x is the
182268 distance in pixels from the left hand side of the canvas. y is
@@ -285,11 +371,17 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False):
285371 to if 1, and then the actual bounding box will be blotted along with
286372 your text.
287373 """
288- if debugText : print "draw_text: (%f,%f) %d degrees: '%s'" % (x ,y ,angle ,s )
289- if ismath :
290- self .draw_math_text (gc ,x ,y ,s ,prop ,angle )
291- else :
292- self .draw_plain_text (gc ,x ,y ,s ,prop ,angle )
374+ if ismath : s = self .strip_math (s )
375+ self .handle_clip_rectangle (gc )
376+ self .emf .SetTextColor (gc .get_rgb ()[:3 ])
377+ self .select_font (prop ,angle )
378+ if isinstance (s , unicode ):
379+ # unicode characters do not seem to work with pyemf
380+ try :
381+ s = s .replace (u'\u2212 ' , '-' ).encode ('iso-8859-1' )
382+ except UnicodeEncodeError :
383+ pass
384+ self .emf .TextOut (x ,y ,s )
293385
294386
295387 def draw_plain_text (self , gc , x , y , s , prop , angle ):
@@ -369,6 +461,22 @@ def get_math_text_width_height(self, s, prop):
369461 return w , h
370462
371463
464+ def get_text_width_height_descent (self , s , prop , ismath ):
465+ """
466+ get the width and height in display coords of the string s
467+ with FontPropertry prop
468+ """
469+ if ismath : s = self .strip_math (s )
470+ font = self ._get_font_ttf (prop )
471+ font .set_text (s , 0.0 )
472+ w , h = font .get_width_height ()
473+ w /= 64.0 # convert from subpixels
474+ h /= 64.0
475+ d = font .get_descent ()
476+ d /= 64.0
477+ return w , h , d
478+
479+
372480 def flipy (self ):
373481 """return true if y small numbers are top for renderer
374482 Is used for drawing text (text.py) and images (image.py) only
@@ -407,7 +515,9 @@ def get_font_handle(self, prop, angle):
407515 handle = self ._fontHandle .get (key )
408516 if handle is None :
409517 handle = self .emf .CreateFont (- size , 0 , int (angle )* 10 , int (angle )* 10 ,
410- pyemf .FW_NORMAL , 0 , 0 , 0 ,
518+ self .fontweights .get (prop .get_weight (), pyemf .FW_NORMAL ),
519+ int (prop .get_style () == 'italic' ),
520+ 0 , 0 ,
411521 pyemf .ANSI_CHARSET , pyemf .OUT_DEFAULT_PRECIS ,
412522 pyemf .CLIP_DEFAULT_PRECIS , pyemf .DEFAULT_QUALITY ,
413523 pyemf .DEFAULT_PITCH | pyemf .FF_DONTCARE , face );
0 commit comments