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
72 if use_dom_implementation!=0:
74 from xml.dom import implementation
75 from xml.dom.ext import PrettyPrint
77 raise ImportError("PyXML is required for using the dom implementation")
78 #The implementation is used for the creating the XML document.
79 #The prettyprint module is used for converting the xml document object to a xml file
82 # assert sys.version_info[0]>=2
83 # if sys.version_info[1]<2:
88 sys.setrecursionlimit=50
89 #The recursion limit is set conservative so mistakes like s=svg() s.addElement(s)
90 #won't eat up too much processor time.
92 #the following code is pasted form xml.sax.saxutils
93 #it makes it possible to run the code without the xml sax package installed
94 #To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts
95 def _escape(data, entities={}):
96 """Escape &, <, and > in a string of data.
98 You can escape other strings of data by passing a dictionary as
99 the optional entities parameter. The keys and values must all be
100 strings; each key will be replaced with its corresponding value.
102 data = data.replace("&", "&")
103 data = data.replace("<", "<")
104 data = data.replace(">", ">")
105 for chars, entity in list(entities.items()):
106 data = data.replace(chars, entity)
109 def _quoteattr(data, entities={}):
110 """Escape and quote an attribute value.
112 Escape &, <, and > in a string of data, then quote it for use as
113 an attribute value. The \" character will be escaped as well, if
116 You can escape other strings of data by passing a dictionary as
117 the optional entities parameter. The keys and values must all be
118 strings; each key will be replaced with its corresponding value.
120 data = _escape(data, entities)
123 data = '"%s"' % data.replace('"', """)
133 """formats a list of xy pairs"""
135 for e in a: #this could be done more elegant
140 """formats a tuple"""
147 """formats a list of numbers"""
151 """class used to create a pathdata object which can be used for a path.
152 although most methods are pretty straightforward it might be useful to look at the SVG specification."""
153 #I didn't test the methods below.
154 def __init__(self,x=None,y=None):
156 if x is not None and y is not None:
157 self.path.append('M '+str(x)+' '+str(y))
160 self.path.append('z')
162 """move to absolute"""
163 self.path.append('M '+str(x)+' '+str(y))
164 def relmove(self,x,y):
165 """move to relative"""
166 self.path.append('m '+str(x)+' '+str(y))
168 """line to absolute"""
169 self.path.append('L '+str(x)+' '+str(y))
170 def relline(self,x,y):
171 """line to relative"""
172 self.path.append('l '+str(x)+' '+str(y))
174 """horizontal line to absolute"""
175 self.path.append('H'+str(x))
176 def relhline(self,x):
177 """horizontal line to relative"""
178 self.path.append('h'+str(x))
180 """verical line to absolute"""
181 self.path.append('V'+str(y))
182 def relvline(self,y):
183 """vertical line to relative"""
184 self.path.append('v'+str(y))
185 def bezier(self,x1,y1,x2,y2,x,y):
186 """bezier with xy1 and xy2 to xy absolut"""
187 self.path.append('C'+str(x1)+','+str(y1)+' '+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
188 def relbezier(self,x1,y1,x2,y2,x,y):
189 """bezier with xy1 and xy2 to xy relative"""
190 self.path.append('c'+str(x1)+','+str(y1)+' '+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
191 def smbezier(self,x2,y2,x,y):
192 """smooth bezier with xy2 to xy absolut"""
193 self.path.append('S'+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
194 def relsmbezier(self,x2,y2,x,y):
195 """smooth bezier with xy2 to xy relative"""
196 self.path.append('s'+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
197 def qbezier(self,x1,y1,x,y):
198 """quadratic bezier with xy1 to xy absolut"""
199 self.path.append('Q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y))
200 def relqbezier(self,x1,y1,x,y):
201 """quadratic bezier with xy1 to xy relative"""
202 self.path.append('q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y))
203 def smqbezier(self,x,y):
204 """smooth quadratic bezier to xy absolut"""
205 self.path.append('T'+str(x)+','+str(y))
206 def relsmqbezier(self,x,y):
207 """smooth quadratic bezier to xy relative"""
208 self.path.append('t'+str(x)+','+str(y))
209 def ellarc(self,rx,ry,xrot,laf,sf,x,y):
210 """elliptival arc with rx and ry rotating with xrot using large-arc-flag and sweep-flag to xy absolut"""
211 self.path.append('A'+str(rx)+','+str(ry)+' '+str(xrot)+' '+str(laf)+' '+str(sf)+' '+str(x)+' '+str(y))
212 def relellarc(self,rx,ry,xrot,laf,sf,x,y):
213 """elliptival arc with rx and ry rotating with xrot using large-arc-flag and sweep-flag to xy relative"""
214 self.path.append('a'+str(rx)+','+str(ry)+' '+str(xrot)+' '+str(laf)+' '+str(sf)+' '+str(x)+' '+str(y))
216 return ' '.join(self.path)
222 """SVGelement(type,attributes,elements,text,namespace,**args)
223 Creates a arbitrary svg element and is intended to be subclassed not used on its own.
224 This element is the base of every svg element it defines a class which resembles
225 a xml-element. The main advantage of this kind of implementation is that you don't
226 have to create a toXML method for every different graph object. Every element
227 consists of a type, attribute, optional subelements, optional text and an optional
228 namespace. Note the elements==None, if elements = None:self.elements=[] construction.
229 This is done because if you default to elements=[] every object has a reference
230 to the same empty list."""
231 def __init__(self,type='',attributes=None,elements=None,text='',namespace='',cdata=None,**args):
236 self.attributes=attributes
240 self.elements=elements
242 self.namespace=namespace
244 for arg in list(args.keys()):
245 self.attributes[arg]=args[arg]
246 def addElement(self,SVGelement):
247 """adds an element to a SVGelement
249 SVGelement.addElement(SVGelement)
251 self.elements.append(SVGelement)
253 def toXml(self,level,f):
255 f.write('<'+self.type)
256 for attkey in list(self.attributes.keys()):
257 f.write(' '+_escape(str(attkey))+'='+_quoteattr(str(self.attributes[attkey])))
259 f.write(' xmlns="'+ _escape(str(self.namespace))+'" ')
260 if self.elements or self.text or self.cdata:
264 for element in self.elements:
265 element.toXml(level+1,f)
267 f.write('\n'+'\t'*(level+1)+'<![CDATA[')
268 for line in self.cdata.splitlines():
269 f.write('\n'+'\t'*(level+2)+line)
270 f.write('\n'+'\t'*(level+1)+']]>\n')
272 if type(self.text)==type(''): #If the text is only text
273 f.write(_escape(str(self.text)))
274 else: #If the text is a spannedtext class
275 f.write(str(self.text))
277 f.write('\t'*level+'</'+self.type+'>\n')
279 f.write('</'+self.type+'>\n')
281 f.write('\t'*level+'</'+self.type+'>\n')
285 class tspan(SVGelement):
286 """ts=tspan(text='',**args)
288 a tspan element can be used for applying formatting to a textsection
290 ts=tspan('this text is bold')
291 ts.attributes['font-weight']='bold'
296 def __init__(self,text=None,**args):
297 SVGelement.__init__(self,'tspan',**args)
302 for key,value in list(self.attributes.items()):
303 s+= ' %s="%s"' % (key,value)
309 class tref(SVGelement):
310 """tr=tref(link='',**args)
312 a tref element can be used for referencing text by a link to its id.
314 tr=tref('#linktotext')
319 def __init__(self,link,**args):
320 SVGelement.__init__(self,'tref',{'xlink:href':link},**args)
324 for key,value in list(self.attributes.items()):
325 s+= ' %s="%s"' % (key,value)
330 """st=spannedtext(textlist=[])
332 a spannedtext can be used for text which consists of text, tspan's and tref's
333 You can use it to add to a text element or path element. Don't add it directly
334 to a svg or a group element.
337 ts=tspan('this text is bold')
338 ts.attributes['font-weight']='bold'
339 tr=tref('#linktotext')
340 tr.attributes['fill']='red'
344 st.addtext('This text is not bold')
347 def __init__(self,textlist=None):
351 self.textlist=textlist
352 def addtext(self,text=''):
353 self.textlist.append(text)
354 def addtspan(self,tspan):
355 self.textlist.append(tspan)
356 def addtref(self,tref):
357 self.textlist.append(tref)
360 for element in self.textlist:
364 class rect(SVGelement):
365 """r=rect(width,height,x,y,fill,stroke,stroke_width,**args)
367 a rectangle is defined by a width and height and a xy pair
369 def __init__(self,x=None,y=None,width=None,height=None,fill=None,stroke=None,stroke_width=None,**args):
370 if width==None or height==None:
372 raise ValueError('height is required')
374 raise ValueError('width is required')
376 raise ValueError('both height and width are required')
377 SVGelement.__init__(self,'rect',{'width':width,'height':height},**args)
379 self.attributes['x']=x
381 self.attributes['y']=y
383 self.attributes['fill']=fill
385 self.attributes['stroke']=stroke
386 if stroke_width!=None:
387 self.attributes['stroke-width']=stroke_width
389 class ellipse(SVGelement):
390 """e=ellipse(rx,ry,x,y,fill,stroke,stroke_width,**args)
392 an ellipse is defined as a center and a x and y radius.
394 def __init__(self,cx=None,cy=None,rx=None,ry=None,fill=None,stroke=None,stroke_width=None,**args):
395 if rx==None or ry== None:
397 raise ValueError('rx is required')
399 raise ValueError('ry is required')
401 raise ValueError('both rx and ry are required')
402 SVGelement.__init__(self,'ellipse',{'rx':rx,'ry':ry},**args)
404 self.attributes['cx']=cx
406 self.attributes['cy']=cy
408 self.attributes['fill']=fill
410 self.attributes['stroke']=stroke
411 if stroke_width!=None:
412 self.attributes['stroke-width']=stroke_width
415 class circle(SVGelement):
416 """c=circle(x,y,radius,fill,stroke,stroke_width,**args)
418 The circle creates an element using a x, y and radius values eg
420 def __init__(self,cx=None,cy=None,r=None,fill=None,stroke=None,stroke_width=None,**args):
422 raise ValueError('r is required')
423 SVGelement.__init__(self,'circle',{'r':r},**args)
425 self.attributes['cx']=cx
427 self.attributes['cy']=cy
429 self.attributes['fill']=fill
431 self.attributes['stroke']=stroke
432 if stroke_width!=None:
433 self.attributes['stroke-width']=stroke_width
436 """p=point(x,y,color)
438 A point is defined as a circle with a size 1 radius. It may be more efficient to use a
439 very small rectangle if you use many points because a circle is difficult to render.
441 def __init__(self,x,y,fill='black',**args):
442 circle.__init__(self,x,y,1,fill,**args)
444 class line(SVGelement):
445 """l=line(x1,y1,x2,y2,stroke,stroke_width,**args)
447 A line is defined by a begin x,y pair and an end x,y pair
449 def __init__(self,x1=None,y1=None,x2=None,y2=None,stroke=None,stroke_width=None,**args):
450 SVGelement.__init__(self,'line',**args)
452 self.attributes['x1']=x1
454 self.attributes['y1']=y1
456 self.attributes['x2']=x2
458 self.attributes['y2']=y2
459 if stroke_width!=None:
460 self.attributes['stroke-width']=stroke_width
462 self.attributes['stroke']=stroke
464 class polyline(SVGelement):
465 """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
467 a polyline is defined by a list of xy pairs
469 def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args):
470 SVGelement.__init__(self,'polyline',{'points':_xypointlist(points)},**args)
472 self.attributes['fill']=fill
473 if stroke_width!=None:
474 self.attributes['stroke-width']=stroke_width
476 self.attributes['stroke']=stroke
478 class polygon(SVGelement):
479 """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
481 a polygon is defined by a list of xy pairs
483 def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args):
484 SVGelement.__init__(self,'polygon',{'points':_xypointlist(points)},**args)
486 self.attributes['fill']=fill
487 if stroke_width!=None:
488 self.attributes['stroke-width']=stroke_width
490 self.attributes['stroke']=stroke
492 class path(SVGelement):
493 """p=path(path,fill,stroke,stroke_width,**args)
495 a path is defined by a path object and optional width, stroke and fillcolor
497 def __init__(self,pathdata,fill=None,stroke=None,stroke_width=None,id=None,**args):
498 SVGelement.__init__(self,'path',{'d':str(pathdata)},**args)
500 self.attributes['stroke']=stroke
502 self.attributes['fill']=fill
503 if stroke_width!=None:
504 self.attributes['stroke-width']=stroke_width
506 self.attributes['id']=id
509 class text(SVGelement):
510 """t=text(x,y,text,font_size,font_family,**args)
512 a text element can bge used for displaying text on the screen
514 def __init__(self,x=None,y=None,text=None,font_size=None,font_family=None,text_anchor=None,**args):
515 SVGelement.__init__(self,'text',**args)
517 self.attributes['x']=x
519 self.attributes['y']=y
521 self.attributes['font-size']=font_size
522 if font_family!=None:
523 self.attributes['font-family']=font_family
526 if text_anchor!=None:
527 self.attributes['text-anchor']=text_anchor
530 class textpath(SVGelement):
531 """tp=textpath(text,link,**args)
533 a textpath places a text on a path which is referenced by a link.
535 def __init__(self,link,text=None,**args):
536 SVGelement.__init__(self,'textPath',{'xlink:href':link},**args)
540 class pattern(SVGelement):
541 """p=pattern(x,y,width,height,patternUnits,**args)
543 A pattern is used to fill or stroke an object using a pre-defined
544 graphic object which can be replicated ("tiled") at fixed intervals
545 in x and y to cover the areas to be painted.
547 def __init__(self,x=None,y=None,width=None,height=None,patternUnits=None,**args):
548 SVGelement.__init__(self,'pattern',**args)
550 self.attributes['x']=x
552 self.attributes['y']=y
554 self.attributes['width']=width
556 self.attributes['height']=height
557 if patternUnits!=None:
558 self.attributes['patternUnits']=patternUnits
560 class title(SVGelement):
561 """t=title(text,**args)
563 a title is a text element. The text is displayed in the title bar
564 add at least one to the root svg element
566 def __init__(self,text=None,**args):
567 SVGelement.__init__(self,'title',**args)
571 class description(SVGelement):
572 """d=description(text,**args)
574 a description can be added to any element and is used for a tooltip
575 Add this element before adding other elements.
577 def __init__(self,text=None,**args):
578 SVGelement.__init__(self,'desc',**args)
582 class lineargradient(SVGelement):
583 """lg=lineargradient(x1,y1,x2,y2,id,**args)
585 defines a lineargradient using two xy pairs.
586 stop elements van be added to define the gradient colors.
588 def __init__(self,x1=None,y1=None,x2=None,y2=None,id=None,**args):
589 SVGelement.__init__(self,'linearGradient',**args)
591 self.attributes['x1']=x1
593 self.attributes['y1']=y1
595 self.attributes['x2']=x2
597 self.attributes['y2']=y2
599 self.attributes['id']=id
601 class radialgradient(SVGelement):
602 """rg=radialgradient(cx,cy,r,fx,fy,id,**args)
604 defines a radial gradient using a outer circle which are defined by a cx,cy and r and by using a focalpoint.
605 stop elements van be added to define the gradient colors.
607 def __init__(self,cx=None,cy=None,r=None,fx=None,fy=None,id=None,**args):
608 SVGelement.__init__(self,'radialGradient',**args)
610 self.attributes['cx']=cx
612 self.attributes['cy']=cy
614 self.attributes['r']=r
616 self.attributes['fx']=fx
618 self.attributes['fy']=fy
620 self.attributes['id']=id
622 class stop(SVGelement):
623 """st=stop(offset,stop_color,**args)
625 Puts a stop color at the specified radius
627 def __init__(self,offset,stop_color=None,**args):
628 SVGelement.__init__(self,'stop',{'offset':offset},**args)
630 self.attributes['stop-color']=stop_color
632 class style(SVGelement):
633 """st=style(type,cdata=None,**args)
635 Add a CDATA element to this element for defing in line stylesheets etc..
637 def __init__(self,type,cdata=None,**args):
638 SVGelement.__init__(self,'style',{'type':type},cdata=cdata, **args)
641 class image(SVGelement):
642 """im=image(url,width,height,x,y,**args)
644 adds an image to the drawing. Supported formats are .png, .jpg and .svg.
646 def __init__(self,url,x=None,y=None,width=None,height=None,**args):
647 if width==None or height==None:
649 raise ValueError('height is required')
651 raise ValueError('width is required')
653 raise ValueError('both height and width are required')
654 SVGelement.__init__(self,'image',{'xlink:href':url,'width':width,'height':height},**args)
656 self.attributes['x']=x
658 self.attributes['y']=y
660 class cursor(SVGelement):
661 """c=cursor(url,**args)
663 defines a custom cursor for a element or a drawing
665 def __init__(self,url,**args):
666 SVGelement.__init__(self,'cursor',{'xlink:href':url},**args)
669 class marker(SVGelement):
670 """m=marker(id,viewbox,refX,refY,markerWidth,markerHeight,**args)
672 defines a marker which can be used as an endpoint for a line or other pathtypes
673 add an element to it which should be used as a marker.
675 def __init__(self,id=None,viewBox=None,refx=None,refy=None,markerWidth=None,markerHeight=None,**args):
676 SVGelement.__init__(self,'marker',**args)
678 self.attributes['id']=id
680 self.attributes['viewBox']=_viewboxlist(viewBox)
682 self.attributes['refX']=refx
684 self.attributes['refY']=refy
685 if markerWidth!=None:
686 self.attributes['markerWidth']=markerWidth
687 if markerHeight!=None:
688 self.attributes['markerHeight']=markerHeight
690 class group(SVGelement):
691 """g=group(id,**args)
693 a group is defined by an id and is used to contain elements
694 g.addElement(SVGelement)
696 def __init__(self,id=None,**args):
697 SVGelement.__init__(self,'g',**args)
699 self.attributes['id']=id
701 class symbol(SVGelement):
702 """sy=symbol(id,viewbox,**args)
704 defines a symbol which can be used on different places in your graph using
705 the use element. A symbol is not rendered but you can use 'use' elements to
706 display it by referencing its id.
707 sy.addElement(SVGelement)
710 def __init__(self,id=None,viewBox=None,**args):
711 SVGelement.__init__(self,'symbol',**args)
713 self.attributes['id']=id
715 self.attributes['viewBox']=_viewboxlist(viewBox)
717 class defs(SVGelement):
720 container for defining elements
722 def __init__(self,**args):
723 SVGelement.__init__(self,'defs',**args)
725 class switch(SVGelement):
728 Elements added to a switch element which are "switched" by the attributes
729 requiredFeatures, requiredExtensions and systemLanguage.
730 Refer to the SVG specification for details.
732 def __init__(self,**args):
733 SVGelement.__init__(self,'switch',**args)
736 class use(SVGelement):
737 """u=use(link,x,y,width,height,**args)
739 references a symbol by linking to its id and its position, height and width
741 def __init__(self,link,x=None,y=None,width=None,height=None,**args):
742 SVGelement.__init__(self,'use',{'xlink:href':link},**args)
744 self.attributes['x']=x
746 self.attributes['y']=y
749 self.attributes['width']=width
751 self.attributes['height']=height
754 class link(SVGelement):
755 """a=link(url,**args)
757 a link is defined by a hyperlink. add elements which have to be linked
758 a.addElement(SVGelement)
760 def __init__(self,link='',**args):
761 SVGelement.__init__(self,'a',{'xlink:href':link},**args)
763 class view(SVGelement):
766 a view can be used to create a view with different attributes"""
767 def __init__(self,id=None,**args):
768 SVGelement.__init__(self,'view',**args)
770 self.attributes['id']=id
772 class script(SVGelement):
773 """sc=script(type,type,cdata,**args)
775 adds a script element which contains CDATA to the SVG drawing
778 def __init__(self,type,cdata=None,**args):
779 SVGelement.__init__(self,'script',{'type':type},cdata=cdata,**args)
781 class animate(SVGelement):
782 """an=animate(attribute,from,to,during,**args)
784 animates an attribute.
786 def __init__(self,attribute,fr=None,to=None,dur=None,**args):
787 SVGelement.__init__(self,'animate',{'attributeName':attribute},**args)
789 self.attributes['from']=fr
791 self.attributes['to']=to
793 self.attributes['dur']=dur
795 class animateMotion(SVGelement):
796 """an=animateMotion(pathdata,dur,**args)
798 animates a SVGelement over the given path in dur seconds
800 def __init__(self,pathdata,dur,**args):
801 SVGelement.__init__(self,'animateMotion',**args)
803 self.attributes['path']=str(pathdata)
805 self.attributes['dur']=dur
807 class animateTransform(SVGelement):
808 """antr=animateTransform(type,from,to,dur,**args)
810 transform an element from and to a value.
812 def __init__(self,type=None,fr=None,to=None,dur=None,**args):
813 SVGelement.__init__(self,'animateTransform',{'attributeName':'transform'},**args)
814 #As far as I know the attributeName is always transform
816 self.attributes['type']=type
818 self.attributes['from']=fr
820 self.attributes['to']=to
822 self.attributes['dur']=dur
823 class animateColor(SVGelement):
824 """ac=animateColor(attribute,type,from,to,dur,**args)
826 Animates the color of a element
828 def __init__(self,attribute,type=None,fr=None,to=None,dur=None,**args):
829 SVGelement.__init__(self,'animateColor',{'attributeName':attribute},**args)
831 self.attributes['type']=type
833 self.attributes['from']=fr
835 self.attributes['to']=to
837 self.attributes['dur']=dur
838 class set(SVGelement):
839 """st=set(attribute,to,during,**args)
841 sets an attribute to a value for a
843 def __init__(self,attribute,to=None,dur=None,**args):
844 SVGelement.__init__(self,'set',{'attributeName':attribute},**args)
846 self.attributes['to']=to
848 self.attributes['dur']=dur
852 class svg(SVGelement):
853 """s=svg(viewbox,width,height,**args)
855 a svg or element is the root of a drawing add all elements to a svg element.
856 You can have different svg elements in one svg file
857 s.addElement(SVGelement)
861 s=svg((0,0,100,100),'100%','100%')
867 def __init__(self,viewBox=None, width=None, height=None,**args):
868 SVGelement.__init__(self,'svg',**args)
870 self.attributes['viewBox']=_viewboxlist(viewBox)
872 self.attributes['width']=width
874 self.attributes['height']=height
875 self.namespace="http://www.w3.org/2000/svg"
880 this is the actual SVG document. It needs a svg element as a root.
881 Use the addSVG method to set the svg to the root. Use the toXml method to write the SVG
882 source to the screen or to a file
885 d.toXml(optionalfilename)
890 def setSVG(self,svg):
892 #Voeg een element toe aan de grafiek toe.
893 if use_dom_implementation==0:
894 def toXml(self, filename='',compress=False):
897 xml.write("<?xml version='1.0' encoding='UTF-8'?>\n")
898 xml.write("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd \">\n")
899 self.svg.toXml(0,xml)
904 zf=gzip.GzipFile(fileobj=f,mode='wb')
905 zf.write(xml.getvalue())
910 return xml.getvalue()
912 if filename[-4:]=='svgz':
914 f=gzip.GzipFile(filename=filename,mode="wb", compresslevel=9)
915 f.write(xml.getvalue())
919 f.write(xml.getvalue())
923 def toXml(self,filename='',compress=False):
924 """drawing.toXml() ---->to the screen
925 drawing.toXml(filename)---->to the file
926 writes a svg drawing to the screen or to a file
927 compresses if filename ends with svgz or if compress is true
929 doctype = implementation.createDocumentType('svg',"-//W3C//DTD SVG 1.0//EN""",'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd ')
932 #root is defined global so it can be used by the appender. Its also possible to use it as an arugument but
933 #that is a bit messy.
934 root=implementation.createDocument(None,None,doctype)
935 #Create the xml document.
937 def appender(element,elementroot):
938 """This recursive function appends elements to an element and sets the attributes
939 and type. It stops when alle elements have been appended"""
940 if element.namespace:
941 e=root.createElementNS(element.namespace,element.type)
943 e=root.createElement(element.type)
945 textnode=root.createTextNode(element.text)
946 e.appendChild(textnode)
947 for attribute in list(element.attributes.keys()): #in element.attributes is supported from python 2.2
948 e.setAttribute(attribute,str(element.attributes[attribute]))
950 for el in element.elements:
952 elementroot.appendChild(e)
954 root=appender(self.svg,root)
958 PrettyPrint(root,xml)
962 zf=gzip.GzipFile(fileobj=f,mode='wb')
963 zf.write(xml.getvalue())
968 return xml.getvalue()
971 if filename[-4:]=='svgz':
975 PrettyPrint(root,xml)
976 f=gzip.GzipFile(filename=filename,mode='wb',compresslevel=9)
977 f.write(xml.getvalue())
984 print("Cannot write SVG file: " + filename)
987 import xml.parsers.xmlproc.xmlval
989 raise ImportError('PyXml is required for validating SVG')
991 xv=xml.parsers.xmlproc.xmlval.XMLValidator()
995 raise # "SVG is not well formed, see messages above"
997 print("SVG well formed")
998 if __name__=='__main__':
1002 s=svg((0,0,100,100))
1003 r=rect(-100,-100,300,300,'cyan')
1006 t=title('SVGdraw Demo')
1008 g=group('animations')
1011 c=circle(0,0,1,'red')
1015 pd.relsmbezier(10,5,0,10)
1016 pd.relsmbezier(-10,5,0,10)
1017 an=animateMotion(pd,10)
1018 an.attributes['rotate']='auto-reverse'
1019 an.attributes['repeatCount']="indefinite"
1022 for i in range(20,120,20):
1023 u=use('#animations',i,0)
1025 for i in range(0,120,20):
1026 for j in range(5,105,10):
1027 c=circle(i,j,1,'red','black',.5)