Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f3a8cb9

Browse files
committed
First pass at SVG support
svn path=/branches/transforms/; revision=3983
1 parent c511c7f commit f3a8cb9

1 file changed

Lines changed: 85 additions & 58 deletions

File tree

lib/matplotlib/backends/backend_svg.py

Lines changed: 85 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from matplotlib.font_manager import findfont, FontProperties
1111
from matplotlib.ft2font import FT2Font, KERNING_DEFAULT, LOAD_NO_HINTING
1212
from matplotlib.mathtext import MathTextParser
13+
from matplotlib.path import Path
14+
from matplotlib.transforms import Affine2D
1315

1416
from xml.sax.saxutils import escape as escape_xml_text
1517

@@ -39,6 +41,7 @@ def __init__(self, width, height, svgwriter, basename=None):
3941
self._imaged = {}
4042
self._clipd = {}
4143
self._char_defs = {}
44+
self._markers = {}
4245
self.mathtext_parser = MathTextParser('SVG')
4346
self.fontd = {}
4447
svgwriter.write(svgProlog%(width,height,width,height))
@@ -74,7 +77,7 @@ def _get_style(self, gc, rgbFace):
7477
if rgbFace is None:
7578
fill = 'none'
7679
else:
77-
fill = rgb2hex(rgbFace)
80+
fill = rgb2hex(rgbFace[:3])
7881

7982
offset, seq = gc.get_dashes()
8083
if seq is None:
@@ -103,28 +106,38 @@ def _get_style(self, gc, rgbFace):
103106

104107
def _get_gc_clip_svg(self, gc):
105108
cliprect = gc.get_clip_rectangle()
106-
if cliprect is None:
107-
return '', None
108-
else:
109-
# See if we've already seen this clip rectangle
110-
key = hash(cliprect)
111-
if self._clipd.get(key) is None: # If not, store a new clipPath
112-
self._clipd[key] = cliprect
113-
x, y, w, h = cliprect
109+
clippath, clippath_trans = gc.get_clip_path()
110+
if clippath is not None:
111+
pathkey = (hash(clippath), hash(clippath_trans))
112+
path = ''
113+
if self._clipd.get(pathkey) is None:
114+
self._clipd[pathkey] = clippath
115+
path_data = self._convert_path(clippath, clippath_trans)
116+
path = """\
117+
<defs>
118+
<clipPath id="%(pathkey)s">
119+
<path d="%(path_data)s"/>
120+
</clipPath>
121+
</defs>
122+
""" % locals()
123+
return path, pathkey
124+
elif cliprect is not None:
125+
rectkey = hash(cliprect)
126+
box = ''
127+
if self._clipd.get(rectkey) is None:
128+
self._clipd[rectkey] = cliprect
129+
x, y, w, h = cliprect.bounds
114130
y = self.height-(y+h)
115-
style = "stroke: gray; fill: none;"
116131
box = """\
117132
<defs>
118-
<clipPath id="%(key)s">
119-
<rect x="%(x)s" y="%(y)s" width="%(w)s" height="%(h)s"
120-
style="%(style)s"/>
133+
<clipPath id="%(rectkey)s">
134+
<rect x="%(x)s" y="%(y)s" width="%(w)s" height="%(h)s"/>
121135
</clipPath>
122136
</defs>
123137
""" % locals()
124-
return box, key
125-
else:
126-
# return id of previously defined clipPath
127-
return '', key
138+
return box, rectkey
139+
140+
return '', None
128141

129142
def open_group(self, s):
130143
self._groupd[s] = self._groupd.get(s,0) + 1
@@ -133,20 +146,66 @@ def open_group(self, s):
133146
def close_group(self, s):
134147
self._svgwriter.write('</g>\n')
135148

136-
def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2, rotation):
137-
"""
138-
Ignores angles for now
139-
"""
140-
details = 'cx="%s" cy="%s" rx="%s" ry="%s" transform="rotate(%1.1f %s %s)"' % \
141-
(x, self.height-y, width/2.0, height/2.0, -rotation, x, self.height-y)
142-
self._draw_svg_element('ellipse', details, gc, rgbFace)
143-
144149
def option_image_nocomposite(self):
145150
"""
146151
if svg.image_noscale is True, compositing multiple images into one is prohibited
147152
"""
148153
return rcParams['svg.image_noscale']
149154

155+
_path_commands = {
156+
Path.MOVETO: 'M%s %s',
157+
Path.LINETO: 'L%s %s',
158+
Path.CURVE3: 'Q%s %s %s %s',
159+
Path.CURVE4: 'C%s %s %s %s %s %s'
160+
}
161+
162+
def _make_flip_transform(self, transform):
163+
return (transform +
164+
Affine2D()
165+
.scale(1.0, -1.0)
166+
.translate(0.0, self.height))
167+
168+
def _convert_path(self, path, transform):
169+
tpath = transform.transform_path(path)
170+
171+
path_data = []
172+
appender = path_data.append
173+
path_commands = self._path_commands
174+
currpos = 0
175+
for points, code in tpath.iter_segments():
176+
if code == Path.CLOSEPOLY:
177+
segment = 'z'
178+
else:
179+
segment = path_commands[code] % tuple(points)
180+
181+
if currpos + len(segment) > 75:
182+
appender("\n")
183+
currpos = 0
184+
appender(segment)
185+
currpos += len(segment)
186+
return ''.join(path_data)
187+
188+
def draw_path(self, gc, path, transform, rgbFace=None):
189+
trans_and_flip = self._make_flip_transform(transform)
190+
path_data = self._convert_path(path, trans_and_flip)
191+
self._draw_svg_element('path', 'd="%s"' % path_data, gc, rgbFace)
192+
193+
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
194+
write = self._svgwriter.write
195+
196+
key = self._convert_path(marker_path, marker_trans + Affine2D().scale(0, -1.0))
197+
name = self._markers.get(key)
198+
if name is None:
199+
name = 'm_%x' % len(self._markers)
200+
write('<defs><path id="%s" d="%s"/></defs>\n' % (name, key))
201+
self._markers[key] = name
202+
203+
trans_and_flip = self._make_flip_transform(trans)
204+
tpath = trans_and_flip.transform_path(path)
205+
for x, y in tpath.vertices:
206+
details = 'xlink:href="#%s" transform="translate(%f, %f)"' % (name, x, y)
207+
self._draw_svg_element('use', details, gc, rgbFace)
208+
150209
def draw_image(self, x, y, im, bbox):
151210
trans = [1,0,0,1,0,0]
152211
transstr = ''
@@ -204,38 +263,6 @@ def draw_image(self, x, y, im, bbox):
204263
'xlink:href="%s" %s/>\n'%(x/trans[0], (self.height-y)/trans[3]-h, w, h, hrefstr, transstr)
205264
)
206265

207-
def draw_line(self, gc, x1, y1, x2, y2):
208-
details = 'd="M%s,%sL%s,%s"' % (x1, self.height-y1,
209-
x2, self.height-y2)
210-
self._draw_svg_element('path', details, gc, None)
211-
212-
def draw_lines(self, gc, x, y, transform=None):
213-
if len(x)==0: return
214-
if len(x)!=len(y):
215-
raise ValueError('x and y must be the same length')
216-
217-
y = self.height - y
218-
details = ['d="M%s,%s' % (x[0], y[0])]
219-
xys = zip(x[1:], y[1:])
220-
details.extend(['L%s,%s' % tup for tup in xys])
221-
details.append('"')
222-
details = ''.join(details)
223-
self._draw_svg_element('path', details, gc, None)
224-
225-
def draw_point(self, gc, x, y):
226-
# result seems to have a hole in it...
227-
self.draw_arc(gc, gc.get_rgb(), x, y, 1, 0, 0, 0, 0)
228-
229-
def draw_polygon(self, gc, rgbFace, points):
230-
details = 'points = "%s"' % ' '.join(['%s,%s'%(x,self.height-y)
231-
for x, y in points])
232-
self._draw_svg_element('polygon', details, gc, rgbFace)
233-
234-
def draw_rectangle(self, gc, rgbFace, x, y, width, height):
235-
details = 'width="%s" height="%s" x="%s" y="%s"' % (width, height, x,
236-
self.height-y-height)
237-
self._draw_svg_element('rect', details, gc, rgbFace)
238-
239266
def draw_text(self, gc, x, y, s, prop, angle, ismath):
240267
if ismath:
241268
self._draw_mathtext(gc, x, y, s, prop, angle)
@@ -467,7 +494,7 @@ def print_svgz(self, filename, *args, **kwargs):
467494
return self._print_svg(filename, svgwriter)
468495

469496
def _print_svg(self, filename, svgwriter):
470-
self.figure.dpi.set(72)
497+
self.figure.set_dpi(72.0)
471498
width, height = self.figure.get_size_inches()
472499
w, h = width*72, height*72
473500

0 commit comments

Comments
 (0)