Package obitools :: Module SVGdraw
[hide private]
[frames] | no frames]

Source Code for Module obitools.SVGdraw

   1  #!/usr/bin/env python 
   2  ##Copyright (c) 2002, Fedor Baart & Hans de Wit (Stichting Farmaceutische Kengetallen) 
   3  ##All rights reserved. 
   4  ## 
   5  ##Redistribution and use in source and binary forms, with or without modification, 
   6  ##are permitted provided that the following conditions are met: 
   7  ## 
   8  ##Redistributions of source code must retain the above copyright notice, this 
   9  ##list of conditions and the following disclaimer. 
  10  ## 
  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. 
  14  ## 
  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. 
  18  ## 
  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. 
  29   
  30  ##Thanks to Gerald Rosennfellner for his help and useful comments. 
  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  # 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 
  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  #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 
  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  #The recursion limit is set conservative so mistakes like s=svg() s.addElement(s) 
  92  #won't eat up too much processor time. 
  93   
  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. 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("&", "&amp;") 105 data = data.replace("<", "&lt;") 106 data = data.replace(">", "&gt;") 107 for chars, entity in entities.items(): 108 data = data.replace(chars, entity) 109 return data
110
111 -def _quoteattr(data, entities={}):
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('"', "&quot;") 126 else: 127 data = "'%s'" % data 128 else: 129 data = '"%s"' % data 130 return data
131 132 133
134 -def _xypointlist(a):
135 """formats a list of xy pairs""" 136 s='' 137 for e in a: #this could be done more elegant 138 s+=str(e)[1:-1] +' ' 139 return s
140
141 -def _viewboxlist(a):
142 """formats a tuple""" 143 s='' 144 for e in a: 145 s+=str(e)+' ' 146 return s
147
148 -def _pointlist(a):
149 """formats a list of numbers""" 150 return str(a)[1:-1]
151
152 -class pathdata:
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):
157 self.path=[] 158 if x is not None and y is not None: 159 self.path.append('M '+str(x)+' '+str(y))
160 - def closepath(self):
161 """ends the path""" 162 self.path.append('z')
163 - def move(self,x,y):
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))
169 - def line(self,x,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))
175 - def hline(self,x):
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))
181 - def vline(self,y):
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))
217 - def __repr__(self):
218 return ' '.join(self.path)
219 220 221 222
223 -class SVGelement:
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]
248 - def addElement(self,SVGelement):
249 """adds an element to a SVGelement 250 251 SVGelement.addElement(SVGelement) 252 """ 253 self.elements.append(SVGelement)
254 255 #def toXml(self,level,f, preserveWhitespace=False):
256 - def toXml(self,level,f, **kwargs):
257 preserve = kwargs.get("preserveWhitespace", False) 258 if preserve: 259 #print "PRESERVING" 260 NEWLINE = "" 261 TAB = "" 262 else: 263 #print "NOT PRESE" 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(''): #If the text is only text 285 f.write(_escape(str(self.text))) 286 else: #If the text is a spannedtext class 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
297 -class tspan(SVGelement):
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 """
308 - def __init__(self,text=None,**args):
309 SVGelement.__init__(self,'tspan',**args) 310 if self.text<>None: 311 self.text=text
312 - def __repr__(self):
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 """
331 - def __init__(self,link,**args):
332 SVGelement.__init__(self,'tref',{'xlink:href':link},**args)
333 - def __repr__(self):
334 s="<tref" 335 336 for key,value in self.attributes.items(): 337 s+= ' %s="%s"' % (key,value) 338 s+='/>' 339 return s
340
341 -class spannedtext:
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
401 -class ellipse(SVGelement):
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
427 -class circle(SVGelement):
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
447 -class point(circle):
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):
454 circle.__init__(self,x,y,1,fill,**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
476 -class polyline(SVGelement):
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
490 -class polygon(SVGelement):
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 #print "FOO PRE" 545 SVGelement.toXml(self,level, f, preserveWhitespace=True) 546 else: 547 #print "FOO NOT" 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
560 -class pattern(SVGelement):
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
580 -class title(SVGelement):
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 """
586 - def __init__(self,text=None,**args):
587 SVGelement.__init__(self,'title',**args) 588 if text<>None: 589 self.text=text
590
591 -class description(SVGelement):
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 """
597 - def __init__(self,text=None,**args):
598 SVGelement.__init__(self,'desc',**args) 599 if text<>None: 600 self.text=text
601
602 -class lineargradient(SVGelement):
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
621 -class radialgradient(SVGelement):
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
652 -class style(SVGelement):
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):
658 SVGelement.__init__(self,'style',{'type':type},cdata=cdata, **args)
659 660
661 -class image(SVGelement):
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
680 -class cursor(SVGelement):
681 """c=cursor(url,**args) 682 683 defines a custom cursor for a element or a drawing 684 """
685 - def __init__(self,url,**args):
686 SVGelement.__init__(self,'cursor',{'xlink:href':url},**args)
687 688
689 -class marker(SVGelement):
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
710 -class group(SVGelement):
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 """
716 - def __init__(self,id=None,**args):
717 SVGelement.__init__(self,'g',**args) 718 if id<>None: 719 self.attributes['id']=id
720
721 -class symbol(SVGelement):
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):
731 SVGelement.__init__(self,'symbol',**args) 732 if id<>None: 733 self.attributes['id']=id 734 if viewBox<>None: 735 self.attributes['viewBox']=_viewboxlist(viewBox)
736
737 -class defs(SVGelement):
738 """d=defs(**args) 739 740 container for defining elements 741 """
742 - def __init__(self,**args):
743 SVGelement.__init__(self,'defs',**args)
744
745 -class switch(SVGelement):
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 """
752 - def __init__(self,**args):
753 SVGelement.__init__(self,'switch',**args)
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 782
783 -class view(SVGelement):
784 """v=view(id,**args) 785 786 a view can be used to create a view with different attributes"""
787 - def __init__(self,id=None,**args):
788 SVGelement.__init__(self,'view',**args) 789 if id<>None: 790 self.attributes['id']=id
791
792 -class script(SVGelement):
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):
799 SVGelement.__init__(self,'script',{'type':type},cdata=cdata,**args)
800
801 -class animate(SVGelement):
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
815 -class animateMotion(SVGelement):
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):
821 SVGelement.__init__(self,'animateMotion',**args) 822 if pathdata<>None: 823 self.attributes['path']=str(pathdata) 824 if dur<>None: 825 self.attributes['dur']=dur
826
827 -class animateTransform(SVGelement):
828 """antr=animateTransform(type,from,to,dur,**args) 829 830 transform an element from and to a value. 831 """
832 - def __init__(self,type=None,fr=None,to=None,dur=None,**args):
833 SVGelement.__init__(self,'animateTransform',{'attributeName':'transform'},**args) 834 #As far as I know the attributeName is always transform 835 if type<>None: 836 self.attributes['type']=type 837 if fr<>None: 838 self.attributes['from']=fr 839 if to<>None: 840 self.attributes['to']=to 841 if dur<>None: 842 self.attributes['dur']=dur
843 -class animateColor(SVGelement):
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
897 -class drawing:
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
908 - def __init__(self):
909 self.svg=None
910 - def setSVG(self,svg):
911 self.svg=svg
912 #Voeg een element toe aan de grafiek toe. 913 if use_dom_implementation==0:
914 - def toXml(self, filename='',compress=False):
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:
945 - def toXml(self,filename='',compress=False):
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 #root is defined global so it can be used by the appender. Its also possible to use it as an arugument but 955 #that is a bit messy. 956 root=implementation.createDocument(None,None,doctype) 957 #Create the xml document. 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(): #in element.attributes is supported from python 2.2 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
1007 - def validate(self):
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