1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 __doc__="""Use SVGdraw to generate your SVGdrawings.
33
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
36
37 d=drawing()
38 #then you create a SVG root element
39 s=svg()
40 #then you add some elements eg a circle and add it to the svg root element
41 c=circle()
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
46 s.addElement(c)
47 #then you add the svg root element to the drawing
48 d.setSVG(s)
49 #and finaly you xmlify the drawing
50 d.toXml()
51
52
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"""
58
59 __version__="1.0"
60
61
62
63
64
65
66
67
68
69
70 use_dom_implementation=0
71
72
73 import exceptions
74 if use_dom_implementation<>0:
75 try:
76 from xml.dom import implementation
77 from xml.dom.ext import PrettyPrint
78 except:
79 raise exceptions.ImportError, "PyXML is required for using the dom implementation"
80
81
82
83 import sys
84 assert sys.version_info[0]>=2
85 if sys.version_info[1]<2:
86 True=1
87 False=0
88 file=open
89
90 sys.setrecursionlimit=50
91
92
93
94
95
96
98 """Escape &, <, and > in a string of data.
99
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.
103 """
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)
109 return data
110
112 """Escape and quote an attribute value.
113
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
116 necessary.
117
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.
121 """
122 data = _escape(data, entities)
123 if '"' in data:
124 if "'" in data:
125 data = '"%s"' % data.replace('"', """)
126 else:
127 data = "'%s'" % data
128 else:
129 data = '"%s"' % data
130 return data
131
132
133
135 """formats a list of xy pairs"""
136 s=''
137 for e in a:
138 s+=str(e)[1:-1] +' '
139 return s
140
142 """formats a tuple"""
143 s=''
144 for e in a:
145 s+=str(e)+' '
146 return s
147
149 """formats a list of numbers"""
150 return str(a)[1:-1]
151
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
157 self.path=[]
158 if x is not None and y is not None:
159 self.path.append('M '+str(x)+' '+str(y))
163 - def move(self,x,y):
164 """move to absolute"""
165 self.path.append('M '+str(x)+' '+str(y))
167 """move to relative"""
168 self.path.append('m '+str(x)+' '+str(y))
169 - def line(self,x,y):
170 """line to absolute"""
171 self.path.append('L '+str(x)+' '+str(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))
179 """horizontal line to relative"""
180 self.path.append('h'+str(x))
182 """verical line to absolute"""
183 self.path.append('V'+str(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))
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))
194 """smooth bezier with xy2 to xy absolut"""
195 self.path.append('S'+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
197 """smooth bezier with xy2 to xy relative"""
198 self.path.append('s'+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
200 """quadratic bezier with xy1 to xy absolut"""
201 self.path.append('Q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y))
203 """quadratic bezier with xy1 to xy relative"""
204 self.path.append('q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y))
206 """smooth quadratic bezier to xy absolut"""
207 self.path.append('T'+str(x)+','+str(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))
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)
219
220
221
222
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):
234 self.type=type
235 if attributes==None:
236 self.attributes={}
237 else:
238 self.attributes=attributes
239 if elements==None:
240 self.elements=[]
241 else:
242 self.elements=elements
243 self.text=text
244 self.namespace=namespace
245 self.cdata=cdata
246 for arg in args.keys():
247 self.attributes[arg]=args[arg]
249 """adds an element to a SVGelement
250
251 SVGelement.addElement(SVGelement)
252 """
253 self.elements.append(SVGelement)
254
255
256 - def toXml(self,level,f, **kwargs):
257 preserve = kwargs.get("preserveWhitespace", False)
258 if preserve:
259
260 NEWLINE = ""
261 TAB = ""
262 else:
263
264 NEWLINE = "\n"
265 TAB = "\t"
266 f.write(TAB*level)
267 f.write('<'+self.type)
268 for attkey in self.attributes.keys():
269 f.write(' '+_escape(str(attkey))+'='+_quoteattr(str(self.attributes[attkey])))
270 if self.namespace:
271 f.write(' xmlns="'+ _escape(str(self.namespace))+'" ')
272 if self.elements or self.text or self.cdata:
273 f.write('>')
274 if self.elements:
275 f.write(NEWLINE)
276 for element in self.elements:
277 element.toXml(level+1,f, preserveWhitespace=preserve)
278 if self.cdata:
279 f.write(NEWLINE+TAB*(level+1)+'<![CDATA[')
280 for line in self.cdata.splitlines():
281 f.write(NEWLINE+TAB*(level+2)+line)
282 f.write(NEWLINE+TAB*(level+1)+']]>'+NEWLINE)
283 if self.text:
284 if type(self.text)==type(''):
285 f.write(_escape(str(self.text)))
286 else:
287 f.write(str(self.text))
288 if self.elements:
289 f.write(TAB*level+'</'+self.type+'>'+NEWLINE)
290 elif self.text:
291 f.write('</'+self.type+'>'+NEWLINE)
292 elif self.cdata:
293 f.write(TAB*level+'</'+self.type+'>'+NEWLINE)
294 else:
295 f.write('/>'+NEWLINE)
296
298 """ts=tspan(text='',**args)
299
300 a tspan element can be used for applying formatting to a textsection
301 usage:
302 ts=tspan('this text is bold')
303 ts.attributes['font-weight']='bold'
304 st=spannedtext()
305 st.addtspan(ts)
306 t=text(3,5,st)
307 """
313 s="<tspan"
314 for key,value in self.attributes.items():
315 s+= ' %s="%s"' % (key,value)
316 s+='>'
317 s+=self.text
318 s+='</tspan>'
319 return s
320
321 -class tref(SVGelement):
322 """tr=tref(link='',**args)
323
324 a tref element can be used for referencing text by a link to its id.
325 usage:
326 tr=tref('#linktotext')
327 st=spannedtext()
328 st.addtref(tr)
329 t=text(3,5,st)
330 """
334 s="<tref"
335
336 for key,value in self.attributes.items():
337 s+= ' %s="%s"' % (key,value)
338 s+='/>'
339 return s
340
342 """st=spannedtext(textlist=[])
343
344 a spannedtext can be used for text which consists of text, tspan's and tref's
345 You can use it to add to a text element or path element. Don't add it directly
346 to a svg or a group element.
347 usage:
348
349 ts=tspan('this text is bold')
350 ts.attributes['font-weight']='bold'
351 tr=tref('#linktotext')
352 tr.attributes['fill']='red'
353 st=spannedtext()
354 st.addtspan(ts)
355 st.addtref(tr)
356 st.addtext('This text is not bold')
357 t=text(3,5,st)
358 """
359 - def __init__(self,textlist=None):
360 if textlist==None:
361 self.textlist=[]
362 else:
363 self.textlist=textlist
364 - def addtext(self,text=''):
365 self.textlist.append(text)
366 - def addtspan(self,tspan):
367 self.textlist.append(tspan)
368 - def addtref(self,tref):
369 self.textlist.append(tref)
370 - def __repr__(self):
371 s=""
372 for element in self.textlist:
373 s+=str(element)
374 return s
375
376 -class rect(SVGelement):
377 """r=rect(width,height,x,y,fill,stroke,stroke_width,**args)
378
379 a rectangle is defined by a width and height and a xy pair
380 """
381 - def __init__(self,x=None,y=None,width=None,height=None,fill=None,stroke=None,stroke_width=None,**args):
382 if width==None or height==None:
383 if width<>None:
384 raise ValueError, 'height is required'
385 if height<>None:
386 raise ValueError, 'width is required'
387 else:
388 raise ValueError, 'both height and width are required'
389 SVGelement.__init__(self,'rect',{'width':width,'height':height},**args)
390 if x<>None:
391 self.attributes['x']=x
392 if y<>None:
393 self.attributes['y']=y
394 if fill<>None:
395 self.attributes['fill']=fill
396 if stroke<>None:
397 self.attributes['stroke']=stroke
398 if stroke_width<>None:
399 self.attributes['stroke-width']=stroke_width
400
402 """e=ellipse(rx,ry,x,y,fill,stroke,stroke_width,**args)
403
404 an ellipse is defined as a center and a x and y radius.
405 """
406 - def __init__(self,cx=None,cy=None,rx=None,ry=None,fill=None,stroke=None,stroke_width=None,**args):
407 if rx==None or ry== None:
408 if rx<>None:
409 raise ValueError, 'rx is required'
410 if ry<>None:
411 raise ValueError, 'ry is required'
412 else:
413 raise ValueError, 'both rx and ry are required'
414 SVGelement.__init__(self,'ellipse',{'rx':rx,'ry':ry},**args)
415 if cx<>None:
416 self.attributes['cx']=cx
417 if cy<>None:
418 self.attributes['cy']=cy
419 if fill<>None:
420 self.attributes['fill']=fill
421 if stroke<>None:
422 self.attributes['stroke']=stroke
423 if stroke_width<>None:
424 self.attributes['stroke-width']=stroke_width
425
426
428 """c=circle(x,y,radius,fill,stroke,stroke_width,**args)
429
430 The circle creates an element using a x, y and radius values eg
431 """
432 - def __init__(self,cx=None,cy=None,r=None,fill=None,stroke=None,stroke_width=None,**args):
433 if r==None:
434 raise ValueError, 'r is required'
435 SVGelement.__init__(self,'circle',{'r':r},**args)
436 if cx<>None:
437 self.attributes['cx']=cx
438 if cy<>None:
439 self.attributes['cy']=cy
440 if fill<>None:
441 self.attributes['fill']=fill
442 if stroke<>None:
443 self.attributes['stroke']=stroke
444 if stroke_width<>None:
445 self.attributes['stroke-width']=stroke_width
446
448 """p=point(x,y,color)
449
450 A point is defined as a circle with a size 1 radius. It may be more efficient to use a
451 very small rectangle if you use many points because a circle is difficult to render.
452 """
453 - def __init__(self,x,y,fill='black',**args):
455
456 -class line(SVGelement):
457 """l=line(x1,y1,x2,y2,stroke,stroke_width,**args)
458
459 A line is defined by a begin x,y pair and an end x,y pair
460 """
461 - def __init__(self,x1=None,y1=None,x2=None,y2=None,stroke=None,stroke_width=None,**args):
462 SVGelement.__init__(self,'line',**args)
463 if x1<>None:
464 self.attributes['x1']=x1
465 if y1<>None:
466 self.attributes['y1']=y1
467 if x2<>None:
468 self.attributes['x2']=x2
469 if y2<>None:
470 self.attributes['y2']=y2
471 if stroke_width<>None:
472 self.attributes['stroke-width']=stroke_width
473 if stroke<>None:
474 self.attributes['stroke']=stroke
475
477 """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
478
479 a polyline is defined by a list of xy pairs
480 """
481 - def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args):
482 SVGelement.__init__(self,'polyline',{'points':_xypointlist(points)},**args)
483 if fill<>None:
484 self.attributes['fill']=fill
485 if stroke_width<>None:
486 self.attributes['stroke-width']=stroke_width
487 if stroke<>None:
488 self.attributes['stroke']=stroke
489
491 """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
492
493 a polygon is defined by a list of xy pairs
494 """
495 - def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args):
496 SVGelement.__init__(self,'polygon',{'points':_xypointlist(points)},**args)
497 if fill<>None:
498 self.attributes['fill']=fill
499 if stroke_width<>None:
500 self.attributes['stroke-width']=stroke_width
501 if stroke<>None:
502 self.attributes['stroke']=stroke
503
504 -class path(SVGelement):
505 """p=path(path,fill,stroke,stroke_width,**args)
506
507 a path is defined by a path object and optional width, stroke and fillcolor
508 """
509 - def __init__(self,pathdata,fill=None,stroke=None,stroke_width=None,id=None,**args):
510 SVGelement.__init__(self,'path',{'d':str(pathdata)},**args)
511 if stroke<>None:
512 self.attributes['stroke']=stroke
513 if fill<>None:
514 self.attributes['fill']=fill
515 if stroke_width<>None:
516 self.attributes['stroke-width']=stroke_width
517 if id<>None:
518 self.attributes['id']=id
519
520
521 -class text(SVGelement):
522 """t=text(x,y,text,font_size,font_family,**args)
523
524 a text element can bge used for displaying text on the screen
525 """
526 - def __init__(self,x=None,y=None,text=None,font_size=None,font_family=None,text_anchor=None,**args):
527 SVGelement.__init__(self,'text',**args)
528 if x<>None:
529 self.attributes['x']=x
530 if y<>None:
531 self.attributes['y']=y
532 if font_size<>None:
533 self.attributes['font-size']=font_size
534 if font_family<>None:
535 self.attributes['font-family']=font_family
536 if text<>None:
537 self.text=text
538 if text_anchor<>None:
539 self.attributes['text-anchor']=text_anchor
540
541 - def toXml(self,level,f, **kwargs):
542 preserve = self.attributes.get("xml:space", None)
543 if preserve == "preserve":
544
545 SVGelement.toXml(self,level, f, preserveWhitespace=True)
546 else:
547
548 SVGelement.toXml(self, level, f, preserveWhitespace=False)
549
550 -class textpath(SVGelement):
551 """tp=textpath(text,link,**args)
552
553 a textpath places a text on a path which is referenced by a link.
554 """
555 - def __init__(self,link,text=None,**args):
556 SVGelement.__init__(self,'textPath',{'xlink:href':link},**args)
557 if text<>None:
558 self.text=text
559
561 """p=pattern(x,y,width,height,patternUnits,**args)
562
563 A pattern is used to fill or stroke an object using a pre-defined
564 graphic object which can be replicated ("tiled") at fixed intervals
565 in x and y to cover the areas to be painted.
566 """
567 - def __init__(self,x=None,y=None,width=None,height=None,patternUnits=None,**args):
568 SVGelement.__init__(self,'pattern',**args)
569 if x<>None:
570 self.attributes['x']=x
571 if y<>None:
572 self.attributes['y']=y
573 if width<>None:
574 self.attributes['width']=width
575 if height<>None:
576 self.attributes['height']=height
577 if patternUnits<>None:
578 self.attributes['patternUnits']=patternUnits
579
581 """t=title(text,**args)
582
583 a title is a text element. The text is displayed in the title bar
584 add at least one to the root svg element
585 """
590
592 """d=description(text,**args)
593
594 a description can be added to any element and is used for a tooltip
595 Add this element before adding other elements.
596 """
601
603 """lg=lineargradient(x1,y1,x2,y2,id,**args)
604
605 defines a lineargradient using two xy pairs.
606 stop elements van be added to define the gradient colors.
607 """
608 - def __init__(self,x1=None,y1=None,x2=None,y2=None,id=None,**args):
609 SVGelement.__init__(self,'linearGradient',**args)
610 if x1<>None:
611 self.attributes['x1']=x1
612 if y1<>None:
613 self.attributes['y1']=y1
614 if x2<>None:
615 self.attributes['x2']=x2
616 if y2<>None:
617 self.attributes['y2']=y2
618 if id<>None:
619 self.attributes['id']=id
620
622 """rg=radialgradient(cx,cy,r,fx,fy,id,**args)
623
624 defines a radial gradient using a outer circle which are defined by a cx,cy and r and by using a focalpoint.
625 stop elements van be added to define the gradient colors.
626 """
627 - def __init__(self,cx=None,cy=None,r=None,fx=None,fy=None,id=None,**args):
628 SVGelement.__init__(self,'radialGradient',**args)
629 if cx<>None:
630 self.attributes['cx']=cx
631 if cy<>None:
632 self.attributes['cy']=cy
633 if r<>None:
634 self.attributes['r']=r
635 if fx<>None:
636 self.attributes['fx']=fx
637 if fy<>None:
638 self.attributes['fy']=fy
639 if id<>None:
640 self.attributes['id']=id
641
642 -class stop(SVGelement):
643 """st=stop(offset,stop_color,**args)
644
645 Puts a stop color at the specified radius
646 """
647 - def __init__(self,offset,stop_color=None,**args):
648 SVGelement.__init__(self,'stop',{'offset':offset},**args)
649 if stop_color<>None:
650 self.attributes['stop-color']=stop_color
651
653 """st=style(type,cdata=None,**args)
654
655 Add a CDATA element to this element for defing in line stylesheets etc..
656 """
657 - def __init__(self,type,cdata=None,**args):
659
660
662 """im=image(url,width,height,x,y,**args)
663
664 adds an image to the drawing. Supported formats are .png, .jpg and .svg.
665 """
666 - def __init__(self,url,x=None,y=None,width=None,height=None,**args):
667 if width==None or height==None:
668 if width<>None:
669 raise ValueError, 'height is required'
670 if height<>None:
671 raise ValueError, 'width is required'
672 else:
673 raise ValueError, 'both height and width are required'
674 SVGelement.__init__(self,'image',{'xlink:href':url,'width':width,'height':height},**args)
675 if x<>None:
676 self.attributes['x']=x
677 if y<>None:
678 self.attributes['y']=y
679
681 """c=cursor(url,**args)
682
683 defines a custom cursor for a element or a drawing
684 """
687
688
690 """m=marker(id,viewbox,refX,refY,markerWidth,markerHeight,**args)
691
692 defines a marker which can be used as an endpoint for a line or other pathtypes
693 add an element to it which should be used as a marker.
694 """
695 - def __init__(self,id=None,viewBox=None,refx=None,refy=None,markerWidth=None,markerHeight=None,**args):
696 SVGelement.__init__(self,'marker',**args)
697 if id<>None:
698 self.attributes['id']=id
699 if viewBox<>None:
700 self.attributes['viewBox']=_viewboxlist(viewBox)
701 if refx<>None:
702 self.attributes['refX']=refx
703 if refy<>None:
704 self.attributes['refY']=refy
705 if markerWidth<>None:
706 self.attributes['markerWidth']=markerWidth
707 if markerHeight<>None:
708 self.attributes['markerHeight']=markerHeight
709
711 """g=group(id,**args)
712
713 a group is defined by an id and is used to contain elements
714 g.addElement(SVGelement)
715 """
720
722 """sy=symbol(id,viewbox,**args)
723
724 defines a symbol which can be used on different places in your graph using
725 the use element. A symbol is not rendered but you can use 'use' elements to
726 display it by referencing its id.
727 sy.addElement(SVGelement)
728 """
729
730 - def __init__(self,id=None,viewBox=None,**args):
736
737 -class defs(SVGelement):
738 """d=defs(**args)
739
740 container for defining elements
741 """
744
746 """sw=switch(**args)
747
748 Elements added to a switch element which are "switched" by the attributes
749 requiredFeatures, requiredExtensions and systemLanguage.
750 Refer to the SVG specification for details.
751 """
754
755
756 -class use(SVGelement):
757 """u=use(link,x,y,width,height,**args)
758
759 references a symbol by linking to its id and its position, height and width
760 """
761 - def __init__(self,link,x=None,y=None,width=None,height=None,**args):
762 SVGelement.__init__(self,'use',{'xlink:href':link},**args)
763 if x<>None:
764 self.attributes['x']=x
765 if y<>None:
766 self.attributes['y']=y
767
768 if width<>None:
769 self.attributes['width']=width
770 if height<>None:
771 self.attributes['height']=height
772
773
774 -class link(SVGelement):
775 """a=link(url,**args)
776
777 a link is defined by a hyperlink. add elements which have to be linked
778 a.addElement(SVGelement)
779 """
782
783 -class view(SVGelement):
784 """v=view(id,**args)
785
786 a view can be used to create a view with different attributes"""
791
793 """sc=script(type,type,cdata,**args)
794
795 adds a script element which contains CDATA to the SVG drawing
796
797 """
798 - def __init__(self,type,cdata=None,**args):
800
802 """an=animate(attribute,from,to,during,**args)
803
804 animates an attribute.
805 """
806 - def __init__(self,attribute,fr=None,to=None,dur=None,**args):
807 SVGelement.__init__(self,'animate',{'attributeName':attribute},**args)
808 if fr<>None:
809 self.attributes['from']=fr
810 if to<>None:
811 self.attributes['to']=to
812 if dur<>None:
813 self.attributes['dur']=dur
814
816 """an=animateMotion(pathdata,dur,**args)
817
818 animates a SVGelement over the given path in dur seconds
819 """
820 - def __init__(self,pathdata,dur,**args):
826
844 """ac=animateColor(attribute,type,from,to,dur,**args)
845
846 Animates the color of a element
847 """
848 - def __init__(self,attribute,type=None,fr=None,to=None,dur=None,**args):
849 SVGelement.__init__(self,'animateColor',{'attributeName':attribute},**args)
850 if type<>None:
851 self.attributes['type']=type
852 if fr<>None:
853 self.attributes['from']=fr
854 if to<>None:
855 self.attributes['to']=to
856 if dur<>None:
857 self.attributes['dur']=dur
858 -class set(SVGelement):
859 """st=set(attribute,to,during,**args)
860
861 sets an attribute to a value for a
862 """
863 - def __init__(self,attribute,to=None,dur=None,**args):
864 SVGelement.__init__(self,'set',{'attributeName':attribute},**args)
865 if to<>None:
866 self.attributes['to']=to
867 if dur<>None:
868 self.attributes['dur']=dur
869
870
871
872 -class svg(SVGelement):
873 """s=svg(viewbox,width,height,**args)
874
875 a svg or element is the root of a drawing add all elements to a svg element.
876 You can have different svg elements in one svg file
877 s.addElement(SVGelement)
878
879 eg
880 d=drawing()
881 s=svg((0,0,100,100),'100%','100%')
882 c=circle(50,50,20)
883 s.addElement(c)
884 d.setSVG(s)
885 d.toXml()
886 """
887 - def __init__(self,viewBox=None, width=None, height=None,**args):
888 SVGelement.__init__(self,'svg',**args)
889 if viewBox<>None:
890 self.attributes['viewBox']=_viewboxlist(viewBox)
891 if width<>None:
892 self.attributes['width']=width
893 if height<>None:
894 self.attributes['height']=height
895 self.namespace="http://www.w3.org/2000/svg"
896
898 """d=drawing()
899
900 this is the actual SVG document. It needs a svg element as a root.
901 Use the addSVG method to set the svg to the root. Use the toXml method to write the SVG
902 source to the screen or to a file
903 d=drawing()
904 d.addSVG(svg)
905 d.toXml(optionalfilename)
906 """
907
912
913 if use_dom_implementation==0:
915 import cStringIO
916 xml=cStringIO.StringIO()
917 xml.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n')
918 xml.write("""<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
919 "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"
920 [<!ATTLIST svg xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink">]>\n""")
921 self.svg.toXml(0,xml)
922 if not filename:
923 if compress:
924 import gzip
925 f=cStringIO.StringIO()
926 zf=gzip.GzipFile(fileobj=f,mode='wb')
927 zf.write(xml.getvalue())
928 zf.close()
929 f.seek(0)
930 return f.read()
931 else:
932 return xml.getvalue()
933 else:
934 if filename[-4:]=='svgz':
935 import gzip
936 f=gzip.GzipFile(filename=filename,mode="wb", compresslevel=9)
937 f.write(xml.getvalue())
938 f.close()
939 else:
940 f=file(filename,'w')
941 f.write(xml.getvalue())
942 f.close()
943
944 else:
946 """drawing.toXml() ---->to the screen
947 drawing.toXml(filename)---->to the file
948 writes a svg drawing to the screen or to a file
949 compresses if filename ends with svgz or if compress is true
950 """
951 doctype = implementation.createDocumentType('svg',"-//W3C//DTD SVG 1.0//EN""",'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd ')
952
953 global root
954
955
956 root=implementation.createDocument(None,None,doctype)
957
958 global appender
959 def appender(element,elementroot):
960 """This recursive function appends elements to an element and sets the attributes
961 and type. It stops when alle elements have been appended"""
962 if element.namespace:
963 e=root.createElementNS(element.namespace,element.type)
964 else:
965 e=root.createElement(element.type)
966 if element.text:
967 textnode=root.createTextNode(element.text)
968 e.appendChild(textnode)
969 for attribute in element.attributes.keys():
970 e.setAttribute(attribute,str(element.attributes[attribute]))
971 if element.elements:
972 for el in element.elements:
973 e=appender(el,e)
974 elementroot.appendChild(e)
975 return elementroot
976 root=appender(self.svg,root)
977 if not filename:
978 import cStringIO
979 xml=cStringIO.StringIO()
980 PrettyPrint(root,xml)
981 if compress:
982 import gzip
983 f=cStringIO.StringIO()
984 zf=gzip.GzipFile(fileobj=f,mode='wb')
985 zf.write(xml.getvalue())
986 zf.close()
987 f.seek(0)
988 return f.read()
989 else:
990 return xml.getvalue()
991 else:
992 try:
993 if filename[-4:]=='svgz':
994 import gzip
995 import cStringIO
996 xml=cStringIO.StringIO()
997 PrettyPrint(root,xml)
998 f=gzip.GzipFile(filename=filename,mode='wb',compresslevel=9)
999 f.write(xml.getvalue())
1000 f.close()
1001 else:
1002 f=open(filename,'w')
1003 PrettyPrint(root,f)
1004 f.close()
1005 except:
1006 print "Cannot write SVG file: " + filename
1008 try:
1009 import xml.parsers.xmlproc.xmlval
1010 except:
1011 raise exceptions.ImportError,'PyXml is required for validating SVG'
1012 svg=self.toXml()
1013 xv=xml.parsers.xmlproc.xmlval.XMLValidator()
1014 try:
1015 xv.feed(svg)
1016 except:
1017 raise "SVG is not well formed, see messages above"
1018 else:
1019 print "SVG well formed"
1020 if __name__=='__main__':
1021
1022
1023 d=drawing()
1024 s=svg((0,0,100,100))
1025 r=rect(-100,-100,300,300,'cyan')
1026 s.addElement(r)
1027
1028 t=title('SVGdraw Demo')
1029 s.addElement(t)
1030 g=group('animations')
1031 e=ellipse(0,0,5,2)
1032 g.addElement(e)
1033 c=circle(0,0,1,'red')
1034 g.addElement(c)
1035 pd=pathdata(0,-10)
1036 for i in range(6):
1037 pd.relsmbezier(10,5,0,10)
1038 pd.relsmbezier(-10,5,0,10)
1039 an=animateMotion(pd,10)
1040 an.attributes['rotate']='auto-reverse'
1041 an.attributes['repeatCount']="indefinite"
1042 g.addElement(an)
1043 s.addElement(g)
1044 for i in range(20,120,20):
1045 u=use('#animations',i,0)
1046 s.addElement(u)
1047 for i in range(0,120,20):
1048 for j in range(5,105,10):
1049 c=circle(i,j,1,'red','black',.5)
1050 s.addElement(c)
1051 d.setSVG(s)
1052
1053 print d.toXml()
1054