11from __future__ import division
22
3- import os , codecs , base64 , tempfile
3+ import os , codecs , base64 , tempfile , urllib
44
55from matplotlib import verbose , __version__ , rcParams
66from matplotlib .backend_bases import RendererBase , GraphicsContextBase ,\
77 FigureManagerBase , FigureCanvasBase
88from matplotlib .colors import rgb2hex
99from matplotlib .figure import Figure
10- from matplotlib .font_manager import fontManager
11- from matplotlib .ft2font import FT2Font
10+ from matplotlib .font_manager import fontManager , FontProperties
11+ from matplotlib .ft2font import FT2Font , KERNING_UNFITTED , KERNING_DEFAULT , KERNING_UNSCALED
1212from matplotlib .mathtext import math_parse_s_ft2font_svg
1313
1414backend_version = __version__
@@ -24,6 +24,8 @@ def new_figure_manager(num, *args, **kwargs):
2424_fontd = {}
2525_capstyle_d = {'projecting' : 'square' , 'butt' : 'butt' , 'round' : 'round' ,}
2626class RendererSVG (RendererBase ):
27+ FONT_SCALE = 1200.0
28+
2729 def __init__ (self , width , height , svgwriter , basename = None ):
2830 self .width = width
2931 self .height = height
@@ -35,6 +37,7 @@ def __init__(self, width, height, svgwriter, basename=None):
3537 self .basename = basename
3638 self ._imaged = {}
3739 self ._clipd = {}
40+ self ._char_defs = {}
3841 svgwriter .write (svgProlog % (width ,height ,width ,height ))
3942
4043 def _draw_svg_element (self , element , details , gc , rgbFace ):
@@ -237,68 +240,159 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath):
237240 font = self ._get_font (prop )
238241
239242 thetext = '%s' % s
240- fontfamily = font .family_name
241- fontstyle = font .style_name
243+ fontfamily = font .family_name
244+ fontstyle = font .style_name
242245 fontsize = prop .get_size_in_points ()
243246 color = rgb2hex (gc .get_rgb ())
244247
245- style = 'font-size: %f; font-family: %s; font-style: %s; fill: %s;' % (fontsize , fontfamily ,fontstyle , color )
246- if angle != 0 :
247- transform = 'transform="translate(%f,%f) rotate(%1.1f) translate(%f,%f)"' % (x ,y ,- angle ,- x ,- y ) # Inkscape doesn't support rotate(angle x y)
248- else : transform = ''
248+ if rcParams ['svg.embed_char_paths' ]:
249+ svg = '<g transform="'
250+ if angle != 0 :
251+ svg += 'translate(%f,%f) rotate(%1.1f) ' % (x ,y ,- angle ) # Inkscape doesn't support rotate(angle x y)
252+ else :
253+ svg += 'translate(%f,%f)' % (x ,y )
254+ svg += ' scale(%f)">\n ' % (fontsize / self .FONT_SCALE )
255+
256+ cmap = font .get_charmap ()
257+ lastgind = None
258+ lines = []
259+ currx = 0
260+ for c in s :
261+ charid = self ._add_char_def (prop , c )
262+ ccode = ord (c )
263+ gind = cmap .get (ccode )
264+ if gind is None :
265+ ccode = ord ('?' )
266+ name = '.notdef'
267+ gind = 0
268+ else :
269+ name = font .get_glyph_name (gind )
270+ glyph = font .load_char (ccode )
271+
272+ if lastgind is not None :
273+ kern = font .get_kerning (lastgind , gind , KERNING_UNFITTED )
274+ else :
275+ kern = 0
276+ lastgind = gind
277+ currx += kern / 64.0
278+
279+ svg += ('<use xlink:href="#%s" transform="translate(%s)"/>\n '
280+ % (charid , currx / (fontsize / self .FONT_SCALE )))
281+
282+ currx += glyph .linearHoriAdvance / 65536.0
283+ svg += '</g>\n '
284+ else :
285+ style = 'font-size: %f; font-family: %s; font-style: %s; fill: %s;' % (fontsize , fontfamily ,fontstyle , color )
286+ if angle != 0 :
287+ transform = 'transform="translate(%f,%f) rotate(%1.1f) translate(%f,%f)"' % (x ,y ,- angle ,- x ,- y ) # Inkscape doesn't support rotate(angle x y)
288+ else : transform = ''
249289
250- svg = """\
290+ svg = """\
251291 <text style="%(style)s" x="%(x)f" y="%(y)f" %(transform)s>%(thetext)s</text>
252292""" % locals ()
253293 self ._svgwriter .write (svg )
254294
295+ def _add_char_def (self , prop , char ):
296+ newprop = prop .copy ()
297+ newprop .set_size (self .FONT_SCALE )
298+ font = self ._get_font (newprop )
299+ ps_name = font .get_sfnt ()[(1 ,0 ,0 ,6 )]
300+ id = urllib .quote ('%s-%d' % (ps_name , ord (char )))
301+ if id in self ._char_defs :
302+ return id
303+
304+ path_element = '<path id="%s" ' % (id )
305+ path_data = []
306+ glyph = font .load_char (ord (char ))
307+ currx , curry = 0.0 , 0.0
308+ for step in glyph .path :
309+ if step [0 ] == 0 : # MOVE_TO
310+ path_data .append ("m%s %s" %
311+ (step [1 ] - currx , - step [2 ] - curry ))
312+ elif step [0 ] == 1 : # LINE_TO
313+ path_data .append ("l%s %s" %
314+ (step [1 ] - currx , - step [2 ] - curry ))
315+ elif step [0 ] == 2 : # CURVE3
316+ path_data .append ("q%s %s %s %s" %
317+ (step [1 ] - currx , - step [2 ] - curry ,
318+ step [3 ] - currx , - step [4 ] - curry ))
319+ elif step [0 ] == 3 : # CURVE4
320+ path_data .append ("c%s %s %s %s %s %s" %
321+ (step [1 ] - currx , - step [2 ] - curry ,
322+ step [3 ] - currx , - step [4 ] - curry ,
323+ step [5 ] - currx , - step [6 ] - curry ))
324+ elif step [0 ] == 4 : # ENDPOLY
325+ path_data .append ("Z" )
326+
327+ if step [0 ] != 4 :
328+ currx , curry = step [- 2 ], - step [- 1 ]
329+ path_element += 'd="%s"/>\n ' % " " .join (path_data )
330+
331+ self ._char_defs [id ] = path_element
332+ return id
333+
255334 def _draw_mathtext (self , gc , x , y , s , prop , angle ):
256335 """
257336 Draw math text using matplotlib.mathtext
258337 """
259338 fontsize = prop .get_size_in_points ()
260- width , height , svg_elements = math_parse_s_ft2font_svg (s ,
261- 72 , fontsize )
339+ width , height , svg_elements = math_parse_s_ft2font_svg (s , 72 , fontsize )
262340 svg_glyphs = svg_elements .svg_glyphs
263341 svg_lines = svg_elements .svg_lines
264342 color = rgb2hex (gc .get_rgb ())
265343
266344 self .open_group ("mathtext" )
267345
268- svg = '<text style="fill: %s" x="%f" y="%f"' % (color ,x ,y )
346+ if rcParams ['svg.embed_char_paths' ]:
347+ svg = '<g style="fill: %s" transform="' % color
348+ if angle != 0 :
349+ svg += ( 'translate(%f,%f) rotate(%1.1f)'
350+ % (x ,y ,- angle ) )
351+ else :
352+ svg += 'translate(%f,%f)' % (x , y )
353+ svg += '">\n '
269354
270- if angle != 0 :
271- svg += ( ' transform="translate(%f,%f) rotate(%1.1f) translate(%f,%f)"'
272- % (x ,y ,- angle ,- x ,- y ) ) # Inkscape doesn't support rotate(angle x y)
273- svg += '>\n '
355+ for fontname , fontsize , thetext , new_x , new_y_mtc , metrics in svg_glyphs :
356+ prop = FontProperties (family = fontname , size = fontsize )
357+ charid = self ._add_char_def (prop , thetext )
274358
275- curr_x ,curr_y = 0.0 ,0.0
359+ svg += '<use xlink:href="#%s" transform="translate(%s, %s) scale(%s)"/>\n ' % (charid , new_x , - new_y_mtc , fontsize / self .FONT_SCALE )
360+ svg += '</g>\n '
361+ else : # not rcParams['svg.embed_char_paths']
362+ svg = '<text style="fill: %s" x="%f" y="%f"' % (color ,x ,y )
276363
277- for fontname , fontsize , thetext , new_x , new_y_mtc , metrics in svg_glyphs :
278- if rcParams ["mathtext.mathtext2" ]:
279- new_y = new_y_mtc - height
280- else :
281- new_y = - new_y_mtc
364+ if angle != 0 :
365+ svg += ( ' transform="translate(%f,%f) rotate(%1.1f) translate(%f,%f)"'
366+ % (x ,y ,- angle ,- x ,- y ) ) # Inkscape doesn't support rotate(angle x y)
367+ svg += '>\n '
368+
369+ curr_x ,curr_y = 0.0 ,0.0
370+
371+ for fontname , fontsize , thetext , new_x , new_y_mtc , metrics in svg_glyphs :
372+ if rcParams ["mathtext.mathtext2" ]:
373+ new_y = new_y_mtc - height
374+ else :
375+ new_y = - new_y_mtc
282376
283- svg += '<tspan'
284- svg += ' style="font-size: %f; font-family: %s"' % (fontsize , fontname )
285- xadvance = metrics .advance
286- svg += ' textLength="%f"' % xadvance
377+ svg += '<tspan'
378+ svg += ' style="font-size: %f; font-family: %s"' % (fontsize , fontname )
379+ xadvance = metrics .advance
380+ svg += ' textLength="%f"' % xadvance
287381
288- dx = new_x - curr_x
289- if dx != 0.0 :
290- svg += ' dx="%f"' % dx
382+ dx = new_x - curr_x
383+ if dx != 0.0 :
384+ svg += ' dx="%f"' % dx
291385
292- dy = new_y - curr_y
293- if dy != 0.0 :
294- svg += ' dy="%f"' % dy
386+ dy = new_y - curr_y
387+ if dy != 0.0 :
388+ svg += ' dy="%f"' % dy
295389
296- svg += '>%s</tspan>\n ' % thetext
390+ svg += '>%s</tspan>\n ' % thetext
297391
298- curr_x = new_x + xadvance
299- curr_y = new_y
392+ curr_x = new_x + xadvance
393+ curr_y = new_y
300394
301- svg += '</text>\n '
395+ svg += '</text>\n '
302396
303397 self ._svgwriter .write (svg )
304398 rgbFace = gc .get_rgb ()
@@ -310,6 +404,11 @@ def _draw_mathtext(self, gc, x, y, s, prop, angle):
310404 self .close_group ("mathtext" )
311405
312406 def finish (self ):
407+ if len (self ._char_defs ):
408+ self ._svgwriter .write ('<defs id="fontpaths">\n ' )
409+ for path in self ._char_defs .values ():
410+ self ._svgwriter .write (path )
411+ self ._svgwriter .write ('</defs>\n ' )
313412 self ._svgwriter .write ('</svg>\n ' )
314413
315414 def flipy (self ):
0 commit comments