2 ##Copyright (c) 2002, Fedor Baart & Hans de Wit (Stichting Farmaceutische Kengetallen)
5 ##Redistribution and use in source and binary forms, with or without modification,
6 ##are permitted provided that the following conditions are met:
8 ##Redistributions of source code must retain the above copyright notice, this
9 ##list of conditions and the following disclaimer.
11 ##Redistributions in binary form must reproduce the above copyright notice,
12 ##this list of conditions and the following disclaimer in the documentation and/or
13 ##other materials provided with the distribution.
15 ##Neither the name of the Stichting Farmaceutische Kengetallen nor the names of
16 ##its contributors may be used to endorse or promote products derived from this
17 ##software without specific prior written permission.
19 ##THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 ##AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 ##IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 ##DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
23 ##FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 ##DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 ##SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 ##CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 ##OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 ##OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 ##Thanks to Gerald Rosennfellner for his help and useful comments.
32 __doc__="""Use SVGdraw to generate your SVGdrawings.
34 SVGdraw uses an object model drawing and a method toXML to create SVG graphics
35 by using easy to use classes and methods usualy you start by creating a drawing eg
38 #then you create a SVG root element
40 #then you add some elements eg a circle and add it to the svg root element
42 #you can supply attributes by using named arguments.
43 c=circle(fill='red',stroke='blue')
44 #or by updating the attributes attribute:
45 c.attributes['stroke-width']=1
47 #then you add the svg root element to the drawing
49 #and finaly you xmlify the drawing
53 this results in the svg source of the drawing, which consists of a circle
54 on a white background. Its as easy as that;)
55 This module was created using the SVG specification of www.w3c.org and the
56 O'Reilly (www.oreilly.com) python books as information sources. A svg viewer
57 is available from www.adobe.com"""
61 # there are two possibilities to generate svg:
62 # via a dom implementation and directly using <element>text</element> strings
63 # the latter is way faster (and shorter in coding)
64 # the former is only used in debugging svg programs
65 # maybe it will be removed alltogether after a while
66 # with the following variable you indicate whether to use the dom implementation
67 # Note that PyXML is required for using the dom implementation.
68 # It is also possible to use the standard minidom. But I didn't try that one.
69 # Anyway the text based approach is about 60 times faster than using the full dom implementation.
70 use_dom_implementation=0
74 if use_dom_implementation<>0:
76 from xml.dom import implementation
77 from xml.dom.ext import PrettyPrint
79 raise exceptions.ImportError, "PyXML is required for using the dom implementation"
80 #The implementation is used for the creating the XML document.
81 #The prettyprint module is used for converting the xml document object to a xml file
84 assert sys.version_info[0]>=2
85 if sys.version_info[1]<2:
90 sys.setrecursionlimit=50
91 #The recursion limit is set conservative so mistakes like s=svg() s.addElement(s)
92 #won't eat up too much processor time.
94 #the following code is pasted form xml.sax.saxutils
95 #it makes it possible to run the code without the xml sax package installed
96 #To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts
97 def _escape(data, entities={}):
98 """Escape &, <, and > in a string of data.
100 You can escape other strings of data by passing a dictionary as
101 the optional entities parameter. The keys and values must all be
102 strings; each key will be replaced with its corresponding value.
104 data = data.replace("&", "&")
105 data = data.replace("<", "<")
106 data = data.replace(">", ">")
107 for chars, entity in entities.items():
108 data = data.replace(chars, entity)
111 def _quoteattr(data, entities={}):
112 """Escape and quote an attribute value.
114 Escape &, <, and > in a string of data, then quote it for use as
115 an attribute value. The \" character will be escaped as well, if
118 You can escape other strings of data by passing a dictionary as
119 the optional entities parameter. The keys and values must all be
120 strings; each key will be replaced with its corresponding value.
122 data = _escape(data, entities)
125 data = '"%s"' % data.replace('"', """)
135 """formats a list of xy pairs"""
137 for e in a: #this could be done more elegant
142 """formats a tuple"""
149 """formats a list of numbers"""
153 """class used to create a pathdata object which can be used for a path.
154 although most methods are pretty straightforward it might be useful to look at the SVG specification."""
155 #I didn't test the methods below.
156 def __init__(self,x=None,y=None):
158 if x is not None and y is not None:
159 self.path.append('M '+str(x)+' '+str(y))
162 self.path.append('z')
164 """move to absolute"""
165 self.path.append('M '+str(x)+' '+str(y))
166 def relmove(self,x,y):
167 """move to relative"""
168 self.path.append('m '+str(x)+' '+str(y))
170 """line to absolute"""
171 self.path.append('L '+str(x)+' '+str(y))
172 def relline(self,x,y):
173 """line to relative"""
174 self.path.append('l '+str(x)+' '+str(y))
176 """horizontal line to absolute"""
177 self.path.append('H'+str(x))
178 def relhline(self,x):
179 """horizontal line to relative"""
180 self.path.append('h'+str(x))
182 """verical line to absolute"""
183 self.path.append('V'+str(y))
184 def relvline(self,y):
185 """vertical line to relative"""
186 self.path.append('v'+str(y))
187 def bezier(self,x1,y1,x2,y2,x,y):
188 """bezier with xy1 and xy2 to xy absolut"""
189 self.path.append('C'+str(x1)+','+str(y1)+' '+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
190 def relbezier(self,x1,y1,x2,y2,x,y):
191 """bezier with xy1 and xy2 to xy relative"""
192 self.path.append('c'+str(x1)+','+str(y1)+' '+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
193 def smbezier(self,x2,y2,x,y):
194 """smooth bezier with xy2 to xy absolut"""
195 self.path.append('S'+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
196 def relsmbezier(self,x2,y2,x,y):
197 """smooth bezier with xy2 to xy relative"""
198 self.path.append('s'+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
199 def qbezier(self,x1,y1,x,y):
200 """quadratic bezier with xy1 to xy absolut"""
201 self.path.append('Q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y))
202 def relqbezier(self,x1,y1,x,y):
203 """quadratic bezier with xy1 to xy relative"""
204 self.path.append('q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y))
205 def smqbezier(self,x,y):
206 """smooth quadratic bezier to xy absolut"""
207 self.path.append('T'+str(x)+','+str(y))
208 def relsmqbezier(self,x,y):
209 """smooth quadratic bezier to xy relative"""
210 self.path.append('t'+str(x)+','+str(y))
211 def ellarc(self,rx,ry,xrot,laf,sf,x,y):
212 """elliptival arc with rx and ry rotating with xrot using large-arc-flag and sweep-flag to xy absolut"""
213 self.path.append('A'+str(rx)+','+str(ry)+' '+str(xrot)+' '+str(laf)+' '+str(sf)+' '+str(x)+' '+str(y))
214 def relellarc(self,rx,ry,xrot,laf,sf,x,y):
215 """elliptival arc with rx and ry rotating with xrot using large-arc-flag and sweep-flag to xy relative"""
216 self.path.append('a'+str(rx)+','+str(ry)+' '+str(xrot)+' '+str(laf)+' '+str(sf)+' '+str(x)+' '+str(y))
218 return ' '.join(self.path)
224 """SVGelement(type,attributes,elements,text,namespace,**args)
225 Creates a arbitrary svg element and is intended to be subclassed not used on its own.
226 This element is the base of every svg element it defines a class which resembles
227 a xml-element. The main advantage of this kind of implementation is that you don't
228 have to create a toXML method for every different graph object. Every element
229 consists of a type, attribute, optional subelements, optional text and an optional
230 namespace. Note the elements==None, if elements = None:self.elements=[] construction.
231 This is done because if you default to elements=[] every object has a reference
232 to the same empty list."""
233 def __init__(self,type='',attributes=None,elements=None,text='',namespace='',cdata=None,**args):
238 self.attributes=attributes
242 self.elements=elements
244 self.namespace=namespace
246 for arg in args.keys():
247 self.attributes[arg]=args[arg]
248 def addElement(self,SVGelement):
249 """adds an element to a SVGelement
251 SVGelement.addElement(SVGelement)
253 self.elements.append(SVGelement)
255 def toXml(self,level,f):
257 f.write('<'+self.type)
258 for attkey in self.attributes.keys():
259 f.write(' '+_escape(str(attkey))+'='+_quoteattr(str(self.attributes[attkey])))
261 f.write(' xmlns="'+ _escape(str(self.namespace))+'" ')
262 if self.elements or self.text or self.cdata:
266 for element in self.elements:
267 element.toXml(level+1,f)
269 f.write('\n'+'\t'*(level+1)+'<![CDATA[')
270 for line in self.cdata.splitlines():
271 f.write('\n'+'\t'*(level+2)+line)
272 f.write('\n'+'\t'*(level+1)+']]>\n')
274 if type(self.text)==type(''): #If the text is only text
275 f.write(_escape(str(self.text)))
276 else: #If the text is a spannedtext class
277 f.write(str(self.text))
279 f.write('\t'*level+'</'+self.type+'>\n')
281 f.write('</'+self.type+'>\n')
283 f.write('\t'*level+'</'+self.type+'>\n')
287 class tspan(SVGelement):
288 """ts=tspan(text='',**args)
290 a tspan element can be used for applying formatting to a textsection
292 ts=tspan('this text is bold')
293 ts.attributes['font-weight']='bold'
298 def __init__(self,text=None,**args):
299 SVGelement.__init__(self,'tspan',**args)
304 for key,value in self.attributes.items():
305 s+= ' %s="%s"' % (key,value)
311 class tref(SVGelement):
312 """tr=tref(link='',**args)
314 a tref element can be used for referencing text by a link to its id.
316 tr=tref('#linktotext')
321 def __init__(self,link,**args):
322 SVGelement.__init__(self,'tref',{'xlink:href':link},**args)
326 for key,value in self.attributes.items():
327 s+= ' %s="%s"' % (key,value)
332 """st=spannedtext(textlist=[])
334 a spannedtext can be used for text which consists of text, tspan's and tref's
335 You can use it to add to a text element or path element. Don't add it directly
336 to a svg or a group element.
339 ts=tspan('this text is bold')
340 ts.attributes['font-weight']='bold'
341 tr=tref('#linktotext')
342 tr.attributes['fill']='red'
346 st.addtext('This text is not bold')
349 def __init__(self,textlist=None):
353 self.textlist=textlist
354 def addtext(self,text=''):
355 self.textlist.append(text)
356 def addtspan(self,tspan):
357 self.textlist.append(tspan)
358 def addtref(self,tref):
359 self.textlist.append(tref)
362 for element in self.textlist:
366 class rect(SVGelement):
367 """r=rect(width,height,x,y,fill,stroke,stroke_width,**args)
369 a rectangle is defined by a width and height and a xy pair
371 def __init__(self,x=None,y=None,width=None,height=None,fill=None,stroke=None,stroke_width=None,**args):
372 if width==None or height==None:
374 raise ValueError, 'height is required'
376 raise ValueError, 'width is required'
378 raise ValueError, 'both height and width are required'
379 SVGelement.__init__(self,'rect',{'width':width,'height':height},**args)
381 self.attributes['x']=x
383 self.attributes['y']=y
385 self.attributes['fill']=fill
387 self.attributes['stroke']=stroke
388 if stroke_width<>None:
389 self.attributes['stroke-width']=stroke_width
391 class ellipse(SVGelement):
392 """e=ellipse(rx,ry,x,y,fill,stroke,stroke_width,**args)
394 an ellipse is defined as a center and a x and y radius.
396 def __init__(self,cx=None,cy=None,rx=None,ry=None,fill=None,stroke=None,stroke_width=None,**args):
397 if rx==None or ry== None:
399 raise ValueError, 'rx is required'
401 raise ValueError, 'ry is required'
403 raise ValueError, 'both rx and ry are required'
404 SVGelement.__init__(self,'ellipse',{'rx':rx,'ry':ry},**args)
406 self.attributes['cx']=cx
408 self.attributes['cy']=cy
410 self.attributes['fill']=fill
412 self.attributes['stroke']=stroke
413 if stroke_width<>None:
414 self.attributes['stroke-width']=stroke_width
417 class circle(SVGelement):
418 """c=circle(x,y,radius,fill,stroke,stroke_width,**args)
420 The circle creates an element using a x, y and radius values eg
422 def __init__(self,cx=None,cy=None,r=None,fill=None,stroke=None,stroke_width=None,**args):
424 raise ValueError, 'r is required'
425 SVGelement.__init__(self,'circle',{'r':r},**args)
427 self.attributes['cx']=cx
429 self.attributes['cy']=cy
431 self.attributes['fill']=fill
433 self.attributes['stroke']=stroke
434 if stroke_width<>None:
435 self.attributes['stroke-width']=stroke_width
438 """p=point(x,y,color)
440 A point is defined as a circle with a size 1 radius. It may be more efficient to use a
441 very small rectangle if you use many points because a circle is difficult to render.
443 def __init__(self,x,y,fill='black',**args):
444 circle.__init__(self,x,y,1,fill,**args)
446 class line(SVGelement):
447 """l=line(x1,y1,x2,y2,stroke,stroke_width,**args)
449 A line is defined by a begin x,y pair and an end x,y pair
451 def __init__(self,x1=None,y1=None,x2=None,y2=None,stroke=None,stroke_width=None,**args):
452 SVGelement.__init__(self,'line',**args)
454 self.attributes['x1']=x1
456 self.attributes['y1']=y1
458 self.attributes['x2']=x2
460 self.attributes['y2']=y2
461 if stroke_width<>None:
462 self.attributes['stroke-width']=stroke_width
464 self.attributes['stroke']=stroke
466 class polyline(SVGelement):
467 """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
469 a polyline is defined by a list of xy pairs
471 def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args):
472 SVGelement.__init__(self,'polyline',{'points':_xypointlist(points)},**args)
474 self.attributes['fill']=fill
475 if stroke_width<>None:
476 self.attributes['stroke-width']=stroke_width
478 self.attributes['stroke']=stroke
480 class polygon(SVGelement):
481 """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
483 a polygon is defined by a list of xy pairs
485 def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args):
486 SVGelement.__init__(self,'polygon',{'points':_xypointlist(points)},**args)
488 self.attributes['fill']=fill
489 if stroke_width<>None:
490 self.attributes['stroke-width']=stroke_width
492 self.attributes['stroke']=stroke
494 class path(SVGelement):
495 """p=path(path,fill,stroke,stroke_width,**args)
497 a path is defined by a path object and optional width, stroke and fillcolor
499 def __init__(self,pathdata,fill=None,stroke=None,stroke_width=None,id=None,**args):
500 SVGelement.__init__(self,'path',{'d':str(pathdata)},**args)
502 self.attributes['stroke']=stroke
504 self.attributes['fill']=fill
505 if stroke_width<>None:
506 self.attributes['stroke-width']=stroke_width
508 self.attributes['id']=id
511 class text(SVGelement):
512 """t=text(x,y,text,font_size,font_family,**args)
514 a text element can bge used for displaying text on the screen
516 def __init__(self,x=None,y=None,text=None,font_size=None,font_family=None,text_anchor=None,**args):
517 SVGelement.__init__(self,'text',**args)
519 self.attributes['x']=x
521 self.attributes['y']=y
523 self.attributes['font-size']=font_size
524 if font_family<>None:
525 self.attributes['font-family']=font_family
528 if text_anchor<>None:
529 self.attributes['text-anchor']=text_anchor
532 class textpath(SVGelement):
533 """tp=textpath(text,link,**args)
535 a textpath places a text on a path which is referenced by a link.
537 def __init__(self,link,text=None,**args):
538 SVGelement.__init__(self,'textPath',{'xlink:href':link},**args)
542 class pattern(SVGelement):
543 """p=pattern(x,y,width,height,patternUnits,**args)
545 A pattern is used to fill or stroke an object using a pre-defined
546 graphic object which can be replicated ("tiled") at fixed intervals
547 in x and y to cover the areas to be painted.
549 def __init__(self,x=None,y=None,width=None,height=None,patternUnits=None,**args):
550 SVGelement.__init__(self,'pattern',**args)
552 self.attributes['x']=x
554 self.attributes['y']=y
556 self.attributes['width']=width
558 self.attributes['height']=height
559 if patternUnits<>None:
560 self.attributes['patternUnits']=patternUnits
562 class title(SVGelement):
563 """t=title(text,**args)
565 a title is a text element. The text is displayed in the title bar
566 add at least one to the root svg element
568 def __init__(self,text=None,**args):
569 SVGelement.__init__(self,'title',**args)
573 class description(SVGelement):
574 """d=description(text,**args)
576 a description can be added to any element and is used for a tooltip
577 Add this element before adding other elements.
579 def __init__(self,text=None,**args):
580 SVGelement.__init__(self,'desc',**args)
584 class lineargradient(SVGelement):
585 """lg=lineargradient(x1,y1,x2,y2,id,**args)
587 defines a lineargradient using two xy pairs.
588 stop elements van be added to define the gradient colors.
590 def __init__(self,x1=None,y1=None,x2=None,y2=None,id=None,**args):
591 SVGelement.__init__(self,'linearGradient',**args)
593 self.attributes['x1']=x1
595 self.attributes['y1']=y1
597 self.attributes['x2']=x2
599 self.attributes['y2']=y2
601 self.attributes['id']=id
603 class radialgradient(SVGelement):
604 """rg=radialgradient(cx,cy,r,fx,fy,id,**args)
606 defines a radial gradient using a outer circle which are defined by a cx,cy and r and by using a focalpoint.
607 stop elements van be added to define the gradient colors.
609 def __init__(self,cx=None,cy=None,r=None,fx=None,fy=None,id=None,**args):
610 SVGelement.__init__(self,'radialGradient',**args)
612 self.attributes['cx']=cx
614 self.attributes['cy']=cy
616 self.attributes['r']=r
618 self.attributes['fx']=fx
620 self.attributes['fy']=fy
622 self.attributes['id']=id
624 class stop(SVGelement):
625 """st=stop(offset,stop_color,**args)
627 Puts a stop color at the specified radius
629 def __init__(self,offset,stop_color=None,**args):
630 SVGelement.__init__(self,'stop',{'offset':offset},**args)
632 self.attributes['stop-color']=stop_color
634 class style(SVGelement):
635 """st=style(type,cdata=None,**args)
637 Add a CDATA element to this element for defing in line stylesheets etc..
639 def __init__(self,type,cdata=None,**args):
640 SVGelement.__init__(self,'style',{'type':type},cdata=cdata, **args)
643 class image(SVGelement):
644 """im=image(url,width,height,x,y,**args)
646 adds an image to the drawing. Supported formats are .png, .jpg and .svg.
648 def __init__(self,url,x=None,y=None,width=None,height=None,**args):
649 if width==None or height==None:
651 raise ValueError, 'height is required'
653 raise ValueError, 'width is required'
655 raise ValueError, 'both height and width are required'
656 SVGelement.__init__(self,'image',{'xlink:href':url,'width':width,'height':height},**args)
658 self.attributes['x']=x
660 self.attributes['y']=y
662 class cursor(SVGelement):
663 """c=cursor(url,**args)
665 defines a custom cursor for a element or a drawing
667 def __init__(self,url,**args):
668 SVGelement.__init__(self,'cursor',{'xlink:href':url},**args)
671 class marker(SVGelement):
672 """m=marker(id,viewbox,refX,refY,markerWidth,markerHeight,**args)
674 defines a marker which can be used as an endpoint for a line or other pathtypes
675 add an element to it which should be used as a marker.
677 def __init__(self,id=None,viewBox=None,refx=None,refy=None,markerWidth=None,markerHeight=None,**args):
678 SVGelement.__init__(self,'marker',**args)
680 self.attributes['id']=id
682 self.attributes['viewBox']=_viewboxlist(viewBox)
684 self.attributes['refX']=refx
686 self.attributes['refY']=refy
687 if markerWidth<>None:
688 self.attributes['markerWidth']=markerWidth
689 if markerHeight<>None:
690 self.attributes['markerHeight']=markerHeight
692 class group(SVGelement):
693 """g=group(id,**args)
695 a group is defined by an id and is used to contain elements
696 g.addElement(SVGelement)
698 def __init__(self,id=None,**args):
699 SVGelement.__init__(self,'g',**args)
701 self.attributes['id']=id
703 class symbol(SVGelement):
704 """sy=symbol(id,viewbox,**args)
706 defines a symbol which can be used on different places in your graph using
707 the use element. A symbol is not rendered but you can use 'use' elements to
708 display it by referencing its id.
709 sy.addElement(SVGelement)
712 def __init__(self,id=None,viewBox=None,**args):
713 SVGelement.__init__(self,'symbol',**args)
715 self.attributes['id']=id
717 self.attributes['viewBox']=_viewboxlist(viewBox)
719 class defs(SVGelement):
722 container for defining elements
724 def __init__(self,**args):
725 SVGelement.__init__(self,'defs',**args)
727 class switch(SVGelement):
730 Elements added to a switch element which are "switched" by the attributes
731 requiredFeatures, requiredExtensions and systemLanguage.
732 Refer to the SVG specification for details.
734 def __init__(self,**args):
735 SVGelement.__init__(self,'switch',**args)
738 class use(SVGelement):
739 """u=use(link,x,y,width,height,**args)
741 references a symbol by linking to its id and its position, height and width
743 def __init__(self,link,x=None,y=None,width=None,height=None,**args):
744 SVGelement.__init__(self,'use',{'xlink:href':link},**args)
746 self.attributes['x']=x
748 self.attributes['y']=y
751 self.attributes['width']=width
753 self.attributes['height']=height
756 class link(SVGelement):
757 """a=link(url,**args)
759 a link is defined by a hyperlink. add elements which have to be linked
760 a.addElement(SVGelement)
762 def __init__(self,link='',**args):
763 SVGelement.__init__(self,'a',{'xlink:href':link},**args)
765 class view(SVGelement):
768 a view can be used to create a view with different attributes"""
769 def __init__(self,id=None,**args):
770 SVGelement.__init__(self,'view',**args)
772 self.attributes['id']=id
774 class script(SVGelement):
775 """sc=script(type,type,cdata,**args)
777 adds a script element which contains CDATA to the SVG drawing
780 def __init__(self,type,cdata=None,**args):
781 SVGelement.__init__(self,'script',{'type':type},cdata=cdata,**args)
783 class animate(SVGelement):
784 """an=animate(attribute,from,to,during,**args)
786 animates an attribute.
788 def __init__(self,attribute,fr=None,to=None,dur=None,**args):
789 SVGelement.__init__(self,'animate',{'attributeName':attribute},**args)
791 self.attributes['from']=fr
793 self.attributes['to']=to
795 self.attributes['dur']=dur
797 class animateMotion(SVGelement):
798 """an=animateMotion(pathdata,dur,**args)
800 animates a SVGelement over the given path in dur seconds
802 def __init__(self,pathdata,dur,**args):
803 SVGelement.__init__(self,'animateMotion',**args)
805 self.attributes['path']=str(pathdata)
807 self.attributes['dur']=dur
809 class animateTransform(SVGelement):
810 """antr=animateTransform(type,from,to,dur,**args)
812 transform an element from and to a value.
814 def __init__(self,type=None,fr=None,to=None,dur=None,**args):
815 SVGelement.__init__(self,'animateTransform',{'attributeName':'transform'},**args)
816 #As far as I know the attributeName is always transform
818 self.attributes['type']=type
820 self.attributes['from']=fr
822 self.attributes['to']=to
824 self.attributes['dur']=dur
825 class animateColor(SVGelement):
826 """ac=animateColor(attribute,type,from,to,dur,**args)
828 Animates the color of a element
830 def __init__(self,attribute,type=None,fr=None,to=None,dur=None,**args):
831 SVGelement.__init__(self,'animateColor',{'attributeName':attribute},**args)
833 self.attributes['type']=type
835 self.attributes['from']=fr
837 self.attributes['to']=to
839 self.attributes['dur']=dur
840 class set(SVGelement):
841 """st=set(attribute,to,during,**args)
843 sets an attribute to a value for a
845 def __init__(self,attribute,to=None,dur=None,**args):
846 SVGelement.__init__(self,'set',{'attributeName':attribute},**args)
848 self.attributes['to']=to
850 self.attributes['dur']=dur
854 class svg(SVGelement):
855 """s=svg(viewbox,width,height,**args)
857 a svg or element is the root of a drawing add all elements to a svg element.
858 You can have different svg elements in one svg file
859 s.addElement(SVGelement)
863 s=svg((0,0,100,100),'100%','100%')
869 def __init__(self,viewBox=None, width=None, height=None,**args):
870 SVGelement.__init__(self,'svg',**args)
872 self.attributes['viewBox']=_viewboxlist(viewBox)
874 self.attributes['width']=width
876 self.attributes['height']=height
877 self.namespace="http://www.w3.org/2000/svg"
882 this is the actual SVG document. It needs a svg element as a root.
883 Use the addSVG method to set the svg to the root. Use the toXml method to write the SVG
884 source to the screen or to a file
887 d.toXml(optionalfilename)
892 def setSVG(self,svg):
894 #Voeg een element toe aan de grafiek toe.
895 if use_dom_implementation==0:
896 def toXml(self, filename='',compress=False):
898 xml=cStringIO.StringIO()
899 xml.write("<?xml version='1.0' encoding='UTF-8'?>\n")
900 xml.write("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd \">\n")
901 self.svg.toXml(0,xml)
905 f=cStringIO.StringIO()
906 zf=gzip.GzipFile(fileobj=f,mode='wb')
907 zf.write(xml.getvalue())
912 return xml.getvalue()
914 if filename[-4:]=='svgz':
916 f=gzip.GzipFile(filename=filename,mode="wb", compresslevel=9)
917 f.write(xml.getvalue())
921 f.write(xml.getvalue())
925 def toXml(self,filename='',compress=False):
926 """drawing.toXml() ---->to the screen
927 drawing.toXml(filename)---->to the file
928 writes a svg drawing to the screen or to a file
929 compresses if filename ends with svgz or if compress is true
931 doctype = implementation.createDocumentType('svg',"-//W3C//DTD SVG 1.0//EN""",'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd ')
934 #root is defined global so it can be used by the appender. Its also possible to use it as an arugument but
935 #that is a bit messy.
936 root=implementation.createDocument(None,None,doctype)
937 #Create the xml document.
939 def appender(element,elementroot):
940 """This recursive function appends elements to an element and sets the attributes
941 and type. It stops when alle elements have been appended"""
942 if element.namespace:
943 e=root.createElementNS(element.namespace,element.type)
945 e=root.createElement(element.type)
947 textnode=root.createTextNode(element.text)
948 e.appendChild(textnode)
949 for attribute in element.attributes.keys(): #in element.attributes is supported from python 2.2
950 e.setAttribute(attribute,str(element.attributes[attribute]))
952 for el in element.elements:
954 elementroot.appendChild(e)
956 root=appender(self.svg,root)
959 xml=cStringIO.StringIO()
960 PrettyPrint(root,xml)
963 f=cStringIO.StringIO()
964 zf=gzip.GzipFile(fileobj=f,mode='wb')
965 zf.write(xml.getvalue())
970 return xml.getvalue()
973 if filename[-4:]=='svgz':
976 xml=cStringIO.StringIO()
977 PrettyPrint(root,xml)
978 f=gzip.GzipFile(filename=filename,mode='wb',compresslevel=9)
979 f.write(xml.getvalue())
986 print "Cannot write SVG file: " + filename
989 import xml.parsers.xmlproc.xmlval
991 raise exceptions.ImportError,'PyXml is required for validating SVG'
993 xv=xml.parsers.xmlproc.xmlval.XMLValidator()
997 raise "SVG is not well formed, see messages above"
999 print "SVG well formed"
1000 if __name__=='__main__':
1004 s=svg((0,0,100,100))
1005 r=rect(-100,-100,300,300,'cyan')
1008 t=title('SVGdraw Demo')
1010 g=group('animations')
1013 c=circle(0,0,1,'red')
1017 pd.relsmbezier(10,5,0,10)
1018 pd.relsmbezier(-10,5,0,10)
1019 an=animateMotion(pd,10)
1020 an.attributes['rotate']='auto-reverse'
1021 an.attributes['repeatCount']="indefinite"
1024 for i in range(20,120,20):
1025 u=use('#animations',i,0)
1027 for i in range(0,120,20):
1028 for j in range(5,105,10):
1029 c=circle(i,j,1,'red','black',.5)