1
1
from __future__ import division
2
2
3
- import os , codecs , base64 , tempfile
3
+ import os , codecs , base64 , tempfile , urllib
4
4
5
5
from matplotlib import verbose , __version__ , rcParams
6
6
from matplotlib .backend_bases import RendererBase , GraphicsContextBase ,\
7
7
FigureManagerBase , FigureCanvasBase
8
8
from matplotlib .colors import rgb2hex
9
9
from 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
12
12
from matplotlib .mathtext import math_parse_s_ft2font_svg
13
13
14
14
backend_version = __version__
@@ -24,6 +24,8 @@ def new_figure_manager(num, *args, **kwargs):
24
24
_fontd = {}
25
25
_capstyle_d = {'projecting' : 'square' , 'butt' : 'butt' , 'round' : 'round' ,}
26
26
class RendererSVG (RendererBase ):
27
+ FONT_SCALE = 1200.0
28
+
27
29
def __init__ (self , width , height , svgwriter , basename = None ):
28
30
self .width = width
29
31
self .height = height
@@ -35,6 +37,7 @@ def __init__(self, width, height, svgwriter, basename=None):
35
37
self .basename = basename
36
38
self ._imaged = {}
37
39
self ._clipd = {}
40
+ self ._char_defs = {}
38
41
svgwriter .write (svgProlog % (width ,height ,width ,height ))
39
42
40
43
def _draw_svg_element (self , element , details , gc , rgbFace ):
@@ -237,68 +240,159 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath):
237
240
font = self ._get_font (prop )
238
241
239
242
thetext = '%s' % s
240
- fontfamily = font .family_name
241
- fontstyle = font .style_name
243
+ fontfamily = font .family_name
244
+ fontstyle = font .style_name
242
245
fontsize = prop .get_size_in_points ()
243
246
color = rgb2hex (gc .get_rgb ())
244
247
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 = ''
249
289
250
- svg = """\
290
+ svg = """\
251
291
<text style="%(style)s" x="%(x)f" y="%(y)f" %(transform)s>%(thetext)s</text>
252
292
""" % locals ()
253
293
self ._svgwriter .write (svg )
254
294
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
+
255
334
def _draw_mathtext (self , gc , x , y , s , prop , angle ):
256
335
"""
257
336
Draw math text using matplotlib.mathtext
258
337
"""
259
338
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 )
262
340
svg_glyphs = svg_elements .svg_glyphs
263
341 svg_lines = svg_elements .svg_lines
264
342
color = rgb2hex (gc .get_rgb ())
265
343
266
344
self .open_group ("mathtext" )
267
345
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 '
269
354
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 )
274
358
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 )
276
363
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
282
376
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
287
381
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
291
385
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
295
389
296
- svg += '>%s</tspan>\n ' % thetext
390
+ svg += '>%s</tspan>\n ' % thetext
297
391
298
- curr_x = new_x + xadvance
299
- curr_y = new_y
392
+ curr_x = new_x + xadvance
393
+ curr_y = new_y
300
394
301
- svg += '</text>\n '
395
+ svg += '</text>\n '
302
396
303
397
self ._svgwriter .write (svg )
304
398
rgbFace = gc .get_rgb ()
@@ -310,6 +404,11 @@ def _draw_mathtext(self, gc, x, y, s, prop, angle):
310
404
self .close_group ("mathtext" )
311
405
312
406
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 ' )
313
412
self ._svgwriter .write ('</svg>\n ' )
314
413
315
414
def flipy (self ):
0 commit comments