aboutsummaryrefslogtreecommitdiff
path: root/gn2/utility/svg.py
diff options
context:
space:
mode:
Diffstat (limited to 'gn2/utility/svg.py')
-rw-r--r--gn2/utility/svg.py1179
1 files changed, 1179 insertions, 0 deletions
diff --git a/gn2/utility/svg.py b/gn2/utility/svg.py
new file mode 100644
index 00000000..912cd04c
--- /dev/null
+++ b/gn2/utility/svg.py
@@ -0,0 +1,1179 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# Contact Drs. Robert W. Williams and Xiaodong Zhou (2010)
+# at rwilliams@uthsc.edu and xzhou15@uthsc.edu
+#
+#
+#
+# This module is used by GeneNetwork project (www.genenetwork.org)
+#
+# Created by GeneNetwork Core Team 2010/08/10
+#
+# Last updated by GeneNetwork Core Team 2010/10/20
+
+#!/usr/bin/env python
+# Copyright (c) 2002, Fedor Baart & Hans de Wit (Stichting Farmaceutische Kengetallen)
+# All rights reserved.
+##
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+##
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+##
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation and/or
+# other materials provided with the distribution.
+##
+# Neither the name of the Stichting Farmaceutische Kengetallen nor the names of
+# its contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+##
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Thanks to Gerald Rosennfellner for his help and useful comments.
+
+import sys
+import exceptions
+__doc__ = """Use SVGdraw to generate your SVGdrawings.
+
+SVGdraw uses an object model drawing and a method toXML to create SVG graphics
+by using easy to use classes and methods usualy you start by creating a drawing eg
+
+ d=drawing()
+ # then you create a SVG root element
+ s=svg()
+ # then you add some elements eg a circle and add it to the svg root element
+ c=circle()
+ # you can supply attributes by using named arguments.
+ c=circle(fill='red',stroke='blue')
+ # or by updating the attributes attribute:
+ c.attributes['stroke-width']=1
+ s.addElement(c)
+ # then you add the svg root element to the drawing
+ d.setSVG(s)
+ # and finaly you xmlify the drawing
+ d.toXml()
+
+
+this results in the svg source of the drawing, which consists of a circle
+on a white background. Its as easy as that;)
+This module was created using the SVG specification of www.w3c.org and the
+O'Reilly (www.oreilly.com) python books as information sources. A svg viewer
+is available from www.adobe.com"""
+
+__version__ = "1.0"
+
+# there are two possibilities to generate svg:
+# via a dom implementation and directly using <element>text</element> strings
+# the latter is way faster (and shorter in coding)
+# the former is only used in debugging svg programs
+# maybe it will be removed alltogether after a while
+# with the following variable you indicate whether to use the dom implementation
+# Note that PyXML is required for using the dom implementation.
+# It is also possible to use the standard minidom. But I didn't try that one.
+# Anyway the text based approach is about 60 times faster than using the full dom implementation.
+use_dom_implementation = 0
+
+
+if use_dom_implementation != 0:
+ try:
+ from xml.dom import implementation
+ from xml.dom.ext import PrettyPrint
+ except:
+ raise exceptions.ImportError(
+ "PyXML is required for using the dom implementation")
+# The implementation is used for the creating the XML document.
+# The prettyprint module is used for converting the xml document object to a xml file
+
+sys.setrecursionlimit = 50
+# The recursion limit is set conservative so mistakes like s=svg() s.addElement(s)
+# won't eat up too much processor time.
+
+# the following code is pasted form xml.sax.saxutils
+# it makes it possible to run the code without the xml sax package installed
+# To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts
+
+
+def _escape(data, entities={}):
+ """Escape &, <, and > in a string of data.
+
+ You can escape other strings of data by passing a dictionary as
+ the optional entities parameter. The keys and values must all be
+ strings; each key will be replaced with its corresponding value.
+ """
+ # data = data.replace("&", "&amp;")
+ data = data.replace("<", "&lt;")
+ data = data.replace(">", "&gt;")
+ for chars, entity in list(entities.items()):
+ data = data.replace(chars, entity)
+ return data
+
+
+def _quoteattr(data, entities={}):
+ """Escape and quote an attribute value.
+
+ Escape &, <, and > in a string of data, then quote it for use as
+ an attribute value. The \" character will be escaped as well, if
+ necessary.
+
+ You can escape other strings of data by passing a dictionary as
+ the optional entities parameter. The keys and values must all be
+ strings; each key will be replaced with its corresponding value.
+ """
+ data = _escape(data, entities)
+ if '"' in data:
+ if "'" in data:
+ data = '"%s"' % data.replace('"', "&quot;")
+ else:
+ data = "'%s'" % data
+ else:
+ data = '"%s"' % data
+ return data
+
+
+def _xypointlist(a):
+ """formats a list of xy pairs"""
+ s = ''
+ for e in a: # this could be done more elegant
+ s += str(e)[1:-1] + ' '
+ return s
+
+
+def _viewboxlist(a):
+ """formats a tuple"""
+ s = ''
+ for e in a:
+ s += str(e) + ' '
+ return s
+
+
+def _pointlist(a):
+ """formats a list of numbers"""
+ return str(a)[1:-1]
+
+
+class pathdata:
+ """class used to create a pathdata object which can be used for a path.
+ although most methods are pretty straightforward it might be useful to look at the SVG specification."""
+ # I didn't test the methods below.
+
+ def __init__(self, x=None, y=None):
+ self.path = []
+ if x is not None and y is not None:
+ self.path.append('M ' + str(x) + ' ' + str(y))
+
+ def closepath(self):
+ """ends the path"""
+ self.path.append('z')
+
+ def move(self, x, y):
+ """move to absolute"""
+ self.path.append('M ' + str(x) + ' ' + str(y))
+
+ def relmove(self, x, y):
+ """move to relative"""
+ self.path.append('m ' + str(x) + ' ' + str(y))
+
+ def line(self, x, y):
+ """line to absolute"""
+ self.path.append('L ' + str(x) + ' ' + str(y))
+
+ def relline(self, x, y):
+ """line to relative"""
+ self.path.append('l ' + str(x) + ' ' + str(y))
+
+ def hline(self, x):
+ """horizontal line to absolute"""
+ self.path.append('H' + str(x))
+
+ def relhline(self, x):
+ """horizontal line to relative"""
+ self.path.append('h' + str(x))
+
+ def vline(self, y):
+ """verical line to absolute"""
+ self.path.append('V' + str(y))
+
+ def relvline(self, y):
+ """vertical line to relative"""
+ self.path.append('v' + str(y))
+
+ def bezier(self, x1, y1, x2, y2, x, y):
+ """bezier with xy1 and xy2 to xy absolut"""
+ self.path.append('C' + str(x1) + ',' + str(y1) + ' ' + str(x2)
+ + ',' + str(y2) + ' ' + str(x) + ',' + str(y))
+
+ def relbezier(self, x1, y1, x2, y2, x, y):
+ """bezier with xy1 and xy2 to xy relative"""
+ self.path.append('c' + str(x1) + ',' + str(y1) + ' ' + str(x2)
+ + ',' + str(y2) + ' ' + str(x) + ',' + str(y))
+
+ def smbezier(self, x2, y2, x, y):
+ """smooth bezier with xy2 to xy absolut"""
+ self.path.append('S' + str(x2) + ',' + str(y2) + \
+ ' ' + str(x) + ',' + str(y))
+
+ def relsmbezier(self, x2, y2, x, y):
+ """smooth bezier with xy2 to xy relative"""
+ self.path.append('s' + str(x2) + ',' + str(y2) + \
+ ' ' + str(x) + ',' + str(y))
+
+ def qbezier(self, x1, y1, x, y):
+ """quadratic bezier with xy1 to xy absolut"""
+ self.path.append('Q' + str(x1) + ',' + str(y1) + \
+ ' ' + str(x) + ',' + str(y))
+
+ def relqbezier(self, x1, y1, x, y):
+ """quadratic bezier with xy1 to xy relative"""
+ self.path.append('q' + str(x1) + ',' + str(y1) + \
+ ' ' + str(x) + ',' + str(y))
+
+ def smqbezier(self, x, y):
+ """smooth quadratic bezier to xy absolut"""
+ self.path.append('T' + str(x) + ',' + str(y))
+
+ def relsmqbezier(self, x, y):
+ """smooth quadratic bezier to xy relative"""
+ self.path.append('t' + str(x) + ',' + str(y))
+
+ def ellarc(self, rx, ry, xrot, laf, sf, x, y):
+ """elliptival arc with rx and ry rotating with xrot using large-arc-flag and sweep-flag to xy absolut"""
+ self.path.append('A' + str(rx) + ',' + str(ry) + ' ' + str(xrot)
+ + ' ' + str(laf) + ' ' + str(sf) + ' ' + str(x) + ' ' + str(y))
+
+ def relellarc(self, rx, ry, xrot, laf, sf, x, y):
+ """elliptival arc with rx and ry rotating with xrot using large-arc-flag and sweep-flag to xy relative"""
+ self.path.append('a' + str(rx) + ',' + str(ry) + ' ' + str(xrot)
+ + ' ' + str(laf) + ' ' + str(sf) + ' ' + str(x) + ' ' + str(y))
+
+ def __repr__(self):
+ return ' '.join(self.path)
+
+
+class SVGelement:
+ """SVGelement(type,attributes,elements,text,namespace,**args)
+ Creates a arbitrary svg element and is intended to be subclassed not used on its own.
+ This element is the base of every svg element it defines a class which resembles
+ a xml-element. The main advantage of this kind of implementation is that you don't
+ have to create a toXML method for every different graph object. Every element
+ consists of a type, attribute, optional subelements, optional text and an optional
+ namespace. Note the elements==None, if elements = None:self.elements=[] construction.
+ This is done because if you default to elements=[] every object has a reference
+ to the same empty list."""
+
+ def __init__(self, type='', attributes=None, elements=None, text='', namespace='', cdata=None, **args):
+ self.type = type
+ if attributes == None:
+ self.attributes = {}
+ else:
+ self.attributes = attributes
+ if elements == None:
+ self.elements = []
+ else:
+ self.elements = elements
+ self.text = text
+ self.namespace = namespace
+ self.cdata = cdata
+ for arg in list(args.keys()):
+ arg2 = arg.replace("__", ":")
+ arg2 = arg2.replace("_", "-")
+ self.attributes[arg2] = args[arg]
+
+ def addElement(self, SVGelement):
+ """adds an element to a SVGelement
+
+ SVGelement.addElement(SVGelement)
+ """
+ self.elements.append(SVGelement)
+
+ def toXml(self, level, f):
+ f.write('\t' * level)
+ f.write('<' + self.type)
+ for attkey in list(self.attributes.keys()):
+ f.write(' ' + _escape(str(attkey)) + '='
+ + _quoteattr(str(self.attributes[attkey])))
+ if self.namespace:
+ f.write(' xmlns="' + _escape(str(self.namespace))
+ + '" xmlns:xlink="http://www.w3.org/1999/xlink"')
+ if self.elements or self.text or self.cdata:
+ f.write('>')
+ if self.elements:
+ f.write('\n')
+ for element in self.elements:
+ element.toXml(level + 1, f)
+ if self.cdata:
+ f.write('\n' + '\t' * (level + 1) + '<![CDATA[')
+ for line in self.cdata.splitlines():
+ f.write('\n' + '\t' * (level + 2) + line)
+ f.write('\n' + '\t' * (level + 1) + ']]>\n')
+ if self.text:
+ if isinstance(self.text, type('')): # If the text is only text
+ f.write(_escape(str(self.text)))
+ else: # If the text is a spannedtext class
+ f.write(str(self.text))
+ if self.elements:
+ f.write('\t' * level + '</' + self.type + '>\n')
+ elif self.text:
+ f.write('</' + self.type + '>\n')
+ elif self.cdata:
+ f.write('\t' * level + '</' + self.type + '>\n')
+ else:
+ f.write('/>\n')
+
+
+class tspan(SVGelement):
+ """ts=tspan(text='',**args)
+
+ a tspan element can be used for applying formatting to a textsection
+ usage:
+ ts=tspan('this text is bold')
+ ts.attributes['font-weight']='bold'
+ st=spannedtext()
+ st.addtspan(ts)
+ t=text(3,5,st)
+ """
+
+ def __init__(self, text=None, **args):
+ SVGelement.__init__(self, 'tspan', **args)
+ if self.text != None:
+ self.text = text
+
+ def __repr__(self):
+ s = "<tspan"
+ for key, value in list(self.attributes.items()):
+ s += ' %s="%s"' % (key, value)
+ s += '>'
+ s += self.text
+ s += '</tspan>'
+ return s
+
+
+class tref(SVGelement):
+ """tr=tref(link='',**args)
+
+ a tref element can be used for referencing text by a link to its id.
+ usage:
+ tr=tref('#linktotext')
+ st=spannedtext()
+ st.addtref(tr)
+ t=text(3,5,st)
+ """
+
+ def __init__(self, link, **args):
+ SVGelement.__init__(self, 'tref', {'xlink:href': link}, **args)
+
+ def __repr__(self):
+ s = "<tref"
+
+ for key, value in list(self.attributes.items()):
+ s += ' %s="%s"' % (key, value)
+ s += '/>'
+ return s
+
+
+class spannedtext:
+ """st=spannedtext(textlist=[])
+
+ a spannedtext can be used for text which consists of text, tspan's and tref's
+ You can use it to add to a text element or path element. Don't add it directly
+ to a svg or a group element.
+ usage:
+
+ ts=tspan('this text is bold')
+ ts.attributes['font-weight']='bold'
+ tr=tref('#linktotext')
+ tr.attributes['fill']='red'
+ st=spannedtext()
+ st.addtspan(ts)
+ st.addtref(tr)
+ st.addtext('This text is not bold')
+ t=text(3,5,st)
+ """
+
+ def __init__(self, textlist=None):
+ if textlist == None:
+ self.textlist = []
+ else:
+ self.textlist = textlist
+
+ def addtext(self, text=''):
+ self.textlist.append(text)
+
+ def addtspan(self, tspan):
+ self.textlist.append(tspan)
+
+ def addtref(self, tref):
+ self.textlist.append(tref)
+
+ def __repr__(self):
+ s = ""
+ for element in self.textlist:
+ s += str(element)
+ return s
+
+
+class rect(SVGelement):
+ """r=rect(width,height,x,y,fill,stroke,stroke_width,**args)
+
+ a rectangle is defined by a width and height and a xy pair
+ """
+
+ def __init__(self, x=None, y=None, width=None, height=None, fill=None, stroke=None, stroke_width=None, **args):
+ if width == None or height == None:
+ raise ValueError('both height and width are required')
+
+ SVGelement.__init__(
+ self, 'rect', {'width': width, 'height': height}, **args)
+ if x != None:
+ self.attributes['x'] = x
+ if y != None:
+ self.attributes['y'] = y
+ if fill != None:
+ self.attributes['fill'] = fill
+ if stroke != None:
+ self.attributes['stroke'] = stroke
+ if stroke_width != None:
+ self.attributes['stroke-width'] = stroke_width
+
+
+class ellipse(SVGelement):
+ """e=ellipse(rx,ry,x,y,fill,stroke,stroke_width,**args)
+
+ an ellipse is defined as a center and a x and y radius.
+ """
+
+ def __init__(self, cx=None, cy=None, rx=None, ry=None, fill=None, stroke=None, stroke_width=None, **args):
+ if rx == None or ry == None:
+ raise ValueError('both rx and ry are required')
+
+ SVGelement.__init__(self, 'ellipse', {'rx': rx, 'ry': ry}, **args)
+ if cx != None:
+ self.attributes['cx'] = cx
+ if cy != None:
+ self.attributes['cy'] = cy
+ if fill != None:
+ self.attributes['fill'] = fill
+ if stroke != None:
+ self.attributes['stroke'] = stroke
+ if stroke_width != None:
+ self.attributes['stroke-width'] = stroke_width
+
+
+class circle(SVGelement):
+ """c=circle(x,y,radius,fill,stroke,stroke_width,**args)
+
+ The circle creates an element using a x, y and radius values eg
+ """
+
+ def __init__(self, cx=None, cy=None, r=None, fill=None, stroke=None, stroke_width=None, **args):
+ if r == None:
+ raise ValueError('r is required')
+ SVGelement.__init__(self, 'circle', {'r': r}, **args)
+ if cx != None:
+ self.attributes['cx'] = cx
+ if cy != None:
+ self.attributes['cy'] = cy
+ if fill != None:
+ self.attributes['fill'] = fill
+ if stroke != None:
+ self.attributes['stroke'] = stroke
+ if stroke_width != None:
+ self.attributes['stroke-width'] = stroke_width
+
+
+class point(circle):
+ """p=point(x,y,color)
+
+ A point is defined as a circle with a size 1 radius. It may be more efficient to use a
+ very small rectangle if you use many points because a circle is difficult to render.
+ """
+
+ def __init__(self, x, y, fill='black', **args):
+ circle.__init__(self, x, y, 1, fill, **args)
+
+
+class line(SVGelement):
+ """l=line(x1,y1,x2,y2,stroke,stroke_width,**args)
+
+ A line is defined by a begin x,y pair and an end x,y pair
+ """
+
+ def __init__(self, x1=None, y1=None, x2=None, y2=None, stroke=None, stroke_width=None, **args):
+ SVGelement.__init__(self, 'line', **args)
+ if x1 != None:
+ self.attributes['x1'] = x1
+ if y1 != None:
+ self.attributes['y1'] = y1
+ if x2 != None:
+ self.attributes['x2'] = x2
+ if y2 != None:
+ self.attributes['y2'] = y2
+ if stroke_width != None:
+ self.attributes['stroke-width'] = stroke_width
+ if stroke != None:
+ self.attributes['stroke'] = stroke
+
+
+class polyline(SVGelement):
+ """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
+
+ a polyline is defined by a list of xy pairs
+ """
+
+ def __init__(self, points, fill=None, stroke=None, stroke_width=None, **args):
+ SVGelement.__init__(self, 'polyline', {
+ 'points': _xypointlist(points)}, **args)
+ if fill != None:
+ self.attributes['fill'] = fill
+ if stroke_width != None:
+ self.attributes['stroke-width'] = stroke_width
+ if stroke != None:
+ self.attributes['stroke'] = stroke
+
+
+class polygon(SVGelement):
+ """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
+
+ a polygon is defined by a list of xy pairs
+ """
+
+ def __init__(self, points, fill=None, stroke=None, stroke_width=None, **args):
+ SVGelement.__init__(
+ self, 'polygon', {'points': _xypointlist(points)}, **args)
+ if fill != None:
+ self.attributes['fill'] = fill
+ if stroke_width != None:
+ self.attributes['stroke-width'] = stroke_width
+ if stroke != None:
+ self.attributes['stroke'] = stroke
+
+
+class path(SVGelement):
+ """p=path(path,fill,stroke,stroke_width,**args)
+
+ a path is defined by a path object and optional width, stroke and fillcolor
+ """
+
+ def __init__(self, pathdata, fill=None, stroke=None, stroke_width=None, id=None, **args):
+ SVGelement.__init__(self, 'path', {'d': str(pathdata)}, **args)
+ if stroke != None:
+ self.attributes['stroke'] = stroke
+ if fill != None:
+ self.attributes['fill'] = fill
+ if stroke_width != None:
+ self.attributes['stroke-width'] = stroke_width
+ if id != None:
+ self.attributes['id'] = id
+
+
+class text(SVGelement):
+ """t=text(x,y,text,font_size,font_family,**args)
+
+ a text element can bge used for displaying text on the screen
+ """
+
+ def __init__(self, x=None, y=None, text=None, font_size=None, font_family=None, text_anchor=None, **args):
+ SVGelement.__init__(self, 'text', **args)
+ if x != None:
+ self.attributes['x'] = x
+ if y != None:
+ self.attributes['y'] = y
+ if font_size != None:
+ self.attributes['font-size'] = font_size
+ if font_family != None:
+ self.attributes['font-family'] = font_family
+ if text != None:
+ self.text = text
+ if text_anchor != None:
+ self.attributes['text-anchor'] = text_anchor
+
+
+class textpath(SVGelement):
+ """tp=textpath(text,link,**args)
+
+ a textpath places a text on a path which is referenced by a link.
+ """
+
+ def __init__(self, link, text=None, **args):
+ SVGelement.__init__(self, 'textPath', {'xlink:href': link}, **args)
+ if text != None:
+ self.text = text
+
+
+class pattern(SVGelement):
+ """p=pattern(x,y,width,height,patternUnits,**args)
+
+ A pattern is used to fill or stroke an object using a pre-defined
+ graphic object which can be replicated ("tiled") at fixed intervals
+ in x and y to cover the areas to be painted.
+ """
+
+ def __init__(self, x=None, y=None, width=None, height=None, patternUnits=None, **args):
+ SVGelement.__init__(self, 'pattern', **args)
+ if x != None:
+ self.attributes['x'] = x
+ if y != None:
+ self.attributes['y'] = y
+ if width != None:
+ self.attributes['width'] = width
+ if height != None:
+ self.attributes['height'] = height
+ if patternUnits != None:
+ self.attributes['patternUnits'] = patternUnits
+
+
+class title(SVGelement):
+ """t=title(text,**args)
+
+ a title is a text element. The text is displayed in the title bar
+ add at least one to the root svg element
+ """
+
+ def __init__(self, text=None, **args):
+ SVGelement.__init__(self, 'title', **args)
+ if text != None:
+ self.text = text
+
+
+class description(SVGelement):
+ """d=description(text,**args)
+
+ a description can be added to any element and is used for a tooltip
+ Add this element before adding other elements.
+ """
+
+ def __init__(self, text=None, **args):
+ SVGelement.__init__(self, 'desc', **args)
+ if text != None:
+ self.text = text
+
+
+class lineargradient(SVGelement):
+ """lg=lineargradient(x1,y1,x2,y2,id,**args)
+
+ defines a lineargradient using two xy pairs.
+ stop elements van be added to define the gradient colors.
+ """
+
+ def __init__(self, x1=None, y1=None, x2=None, y2=None, id=None, **args):
+ SVGelement.__init__(self, 'linearGradient', **args)
+ if x1 != None:
+ self.attributes['x1'] = x1
+ if y1 != None:
+ self.attributes['y1'] = y1
+ if x2 != None:
+ self.attributes['x2'] = x2
+ if y2 != None:
+ self.attributes['y2'] = y2
+ if id != None:
+ self.attributes['id'] = id
+
+
+class radialgradient(SVGelement):
+ """rg=radialgradient(cx,cy,r,fx,fy,id,**args)
+
+ defines a radial gradient using a outer circle which are defined by a cx,cy and r and by using a focalpoint.
+ stop elements van be added to define the gradient colors.
+ """
+
+ def __init__(self, cx=None, cy=None, r=None, fx=None, fy=None, id=None, **args):
+ SVGelement.__init__(self, 'radialGradient', **args)
+ if cx != None:
+ self.attributes['cx'] = cx
+ if cy != None:
+ self.attributes['cy'] = cy
+ if r != None:
+ self.attributes['r'] = r
+ if fx != None:
+ self.attributes['fx'] = fx
+ if fy != None:
+ self.attributes['fy'] = fy
+ if id != None:
+ self.attributes['id'] = id
+
+
+class stop(SVGelement):
+ """st=stop(offset,stop_color,**args)
+
+ Puts a stop color at the specified radius
+ """
+
+ def __init__(self, offset, stop_color=None, **args):
+ SVGelement.__init__(self, 'stop', {'offset': offset}, **args)
+ if stop_color != None:
+ self.attributes['stop-color'] = stop_color
+
+
+class style(SVGelement):
+ """st=style(type,cdata=None,**args)
+
+ Add a CDATA element to this element for defing in line stylesheets etc..
+ """
+
+ def __init__(self, type, cdata=None, **args):
+ SVGelement.__init__(self, 'style', {'type': type}, cdata=cdata, **args)
+
+
+class image(SVGelement):
+ """im=image(url,width,height,x,y,**args)
+
+ adds an image to the drawing. Supported formats are .png, .jpg and .svg.
+ """
+
+ def __init__(self, url, x=None, y=None, width=None, height=None, **args):
+ if width == None or height == None:
+ raise ValueError('both height and width are required')
+ SVGelement.__init__(
+ self, 'image', {'xlink:href': url, 'width': width, 'height': height}, **args)
+ if x != None:
+ self.attributes['x'] = x
+ if y != None:
+ self.attributes['y'] = y
+
+
+class cursor(SVGelement):
+ """c=cursor(url,**args)
+
+ defines a custom cursor for a element or a drawing
+ """
+
+ def __init__(self, url, **args):
+ SVGelement.__init__(self, 'cursor', {'xlink:href': url}, **args)
+
+
+class marker(SVGelement):
+ """m=marker(id,viewbox,refX,refY,markerWidth,markerHeight,**args)
+
+ defines a marker which can be used as an endpoint for a line or other pathtypes
+ add an element to it which should be used as a marker.
+ """
+
+ def __init__(self, id=None, viewBox=None, refx=None, refy=None, markerWidth=None, markerHeight=None, **args):
+ SVGelement.__init__(self, 'marker', **args)
+ if id != None:
+ self.attributes['id'] = id
+ if viewBox != None:
+ self.attributes['viewBox'] = _viewboxlist(viewBox)
+ if refx != None:
+ self.attributes['refX'] = refx
+ if refy != None:
+ self.attributes['refY'] = refy
+ if markerWidth != None:
+ self.attributes['markerWidth'] = markerWidth
+ if markerHeight != None:
+ self.attributes['markerHeight'] = markerHeight
+
+
+class group(SVGelement):
+ """g=group(id,**args)
+
+ a group is defined by an id and is used to contain elements
+ g.addElement(SVGelement)
+ """
+
+ def __init__(self, id=None, **args):
+ SVGelement.__init__(self, 'g', **args)
+ if id != None:
+ self.attributes['id'] = id
+
+
+class symbol(SVGelement):
+ """sy=symbol(id,viewbox,**args)
+
+ defines a symbol which can be used on different places in your graph using
+ the use element. A symbol is not rendered but you can use 'use' elements to
+ display it by referencing its id.
+ sy.addElement(SVGelement)
+ """
+
+ def __init__(self, id=None, viewBox=None, **args):
+ SVGelement.__init__(self, 'symbol', **args)
+ if id != None:
+ self.attributes['id'] = id
+ if viewBox != None:
+ self.attributes['viewBox'] = _viewboxlist(viewBox)
+
+
+class defs(SVGelement):
+ """d=defs(**args)
+
+ container for defining elements
+ """
+
+ def __init__(self, **args):
+ SVGelement.__init__(self, 'defs', **args)
+
+
+class switch(SVGelement):
+ """sw=switch(**args)
+
+ Elements added to a switch element which are "switched" by the attributes
+ requiredFeatures, requiredExtensions and systemLanguage.
+ Refer to the SVG specification for details.
+ """
+
+ def __init__(self, **args):
+ SVGelement.__init__(self, 'switch', **args)
+
+
+class use(SVGelement):
+ """u=use(link,x,y,width,height,**args)
+
+ references a symbol by linking to its id and its position, height and width
+ """
+
+ def __init__(self, link, x=None, y=None, width=None, height=None, **args):
+ SVGelement.__init__(self, 'use', {'xlink:href': link}, **args)
+ if x != None:
+ self.attributes['x'] = x
+ if y != None:
+ self.attributes['y'] = y
+
+ if width != None:
+ self.attributes['width'] = width
+ if height != None:
+ self.attributes['height'] = height
+
+
+class link(SVGelement):
+ """a=link(url,**args)
+
+ a link is defined by a hyperlink. add elements which have to be linked
+ a.addElement(SVGelement)
+ """
+
+ def __init__(self, link='', **args):
+ SVGelement.__init__(self, 'a', {'xlink:href': link}, **args)
+
+
+class view(SVGelement):
+ """v=view(id,**args)
+
+ a view can be used to create a view with different attributes"""
+
+ def __init__(self, id=None, **args):
+ SVGelement.__init__(self, 'view', **args)
+ if id != None:
+ self.attributes['id'] = id
+
+
+class script(SVGelement):
+ """sc=script(type,type,cdata,**args)
+
+ adds a script element which contains CDATA to the SVG drawing
+
+ """
+
+ def __init__(self, type, cdata=None, **args):
+ SVGelement.__init__(
+ self, 'script', {'type': type}, cdata=cdata, **args)
+
+
+class animate(SVGelement):
+ """an=animate(attribute,from,to,during,**args)
+
+ animates an attribute.
+ """
+
+ def __init__(self, attribute, fr=None, to=None, dur=None, **args):
+ SVGelement.__init__(
+ self, 'animate', {'attributeName': attribute}, **args)
+ if fr != None:
+ self.attributes['from'] = fr
+ if to != None:
+ self.attributes['to'] = to
+ if dur != None:
+ self.attributes['dur'] = dur
+
+
+class animateMotion(SVGelement):
+ """an=animateMotion(pathdata,dur,**args)
+
+ animates a SVGelement over the given path in dur seconds
+ """
+
+ def __init__(self, pathdata, dur, **args):
+ SVGelement.__init__(self, 'animateMotion', **args)
+ if pathdata != None:
+ self.attributes['path'] = str(pathdata)
+ if dur != None:
+ self.attributes['dur'] = dur
+
+
+class animateTransform(SVGelement):
+ """antr=animateTransform(type,from,to,dur,**args)
+
+ transform an element from and to a value.
+ """
+
+ def __init__(self, type=None, fr=None, to=None, dur=None, **args):
+ SVGelement.__init__(self, 'animateTransform', {
+ 'attributeName': 'transform'}, **args)
+ # As far as I know the attributeName is always transform
+ if type != None:
+ self.attributes['type'] = type
+ if fr != None:
+ self.attributes['from'] = fr
+ if to != None:
+ self.attributes['to'] = to
+ if dur != None:
+ self.attributes['dur'] = dur
+
+
+class animateColor(SVGelement):
+ """ac=animateColor(attribute,type,from,to,dur,**args)
+
+ Animates the color of a element
+ """
+
+ def __init__(self, attribute, type=None, fr=None, to=None, dur=None, **args):
+ SVGelement.__init__(self, 'animateColor', {
+ 'attributeName': attribute}, **args)
+ if type != None:
+ self.attributes['type'] = type
+ if fr != None:
+ self.attributes['from'] = fr
+ if to != None:
+ self.attributes['to'] = to
+ if dur != None:
+ self.attributes['dur'] = dur
+
+
+class set(SVGelement):
+ """st=set(attribute,to,during,**args)
+
+ sets an attribute to a value for a
+ """
+
+ def __init__(self, attribute, to=None, dur=None, **args):
+ SVGelement.__init__(self, 'set', {'attributeName': attribute}, **args)
+ if to != None:
+ self.attributes['to'] = to
+ if dur != None:
+ self.attributes['dur'] = dur
+
+
+class svg(SVGelement):
+ """s=svg(viewbox,width,height,**args)
+
+ a svg or element is the root of a drawing add all elements to a svg element.
+ You can have different svg elements in one svg file
+ s.addElement(SVGelement)
+
+ eg
+ d=drawing()
+ s=svg((0,0,100,100),'100%','100%')
+ c=circle(50,50,20)
+ s.addElement(c)
+ d.setSVG(s)
+ d.toXml()
+ """
+
+ def __init__(self, viewBox=None, width=None, height=None, **args):
+ SVGelement.__init__(self, 'svg', **args)
+ if viewBox != None:
+ self.attributes['viewBox'] = _viewboxlist(viewBox)
+ if width != None:
+ self.attributes['width'] = width
+ if height != None:
+ self.attributes['height'] = height
+ self.namespace = "http://www.w3.org/2000/svg"
+
+
+class drawing:
+ """d=drawing()
+
+ this is the actual SVG document. It needs a svg element as a root.
+ Use the addSVG method to set the svg to the root. Use the toXml method to write the SVG
+ source to the screen or to a file
+ d=drawing()
+ d.addSVG(svg)
+ d.toXml(optionalfilename)
+ """
+
+ def __init__(self, entity={}):
+ self.svg = None
+ self.entity = entity
+
+ def setSVG(self, svg):
+ self.svg = svg
+ # Voeg een element toe aan de grafiek toe.
+ if use_dom_implementation == 0:
+ def toXml(self, filename='', compress=False):
+ import io
+ xml = io.StringIO()
+ xml.write("<?xml version='1.0' encoding='UTF-8'?>\n")
+ xml.write(
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\"")
+ if self.entity:
+ xml.write(" [\n")
+ for item in list(self.entity.keys()):
+ xml.write("<!ENTITY %s \"%s\">\n" %
+ (item, self.entity[item]))
+ xml.write("]")
+ xml.write(">\n")
+ self.svg.toXml(0, xml)
+ if not filename:
+ if compress:
+ import gzip
+ f = io.StringIO()
+ zf = gzip.GzipFile(fileobj=f, mode='wb')
+ zf.write(xml.getvalue())
+ zf.close()
+ f.seek(0)
+ return f.read()
+ else:
+ return xml.getvalue()
+ else:
+ if filename[-4:] == 'svgz':
+ import gzip
+ f = gzip.GzipFile(filename=filename,
+ mode="wb", compresslevel=9)
+ f.write(xml.getvalue())
+ f.close()
+ else:
+ f = file(filename, 'w')
+ f.write(xml.getvalue())
+ f.close()
+
+ else:
+ def toXml(self, filename='', compress=False):
+ """drawing.toXml() ---->to the screen
+ drawing.toXml(filename)---->to the file
+ writes a svg drawing to the screen or to a file
+ compresses if filename ends with svgz or if compress is true
+ """
+ doctype = implementation.createDocumentType(
+ 'svg', "-//W3C//DTD SVG 1.0//EN""", 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd ')
+
+ global root
+ # root is defined global so it can be used by the appender. Its also possible to use it as an arugument but
+ # that is a bit messy.
+ root = implementation.createDocument(None, None, doctype)
+ # Create the xml document.
+ global appender
+
+ def appender(element, elementroot):
+ """This recursive function appends elements to an element and sets the attributes
+ and type. It stops when alle elements have been appended"""
+ if element.namespace:
+ e = root.createElementNS(element.namespace, element.type)
+ else:
+ e = root.createElement(element.type)
+ if element.text:
+ textnode = root.createTextNode(element.text)
+ e.appendChild(textnode)
+ # in element.attributes is supported from python 2.2
+ for attribute in list(element.attributes.keys()):
+ e.setAttribute(attribute, str(
+ element.attributes[attribute]))
+ if element.elements:
+ for el in element.elements:
+ e = appender(el, e)
+ elementroot.appendChild(e)
+ return elementroot
+ root = appender(self.svg, root)
+ if not filename:
+ import io
+ xml = io.StringIO()
+ PrettyPrint(root, xml)
+ if compress:
+ import gzip
+ f = io.StringIO()
+ zf = gzip.GzipFile(fileobj=f, mode='wb')
+ zf.write(xml.getvalue())
+ zf.close()
+ f.seek(0)
+ return f.read()
+ else:
+ return xml.getvalue()
+ else:
+ try:
+ if filename[-4:] == 'svgz':
+ import gzip
+ import io
+ xml = io.StringIO()
+ PrettyPrint(root, xml)
+ f = gzip.GzipFile(filename=filename,
+ mode='wb', compresslevel=9)
+ f.write(xml.getvalue())
+ f.close()
+ else:
+ f = open(filename, 'w')
+ PrettyPrint(root, f)
+ f.close()
+ except:
+ print(("Cannot write SVG file: " + filename))
+
+ def validate(self):
+ try:
+ import xml.parsers.xmlproc.xmlval
+ except:
+ raise exceptions.ImportError(
+ 'PyXml is required for validating SVG')
+ svg = self.toXml()
+ xv = xml.parsers.xmlproc.xmlval.XMLValidator()
+ try:
+ xv.feed(svg)
+ except:
+ raise Exception("SVG is not well formed, see messages above")
+ else:
+ print("SVG well formed")
+
+
+if __name__ == '__main__':
+
+ d = drawing()
+ s = svg((0, 0, 100, 100))
+ r = rect(-100, -100, 300, 300, 'cyan')
+ s.addElement(r)
+
+ t = title('SVGdraw Demo')
+ s.addElement(t)
+ g = group('animations')
+ e = ellipse(0, 0, 5, 2)
+ g.addElement(e)
+ c = circle(0, 0, 1, 'red')
+ g.addElement(c)
+ pd = pathdata(0, -10)
+ for i in range(6):
+ pd.relsmbezier(10, 5, 0, 10)
+ pd.relsmbezier(-10, 5, 0, 10)
+ an = animateMotion(pd, 10)
+ an.attributes['rotate'] = 'auto-reverse'
+ an.attributes['repeatCount'] = "indefinite"
+ g.addElement(an)
+ s.addElement(g)
+ for i in range(20, 120, 20):
+ u = use('#animations', i, 0)
+ s.addElement(u)
+ for i in range(0, 120, 20):
+ for j in range(5, 105, 10):
+ c = circle(i, j, 1, 'red', 'black', .5)
+ s.addElement(c)
+ d.setSVG(s)
+
+ print((d.toXml()))