source: revtreeplugin/0.11/revtree/SVGdraw.py

Last change on this file was 3491, checked in by Emmanuel Blot, 15 years ago

Closes #2871: Firefox 3.0 is now supported.

Javascript w/ SVG has been rewritten within Gecko 1.9, which causes even more issues to jQuery 1.2.3

  • Property svn:eol-style set to native
File size: 37.2 KB
RevLine 
[1633]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
34SVGdraw uses an object model drawing and a method toXML to create SVG graphics
35by 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
53this results in the svg source of the drawing, which consists of a circle
54on a white background. Its as easy as that;)
55This module was created using the SVG specification of www.w3c.org and the
56O'Reilly (www.oreilly.com) python books as information sources. A svg viewer
57is available from www.adobe.com"""
58
[3491]59# Note: Emmanuel Blot, 2007, 2008:
[1911]60# The version has been updated to reflect the small changes made to
61# support SVG 1.1 and the hack to support inline SVG (in XHTML host document)
[3491]62__version__="1.0b"
[1633]63
64# there are two possibilities to generate svg:
65# via a dom implementation and directly using <element>text</element> strings
66# the latter is way faster (and shorter in coding)
67# the former is only used in debugging svg programs
68# maybe it will be removed alltogether after a while
69# with the following variable you indicate whether to use the dom implementation
70# Note that PyXML is required for using the dom implementation.
71# It is also possible to use the standard minidom. But I didn't try that one.
72# Anyway the text based approach is about 60 times faster than using the full dom implementation.
73use_dom_implementation=0
74
75
76import exceptions
77if use_dom_implementation<>0:
78    try:
79        from xml.dom import implementation
80        from xml.dom.ext import PrettyPrint
81    except:
82        raise exceptions.ImportError, "PyXML is required for using the dom implementation"
83#The implementation is used for the creating the XML document.
84#The prettyprint module is used for converting the xml document object to a xml file
85
86import sys
87assert sys.version_info[0]>=2
88if sys.version_info[1]<2:
89    True=1
90    False=0
91    file=open
92   
93sys.setrecursionlimit=50
94#The recursion limit is set conservative so mistakes like s=svg() s.addElement(s)
95#won't eat up too much processor time.
96
97#the following code is pasted form xml.sax.saxutils
98#it makes it possible to run the code without the xml sax package installed
99#To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts
100def _escape(data, entities={}):
101    """Escape &, <, and > in a string of data.
102
103    You can escape other strings of data by passing a dictionary as
104    the optional entities parameter.  The keys and values must all be
105    strings; each key will be replaced with its corresponding value.
106    """
107    data = data.replace("&", "&amp;")
108    data = data.replace("<", "&lt;")
109    data = data.replace(">", "&gt;")
110    for chars, entity in entities.items():
111        data = data.replace(chars, entity)
112    return data
113
114def _quoteattr(data, entities={}):
115    """Escape and quote an attribute value.
116
117    Escape &, <, and > in a string of data, then quote it for use as
118    an attribute value.  The \" character will be escaped as well, if
119    necessary.
120
121    You can escape other strings of data by passing a dictionary as
122    the optional entities parameter.  The keys and values must all be
123    strings; each key will be replaced with its corresponding value.
124    """
125    data = _escape(data, entities)
126    if '"' in data:
127        if "'" in data:
128            data = '"%s"' % data.replace('"', "&quot;")
129        else:
130            data = "'%s'" % data
131    else:
132        data = '"%s"' % data
133    return data
134
135
136
137def _xypointlist(a):
138    """formats a list of xy pairs"""
139    s=''
140    for e in a: #this could be done more elegant
141        s+=str(e)[1:-1] +'  '
142    return s
143
144def _viewboxlist(a):
145    """formats a tuple"""
146    s=''
147    for e in a: 
148        s+=str(e)+' '
149    return s
150
151def _pointlist(a):
152    """formats a list of numbers"""
153    return str(a)[1:-1]
154
155class pathdata:
156    """class used to create a pathdata object which can be used for a path.
157    although most methods are pretty straightforward it might be useful to look at the SVG specification."""
158    #I didn't test the methods below.
159    def __init__(self,x=None,y=None):
160        self.path=[]
161        if x is not None and y is not None:
162            self.path.append('M '+str(x)+' '+str(y))
163    def closepath(self):
164        """ends the path"""
165        self.path.append('z')
166    def move(self,x,y):
167        """move to absolute"""
168        self.path.append('M '+str(x)+' '+str(y))
169    def relmove(self,x,y):
170        """move to relative"""
171        self.path.append('m '+str(x)+' '+str(y))
172    def line(self,x,y):
173        """line to absolute"""
174        self.path.append('L '+str(x)+' '+str(y))
175    def relline(self,x,y):
176        """line to relative"""
177        self.path.append('l '+str(x)+' '+str(y))
178    def hline(self,x):
179        """horizontal line to absolute"""
180        self.path.append('H'+str(x))
181    def relhline(self,x):
182        """horizontal line to relative"""
183        self.path.append('h'+str(x))
184    def vline(self,y):
185        """verical line to absolute"""
186        self.path.append('V'+str(y))
187    def relvline(self,y):
188        """vertical line to relative"""
189        self.path.append('v'+str(y))
190    def bezier(self,x1,y1,x2,y2,x,y):
191        """bezier with xy1 and xy2 to xy absolut"""
192        self.path.append('C'+str(x1)+','+str(y1)+' '+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
193    def relbezier(self,x1,y1,x2,y2,x,y):
194        """bezier with xy1 and xy2 to xy relative"""
195        self.path.append('c'+str(x1)+','+str(y1)+' '+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
196    def smbezier(self,x2,y2,x,y):
197        """smooth bezier with xy2 to xy absolut"""
198        self.path.append('S'+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
199    def relsmbezier(self,x2,y2,x,y):
200        """smooth bezier with xy2 to xy relative"""
201        self.path.append('s'+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
202    def qbezier(self,x1,y1,x,y):
203        """quadratic bezier with xy1 to xy absolut"""
204        self.path.append('Q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y))
205    def relqbezier(self,x1,y1,x,y):
206        """quadratic bezier with xy1 to xy relative"""
207        self.path.append('q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y))
208    def smqbezier(self,x,y):
209        """smooth quadratic bezier to xy absolut"""
210        self.path.append('T'+str(x)+','+str(y))
211    def relsmqbezier(self,x,y):
212        """smooth quadratic bezier to xy relative"""
213        self.path.append('t'+str(x)+','+str(y))
214    def ellarc(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 absolut"""
216        self.path.append('A'+str(rx)+','+str(ry)+' '+str(xrot)+' '+str(laf)+' '+str(sf)+' '+str(x)+' '+str(y))
217    def relellarc(self,rx,ry,xrot,laf,sf,x,y):
218        """elliptival arc with rx and ry rotating with xrot using large-arc-flag and sweep-flag  to xy relative"""
219        self.path.append('a'+str(rx)+','+str(ry)+' '+str(xrot)+' '+str(laf)+' '+str(sf)+' '+str(x)+' '+str(y))
220    def __repr__(self):
221        return ' '.join(self.path)
222   
223
224
225     
226class SVGelement:
227    """SVGelement(type,attributes,elements,text,namespace,**args)
228    Creates a arbitrary svg element and is intended to be subclassed not used on its own.
229    This element is the base of every svg element it defines a class which resembles
230    a xml-element. The main advantage of this kind of implementation is that you don't
231    have to create a toXML method for every different graph object. Every element
232    consists of a type, attribute, optional subelements, optional text and an optional
233    namespace. Note the elements==None, if elements = None:self.elements=[] construction.
234    This is done because if you default to elements=[] every object has a reference
235    to the same empty list."""
236    def __init__(self,type='',attributes=None,elements=None,text='',namespace='',cdata=None,**args):
237        self.type=type
238        if attributes==None:
239            self.attributes={}
240        else:
241            self.attributes=attributes
242        if elements==None:
243            self.elements=[]
244        else:
245            self.elements=elements
246        self.text=text
247        self.namespace=namespace
248        self.cdata=cdata
249        for arg in args.keys():
250            self.attributes[arg]=args[arg]
251    def addElement(self,SVGelement):
252        """adds an element to a SVGelement
253
254        SVGelement.addElement(SVGelement)
255        """
256        self.elements.append(SVGelement)
257
[1911]258    def toXml(self,level,f,prefix=''):
[1633]259        f.write('\t'*level)
[1911]260        f.write('<'+prefix+self.type)
[1633]261        for attkey in self.attributes.keys():
262            f.write(' '+_escape(str(attkey))+'='+_quoteattr(str(self.attributes[attkey])))
263        if self.namespace:
264            if isinstance(self.namespace, dict):
265                for (k,v) in self.namespace.items():
266                    f.write(' %s="' % k + _escape(str(v))+'" ')
267            else:
268                f.write(' xmlns="'+ _escape(str(self.namespace))+'" ')
269        if self.elements or self.text or self.cdata:
270            f.write('>')
271        if self.elements:
272            f.write('\n')
273        for element in self.elements:
[1911]274            element.toXml(level+1,f,prefix)
[1633]275        if self.cdata:
276            f.write('\n'+'\t'*(level+1)+'<![CDATA[')
277            for line in self.cdata.splitlines():
278               f.write('\n'+'\t'*(level+2)+line)
279            f.write('\n'+'\t'*(level+1)+']]>\n')
280        if self.text:
281            if type(self.text)==type(''): #If the text is only text
282                f.write(_escape(str(self.text)))
283            else:                         #If the text is a spannedtext class
284                f.write(str(self.text))
285        if self.elements:
[1911]286            f.write('\t'*level+'</'+prefix+self.type+'>\n')
[1633]287        elif self.text: 
[1911]288            f.write('</'+prefix+self.type+'>\n')
[1633]289        elif self.cdata:
[1911]290            f.write('\t'*level+'</'+prefix+self.type+'>\n')
[1633]291        else:
292            f.write('/>\n')
293           
294class tspan(SVGelement):
295    """ts=tspan(text='',**args)
296
297    a tspan element can be used for applying formatting to a textsection
298    usage:
299    ts=tspan('this text is bold')
300    ts.attributes['font-weight']='bold'
301    st=spannedtext()
302    st.addtspan(ts)
303    t=text(3,5,st)
304    """
305    def __init__(self,text=None,**args):
306        SVGelement.__init__(self,'tspan',**args)
307        if self.text<>None:
308            self.text=text
309    def __repr__(self):
310        s="<tspan"
311        for key,value in self.attributes.items():
312         s+= ' %s="%s"' % (key,value)
313        s+='>'
314        s+=self.text
315        s+='</tspan>'
316        return s
317   
318class tref(SVGelement):
319    """tr=tref(link='',**args)
320
321    a tref element can be used for referencing text by a link to its id.
322    usage:
323    tr=tref('#linktotext')
324    st=spannedtext()
325    st.addtref(tr)
326    t=text(3,5,st)
327    """
328    def __init__(self,link,**args):
329        SVGelement.__init__(self,'tref',{'xlink:href':link},**args)
330    def __repr__(self):
331        s="<tref"
332       
333        for key,value in self.attributes.items():
334         s+= ' %s="%s"' % (key,value)
335        s+='/>'
336        return s
337   
338class spannedtext:
339    """st=spannedtext(textlist=[])
340
341    a spannedtext can be used for text which consists of text, tspan's and tref's
342    You can use it to add to a text element or path element. Don't add it directly
343    to a svg or a group element.
344    usage:
345   
346    ts=tspan('this text is bold')
347    ts.attributes['font-weight']='bold'
348    tr=tref('#linktotext')
349    tr.attributes['fill']='red'
350    st=spannedtext()
351    st.addtspan(ts)
352    st.addtref(tr)
353    st.addtext('This text is not bold')
354    t=text(3,5,st)
355    """
356    def __init__(self,textlist=None):
357        if textlist==None:
358            self.textlist=[]
359        else:
360            self.textlist=textlist
361    def addtext(self,text=''):
362        self.textlist.append(text)
363    def addtspan(self,tspan):
364        self.textlist.append(tspan)
365    def addtref(self,tref):
366        self.textlist.append(tref)
367    def __repr__(self):
368        s=""
369        for element in self.textlist:
370            s+=str(element)
371        return s
372   
373class rect(SVGelement):
374    """r=rect(x,y,width,height,fill,stroke,stroke_width,**args)
375   
376    a rectangle is defined by a width and height and a xy pair
377    """
378    def __init__(self,x=None,y=None,width=None,height=None,fill=None,stroke=None,stroke_width=None,**args):
379        if width==None or height==None:
380            if width<>None:
381                raise ValueError, 'height is required'
382            if height<>None:
383                raise ValueError, 'width is required'
384            else:
385                raise ValueError, 'both height and width are required'
386        SVGelement.__init__(self,'rect',{'width':width,'height':height},**args)
387        if x<>None:
388            self.attributes['x']=x
389        if y<>None:
390            self.attributes['y']=y
391        if fill<>None:
392            self.attributes['fill']=fill
393        if stroke<>None:
394            self.attributes['stroke']=stroke
395        if stroke_width<>None:
396            self.attributes['stroke-width']=stroke_width
397           
398class ellipse(SVGelement):
399    """e=ellipse(rx,ry,x,y,fill,stroke,stroke_width,**args)
400
401    an ellipse is defined as a center and a x and y radius.
402    """
403    def __init__(self,cx=None,cy=None,rx=None,ry=None,fill=None,stroke=None,stroke_width=None,**args):
404        if rx==None or ry== None:
405            if rx<>None:
406                raise ValueError, 'rx is required'
407            if ry<>None:
408                raise ValueError, 'ry is required'
409            else:
410                raise ValueError, 'both rx and ry are required'
411        SVGelement.__init__(self,'ellipse',{'rx':rx,'ry':ry},**args)
412        if cx<>None:
413            self.attributes['cx']=cx
414        if cy<>None:
415            self.attributes['cy']=cy
416        if fill<>None:
417            self.attributes['fill']=fill
418        if stroke<>None:
419            self.attributes['stroke']=stroke
420        if stroke_width<>None:
421            self.attributes['stroke-width']=stroke_width
422       
423   
424class circle(SVGelement):
425    """c=circle(x,y,radius,fill,stroke,stroke_width,**args)
426
427    The circle creates an element using a x, y and radius values eg
428    """
429    def __init__(self,cx=None,cy=None,r=None,fill=None,stroke=None,stroke_width=None,**args):
430        if r==None:
431            raise ValueError, 'r is required'
432        SVGelement.__init__(self,'circle',{'r':r},**args)
433        if cx<>None:
434            self.attributes['cx']=cx
435        if cy<>None:
436            self.attributes['cy']=cy
437        if fill<>None:
438            self.attributes['fill']=fill
439        if stroke<>None:
440            self.attributes['stroke']=stroke
441        if stroke_width<>None:
442            self.attributes['stroke-width']=stroke_width
443
444class point(circle):
445    """p=point(x,y,color)
446   
447    A point is defined as a circle with a size 1 radius. It may be more efficient to use a
448    very small rectangle if you use many points because a circle is difficult to render.
449    """
450    def __init__(self,x,y,fill='black',**args):
451        circle.__init__(self,x,y,1,fill,**args)
452
453class line(SVGelement):
454    """l=line(x1,y1,x2,y2,stroke,stroke_width,**args)
455   
456    A line is defined by a begin x,y pair and an end x,y pair
457    """
458    def __init__(self,x1=None,y1=None,x2=None,y2=None,stroke=None,stroke_width=None,**args):
459        SVGelement.__init__(self,'line',**args)
460        if x1<>None:
461            self.attributes['x1']=x1
462        if y1<>None:
463            self.attributes['y1']=y1
464        if x2<>None:
465            self.attributes['x2']=x2
466        if y2<>None:
467            self.attributes['y2']=y2
468        if stroke_width<>None:
469            self.attributes['stroke-width']=stroke_width
470        if stroke<>None:
471            self.attributes['stroke']=stroke
472           
473class polyline(SVGelement):
474    """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
475   
476    a polyline is defined by a list of xy pairs
477    """
478    def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args):
479        SVGelement.__init__(self,'polyline',{'points':_xypointlist(points)},**args)
480        if fill<>None:
481            self.attributes['fill']=fill
482        if stroke_width<>None:
483            self.attributes['stroke-width']=stroke_width
484        if stroke<>None:
485            self.attributes['stroke']=stroke
486
487class polygon(SVGelement):
488    """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
489   
490    a polygon is defined by a list of xy pairs
491    """
492    def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args):
493        SVGelement.__init__(self,'polygon',{'points':_xypointlist(points)},**args)
494        if fill<>None:
495            self.attributes['fill']=fill
496        if stroke_width<>None:
497            self.attributes['stroke-width']=stroke_width
498        if stroke<>None:
499            self.attributes['stroke']=stroke
500
501class path(SVGelement):
502    """p=path(path,fill,stroke,stroke_width,**args)
503
504    a path is defined by a path object and optional width, stroke and fillcolor
505    """
506    def __init__(self,pathdata,fill=None,stroke=None,stroke_width=None,id=None,**args):
507        SVGelement.__init__(self,'path',{'d':str(pathdata)},**args)
508        if stroke<>None:
509            self.attributes['stroke']=stroke
510        if fill<>None:
511            self.attributes['fill']=fill
512        if stroke_width<>None:
513            self.attributes['stroke-width']=stroke_width
514        if id<>None:
515            self.attributes['id']=id
516       
517       
518class text(SVGelement):
519    """t=text(x,y,text,font_size,font_family,**args)
520   
521    a text element can bge used for displaying text on the screen
522    """
523    def __init__(self,x=None,y=None,text=None,font_size=None,font_family=None,text_anchor=None,**args):
524        SVGelement.__init__(self,'text',**args)
525        if x<>None:
526            self.attributes['x']=x
527        if y<>None:
528            self.attributes['y']=y
529        if font_size<>None:
530            self.attributes['font-size']=font_size
531        if font_family<>None:
532            self.attributes['font-family']=font_family
533        if text<>None:
534            self.text=text
535        if text_anchor<>None:
536            self.attributes['text-anchor']=text_anchor
537
538
539class textpath(SVGelement):
540    """tp=textpath(text,link,**args)
541
542    a textpath places a text on a path which is referenced by a link.   
543    """
544    def __init__(self,link,text=None,**args):
545        SVGelement.__init__(self,'textPath',{'xlink:href':link},**args)
546        if text<>None:
547            self.text=text
548
549class pattern(SVGelement):
550    """p=pattern(x,y,width,height,patternUnits,**args)
551
552    A pattern is used to fill or stroke an object using a pre-defined
553    graphic object which can be replicated ("tiled") at fixed intervals
554    in x and y to cover the areas to be painted.
555    """
556    def __init__(self,x=None,y=None,width=None,height=None,patternUnits=None,**args):
557        SVGelement.__init__(self,'pattern',**args)
558        if x<>None:
559            self.attributes['x']=x
560        if y<>None:
561            self.attributes['y']=y
562        if width<>None:
563            self.attributes['width']=width
564        if height<>None:
565            self.attributes['height']=height
566        if patternUnits<>None:
567            self.attributes['patternUnits']=patternUnits
568
569class title(SVGelement):
570    """t=title(text,**args)
571   
572    a title is a text element. The text is displayed in the title bar
573    add at least one to the root svg element
574    """
575    def __init__(self,text=None,**args):
576        SVGelement.__init__(self,'title',**args)
577        if text<>None:
578            self.text=text
579
580class description(SVGelement):
581    """d=description(text,**args)
582   
583    a description can be added to any element and is used for a tooltip
584    Add this element before adding other elements.
585    """
586    def __init__(self,text=None,**args):
587        SVGelement.__init__(self,'desc',**args)
588        if text<>None:
589            self.text=text
590
591class lineargradient(SVGelement):
592    """lg=lineargradient(x1,y1,x2,y2,id,**args)
593
594    defines a lineargradient using two xy pairs.
595    stop elements van be added to define the gradient colors.
596    """
597    def __init__(self,x1=None,y1=None,x2=None,y2=None,id=None,**args):
598        SVGelement.__init__(self,'linearGradient',**args)
599        if x1<>None:
600            self.attributes['x1']=x1
601        if y1<>None:
602            self.attributes['y1']=y1
603        if x2<>None:
604            self.attributes['x2']=x2
605        if y2<>None:
606            self.attributes['y2']=y2
607        if id<>None:
608            self.attributes['id']=id
609
610class radialgradient(SVGelement):
611    """rg=radialgradient(cx,cy,r,fx,fy,id,**args)
612
613    defines a radial gradient using a outer circle which are defined by a cx,cy and r and by using a focalpoint.
614    stop elements van be added to define the gradient colors.
615    """
616    def __init__(self,cx=None,cy=None,r=None,fx=None,fy=None,id=None,**args):
617        SVGelement.__init__(self,'radialGradient',**args)
618        if cx<>None:
619            self.attributes['cx']=cx
620        if cy<>None:
621            self.attributes['cy']=cy
622        if r<>None:
623            self.attributes['r']=r
624        if fx<>None:
625            self.attributes['fx']=fx
626        if fy<>None:
627            self.attributes['fy']=fy
628        if id<>None:
629            self.attributes['id']=id
630           
631class stop(SVGelement):
632    """st=stop(offset,stop_color,**args)
633
634    Puts a stop color at the specified radius
635    """
636    def __init__(self,offset,stop_color=None,**args):
637        SVGelement.__init__(self,'stop',{'offset':offset},**args)
638        if stop_color<>None:
639            self.attributes['stop-color']=stop_color
640           
641class style(SVGelement):
642    """st=style(type,cdata=None,**args)
643
644    Add a CDATA element to this element for defing in line stylesheets etc..
645    """
646    def __init__(self,type,cdata=None,**args):
647        SVGelement.__init__(self,'style',{'type':type},cdata=cdata, **args)
648       
649           
650class image(SVGelement):
651    """im=image(url,width,height,x,y,**args)
652
653    adds an image to the drawing. Supported formats are .png, .jpg and .svg.
654    """
655    def __init__(self,url,x=None,y=None,width=None,height=None,**args):
656        if width==None or height==None:
657            if width<>None:
658                raise ValueError, 'height is required'
659            if height<>None:
660                raise ValueError, 'width is required'
661            else:
662                raise ValueError, 'both height and width are required'
663        SVGelement.__init__(self,'image',{'xlink:href':url,'width':width,'height':height},**args)
664        if x<>None:
665            self.attributes['x']=x
666        if y<>None:
667            self.attributes['y']=y
668 
669class cursor(SVGelement):
670    """c=cursor(url,**args)
671
672    defines a custom cursor for a element or a drawing
673    """
674    def __init__(self,url,**args):
675        SVGelement.__init__(self,'cursor',{'xlink:href':url},**args)
676
677   
678class marker(SVGelement):
679    """m=marker(id,viewbox,refX,refY,markerWidth,markerHeight,**args)
680   
681    defines a marker which can be used as an endpoint for a line or other pathtypes
682    add an element to it which should be used as a marker.
683    """
684    def __init__(self,id=None,viewBox=None,refx=None,refy=None,markerWidth=None,markerHeight=None,**args):
685        SVGelement.__init__(self,'marker',**args)
686        if id<>None:
687            self.attributes['id']=id
688        if viewBox<>None:
689            self.attributes['viewBox']=_viewboxlist(viewBox)
690        if refx<>None:
691            self.attributes['refX']=refx
692        if refy<>None:
693            self.attributes['refY']=refy
694        if markerWidth<>None:
695            self.attributes['markerWidth']=markerWidth
696        if markerHeight<>None:
697            self.attributes['markerHeight']=markerHeight
698       
699class group(SVGelement):
700    """g=group(id,**args)
701   
702    a group is defined by an id and is used to contain elements
703    g.addElement(SVGelement)
704    """
705    def __init__(self,id=None,**args):
706        SVGelement.__init__(self,'g',**args)
707        if id<>None:
708            self.attributes['id']=id
709
710class symbol(SVGelement):
711    """sy=symbol(id,viewbox,**args)
712
713    defines a symbol which can be used on different places in your graph using
714    the use element. A symbol is not rendered but you can use 'use' elements to
715    display it by referencing its id.
716    sy.addElement(SVGelement)
717    """
718   
719    def __init__(self,id=None,viewBox=None,**args):
720        SVGelement.__init__(self,'symbol',**args)
721        if id<>None:
722            self.attributes['id']=id
723        if viewBox<>None:
724            self.attributes['viewBox']=_viewboxlist(viewBox)
725           
726class defs(SVGelement):
727    """d=defs(**args)
728
729    container for defining elements
730    """
731    def __init__(self,**args):
732        SVGelement.__init__(self,'defs',**args)
733
734class switch(SVGelement):
735    """sw=switch(**args)
736
737    Elements added to a switch element which are "switched" by the attributes
738    requiredFeatures, requiredExtensions and systemLanguage.
739    Refer to the SVG specification for details.
740    """
741    def __init__(self,**args):
742        SVGelement.__init__(self,'switch',**args)
743
744       
745class use(SVGelement):
746    """u=use(link,x,y,width,height,**args)
747   
748    references a symbol by linking to its id and its position, height and width
749    """
750    def __init__(self,link,x=None,y=None,width=None,height=None,**args):
751        SVGelement.__init__(self,'use',{'xlink:href':link},**args)
752        if x<>None:
753            self.attributes['x']=x
754        if y<>None:
755            self.attributes['y']=y
756
757        if width<>None:
758            self.attributes['width']=width
759        if height<>None:
760            self.attributes['height']=height
761           
762           
763class link(SVGelement):
764    """a=link(url,**args)
765
766    a link  is defined by a hyperlink. add elements which have to be linked
767    a.addElement(SVGelement)
768    """
769    def __init__(self,link='',**args):
770        SVGelement.__init__(self,'a',{'xlink:href':link},**args)
771       
772class view(SVGelement):
773    """v=view(id,**args)
774
775    a view can be used to create a view with different attributes"""
776    def __init__(self,id=None,**args):
777        SVGelement.__init__(self,'view',**args)
778        if id<>None:
779            self.attributes['id']=id
780
781class script(SVGelement):
782    """sc=script(type,type,cdata,**args)
783
784    adds a script element which contains CDATA to the SVG drawing
785
786    """
787    def __init__(self,type,cdata=None,**args):
788        SVGelement.__init__(self,'script',{'type':type},cdata=cdata,**args)
789       
790class animate(SVGelement):
791    """an=animate(attribute,from,to,during,**args)
792
793    animates an attribute.   
794    """
795    def __init__(self,attribute,fr=None,to=None,dur=None,**args):
796        SVGelement.__init__(self,'animate',{'attributeName':attribute},**args)
797        if fr<>None:
798            self.attributes['from']=fr
799        if to<>None:
800            self.attributes['to']=to
801        if dur<>None:
802            self.attributes['dur']=dur
803       
804class animateMotion(SVGelement):
805    """an=animateMotion(pathdata,dur,**args)
806
807    animates a SVGelement over the given path in dur seconds
808    """
809    def __init__(self,pathdata,dur,**args):
810        SVGelement.__init__(self,'animateMotion',**args)
811        if pathdata<>None:
812            self.attributes['path']=str(pathdata)
813        if dur<>None:
814            self.attributes['dur']=dur
815
816class animateTransform(SVGelement):
817    """antr=animateTransform(type,from,to,dur,**args)
818   
819    transform an element from and to a value.
820    """
821    def __init__(self,type=None,fr=None,to=None,dur=None,**args):
822        SVGelement.__init__(self,'animateTransform',{'attributeName':'transform'},**args)
823        #As far as I know the attributeName is always transform
824        if type<>None:
825            self.attributes['type']=type
826        if fr<>None:
827            self.attributes['from']=fr
828        if to<>None:
829            self.attributes['to']=to
830        if dur<>None:
831            self.attributes['dur']=dur
832class animateColor(SVGelement):
833    """ac=animateColor(attribute,type,from,to,dur,**args)
834
835    Animates the color of a element
836    """
837    def __init__(self,attribute,type=None,fr=None,to=None,dur=None,**args):
838        SVGelement.__init__(self,'animateColor',{'attributeName':attribute},**args)
839        if type<>None:
840            self.attributes['type']=type
841        if fr<>None:
842            self.attributes['from']=fr
843        if to<>None:
844            self.attributes['to']=to
845        if dur<>None:
846            self.attributes['dur']=dur       
847class set(SVGelement):
848    """st=set(attribute,to,during,**args)
849   
850    sets an attribute to a value for a
851    """
852    def __init__(self,attribute,to=None,dur=None,**args):
853        SVGelement.__init__(self,'set',{'attributeName':attribute},**args)
854        if to<>None:
855            self.attributes['to']=to
856        if dur<>None:
857            self.attributes['dur']=dur
858
859
860           
861class svg(SVGelement):
862    """s=svg(viewbox,width,height,**args)
863   
864    a svg or element is the root of a drawing add all elements to a svg element.
865    You can have different svg elements in one svg file
866    s.addElement(SVGelement)
867
868    eg
869    d=drawing()
870    s=svg((0,0,100,100),'100%','100%')
871    c=circle(50,50,20)
872    s.addElement(c)
873    d.setSVG(s)
874    d.toXml()
875    """
[1911]876    def __init__(self,viewBox=None, width=None, height=None,
877                 svgns=None,**args):
[1633]878        SVGelement.__init__(self,'svg',**args)
[1911]879        self._svgns = svgns
[1633]880        if viewBox<>None:
881            self.attributes['viewBox']=_viewboxlist(viewBox)
882        if width<>None:
883            self.attributes['width']=width
884        if height<>None:
885            self.attributes['height']=height
[1911]886        ns = svgns and 'xmlns:svg' or 'xmlns'
887        self.namespace = { ns: "http://www.w3.org/2000/svg",
[3491]888                           'xmlns:xlink': "http://www.w3.org/1999/xlink",
889                           'version': '1.1' }
[1911]890                           
891    def toXml(self,level,f):
892        SVGelement.toXml(self,level,f,self._svgns and 'svg:' or '')
[1633]893       
894class drawing:
895    """d=drawing()
896
897    this is the actual SVG document. It needs a svg element as a root.
898    Use the addSVG method to set the svg to the root. Use the toXml method to write the SVG
899    source to the screen or to a file
900    d=drawing()
901    d.addSVG(svg)
902    d.toXml(optionalfilename)
903    """
904
905    def __init__(self):
906        self.svg=None
907    def setSVG(self,svg):
908        self.svg=svg
909        #Voeg een element toe aan de grafiek toe.
910    if use_dom_implementation==0:     
911        def toXml(self, filename='',compress=False):
912            import cStringIO
913            xml=cStringIO.StringIO()
914            xml.write("<?xml version='1.0' encoding='UTF-8'?>\n")
915            xml.write("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd \">\n")     
916            self.svg.toXml(0,xml)
917            if not filename:
918                if compress:
919                    import gzip
920                    f=cStringIO.StringIO()
921                    zf=gzip.GzipFile(fileobj=f,mode='wb')
922                    zf.write(xml.getvalue())
923                    zf.close()
924                    f.seek(0)
925                    return f.read()
926                else:
927                    return xml.getvalue()
928            else:
929                if filename[-4:]=='svgz':
930                    import gzip
931                    f=gzip.GzipFile(filename=filename,mode="wb", compresslevel=9)
932                    f.write(xml.getvalue())
933                    f.close()
934                else:
935                    f=file(filename,'w')
936                    f.write(xml.getvalue())
937                    f.close()
938
939    else:
940        def toXml(self,filename='',compress=False):
941            """drawing.toXml()        ---->to the screen
942            drawing.toXml(filename)---->to the file
943            writes a svg drawing to the screen or to a file
944            compresses if filename ends with svgz or if compress is true
945            """
946            doctype = implementation.createDocumentType('svg',"-//W3C//DTD SVG 1.0//EN""",'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd ')
947       
948            global root
949            #root is defined global so it can be used by the appender. Its also possible to use it as an arugument but
950            #that is a bit messy.
951            root=implementation.createDocument(None,None,doctype)
952            #Create the xml document.
953            global appender
954            def appender(element,elementroot):
955                """This recursive function appends elements to an element and sets the attributes
956                and type. It stops when alle elements have been appended"""
957                if element.namespace:
958                    e=root.createElementNS(element.namespace,element.type)
959                else:
960                    e=root.createElement(element.type)
961                if element.text:
962                    textnode=root.createTextNode(element.text)
963                    e.appendChild(textnode)
964                for attribute in element.attributes.keys():   #in element.attributes is supported from python 2.2
965                    e.setAttribute(attribute,str(element.attributes[attribute]))
966                if element.elements:
967                    for el in element.elements:
968                        e=appender(el,e)
969                elementroot.appendChild(e)
970                return elementroot
971            root=appender(self.svg,root)
972            if not filename:
973                import cStringIO
974                xml=cStringIO.StringIO()
975                PrettyPrint(root,xml)
976                if compress:
977                    import gzip
978                    f=cStringIO.StringIO()
979                    zf=gzip.GzipFile(fileobj=f,mode='wb')
980                    zf.write(xml.getvalue())
981                    zf.close()
982                    f.seek(0)
983                    return f.read()
984                else:
985                    return xml.getvalue()
986            else:
987                try:
988                    if filename[-4:]=='svgz':
989                        import gzip
990                        import cStringIO
991                        xml=cStringIO.StringIO()
992                        PrettyPrint(root,xml)
993                        f=gzip.GzipFile(filename=filename,mode='wb',compresslevel=9)
994                        f.write(xml.getvalue())
995                        f.close()
996                    else:
997                        f=open(filename,'w')
998                        PrettyPrint(root,f)
999                        f.close()
1000                except:
1001                    print "Cannot write SVG file: " + filename
1002    def validate(self):
1003        try:
1004            import xml.parsers.xmlproc.xmlval
1005        except:
1006            raise exceptions.ImportError,'PyXml is required for validating SVG'
1007        svg=self.toXml()
1008        xv=xml.parsers.xmlproc.xmlval.XMLValidator()
1009        try:
1010            xv.feed(svg)
1011        except:
1012            raise "SVG is not well formed, see messages above"
1013        else:
1014            print "SVG well formed"
1015
1016if __name__=='__main__':
1017    d=drawing()
1018    s=svg((0,0,100,100))
1019    r=rect(-100,-100,300,300,'cyan')
1020    s.addElement(r)
1021   
1022    t=title('SVGdraw Demo')
1023    s.addElement(t)
1024    g=group('animations')
1025    e=ellipse(0,0,5,2)
1026    g.addElement(e)
1027    c=circle(0,0,1,'red')
1028    g.addElement(c)
1029    pd=pathdata(0,-10)
1030    for i in range(6):
1031        pd.relsmbezier(10,5,0,10)
1032        pd.relsmbezier(-10,5,0,10)
1033    an=animateMotion(pd,10)
1034    an.attributes['rotate']='auto-reverse'
1035    an.attributes['repeatCount']="indefinite"
1036    g.addElement(an)
1037    s.addElement(g)
1038    for i in range(20,120,20):
1039        u=use('#animations',i,0)
1040        s.addElement(u)
1041    for i in range(0,120,20):
1042        for j in range(5,105,10):
1043            c=circle(i,j,1,'red','black',.5)
1044            s.addElement(c)
1045    d.setSVG(s)
1046     
1047    print d.toXml()
1048
Note: See TracBrowser for help on using the repository browser.