feat: referentiel projets dynamique + validation nommage by design

Durcit la convention de nommage des projets (dérive constatée : 'Sliding
Automation', 'code_versioning'... au lieu des formes canoniques).

- trilium_api.py : projets_canoniques() lit le référentiel = valeurs du label
  projet sur les notes de type=projet (source unique, pas de constante en dur).
  Note-projet CodeVersioning créée (manquait).
- mcp_server.py : _valider_projet() branché dans les 6 tools de création
  (add_decision/history/backlog, new_conversation, create_entite, add_skill).
  Refuse un projet non canonique (suggestion si faute) ou inconnu (renvoi au
  processus de création de projet). Ne verrouille pas si référentiel illisible.
- lint_audit.py : VAL-nommage aligné sur le référentiel (attrape casse, espace
  ET snake_case ; l'ancien 'contient un espace' ratait code_versioning).
- Données : 79 notes ré-étiquetées vers les 3 formes canoniques.

Quality by design : l'erreur de nommage devient impossible à l'écriture, le
Lint n'est plus que le filet de sécurité.
This commit is contained in:
2026-07-17 14:59:33 +02:00
parent d1e67431b6
commit 943acbc573
2425 changed files with 710525 additions and 6 deletions
@@ -0,0 +1,93 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman
# Purpose: package definition file
# Created: 08.09.2010
# License: MIT License
# Copyright (c) 2010-2018 Manfred Moitzi
"""
A Python library to create SVG drawings.
SVG is a language for describing two-dimensional graphics in XML. SVG allows
for three types of graphic objects: vector graphic shapes (e.g., paths
consisting of straight lines and curves), images and text. Graphical objects
can be grouped, styled, transformed and composed into previously rendered
objects. The feature set includes nested transformations, clipping paths,
alpha masks, filter effects and template objects.
SVG drawings can be interactive and dynamic. Animations can be defined and
triggered either declarative (i.e., by embedding SVG animation elements in
SVG content) or via scripting.
.. seealso:: http://www.w3.org/TR/SVG11/intro.html#AboutSVG
a simple example::
import svgwrite
dwg = svgwrite.Drawing('test.svg', profile='tiny')
dwg.add(dwg.line((0, 0), (10, 0), stroke=svgwrite.rgb(10, 10, 16, '%')))
dwg.add(dwg.text('Test', insert=(0, 0.2)))
dwg.save()
SVG Version
-----------
You can only create two types of SVG drawings:
* *SVG 1.2 Tiny Profile*, use Drawing(profile= ``'tiny'``)
* *SVG 1.1 Full Profile*, use Drawing(profile= ``'full'``)
"""
from .version import __version__, version
VERSION = __version__
__author__ = "mozman <me@mozman.at>"
AUTHOR_NAME = 'Manfred Moitzi'
AUTHOR_EMAIL = 'me@mozman.at'
CYEAR = '2014-2019'
from svgwrite.drawing import Drawing
from svgwrite.utils import rgb
class Unit(object):
""" Add units to values.
"""
def __init__(self, unit='cm'):
""" Unit constructor
:param str unit: specify the unit string
"""
self._unit = unit
def __rmul__(self, other):
""" add unit-string to 'other'. (e.g. 5*cm => '5cm') """
return "%s%s" % (other, self._unit)
def __call__(self, *args):
""" Add unit-strings to all arguments.
:param args: list of values
e.g.: cm(1,2,3) => '1cm,2cm,3cm'
"""
return ','.join(["%s%s" % (arg, self._unit) for arg in args])
cm = Unit('cm')
mm = Unit('mm')
em = Unit('em')
ex = Unit('ex')
px = Unit('px')
inch = Unit('in')
pc = Unit('pc')
pt = Unit('pt')
percent = Unit('%')
deg = Unit('deg')
grad = Unit('grad')
rad = Unit('rad')
Hz = Unit('Hz')
kHz = Unit('kHz')
@@ -0,0 +1,183 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: animate elements
# Created: 31.10.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
from svgwrite.base import BaseElement
from svgwrite.mixins import XLink
from svgwrite.utils import strlist, is_string
class Set(BaseElement, XLink):
""" The **set** element provides a simple means of just setting the value
of an attribute for a specified duration. It supports all attribute types,
including those that cannot reasonably be interpolated, such as string
and boolean values. The **set** element is non-additive. The additive and
accumulate attributes are not allowed, and will be ignored if specified.
"""
elementname = 'set'
def __init__(self, href=None, **extra):
""" Set constructor.
:param href: target svg element, if **href** is not `None`; else
the target SVG Element is the parent SVG Element.
"""
super(Set, self).__init__(**extra)
if href is not None:
self.set_href(href)
def get_xml(self):
self.update_id() # if href is an object - 'id' - attribute may be changed!
return super(Set, self).get_xml()
def set_target(self, attributeName, attributeType=None):
"""
Set animation attributes :ref:`attributeName` and :ref:`attributeType`.
"""
self['attributeName'] = attributeName
if attributeType is not None:
self['attributeType'] = attributeType
def set_event(self, onbegin=None, onend=None, onrepeat=None, onload=None):
"""
Set animation attributes :ref:`onbegin`, :ref:`onend`, :ref:`onrepeat`
and :ref:`onload`.
"""
if onbegin is not None:
self['onbegin'] = onbegin
if onend is not None:
self['onend'] = onend
if onrepeat is not None:
self['onrepeat'] = onrepeat
if onload is not None:
self['onload'] = onload
def set_timing(self, begin=None, end=None, dur=None, min=None, max=None,
restart=None, repeatCount=None, repeatDur=None):
"""
Set animation attributes :ref:`begin`, :ref:`end`, :ref:`dur`,
:ref:`min`, :ref:`max`, :ref:`restart`, :ref:`repeatCount` and
:ref:`repeatDur`.
"""
if begin is not None:
self['begin'] = begin
if end is not None:
self['end'] = end
if dur is not None:
self['dur'] = dur
if min is not None:
self['min'] = min
if max is not None:
self['max'] = max
if restart is not None:
self['restart'] = restart
if repeatCount is not None:
self['repeatCount'] = repeatCount
if repeatDur is not None:
self['repeatDur'] = repeatDur
def freeze(self):
""" Freeze the animation effect. (see also :ref:`fill <animateFill>`)
"""
self['fill'] = 'freeze'
class AnimateMotion(Set):
""" The **animateMotion** element causes a referenced element to move
along a motion path.
"""
elementname = 'animateMotion'
def __init__(self, path=None, href=None, **extra):
"""
:param path: the motion path
:param href: target svg element, if **href** is not `None`; else
the target SVG Element is the parent SVG Element.
"""
super(AnimateMotion, self).__init__(href=href, **extra)
if path is not None:
self['path'] = path
def set_value(self, path=None, calcMode=None, keyPoints=None, rotate=None):
"""
Set animation attributes `path`, `calcMode`, `keyPoints` and `rotate`.
"""
if path is not None:
self['path'] = path
if calcMode is not None:
self['calcMode'] = calcMode
if keyPoints is not None:
self['keyPoints'] = keyPoints
if rotate is not None:
self['rotate'] = rotate
class Animate(Set):
""" The **animate** element allows scalar attributes and properties to be
assigned different values over time .
"""
elementname = 'animate'
def __init__(self, attributeName=None, values=None, href=None, **extra):
"""
:param attributeName: name of the SVG Attribute to animate
:param values: interpolation values, `string` as `<semicolon-list>` or a python `list`
:param href: target svg element, if **href** is not `None`; else
the target SVG Element is the parent SVG Element.
"""
super(Animate, self).__init__(href=href, **extra)
if values is not None:
self.set_value(values)
if attributeName is not None:
self.set_target(attributeName)
def set_value(self, values, calcMode=None, keyTimes=None, keySplines=None,
from_=None, to=None, by=None):
"""
Set animation attributes :ref:`values`, :ref:`calcMode`, :ref:`keyTimes`,
:ref:`keySplines`, :ref:`from`, :ref:`to` and :ref:`by`.
"""
if values is not None:
if not is_string(values):
values = strlist(values, ';')
self['values'] = values
if calcMode is not None:
self['calcMode'] = calcMode
if keyTimes is not None:
self['keyTimes'] = keyTimes
if keySplines is not None:
self['keySplines'] = keySplines
if from_ is not None:
self['from'] = from_
if to is not None:
self['to'] = to
if by is not None:
self['by'] = by
class AnimateColor(Animate):
""" The **animateColor** element specifies a color transformation over
time.
"""
elementname = 'animateColor'
class AnimateTransform(Animate):
""" The **animateTransform** element animates a transformation attribute
on a target element, thereby allowing animations to control translation,
scaling, rotation and/or skewing.
"""
elementname = 'animateTransform'
def __init__(self, transform, element=None, **extra):
"""
:param element: target svg element, if element is not `None`; else
the target svg element is the parent svg element.
:param string transform: ``'translate | scale | rotate | skewX | skewY'``
"""
super(AnimateTransform, self).__init__(element, **extra)
self['type'] = transform
@@ -0,0 +1,285 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman
# Purpose: svg base element
# Created: 08.09.2010
# Copyright (c) 2010-2020, Manfred Moitzi
# License: MIT License
"""
The **BaseElement** is the root for all SVG elements.
"""
from svgwrite.etree import etree
import copy
from svgwrite.params import Parameter
from svgwrite.utils import AutoID
class BaseElement(object):
"""
The **BaseElement** is the root for all SVG elements. The SVG attributes
are stored in **attribs**, and the SVG subelements are stored in
**elements**.
"""
elementname = 'baseElement'
def __init__(self, **extra):
"""
:param extra: extra SVG attributes (keyword arguments)
* add trailing '_' to reserved keywords: ``'class_'``, ``'from_'``
* replace inner '-' by '_': ``'stroke_width'``
SVG attribute names will be checked, if **debug** is `True`.
workaround for removed **attribs** parameter in Version 0.2.2::
# replace
element = BaseElement(attribs=adict)
#by
element = BaseElement()
element.update(adict)
"""
# the keyword 'factory' specifies the object creator
factory = extra.pop('factory', None)
if factory is not None:
# take parameter from 'factory'
self._parameter = factory._parameter
else:
# default parameter debug=True profile='full'
self._parameter = Parameter()
# override debug setting
debug = extra.pop('debug', None)
if debug is not None:
self._parameter.debug = debug
# override profile setting
profile = extra.pop('profile', None)
if profile is not None:
self._parameter.profile = profile
self.attribs = dict()
self.update(extra)
self.elements = list()
def update(self, attribs):
""" Update SVG Attributes from `dict` attribs.
Rules for keys:
1. trailing '_' will be removed (``'class_'`` -> ``'class'``)
2. inner '_' will be replaced by '-' (``'stroke_width'`` -> ``'stroke-width'``)
"""
for key, value in attribs.items():
# remove trailing underscores
# and replace inner underscores
key = key.rstrip('_').replace('_', '-')
self.__setitem__(key, value)
def copy(self):
newobj = copy.copy(self) # shallow copy of object
newobj.attribs = copy.copy(self.attribs) # shallow copy of attributes
newobj.elements = copy.copy(self.elements) # shallow copy of subelements
if 'id' in newobj.attribs: # create a new 'id'
newobj['id'] = newobj.next_id()
return newobj
@property
def debug(self):
return self._parameter.debug
@property
def profile(self):
return self._parameter.profile
@property
def validator(self):
return self._parameter.validator
@validator.setter
def validator(self, value):
self._parameter.validator = value
@property
def version(self):
return self._parameter.get_version()
def set_parameter(self, parameter):
self._parameter = parameter
def next_id(self, value=None):
return AutoID.next_id(value)
def get_id(self):
""" Get the object `id` string, if the object does not have an `id`,
a new `id` will be created.
:returns: `string`
"""
if 'id' not in self.attribs:
self.attribs['id'] = self.next_id()
return self.attribs['id']
def get_iri(self):
"""
Get the `IRI` reference string of the object. (i.e., ``'#id'``).
:returns: `string`
"""
return "#%s" % self.get_id()
def get_funciri(self):
"""
Get the `FuncIRI` reference string of the object. (i.e. ``'url(#id)'``).
:returns: `string`
"""
return "url(%s)" % self.get_iri()
def __getitem__(self, key):
""" Get SVG attribute by `key`.
:param string key: SVG attribute name
:return: SVG attribute value
"""
return self.attribs[key]
def __setitem__(self, key, value):
""" Set SVG attribute by `key` to `value`.
:param string key: SVG attribute name
:param object value: SVG attribute value
"""
# Attribute checking is only done by using the __setitem__() method or
# by self['attribute'] = value
if self.debug:
self.validator.check_svg_attribute_value(self.elementname, key, value)
self.attribs[key] = value
def add(self, element):
""" Add an SVG element as subelement.
:param element: append this SVG element
:returns: the added element
"""
if self.debug:
self.validator.check_valid_children(self.elementname, element.elementname)
self.elements.append(element)
return element
def tostring(self):
""" Get the XML representation as unicode `string`.
:return: unicode XML string of this object and all its subelements
"""
xml = self.get_xml()
# required for Python 2 support
xml_utf8_str = etree.tostring(xml, encoding='utf-8')
return xml_utf8_str.decode('utf-8')
# just Python 3: return etree.tostring(xml, encoding='unicode')
def _repr_svg_(self):
""" Show SVG in IPython, Jupyter Notebook, and Jupyter Lab
:return: unicode XML string of this object and all its subelements
"""
return self.tostring()
def get_xml(self):
""" Get the XML representation as `ElementTree` object.
:return: XML `ElementTree` of this object and all its subelements
"""
xml = etree.Element(self.elementname)
if self.debug:
self.validator.check_all_svg_attribute_values(self.elementname, self.attribs)
for attribute, value in sorted(self.attribs.items()):
# filter 'None' values
if value is not None:
value = self.value_to_string(value)
if value: # just add not empty attributes
xml.set(attribute, value)
for element in self.elements:
xml.append(element.get_xml())
return xml
def value_to_string(self, value):
"""
Converts *value* into a <string> includes a value check, depending
on :attr:`self.debug` and :attr:`self.profile`.
"""
if isinstance(value, (int, float)):
if self.debug:
self.validator.check_svg_type(value, 'number')
if isinstance(value, float) and self.profile == 'tiny':
value = round(value, 4)
return str(value)
def set_desc(self, title=None, desc=None):
""" Insert a **title** and/or a **desc** element as first subelement.
"""
if desc is not None:
self.elements.insert(0, Desc(desc))
if title is not None:
self.elements.insert(0, Title(title))
def set_metadata(self, xmldata):
"""
:param xmldata: an xml.etree.ElementTree - Element() object.
"""
metadata = Metadata(xmldata)
if len(self.elements) == 0:
self.elements.append(metadata)
else:
pos = 0
while self.elements[pos].elementname in ('title', 'desc'):
pos += 1
if pos == len(self.elements):
self.elements.append(metadata)
return
if self.elements[pos].elementname == 'metadata':
self.elements[pos].xml.append(xmldata)
else:
self.elements.insert(pos, metadata)
class Title(object):
elementname = 'title'
def __init__(self, text):
self.xml = etree.Element(self.elementname)
self.xml.text = str(text)
def get_xml(self):
return self.xml
class Desc(Title):
elementname = 'desc'
class Metadata(Title):
elementname = 'metadata'
def __init__(self, xmldata):
"""
:param xmldata: an xml.etree.ElementTree - Element() object.
"""
self.xml = etree.Element('metadata')
self.xml.append(xmldata)
@@ -0,0 +1,287 @@
#coding:utf-8
# Author: mozman
# Purpose: svg container classes
# Created: 15.09.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
"""
The **container** module provides following structural objects:
* :class:`svgwrite.Group`
* :class:`svgwrite.SVG`
* :class:`svgwrite.Defs`
* :class:`svgwrite.Symbol`
* :class:`svgwrite.Marker`
* :class:`svgwrite.Use`
* :class:`svgwrite.Hyperlink`
* :class:`svgwrite.Script`
* :class:`svgwrite.Style`
set/get SVG attributes::
element['attribute'] = value
value = element['attribute']
"""
from urllib.request import urlopen
from svgwrite.utils import font_mimetype, base64_data, find_first_url
from svgwrite.base import BaseElement
from svgwrite.mixins import ViewBox, Transform, XLink
from svgwrite.mixins import Presentation, Clipping
from svgwrite.etree import CDATA
class Group(BaseElement, Transform, Presentation):
""" The **Group** (SVG **g**) element is a container element for grouping
together related graphics elements.
Grouping constructs, when used in conjunction with the **desc** and **title**
elements, provide information about document structure and semantics.
Documents that are rich in structure may be rendered graphically, as speech,
or as braille, and thus promote accessibility.
A group of elements, as well as individual objects, can be given a name using
the **id** attribute. Named groups are needed for several purposes such as
animation and re-usable objects.
"""
elementname = 'g'
class Defs(Group):
""" The **defs** element is a container element for referenced elements. For
understandability and accessibility reasons, it is recommended that, whenever
possible, referenced elements be defined inside of a **defs**.
"""
elementname = 'defs'
class Symbol(BaseElement, ViewBox, Presentation, Clipping):
""" The **symbol** element is used to define graphical template objects which
can be instantiated by a **use** element. The use of **symbol** elements for
graphics that are used multiple times in the same document adds structure and
semantics. Documents that are rich in structure may be rendered graphically,
as speech, or as braille, and thus promote accessibility.
"""
# ITransform interface is not valid for Symbol -> do not inherit from Group
elementname = 'symbol'
class Marker(BaseElement, ViewBox, Presentation):
""" The **marker** element defines the graphics that is to be used for
drawing arrowheads or polymarkers on a given **path**, **line**, **polyline**
or **polygon** element.
Add Marker definitions to a **defs** section, preferred to the **defs** section
of the **main drawing**.
"""
elementname = 'marker'
def __init__(self, insert=None, size=None, orient=None, **extra):
"""
:param 2-tuple insert: reference point (**refX**, **refY**)
:param 2-tuple size: (**markerWidth**, **markerHeight**)
:param orient: ``'auto'`` | `angle`
:param extra: additional SVG attributes as keyword-arguments
"""
super(Marker, self).__init__(**extra)
if insert is not None:
self['refX'] = insert[0]
self['refY'] = insert[1]
if size is not None:
self['markerWidth'] = size[0]
self['markerHeight'] = size[1]
if orient is not None:
self['orient'] = orient
if 'id' not in self.attribs: # an 'id' is necessary
self['id'] = self.next_id()
FONT_TEMPLATE = """@font-face{{
font-family: "{name}";
src: url("{data}");
}}
"""
class SVG(Symbol):
""" A SVG document fragment consists of any number of SVG elements contained
within an **svg** element.
An SVG document fragment can range from an empty fragment (i.e., no content
inside of the **svg** element), to a very simple SVG document fragment containing
a single SVG graphics element such as a **rect**, to a complex, deeply nested
collection of container elements and graphics elements.
"""
elementname = 'svg'
def __init__(self, insert=None, size=None, **extra):
"""
:param 2-tuple insert: insert position (**x**, **y**)
:param 2-tuple size: (**width**, **height**)
:param extra: additional SVG attributes as keyword-arguments
"""
super(SVG, self).__init__(**extra)
if insert is not None:
self['x'] = insert[0]
self['y'] = insert[1]
if size is not None:
self['width'] = size[0]
self['height'] = size[1]
self.defs = Defs(factory=self) # defs container
self.add(self.defs) # add defs as first element
def embed_stylesheet(self, content):
""" Add <style> tag to the defs section.
:param content: style sheet content as string
:return: :class:`~svgwrite.container.Style` object
"""
return self.defs.add(Style(content))
def embed_font(self, name, filename):
""" Embed font as base64 encoded data from font file.
:param name: font name
:param filename: file name of local stored font
"""
data = open(filename, 'rb').read()
self._embed_font_data(name, data, font_mimetype(filename))
def embed_google_web_font(self, name, uri):
""" Embed font as base64 encoded data acquired from google fonts.
:param name: font name
:param uri: google fonts request uri like 'http://fonts.googleapis.com/css?family=Indie+Flower'
"""
font_info = urlopen(uri).read()
font_url = find_first_url(font_info.decode())
if font_url is None:
raise ValueError("Got no font data from uri: '{}'".format(uri))
else:
data = urlopen(font_url).read()
self._embed_font_data(name, data, font_mimetype(font_url))
def _embed_font_data(self, name, data, mimetype):
content = FONT_TEMPLATE.format(name=name, data=base64_data(data, mimetype))
self.embed_stylesheet(content)
class Use(BaseElement, Transform, XLink, Presentation):
""" The **use** element references another element and indicates that the graphical
contents of that element is included/drawn at that given point in the document.
Link to objects by href = ``'#object-id'`` or use the object itself as
href-argument, if the given element has no **id** attribute it gets an
automatic generated id.
"""
elementname = 'use'
def __init__(self, href, insert=None, size=None, **extra):
"""
:param string href: object link (id-string) or an object with an id-attribute
:param 2-tuple insert: insert point (**x**, **y**)
:param 2-tuple size: (**width**, **height**)
:param extra: additional SVG attributes as keyword-arguments
"""
super(Use, self).__init__(**extra)
self.set_href(href)
if insert is not None:
self['x'] = insert[0]
self['y'] = insert[1]
if size is not None:
self['width'] = size[0]
self['height'] = size[1]
def get_xml(self):
self.update_id() # if href is an object - 'id' - attribute may be changed!
return super(Use, self).get_xml()
class Hyperlink(BaseElement, Transform, Presentation):
""" The **a** element indicate links (also known as Hyperlinks or Web links).
The remote resource (the destination for the link) is defined by a `<URI>`
specified by the XLink **xlink:href** attribute. The remote resource may be
any Web resource (e.g., an image, a video clip, a sound bite, a program,
another SVG document, an HTML document, an element within the current
document, an element within a different document, etc.). By activating
these links (by clicking with the mouse, through keyboard input, voice
commands, etc.), users may visit these resources.
A **Hyperlink** is defined for each separate rendered element
contained within the **Hyperlink** class; add sublements as usual with
the `add` method.
"""
elementname = 'a'
def __init__(self, href, target='_blank', **extra):
"""
:param string href: hyperlink to the target resource
:param string target: ``'_blank|_replace|_self|_parent|_top|<XML-name>'``
:param extra: additional SVG attributes as keyword-arguments
"""
super(Hyperlink, self).__init__(**extra)
self['xlink:href'] = href
if target is not None:
self['target'] = target
class Script(BaseElement):
""" The **script** element indicate links to a client-side language. This
is normally a (also known as Hyperlinks or Web links).
The remote resource (the source of the script) is defined by a `<URI>`
specified by the XLink **xlink:href** attribute. The remote resource must
be a text-file that contains the script contents. This script can be used
within the SVG file by catching events or adding the mouseover/mousedown/
mouseup elements to the markup.
"""
elementname = 'script'
def __init__(self, href=None, content="", **extra):
"""
:param string href: hyperlink to the target resource or *None* if using *content*
:param string content: script content
:param extra: additional attributes as keyword-arguments
Use *href* **or** *content*, but not both at the same time.
"""
# removed type parameter, default is "application/ecmascript"
super(Script, self).__init__(**extra)
if href is not None:
self['xlink:href'] = href
self._content = content
def get_xml(self):
xml = super(Script, self).get_xml()
if self._content:
xml.append(CDATA(self._content))
return xml
def append(self, content):
""" Append content to the existing element-content. """
self._content += content
class Style(Script):
""" The *style* element allows style sheets to be embedded directly within
SVG content. SVG's *style* element has the same attributes as the
corresponding element in HTML.
"""
elementname = 'style'
def __init__(self, content="", **extra):
"""
:param string content: stylesheet content
"""
super(Style, self).__init__(content=content, **extra)
self['type'] = "text/css"
@@ -0,0 +1,156 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: colornames
# Created: 06.10.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
colornames = frozenset([
'aliceblue',
'antiquewhite',
'aqua',
'aquamarine',
'azure',
'beige',
'bisque',
'black',
'blanchedalmond',
'blue',
'blueviolet',
'brown',
'burlywood',
'cadetblue',
'chartreuse',
'chocolate',
'coral',
'cornflowerblue',
'cornsilk',
'crimson',
'cyan',
'darkblue',
'darkcyan',
'darkgoldenrod',
'darkgray',
'darkgreen',
'darkgrey',
'darkkhaki',
'darkmagenta',
'darkolivegreen',
'darkorange',
'darkorchid',
'darkred',
'darksalmon',
'darkseagreen',
'darkslateblue',
'darkslategray',
'darkslategrey',
'darkturquoise',
'darkviolet',
'deeppink',
'deepskyblue',
'dimgray',
'dimgrey',
'dodgerblue',
'firebrick',
'floralwhite',
'forestgreen',
'fuchsia',
'gainsboro',
'ghostwhite',
'gold',
'goldenrod',
'gray',
'green',
'greenyellow',
'grey',
'honeydew',
'hotpink',
'indianred',
'indigo',
'ivory',
'khaki',
'lavender',
'lavenderblush',
'lawngreen',
'lemonchiffon',
'lightblue',
'lightcoral',
'lightcyan',
'lightgoldenrodyellow',
'lightgray',
'lightgreen',
'lightgrey',
'lightpink',
'lightsalmon',
'lightseagreen',
'lightskyblue',
'lightslategray',
'lightslategrey',
'lightsteelblue',
'lightyellow',
'lime',
'limegreen',
'linen',
'magenta',
'maroon',
'mediumaquamarine',
'mediumblue',
'mediumorchid',
'mediumpurple',
'mediumseagreen',
'mediumslateblue',
'mediumspringgreen',
'mediumturquoise',
'mediumvioletred',
'midnightblue',
'mintcream',
'mistyrose',
'moccasin',
'navajowhite',
'navy',
'oldlace',
'olive',
'olivedrab',
'orange',
'orangered',
'orchid',
'palegoldenrod',
'palegreen',
'paleturquoise',
'palevioletred',
'papayawhip',
'peachpuff',
'peru',
'pink',
'plum',
'powderblue',
'purple',
'red',
'rosybrown',
'royalblue',
'saddlebrown',
'salmon',
'sandybrown',
'seagreen',
'seashell',
'sienna',
'silver',
'skyblue',
'slateblue',
'slategray',
'slategrey',
'snow',
'springgreen',
'steelblue',
'tan',
'teal',
'thistle',
'tomato',
'turquoise',
'violet',
'wheat',
'white',
'whitesmoke',
'yellow',
'yellowgreen',])
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,30 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: pattern module
# Created: 27.09.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
import re
#coordinate ::= number ("em" | "ex" | "px" | "in" | "cm" | "mm" | "pt" | "pc" | "%")?
coordinate = re.compile(r"(^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)(cm|em|ex|in|mm|pc|pt|px|%)?$")
#length ::= number ("em" | "ex" | "px" | "in" | "cm" | "mm" | "pt" | "pc" | "%")?
length = coordinate
#angle ::= number (~"deg" | ~"grad" | ~"rad")?
angle = re.compile(r"(^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)(deg|rad|grad)?$")
# numbers without units
number = re.compile(r"(^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)$")
# number as percentage value '###%'
percentage = re.compile(r"(^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)%$")
#frequency ::= number (~"Hz" | ~"kHz")
frequency = re.compile(r"(^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)(Hz|kHz)?$")
#time ::= number (~"s" | ~"ms")
time = re.compile(r"(^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)(s|ms)?$")
@@ -0,0 +1,132 @@
#!/usr/bin/env python
# coding:utf-8
# Authors: mozman <me@mozman.at>, Florian Festi
# Purpose: svgparser using re module
# Created: 16.10.2010
# Copyright (c) 2010, Manfred Moitzi & 2020, Florian Festi
# License: MIT License
__all__ = ["is_valid_transferlist", "is_valid_pathdata", "is_valid_animation_timing"]
import re
event_names = [
"focusin", "focusout", "activate", "click", "mousedown", "mouseup", "mouseover",
"mousemove", "mouseout", "DOMSubtreeModified", "DOMNodeInserted", "DOMNodeRemoved",
"DOMNodeRemovedFromDocument", "DOMNodeInsertedtoDocument", "DOMAttrModified",
"DOMCharacterDataModified", "SVGLoad", "SVGUnload", "SVGAbort", "SVGError",
"SVGResize", "SVGScroll", "SVGZoom", "beginEvent", "endEvent", "repeatEvent",
]
c = r"\s*[, ]\s*"
s = r"\s*[; ]\s*"
integer_constant = r"\d+"
exponent = r"([eE][+-]?\d+)"
nonnegative_number = fr"(\d+\.?\d*|\.\d+){exponent}?"
number = r"[+-]?" + nonnegative_number
flag = r"[01]"
comma_delimited_coordinates = fr"\s*{number}({c}{number})*\s*"
two_comma_delimited_numbers = fr"\s*{number}({c}{number}\s*)" "{1}"
four_comma_delimited_numbers = fr"\s*{number}\s*({c}{number}\s*)" "{3}"
six_comma_delimited_numbers = fr"\s*{number}\s*({c}{number}\s*)" "{5}"
comma_delimited_coordinate_pairs = fr"{two_comma_delimited_numbers}({c}{two_comma_delimited_numbers})*"
def is_valid(regex):
reg = re.compile(regex)
def f(term):
return bool(reg.fullmatch(term))
return f
def build_transferlist_parser():
matrix = fr"matrix\s*\(\s*{six_comma_delimited_numbers}\s*\)"
translate = fr"translate\s*\(\s*{number}({c}{number})?\s*\)"
scale = fr"scale\s*\(\s*{number}({c}{number})?\s*\)"
rotate = fr"rotate\s*\(\s*{number}({c}{number}{c}{number})?\s*\)"
skewX = fr"skewX\s*\(\s*{number}\s*\)"
skewY = fr"skewY\s*\(\s*{number}\s*\)"
tl_re = "|".join((fr"(\s*{cmd}\s*)" for cmd in (
matrix, translate, scale, rotate, skewX, skewY)))
return fr"({tl_re})({c}({tl_re}))*"
is_valid_transferlist = is_valid(build_transferlist_parser())
def build_pathdata_parser():
moveto = fr"[mM]\s*{comma_delimited_coordinate_pairs}"
lineto = fr"[lL]\s*{comma_delimited_coordinate_pairs}"
horizontal_lineto = fr"[hH]\s*{comma_delimited_coordinates}"
vertical_lineto = fr"[vV]\s*{comma_delimited_coordinates}"
curveto = fr"[cC]\s*({six_comma_delimited_numbers})({c}{six_comma_delimited_numbers})*"
smooth_curveto = fr"[sS]{four_comma_delimited_numbers}({c}{four_comma_delimited_numbers})*"
quadratic_bezier_curveto = fr"[qQ]{four_comma_delimited_numbers}({c}{four_comma_delimited_numbers})*"
smooth_quadratic_bezier_curveto = fr"[tT]\s*{comma_delimited_coordinate_pairs}"
elliptical_arc_argument = fr"{c}".join((
fr"{nonnegative_number}",
fr"{nonnegative_number}",
fr"{number}",
fr"{flag}",
fr"{flag}",
fr"{number}",
fr"{number}",))
elliptical_arc_argument = r"\s*" + elliptical_arc_argument + r"\s*"
elliptical_arc = fr"[aA]({elliptical_arc_argument})({c}{elliptical_arc_argument})*"
drawto_command = "|".join((fr"(\s*{cmd}\s*)" for cmd in (
moveto, lineto, horizontal_lineto, vertical_lineto, "[zZ]",
curveto, smooth_curveto, quadratic_bezier_curveto,
smooth_quadratic_bezier_curveto, elliptical_arc)))
return f"{moveto}({drawto_command})*"
is_valid_pathdata = is_valid(build_pathdata_parser())
digit2 = r"\d{2}"
digit4 = r"\d{4}"
seconds = fr"\d+(\.\d+)?"
seconds2 = fr"{digit2}(\.\d+)?"
metric = "(h|min|s|ms)"
def clock_val_re():
timecount_val = fr"{seconds}\s*({metric})?"
clock_val = fr"{digit2}:({digit2}:)?{seconds2}"
return fr"({timecount_val}|{clock_val})"
def wall_clock_val_re():
hhmmss = fr"{digit2}:{digit2}(:{seconds2})?"
walltime = fr"{hhmmss}(Z|[+-]?{digit2}:{digit2})?"
date = fr"{digit4}-{digit2}-{digit2}"
datetime = fr"{date}(T{walltime})?"
return "(" + "|".join((walltime, datetime)) + ")"
def build_animation_timing_parser():
clock_val = clock_val_re()
wallclock_val = wall_clock_val_re()
event_ref = "(" + "|".join(event_names) + ")"
id_value = "#?[-_a-zA-Z0-9]+"
wallclock_sync_value = fr"wallclock\(\s*{wallclock_val}\s*\)"
accesskey_value = fr"accessKey\(\s*[a-zA-Z]\s*\)\s*([+-]?{clock_val})?"
repeat_value = fr"({id_value}\.)?repeat\s*\(\s*\d+\s*\)\s*([+-?]{clock_val})?"
event_value = fr"({id_value}\.)?{event_ref}([+-]?{clock_val})?"
offset_value = fr"[-+]?{clock_val}"
syncbase_value = fr"{id_value}\.(begin|end)({offset_value})?"
begin_value = "(" + "|".join((f"({reg})" for reg in (
offset_value, syncbase_value, event_value, repeat_value,
accesskey_value, wallclock_sync_value, "indefinite"))) + ")"
return fr"{begin_value}({s}{begin_value})*"
is_valid_animation_timing = is_valid(build_animation_timing_parser())
@@ -0,0 +1,966 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: tiny12 data
# Created: 15.10.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
from svgwrite.data.types import SVGAttribute, SVGMultiAttribute
from svgwrite.data.types import SVGElement
from svgwrite.data.typechecker import Tiny12TypeChecker as TypeChecker
empty_list = []
attributes = {
'about': SVGAttribute('about', anim=True,
types=frozenset(['string']),
const=empty_list),
'accent-height': SVGAttribute('accent-height', anim=False,
types=frozenset(['number']),
const=empty_list),
'accumulate': SVGAttribute('accumulate', anim=False,
types=empty_list,
const=frozenset(['none', 'sum'])),
'additive': SVGAttribute('additive', anim=False,
types=empty_list,
const=frozenset(['replace', 'sum'])),
'alphabetic': SVGAttribute('alphabetic', anim=False,
types=frozenset(['number']),
const=empty_list),
'arabic-form': SVGAttribute('arabic-form', anim=False,
types=empty_list,
const=frozenset(['terminal', 'initial', 'isolated', 'medial'])),
'ascent': SVGAttribute('ascent', anim=False,
types=frozenset(['number']),
const=empty_list),
'attributeName': SVGAttribute('attributeName', anim=False,
types=frozenset(['name']),
const=empty_list),
'attributeType': SVGAttribute('attributeType', anim=False,
types=empty_list,
const=frozenset(['XML', 'CSS', 'auto'])),
'audio-level': SVGAttribute('audio-level', anim=True,
types=frozenset(['number']),
const=frozenset(['inherit'])),
'bandwidth': SVGAttribute('bandwidth', anim=False,
types=frozenset(['number']),
const=frozenset(['auto'])),
'baseProfile': SVGAttribute('baseProfile', anim=False,
types=empty_list,
const=frozenset(['none', 'tiny', 'basic', 'full'])),
'bbox': SVGAttribute('bbox', anim=False,
types=frozenset(['string']),
const=empty_list),
'begin': SVGAttribute('begin', anim=False,
types=frozenset(['timing-value-list']),
const=frozenset(['indefinite'])),
'buffered-rendering': SVGAttribute('buffered-rendering', anim=True,
types=empty_list,
const=frozenset(['auto', 'dynamic', 'static', 'inherit'])),
'by': SVGAttribute('by', anim=False,
types=frozenset(['string']),
const=empty_list),
'calcMode': SVGAttribute('calcMode', anim=False,
types=empty_list,
const=frozenset(['discrete', 'linear', 'paced', 'spline'])),
'cap-height': SVGAttribute('cap-height', anim=False,
types=frozenset(['number']),
const=empty_list),
'class': SVGAttribute('class', anim=True,
types=frozenset(['list-of-name']),
const=empty_list),
'color': SVGAttribute('color', anim=True,
types=frozenset(['color']),
const=frozenset(['inherit'])),
'color-rendering': SVGAttribute('color-rendering', anim=True,
types=empty_list,
const=frozenset(['auto', 'optimizeSpeed', 'optimizeQuality', 'inherit'])),
'content': SVGAttribute('content', anim=True,
types=frozenset(['string']),
const=empty_list),
'contentScriptType': SVGAttribute('contentScriptType', anim=False,
types=frozenset(['content-type']),
const=empty_list),
'cx': SVGAttribute('cx', anim=True,
types=frozenset(['coordinate']),
const=empty_list),
'cy': SVGAttribute('cy', anim=True,
types=frozenset(['coordinate']),
const=empty_list),
'd': SVGAttribute('d', anim=True,
types=frozenset(['path-data']),
const=empty_list),
'datatype': SVGAttribute('datatype', anim=True,
types=frozenset(['string']),
const=empty_list),
'defaultAction': SVGAttribute('defaultAction', anim=False,
types=empty_list,
const=frozenset(['perform', 'cancel'])),
'descent': SVGAttribute('descent', anim=False,
types=frozenset(['number']),
const=empty_list),
'direction': SVGAttribute('direction', anim=False,
types=empty_list,
const=frozenset(['ltr', 'rtl', 'inherit'])),
'display': SVGAttribute('display', anim=True,
types=empty_list,
const=frozenset(['inline', 'block', 'list-item', 'run-in', 'compact',
'marker', 'table', 'inline-table', 'table-row-group',
'table-header-group', 'table-footer-group', 'table-row',
'table-column-group', 'table-column', 'table-cell',
'table-caption', 'none', 'inherit'])),
'display-align': SVGAttribute('display-align', anim=True,
types=empty_list,
const=frozenset(['auto', 'before', 'center', 'after', 'inherit'])),
'dur': SVGAttribute('dur', anim=False,
types=frozenset(['time']),
const=frozenset(['media', 'indefinite'])),
'editable': SVGAttribute('editable', anim=True,
types=empty_list,
const=frozenset(['none', 'simple'])),
'end': SVGAttribute('end', anim=False,
types=frozenset(['timing-value-list']),
const=frozenset(['indefinite'])),
'ev:event': SVGAttribute('ev:event', anim=False,
types=frozenset(['XML-Name']),
const=empty_list),
'event': SVGAttribute('event', anim=False,
types=frozenset(['XML-Name']),
const=empty_list),
'externalResourcesRequired': SVGAttribute('externalResourcesRequired', anim=False,
types=frozenset(['boolean']),
const=empty_list),
'fill': SVGMultiAttribute({
'*': SVGAttribute(
'fill', anim=True,
types=frozenset(['paint']),
const=frozenset(['inherit'])),
'set animateMotion animate animateColor animateTransform': SVGAttribute(
'fill', anim=False,
types=empty_list,
const=frozenset(['freeze', 'remove']))
}),
'fill-opacity': SVGAttribute('fill-opacity', anim=True,
types=frozenset(['number']),
const=frozenset(['inherit'])),
'fill-rule': SVGAttribute('fill-rule', anim=True,
types=empty_list,
const=frozenset(['nonzero', 'evenodd', 'inherit'])),
'focusHighlight': SVGAttribute('focusHighlight', anim=True,
types=empty_list,
const=frozenset(['auto', 'none'])),
'focusable': SVGAttribute('focusable', anim=True,
types=frozenset(['boolean']),
const=frozenset(['auto'])),
'font-family': SVGAttribute('font-family', anim=True,
types=frozenset(['string']),
const=frozenset(['inherit'])),
'font-size': SVGAttribute('font-size', anim=True,
types=frozenset(['length']),
const=frozenset(['inherit'])),
'font-stretch': SVGAttribute('font-stretch', anim=False,
types=empty_list,
const=frozenset(['condensed', 'normal', 'ultra-condensed', 'expanded',
'narrower', 'inherit', 'semi-condensed', 'extra-condensed',
'ultra-expanded', 'wider', 'semi-expanded', 'extra-expanded'])),
'font-style': SVGAttribute('font-style', anim=True,
types=empty_list,
const=frozenset(['normal', 'italic', 'oblique', 'inherit'])),
'font-variant': SVGAttribute('font-variant', anim=True,
types=empty_list,
const=frozenset(['normal', 'small-caps', 'inherit'])),
'font-weight': SVGAttribute('font-weight', anim=True,
types=empty_list,
const=frozenset(['normal', 'bold', 'bolder', 'lighter', '100', '200',
'300', '400', '500', '600', '700', '800', '900',
'inherit'])),
'from': SVGAttribute('from', anim=False,
types=frozenset(['string']),
const=empty_list),
'g1': SVGAttribute('g1', anim=False,
types=frozenset(['list-of-name']),
const=empty_list),
'g2': SVGAttribute('g2', anim=False,
types=frozenset(['list-of-name']),
const=empty_list),
'glyph-name': SVGAttribute('glyph-name', anim=False,
types=frozenset(['list-of-name']),
const=empty_list),
'gradientUnits': SVGAttribute('gradientUnits', anim=True,
types=empty_list,
const=frozenset(['userSpaceOnUse', 'objectBoundingBox'])),
'handler': SVGAttribute('handler', anim=False,
types=frozenset(['IRI']),
const=empty_list),
'hanging': SVGAttribute('hanging', anim=False,
types=frozenset(['number']),
const=empty_list),
'height': SVGMultiAttribute({
'*': SVGAttribute(
'height', anim=True,
types=frozenset(['length']),
const=empty_list),
'textArea': SVGAttribute(
'height', anim=True,
types=frozenset(['length']),
const=frozenset(['auto'])),
}),
'horiz-adv-x': SVGAttribute('horiz-adv-x', anim=False,
types=frozenset(['number']),
const=empty_list),
'horiz-origin-x': SVGAttribute('horiz-origin-x', anim=False,
types=frozenset(['number']),
const=empty_list),
'id': SVGAttribute('id', anim=False,
types=frozenset(['name']),
const=empty_list),
'ideographic': SVGAttribute('ideographic', anim=False,
types=frozenset(['number']),
const=empty_list),
'image-rendering': SVGAttribute('image-rendering', anim=True,
types=empty_list,
const=frozenset(['auto', 'optimizeSpeed', 'optimizeQuality', 'inherit'])),
'initialVisibility': SVGAttribute('initialVisibility', anim=False,
types=empty_list,
const=frozenset(['whenStarted', 'always'])),
'k': SVGAttribute('k', anim=False,
types=frozenset(['number']),
const=empty_list),
'keyPoints': SVGAttribute('keyPoints', anim=False,
types=frozenset(['semicolon-list']),
const=empty_list),
'keySplines': SVGAttribute('keySplines', anim=False,
types=frozenset(['semicolon-list']),
const=empty_list),
'keyTimes': SVGAttribute('keyTimes', anim=False,
types=frozenset(['semicolon-list']),
const=empty_list),
'lang': SVGAttribute('lang', anim=False,
types=frozenset(['list-of-name']),
const=empty_list),
'line-increment': SVGAttribute('line-increment', anim=True,
types=frozenset(['number']),
const=frozenset(['auto', 'inherit'])),
'mathematical': SVGAttribute('mathematical', anim=False,
types=frozenset(['number']),
const=empty_list),
'max': SVGAttribute('max', anim=False,
types=frozenset(['time']),
const=frozenset(['media'])),
'mediaCharacterEncoding': SVGAttribute('mediaCharacterEncoding', anim=False,
types=frozenset(['string']),
const=empty_list),
'mediaContentEncodings': SVGAttribute('mediaContentEncodings', anim=False,
types=frozenset(['string']),
const=empty_list),
'mediaSize': SVGAttribute('mediaSize', anim=False,
types=frozenset(['number']),
const=empty_list),
'mediaTime': SVGAttribute('mediaTime', anim=False,
types=frozenset(['time']),
const=empty_list),
'min': SVGAttribute('min', anim=False,
types=frozenset(['time']),
const=frozenset(['media'])),
'nav-down': SVGAttribute('nav-down', anim=True,
types=frozenset(['focus']),
const=empty_list),
'nav-down-left': SVGAttribute('nav-down-left', anim=True,
types=frozenset(['focus']),
const=empty_list),
'nav-down-right': SVGAttribute('nav-down-right', anim=True,
types=frozenset(['focus']),
const=empty_list),
'nav-left': SVGAttribute('nav-left', anim=True,
types=frozenset(['focus']),
const=empty_list),
'nav-next': SVGAttribute('nav-next', anim=True,
types=frozenset(['focus']),
const=empty_list),
'nav-prev': SVGAttribute('nav-prev', anim=True,
types=frozenset(['focus']),
const=empty_list),
'nav-right': SVGAttribute('nav-right', anim=True,
types=frozenset(['focus']),
const=empty_list),
'nav-up': SVGAttribute('nav-up', anim=True,
types=frozenset(['focus']),
const=empty_list),
'nav-up-left': SVGAttribute('nav-up-left', anim=True,
types=frozenset(['focus']),
const=empty_list),
'nav-up-right': SVGAttribute('nav-up-right', anim=True,
types=frozenset(['focus']),
const=empty_list),
'observer': SVGAttribute('observer', anim=False,
types=frozenset(['IDREF']),
const=empty_list),
'offset': SVGAttribute('offset', anim=True,
types=frozenset(['number']),
const=empty_list),
'opacity': SVGAttribute('opacity', anim=True,
types=frozenset(['number']),
const=frozenset(['inherit'])),
'origin': SVGAttribute('origin', anim=False,
types=empty_list,
const=frozenset(['default'])),
'overlay': SVGAttribute('overlay', anim=False,
types=empty_list,
const=frozenset(['none', 'top'])),
'overline-position': SVGAttribute('overline-position', anim=False,
types=frozenset(['number']),
const=empty_list),
'overline-thickness': SVGAttribute('overline-thickness', anim=False,
types=frozenset(['number']),
const=empty_list),
'panose-1': SVGAttribute('panose-1', anim=False,
types=frozenset(['list-of-integer']),
const=empty_list),
'path': SVGAttribute('path', anim=False,
types=frozenset(['path-data']),
const=empty_list),
'pathLength': SVGAttribute('pathLength', anim=True,
types=frozenset(['number']),
const=empty_list),
'phase': SVGAttribute('phase', anim=False,
types=empty_list,
const=frozenset(['default', 'capture'])),
'playbackOrder': SVGAttribute('playbackOrder', anim=False,
types=empty_list,
const=frozenset(['all', 'forwardOnly'])),
'pointer-events': SVGAttribute('pointer-events', anim=True,
types=empty_list,
const=frozenset(['visiblePainted', 'visibleFill', 'visibleStroke', 'visible',
'painted', 'fill', 'stroke', 'all', 'none', 'inherit'])),
'points': SVGAttribute('points', anim=True,
types=frozenset(['list-of-number']),
const=empty_list),
'preserveAspectRatio': SVGAttribute('preserveAspectRatio', anim=True,
types=frozenset(['string']),
const=empty_list),
'propagate': SVGAttribute('propagate', anim=False,
types=empty_list,
const=frozenset(['continue', 'stop'])),
'property': SVGAttribute('property', anim=True,
types=frozenset(['string']),
const=empty_list),
'r': SVGAttribute('r', anim=True,
types=frozenset(['length']),
const=empty_list),
'rel': SVGAttribute('rel', anim=True,
types=frozenset(['string']),
const=empty_list),
'repeatCount': SVGAttribute('repeatCount', anim=False,
types=frozenset(['number']),
const=frozenset(['indefinite'])),
'repeatDur': SVGAttribute('repeatDur', anim=False,
types=frozenset(['time']),
const=frozenset(['indefinite'])),
'requiredExtensions': SVGAttribute('requiredExtensions', anim=False,
types=frozenset(['list-of-IRI']),
const=empty_list),
'requiredFeatures': SVGAttribute('requiredFeatures', anim=False,
types=frozenset(['list-of-IRI']),
const=empty_list),
'requiredFonts': SVGAttribute('requiredFonts', anim=False,
types=frozenset(['string']),
const=empty_list),
'requiredFormats': SVGAttribute('requiredFormats', anim=False,
types=frozenset(['anything']),
const=empty_list),
'resource': SVGAttribute('resource', anim=True,
types=frozenset(['string']),
const=empty_list),
'restart': SVGAttribute('restart', anim=False,
types=empty_list,
const=frozenset(['always', 'never', 'whenNotActive'])),
'rev': SVGAttribute('rev', anim=True,
types=frozenset(['string']),
const=empty_list),
'role': SVGAttribute('role', anim=True,
types=frozenset(['string']),
const=empty_list),
'rotate': SVGAttribute('rotate', anim=True,
types=frozenset(['list-of-number']),
const=empty_list),
'rx': SVGAttribute('rx', anim=True,
types=frozenset(['length']),
const=empty_list),
'ry': SVGAttribute('ry', anim=True,
types=frozenset(['length']),
const=empty_list),
'shape-rendering': SVGAttribute('shape-rendering', anim=True,
types=empty_list,
const=frozenset(['auto', 'optimizeSpeed', 'crispEdges', 'geometricPrecision', 'inherit'])),
'slope': SVGAttribute('slope', anim=False,
types=frozenset(['number']),
const=empty_list),
'snapshotTime': SVGAttribute('snapshotTime', anim=False,
types=frozenset(['time']),
const=frozenset(['none'])),
'solid-color': SVGAttribute('solid-color', anim=True,
types=frozenset(['color']),
const=frozenset(['currentColor', 'inherit'])),
'solid-opacity': SVGAttribute('solid-opacity', anim=True,
types=frozenset(['number']),
const=frozenset(['inherit'])),
'stemh': SVGAttribute('stemh', anim=False,
types=frozenset(['number']),
const=empty_list),
'stemv': SVGAttribute('stemv', anim=False,
types=frozenset(['number']),
const=empty_list),
'stop-color': SVGAttribute('stop-color', anim=True,
types=frozenset(['color']),
const=frozenset(['inherit'])),
'stop-opacity': SVGAttribute('stop-opacity', anim=True,
types=frozenset(['number']),
const=frozenset(['inherit'])),
'strikethrough-position': SVGAttribute('strikethrough-position', anim=False,
types=frozenset(['number']),
const=empty_list),
'strikethrough-thickness': SVGAttribute('strikethrough-thickness', anim=False,
types=frozenset(['number']),
const=empty_list),
'stroke': SVGAttribute('stroke', anim=True,
types=frozenset(['paint']),
const=frozenset(['inherit'])),
'stroke-dasharray': SVGAttribute('stroke-dasharray', anim=True,
types=frozenset(['list-of-length']),
const=frozenset(['none', 'inherit'])),
'stroke-dashoffset': SVGAttribute('stroke-dashoffset', anim=True,
types=frozenset(['length']),
const=frozenset(['inherit'])),
'stroke-linecap': SVGAttribute('stroke-linecap', anim=True,
types=empty_list,
const=frozenset(['butt', 'round', 'square', 'inherit'])),
'stroke-linejoin': SVGAttribute('stroke-linejoin', anim=True,
types=empty_list,
const=frozenset(['miter', 'round', 'bevel', 'inherit'])),
'stroke-miterlimit': SVGAttribute('stroke-miterlimit', anim=True,
types=frozenset(['number']),
const=frozenset(['inherit'])),
'stroke-opacity': SVGAttribute('stroke-opacity', anim=True,
types=frozenset(['number']),
const=frozenset(['inherit'])),
'stroke-width': SVGAttribute('stroke-width', anim=True,
types=frozenset(['length']),
const=frozenset(['inherit'])),
'syncBehavior': SVGAttribute('syncBehavior', anim=False,
types=empty_list,
const=frozenset(['canSlip', 'locked', 'independent', 'default'])),
'syncBehaviorDefault': SVGAttribute('syncBehaviorDefault', anim=False,
types=empty_list,
const=frozenset(['canSlip', 'locked', 'independent', 'inherit'])),
'syncMaster': SVGAttribute('syncMaster', anim=False,
types=frozenset(['boolean']),
const=empty_list),
'syncTolerance': SVGAttribute('syncTolerance', anim=False,
types=frozenset(['time']),
const=frozenset(['default'])),
'syncToleranceDefault': SVGAttribute('syncToleranceDefault', anim=False,
types=frozenset(['time']),
const=frozenset(['inherit'])),
'systemLanguage': SVGAttribute('systemLanguage', anim=False,
types=frozenset(['string']),
const=empty_list),
'target': SVGMultiAttribute({
'* a': SVGAttribute(
'target', anim=True,
types=frozenset(['XML-Name']),
const=frozenset(['_replace', '_self', '_parent', '_top', '_blank'])),
'listener': SVGAttribute(
'target', anim=False,
types=frozenset(['XML-Name']),
const=empty_list),
}),
'text-align': SVGAttribute('text-align', anim=True,
types=empty_list,
const=frozenset(['start', 'center', 'end', 'inherit'])),
'text-anchor': SVGAttribute('text-anchor', anim=True,
types=empty_list,
const=frozenset(['start', 'middle', 'end', 'inherit'])),
'text-rendering': SVGAttribute('text-rendering', anim=True,
types=empty_list,
const=frozenset(['auto', 'optimizeSpeed', 'optimizeLegibility', 'geometricPrecision', 'inherit'])),
'timelineBegin': SVGAttribute('timelineBegin', anim=False,
types=empty_list,
const=frozenset(['onLoad', 'onStart'])),
'to': SVGAttribute('to', anim=False,
types=frozenset(['string']),
const=empty_list),
'transform': SVGAttribute('transform', anim=True,
types=frozenset(['transform-list']),
const=frozenset(['none'])),
'transformBehavior': SVGAttribute('transformBehavior', anim=False,
types=empty_list,
const=frozenset(['geometric', 'pinned', 'pinned90', 'pinned180', 'pinned270'])),
'type': SVGMultiAttribute({
'* audio image video': SVGAttribute(
'type', anim=True,
types=frozenset(['content-type']),
const=empty_list),
'handler script': SVGAttribute(
'type', anim=False,
types=frozenset(['content-type']),
const=empty_list),
'animateTransform': SVGAttribute(
'type', anim=False,
types=empty_list,
const=frozenset(['translate', 'scale', 'rotate', 'skewX', 'skewY'])),
}),
'typeof': SVGAttribute('typeof', anim=True,
types=frozenset(['string']),
const=empty_list),
'u1': SVGAttribute('u1', anim=False,
types=frozenset(['string']),
const=empty_list),
'u2': SVGAttribute('u2', anim=False,
types=frozenset(['string']),
const=empty_list),
'underline-position': SVGAttribute('underline-position', anim=False,
types=frozenset(['number']),
const=empty_list),
'underline-thickness': SVGAttribute('underline-thickness', anim=False,
types=frozenset(['number']),
const=empty_list),
'unicode': SVGAttribute('unicode', anim=False,
types=frozenset(['string']),
const=empty_list),
'unicode-bidi': SVGAttribute('unicode-bidi', anim=False,
types=empty_list,
const=frozenset(['normal', 'embed', 'bidi-override', 'inherit'])),
'unicode-range': SVGAttribute('unicode-range', anim=False,
types=frozenset(['string']),
const=empty_list),
'units-per-em': SVGAttribute('units-per-em', anim=False,
types=frozenset(['number']),
const=empty_list),
'values': SVGAttribute('values', anim=False,
types=frozenset(['list-of-number']),
const=empty_list),
'vector-effect': SVGAttribute('vector-effect', anim=True,
types=empty_list,
const=frozenset(['none', 'non-scaling-stroke', 'inherit'])),
'version': SVGAttribute('version', anim=False,
types=empty_list,
const=frozenset(['1.1', '1.2'])),
'viewBox': SVGAttribute('viewBox', anim=True,
types=frozenset(['four-numbers']),
const=empty_list),
'viewport-fill': SVGAttribute('viewport-fill', anim=True,
types=frozenset(['color']),
const=frozenset(['none', 'inherit'])),
'viewport-fill-opacity': SVGAttribute('viewport-fill-opacity', anim=True,
types=frozenset(['number']),
const=frozenset(['inherit'])),
'visibility': SVGAttribute('visibility', anim=True,
types=empty_list,
const=frozenset(['visible', 'hidden', 'inherit'])),
'width': SVGMultiAttribute({
'*': SVGAttribute(
'width', anim=True,
types=frozenset(['length']),
const=empty_list),
'textArea': SVGAttribute(
'width', anim=True,
types=frozenset(['length']),
const=frozenset(['auto'])),
}),
'widths': SVGAttribute('widths', anim=False,
types=frozenset(['string']),
const=empty_list),
'x': SVGMultiAttribute({
'*': SVGAttribute(
'x', anim=True,
types=frozenset(['coordinate']),
const=empty_list),
'text': SVGAttribute(
'x', anim=True,
types=frozenset(['list-of-coordinate']),
const=empty_list),
}),
'x-height': SVGAttribute('x-height', anim=False,
types=frozenset(['number']),
const=empty_list),
'x1': SVGAttribute('x1', anim=True,
types=frozenset(['coordinate']),
const=empty_list),
'x2': SVGAttribute('x2', anim=True,
types=frozenset(['coordinate']),
const=empty_list),
'xlink:actuate': SVGMultiAttribute({
'*': SVGAttribute(
'xlink:actuate', anim=False,
types=empty_list,
const=frozenset(['onLoad'])),
'a': SVGAttribute(
'xlink:actuate', anim=False,
types=empty_list,
const=frozenset(['onRequest'])),
}),
'xlink:arcrole': SVGAttribute('xlink:arcrole', anim=False,
types=frozenset(['IRI']),
const=empty_list),
'xlink:href': SVGAttribute('xlink:href', anim=True,
types=frozenset(['IRI']),
const=empty_list),
'xlink:role': SVGAttribute('xlink:role', anim=False,
types=frozenset(['IRI']),
const=empty_list),
'xlink:show': SVGMultiAttribute({
'*': SVGAttribute(
'xlink:show', anim=False,
types=empty_list,
const=frozenset(['other'])),
'animation audio foreignObject image use video': SVGAttribute(
'xlink:show', anim=False,
types=empty_list,
const=frozenset(['embed'])),
'a': SVGAttribute(
'xlink:show', anim=False,
types=empty_list,
const=frozenset(['new', 'replace'])),
}),
'xlink:title': SVGAttribute('xlink:title', anim=False,
types=frozenset(['string']),
const=empty_list),
'xlink:type': SVGAttribute('xlink:type', anim=True,
types=empty_list,
const=frozenset(['simple'])),
'xmlns': SVGAttribute('xmlns', anim=False,
types=frozenset(['IRI']),
const=empty_list),
'xmlns:xlink': SVGAttribute('xmlns:xlink', anim=False,
types=frozenset(['IRI']),
const=empty_list),
'xmlns:ev': SVGAttribute('xmlns:ev', anim=False,
types=frozenset(['IRI']),
const=empty_list),
'xml:base': SVGAttribute('xml:base', anim=False,
types=frozenset(['IRI']),
const=empty_list),
'xml:id': SVGAttribute('xml:id', anim=False,
types=frozenset(['name']),
const=empty_list),
'xml:lang': SVGAttribute('xml:lang', anim=False,
types=frozenset(['name']),
const=empty_list),
'xml:space': SVGMultiAttribute({
'*': SVGAttribute(
'xml:space', anim=False,
types=empty_list,
const=frozenset(['preserve', 'default'])),
'handler script': SVGAttribute(
'xml:space', anim=False,
types=empty_list,
const=frozenset(['preserve'])),
}),
'y': SVGMultiAttribute({
'*': SVGAttribute(
'y', anim=True,
types=frozenset(['coordinate']),
const=empty_list),
'text': SVGAttribute(
'y', anim=True,
types=frozenset(['list-of-coordinate']),
const=empty_list),
}),
'y1': SVGAttribute('y1', anim=True,
types=frozenset(['coordinate']),
const=empty_list),
'y2': SVGAttribute('y2', anim=True,
types=frozenset(['coordinate']),
const=empty_list),
'zoomAndPan': SVGAttribute('zoomAndPan', anim=False,
types=empty_list,
const=frozenset(['disable', 'magnify'])),
}
attribute_names = ['slope', 'keySplines', 'rx', 'accumulate', 'bandwidth',
'attributeType', 'unicode', 'nav-right', 'arabic-form',
'y2', 'horiz-origin-x', 'underline-position', 'zoomAndPan',
'cap-height', 'defaultAction', 'to', 'syncBehavior',
'alphabetic', 'g2', 'g1', 'panose-1', 'strikethrough-thickness',
'attributeName', 'bbox', 'nav-up-left', 'nav-left',
'restart', 'target', 'xlink:actuate', 'rotate', 'resource',
'd', 'syncToleranceDefault', 'initialVisibility',
'transformBehavior', 'nav-up-right', 'keyTimes',
'x', 'requiredFormats', 'nav-next', 'glyph-name',
'xml:lang', 'mathematical', 'observer', 'repeatDur',
'hanging', 'y1', 'xml:base', 'ascent', 'event',
'strikethrough-position', 'overlay', 'rev', 'ry',
'overline-thickness', 'content', 'version', 'rel',
'focusable', 'requiredFonts', 'nav-down-right', 'xml:id',
'offset', 'additive', 'underline-thickness', 'font-family',
'by', 'mediaTime', 'timelineBegin', 'about', 'horiz-adv-x',
'widths', 'k', 'requiredFeatures', 'preserveAspectRatio',
'contentScriptType', 'origin', 'xml:space', 'xlink:href',
'height', 'baseProfile', 'cy', 'cx', 'path', 'xlink:role',
'from', 'u1', 'transform', 'units-per-em', 'u2', 'width',
'handler', 'font-variant', 'x-height', 'dur', 'xlink:arcrole',
'type', 'focusHighlight', 'mediaCharacterEncoding',
'xlink:title', 'editable', 'stemv', 'systemLanguage',
'x2', 'x1', 'ideographic', 'xlink:show', 'overline-position',
'syncTolerance', 'gradientUnits', 'r', 'values', 'typeof',
'mediaContentEncodings', 'property', 'requiredExtensions',
'repeatCount', 'ev:event', 'nav-down', 'mediaSize', 'pathLength',
'syncMaster', 'font-style', 'fill', 'end', 'descent',
'calcMode', 'min', 'stemh', 'id', 'unicode-range',
'nav-up', 'font-stretch', 'role', 'font-weight', 'begin',
'xlink:type', 'syncBehaviorDefault', 'max', 'snapshotTime',
'playbackOrder', 'keyPoints', 'nav-prev', 'propagate',
'phase', 'externalResourcesRequired', 'nav-down-left',
'class', 'lang', 'datatype', 'viewBox', 'points',
'accent-height', 'y']
property_names = ['stroke-linejoin', 'font-size', 'text-rendering', 'color-rendering',
'fill-opacity', 'color', 'shape-rendering', 'solid-color', 'stroke',
'stroke-linecap', 'vector-effect', 'stroke-width', 'font-style',
'fill', 'solid-opacity', 'fill-rule', 'viewport-fill-opacity',
'display-align', 'buffered-rendering', 'stroke-miterlimit',
'font-variant', 'stop-opacity', 'font-weight', 'opacity', 'direction',
'audio-level', 'visibility', 'unicode-bidi', 'line-increment',
'image-rendering', 'font-family', 'viewport-fill', 'text-align',
'stroke-opacity', 'stroke-dashoffset', 'text-anchor', 'stop-color',
'pointer-events', 'stroke-dasharray', 'display']
media_group_names = ['audio-level', 'buffered-rendering', 'display', 'image-rendering',
'pointer-events', 'shape-rendering', 'text-rendering',
'viewport-fill', 'viewport-fill-opacity', 'visibility']
elements = {
'a': SVGElement('a',
attributes=['about', 'class', 'content', 'datatype', 'externalResourcesRequired', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'target', 'transform', 'typeof', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['likeparent', 'defs', 'text', 'g', 'textArea', 'svg']),
'animate': SVGElement('animate',
attributes=['about', 'accumulate', 'additive', 'attributeName', 'attributeType', 'begin', 'by', 'calcMode', 'class', 'content', 'datatype', 'dur', 'end', 'fill', 'from', 'id', 'keySplines', 'keyTimes', 'max', 'min', 'property', 'rel', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'restart', 'rev', 'role', 'systemLanguage', 'to', 'typeof', 'values', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'handler', 'metadata', 'switch', 'title']),
'animateColor': SVGElement('animateColor',
attributes=['about', 'accumulate', 'additive', 'attributeName', 'attributeType', 'begin', 'by', 'calcMode', 'class', 'content', 'datatype', 'dur', 'end', 'fill', 'from', 'id', 'keySplines', 'keyTimes', 'max', 'min', 'property', 'rel', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'restart', 'rev', 'role', 'systemLanguage', 'to', 'typeof', 'values', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'handler', 'metadata', 'switch', 'title']),
'animateMotion': SVGElement('animateMotion',
attributes=['about', 'accumulate', 'additive', 'begin', 'by', 'calcMode', 'class', 'content', 'datatype', 'dur', 'end', 'fill', 'from', 'id', 'keyPoints', 'keySplines', 'keyTimes', 'max', 'min', 'origin', 'path', 'property', 'rel', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'restart', 'rev', 'role', 'rotate', 'systemLanguage', 'to', 'typeof', 'values', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'handler', 'metadata', 'mpath', 'switch', 'title']),
'animateTransform': SVGElement('animateTransform',
attributes=['about', 'accumulate', 'additive', 'attributeName', 'attributeType', 'begin', 'by', 'calcMode', 'class', 'content', 'datatype', 'dur', 'end', 'fill', 'from', 'id', 'keySplines', 'keyTimes', 'max', 'min', 'property', 'rel', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'restart', 'rev', 'role', 'systemLanguage', 'to', 'type', 'typeof', 'values', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'handler', 'metadata', 'switch', 'title']),
'animation': SVGElement('animation',
attributes=['about', 'begin', 'class', 'content', 'datatype', 'dur', 'end', 'externalResourcesRequired', 'fill', 'focusHighlight', 'focusable', 'height', 'id', 'initialVisibility', 'max', 'min', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'preserveAspectRatio', 'property', 'rel', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'restart', 'rev', 'role', 'syncBehavior', 'syncMaster', 'syncTolerance', 'systemLanguage', 'transform', 'typeof', 'width', 'x', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space', 'y'],
properties=media_group_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'audio': SVGElement('audio',
attributes=['about', 'begin', 'class', 'content', 'datatype', 'dur', 'end', 'externalResourcesRequired', 'fill', 'id', 'max', 'min', 'property', 'rel', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'restart', 'rev', 'role', 'syncBehavior', 'syncMaster', 'syncTolerance', 'systemLanguage', 'type', 'typeof', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=media_group_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'circle': SVGElement('circle',
attributes=['about', 'class', 'content', 'cx', 'cy', 'datatype', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'r', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'transform', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'defs': SVGElement('defs',
attributes=['about', 'class', 'content', 'datatype', 'id', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['a', 'animate', 'animateColor', 'animateMotion', 'animateTransform', 'animation', 'audio', 'circle', 'defs', 'desc', 'discard', 'ellipse', 'font', 'font-face', 'foreignObject', 'g', 'handler', 'image', 'line', 'linearGradient', 'listener', 'metadata', 'path', 'polygon', 'polyline', 'prefetch', 'radialGradient', 'rect', 'script', 'set', 'solidColor', 'switch', 'text', 'textArea', 'title', 'use', 'video']),
'desc': SVGElement('desc',
attributes=['about', 'class', 'content', 'datatype', 'id', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=media_group_names,
children=empty_list),
'discard': SVGElement('discard',
attributes=['about', 'begin', 'class', 'content', 'datatype', 'id', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'typeof', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'handler', 'metadata', 'switch', 'title']),
'ellipse': SVGElement('ellipse',
attributes=['about', 'class', 'content', 'cx', 'cy', 'datatype', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'rx', 'ry', 'systemLanguage', 'transform', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'font': SVGElement('font',
attributes=['about', 'class', 'content', 'datatype', 'externalResourcesRequired', 'horiz-adv-x', 'horiz-origin-x', 'id', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'font-face', 'glyph', 'hkern', 'metadata', 'missing-glyph', 'switch', 'title']),
'font-face': SVGElement('font-face',
attributes=['about', 'accent-height', 'alphabetic', 'ascent', 'bbox', 'cap-height', 'class', 'content', 'datatype', 'descent', 'externalResourcesRequired', 'font-family', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'hanging', 'id', 'ideographic', 'mathematical', 'overline-position', 'overline-thickness', 'panose-1', 'property', 'rel', 'resource', 'rev', 'role', 'slope', 'stemh', 'stemv', 'strikethrough-position', 'strikethrough-thickness', 'typeof', 'underline-position', 'underline-thickness', 'unicode-range', 'units-per-em', 'widths', 'x-height', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'font-face-src', 'metadata', 'switch', 'title']),
'font-face-src': SVGElement('font-face-src',
attributes=['about', 'class', 'content', 'datatype', 'id', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'font-face-uri', 'metadata', 'switch', 'title']),
'font-face-uri': SVGElement('font-face-uri',
attributes=['about', 'class', 'content', 'datatype', 'externalResourcesRequired', 'id', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'metadata', 'switch', 'title']),
'foreignObject': SVGElement('foreignObject',
attributes=['about', 'class', 'content', 'datatype', 'externalResourcesRequired', 'focusHighlight', 'focusable', 'height', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'transform', 'typeof', 'width', 'x', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space', 'y'],
properties=property_names,
children=['desc', 'metadata', 'svg', 'switch', 'title']),
'g': SVGElement('g',
attributes=['about', 'class', 'content', 'datatype', 'externalResourcesRequired', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'transform', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['a', 'animate', 'animateColor', 'animateMotion', 'animateTransform', 'animation', 'audio', 'circle', 'defs', 'desc', 'discard', 'ellipse', 'font', 'font-face', 'foreignObject', 'g', 'handler', 'image', 'line', 'linearGradient', 'listener', 'metadata', 'path', 'polygon', 'polyline', 'prefetch', 'radialGradient', 'rect', 'script', 'set', 'solidColor', 'switch', 'text', 'textArea', 'title', 'use', 'video']),
'glyph': SVGElement('glyph',
attributes=['about', 'arabic-form', 'class', 'content', 'd', 'datatype', 'glyph-name', 'horiz-adv-x', 'id', 'lang', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'unicode', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'metadata', 'switch', 'title']),
'handler': SVGElement('handler',
attributes=['about', 'class', 'content', 'datatype', 'ev:event', 'externalResourcesRequired', 'id', 'property', 'rel', 'resource', 'rev', 'role', 'type', 'typeof', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'metadata', 'switch', 'title']),
'hkern': SVGElement('hkern',
attributes=['about', 'class', 'content', 'datatype', 'g1', 'g2', 'id', 'k', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'u1', 'u2', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'metadata', 'switch', 'title']),
'image': SVGElement('image',
attributes=['about', 'class', 'content', 'datatype', 'externalResourcesRequired', 'focusHighlight', 'focusable', 'height', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'opacity', 'preserveAspectRatio', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'transform', 'type', 'typeof', 'width', 'x', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space', 'y'],
properties=media_group_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'line': SVGElement('line',
attributes=['about', 'class', 'content', 'datatype', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'transform', 'typeof', 'x1', 'x2', 'xml:base', 'xml:id', 'xml:lang', 'xml:space', 'y1', 'y2'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'linearGradient': SVGElement('linearGradient',
attributes=['about', 'class', 'content', 'datatype', 'gradientUnits', 'id', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'x1', 'x2', 'xml:base', 'xml:id', 'xml:lang', 'xml:space', 'y1', 'y2'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'metadata', 'set', 'stop', 'switch', 'title']),
'listener': SVGElement('listener',
attributes=['about', 'class', 'content', 'datatype', 'defaultAction', 'event', 'handler', 'id', 'observer', 'phase', 'propagate', 'property', 'rel', 'resource', 'rev', 'role', 'target', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=empty_list),
'metadata': SVGElement('metadata',
attributes=['about', 'class', 'content', 'datatype', 'id', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=media_group_names,
children=empty_list),
'missing-glyph': SVGElement('missing-glyph',
attributes=['about', 'class', 'content', 'd', 'datatype', 'horiz-adv-x', 'id', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'metadata', 'switch', 'title']),
'mpath': SVGElement('mpath',
attributes=['about', 'class', 'content', 'datatype', 'id', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'metadata', 'switch', 'title']),
'path': SVGElement('path',
attributes=['about', 'class', 'content', 'd', 'datatype', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'pathLength', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'transform', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'polygon': SVGElement('polygon',
attributes=['about', 'class', 'content', 'datatype', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'points', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'transform', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'polyline': SVGElement('polyline',
attributes=['about', 'class', 'content', 'datatype', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'points', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'transform', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'prefetch': SVGElement('prefetch',
attributes=['about', 'bandwidth', 'class', 'content', 'datatype', 'id', 'mediaCharacterEncoding', 'mediaContentEncodings', 'mediaSize', 'mediaTime', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'metadata', 'switch', 'title']),
'radialGradient': SVGElement('radialGradient',
attributes=['about', 'class', 'content', 'cx', 'cy', 'datatype', 'gradientUnits', 'id', 'property', 'r', 'rel', 'resource', 'rev', 'role', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'metadata', 'set', 'stop', 'switch', 'title']),
'rect': SVGElement('rect',
attributes=['about', 'class', 'content', 'datatype', 'focusHighlight', 'focusable', 'height', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'rx', 'ry', 'systemLanguage', 'transform', 'typeof', 'width', 'x', 'xml:base', 'xml:id', 'xml:lang', 'xml:space', 'y'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'script': SVGElement('script',
attributes=['about', 'class', 'content', 'datatype', 'externalResourcesRequired', 'id', 'property', 'rel', 'resource', 'rev', 'role', 'type', 'typeof', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'metadata', 'switch', 'title']),
'set': SVGElement('set',
attributes=['about', 'attributeName', 'attributeType', 'begin', 'class', 'content', 'datatype', 'dur', 'end', 'fill', 'id', 'max', 'min', 'property', 'rel', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'restart', 'rev', 'role', 'systemLanguage', 'to', 'typeof', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=['desc', 'handler', 'metadata', 'switch', 'title']),
'solidColor': SVGElement('solidColor',
attributes=['about', 'class', 'content', 'datatype', 'id', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'stop': SVGElement('stop',
attributes=['about', 'class', 'content', 'datatype', 'id', 'offset', 'property', 'rel', 'resource', 'rev', 'role', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'metadata', 'set', 'switch', 'title']),
'svg': SVGElement('svg',
attributes=['about', 'baseProfile', 'class', 'content', 'contentScriptType', 'datatype', 'externalResourcesRequired', 'focusHighlight', 'focusable', 'height', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'playbackOrder', 'preserveAspectRatio', 'property', 'rel', 'resource', 'rev', 'role', 'snapshotTime', 'syncBehaviorDefault', 'syncToleranceDefault', 'timelineBegin', 'transform', 'typeof', 'version', 'viewBox', 'width', 'xml:base', 'xml:id', 'xml:lang', 'xml:space', 'xmlns', 'xmlns:xlink', 'xmlns:ev', 'zoomAndPan'],
properties=property_names,
children=['a', 'animate', 'animateColor', 'animateMotion', 'animateTransform', 'animation', 'audio', 'circle', 'defs', 'desc', 'discard', 'ellipse', 'font', 'font-face', 'foreignObject', 'g', 'handler', 'image', 'line', 'linearGradient', 'listener', 'metadata', 'path', 'polygon', 'polyline', 'prefetch', 'radialGradient', 'rect', 'script', 'set', 'solidColor', 'switch', 'text', 'textArea', 'title', 'use', 'video']),
'switch': SVGElement('switch',
attributes=['about', 'class', 'content', 'datatype', 'externalResourcesRequired', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'transform', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['likeparent', 'set', 'textArea', 'text', 'image', 'missing-glyph', 'font-face', 'video', 'path', 'animate', 'font', 'ellipse', 'glyph', 'use', 'font-face-src', 'polygon', 'script', 'handler', 'circle', 'radialGradient', 'prefetch', 'defs', 'mpath', 'stop', 'animateMotion', 'animateColor', 'discard', 'solidColor', 'hkern', 'line', 'animation', 'rect', 'g', 'svg', 'animateTransform', 'linearGradient', 'font-face-uri', 'foreignObject', 'polyline', 'audio']),
'tbreak': SVGElement('tbreak',
attributes=['about', 'class', 'content', 'datatype', 'id', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=empty_list,
children=empty_list),
'text': SVGElement('text',
attributes=['about', 'class', 'content', 'datatype', 'editable', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'rotate', 'systemLanguage', 'transform', 'typeof', 'x', 'xml:base', 'xml:id', 'xml:lang', 'xml:space', 'y'],
properties=property_names,
children=['a', 'animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title', 'tspan']),
'textArea': SVGElement('textArea',
attributes=['about', 'class', 'content', 'datatype', 'editable', 'focusHighlight', 'focusable', 'height', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'transform', 'typeof', 'width', 'x', 'xml:base', 'xml:id', 'xml:lang', 'xml:space', 'y'],
properties=property_names,
children=['a', 'animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'tbreak', 'title', 'tspan']),
'title': SVGElement('title',
attributes=['about', 'class', 'content', 'datatype', 'id', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=media_group_names,
children=empty_list),
'tspan': SVGElement('tspan',
attributes=['about', 'class', 'content', 'datatype', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'typeof', 'xml:base', 'xml:id', 'xml:lang', 'xml:space'],
properties=property_names,
children=['likeparent', 'text', 'textArea']),
'use': SVGElement('use',
attributes=['about', 'class', 'content', 'datatype', 'externalResourcesRequired', 'focusHighlight', 'focusable', 'id', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'property', 'rel', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'rev', 'role', 'systemLanguage', 'transform', 'typeof', 'x', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space', 'y'],
properties=property_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
'video': SVGElement('video',
attributes=['about', 'begin', 'class', 'content', 'datatype', 'dur', 'end', 'externalResourcesRequired', 'fill', 'focusHighlight', 'focusable', 'height', 'id', 'initialVisibility', 'max', 'min', 'nav-down', 'nav-down-left', 'nav-down-right', 'nav-left', 'nav-next', 'nav-prev', 'nav-right', 'nav-up', 'nav-up-left', 'nav-up-right', 'overlay', 'preserveAspectRatio', 'property', 'rel', 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures', 'requiredFonts', 'requiredFormats', 'resource', 'restart', 'rev', 'role', 'syncBehavior', 'syncMaster', 'syncTolerance', 'systemLanguage', 'transform', 'transformBehavior', 'type', 'typeof', 'width', 'x', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:id', 'xml:lang', 'xml:space', 'y'],
properties=media_group_names,
children=['animate', 'animateColor', 'animateMotion', 'animateTransform', 'desc', 'discard', 'handler', 'metadata', 'set', 'switch', 'title']),
}
@@ -0,0 +1,342 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: typechecker
# Created: 15.10.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
import re
from svgwrite.data import pattern
from svgwrite.data.colors import colornames
from svgwrite.data.svgparser import is_valid_transferlist, is_valid_pathdata, is_valid_animation_timing
from svgwrite.utils import is_string
def iterflatlist(values):
""" Flatten nested *values*, returns an *iterator*. """
for element in values:
if hasattr(element, "__iter__") and not is_string(element):
for item in iterflatlist(element):
yield item
else:
yield element
INVALID_NAME_CHARS = frozenset([' ', '\t', '\r', '\n', ',', '(', ')'])
WHITESPACE = frozenset([' ', '\t', '\r', '\n'])
SHAPE_PATTERN = re.compile(r"^rect\((.*),(.*),(.*),(.*)\)$")
FUNCIRI_PATTERN = re.compile(r"^url\((.*)\)$")
ICCCOLOR_PATTERN = re.compile(r"^icc-color\((.*)\)$")
COLOR_HEXDIGIT_PATTERN = re.compile(r"^#[a-fA-F0-9]{3}([a-fA-F0-9]{3})?$")
COLOR_RGB_INTEGER_PATTERN = re.compile(r"^rgb\( *\d+ *, *\d+ *, *\d+ *\)$")
COLOR_RGB_PERCENTAGE_PATTERN = re.compile(r"^rgb\( *\d+(\.\d*)?% *, *\d+(\.\d*)?% *, *\d+(\.\d*)?% *\)$")
NMTOKEN_PATTERN = re.compile(r"^[a-zA-Z_:][\w\-\.:]*$")
class Full11TypeChecker(object):
def get_version(self):
return '1.1', 'full'
def is_angle(self, value):
#angle ::= number (~"deg" | ~"grad" | ~"rad")?
if self.is_number(value):
return True
elif is_string(value):
return pattern.angle.match(value.strip()) is not None
return False
def is_anything(self, value):
#anything ::= Char*
return bool(str(value).strip())
is_string = is_anything
is_content_type = is_anything
def is_color(self, value):
#color ::= "#" hexdigit hexdigit hexdigit (hexdigit hexdigit hexdigit)?
# | "rgb(" wsp* integer comma integer comma integer wsp* ")"
# | "rgb(" wsp* number "%" comma number "%" comma number "%" wsp* ")"
# | color-keyword
#hexdigit ::= [0-9A-Fa-f]
#comma ::= wsp* "," wsp*
value = str(value).strip()
if value.startswith('#'):
if COLOR_HEXDIGIT_PATTERN.match(value):
return True
else:
return False
elif value.startswith('rgb('):
if COLOR_RGB_INTEGER_PATTERN.match(value):
return True
elif COLOR_RGB_PERCENTAGE_PATTERN.match(value):
return True
return False
return self.is_color_keyword(value)
def is_color_keyword(self, value):
return value.strip() in colornames
def is_frequency(self, value):
# frequency ::= number (~"Hz" | ~"kHz")
if self.is_number(value):
return True
elif is_string(value):
return pattern.frequency.match(value.strip()) is not None
return False
def is_FuncIRI(self, value):
# FuncIRI ::= "url(" <IRI> ")"
res = FUNCIRI_PATTERN.match(str(value).strip())
if res:
return self.is_IRI(res.group(1))
return False
def is_icccolor(self, value):
# icccolor ::= "icc-color(" name (comma-wsp number)+ ")"
res = ICCCOLOR_PATTERN.match(str(value).strip())
if res:
return self.is_list_of_T(res.group(1), 'name')
return False
def is_integer(self, value):
if isinstance(value, float):
return False
try:
number = int(value)
return True
except:
return False
def is_IRI(self, value):
# Internationalized Resource Identifiers
# a more generalized complement to Uniform Resource Identifiers (URIs)
# nearly everything can be a valid <IRI>
# only a none-empty string ist a valid input
if is_string(value):
return bool(value.strip())
else:
return False
def is_length(self, value):
# length ::= number ("em" | "ex" | "px" | "in" | "cm" | "mm" | "pt" | "pc" | "%")?
if value is None:
return False
if isinstance(value, (int, float)):
return self.is_number(value)
elif is_string(value):
result = pattern.length.match(value.strip())
if result:
number, tmp, unit = result.groups()
return self.is_number(number) # for tiny check!
return False
is_coordinate = is_length
def is_list_of_T(self, value, t='string'):
def split(value):
#TODO: improve split function!!!!
if isinstance(value, (int, float)):
return (value, )
if is_string(value):
return iterflatlist(v.split(',') for v in value.split(' '))
return value
#list-of-Ts ::= T
# | T comma-wsp list-of-Ts
#comma-wsp ::= (wsp+ ","? wsp*) | ("," wsp*)
#wsp ::= (#x20 | #x9 | #xD | #xA)
checker = self.get_func_by_name(t)
for v in split(value):
if not checker(v):
return False
return True
def is_four_numbers(self, value):
def split(value):
if is_string(value):
values = iterflatlist( (v.strip().split(' ') for v in value.split(',')) )
return (v for v in values if v)
else:
return iterflatlist(value)
values = list(split(value))
if len(values) != 4:
return False
checker = self.get_func_by_name('number')
for v in values:
if not checker(v):
return False
return True
def is_semicolon_list(self, value):
#a semicolon-separated list of values
# | value comma-wsp list-of-values
#comma-wsp ::= (wsp+ ";" wsp*) | ("," wsp*)
#wsp ::= (#x20 | #x9 | #xD | #xA)
return self.is_list_of_T(value.replace(';', ' '), 'string')
def is_name(self, value):
# name ::= [^,()#x20#x9#xD#xA] /* any char except ",", "(", ")" or wsp */
chars = frozenset(str(value).strip())
if not chars or INVALID_NAME_CHARS.intersection(chars):
return False
else:
return True
def is_number(self, value):
try:
number = float(value)
return True
except:
return False
def is_number_optional_number(self, value):
#number-optional-number ::= number
# | number comma-wsp number
if is_string(value):
values = re.split('[ ,]+', value.strip())
if 0 < len(values) < 3: # 1 or 2 numbers
for v in values:
if not self.is_number(v):
return False
return True
else:
try: # is it a 2-tuple
n1, n2 = value
if self.is_number(n1) and \
self.is_number(n2):
return True
except TypeError: # just one value
return self.is_number(value)
except ValueError: # more than 2 values
pass
return False
def is_paint(self, value):
#paint ::= "none" |
# "currentColor" |
# <color> [<icccolor>] |
# <funciri> [ "none" | "currentColor" | <color> [<icccolor>] |
# "inherit"
def split_values(value):
try:
funcIRI, value = value.split(")", 1)
values = [funcIRI+")"]
values.extend(split_values(value))
return values
except ValueError:
return value.split()
values = split_values(str(value).strip())
for value in [v.strip() for v in values]:
if value in ('none', 'currentColor', 'inherit'):
continue
elif self.is_color(value):
continue
elif self.is_icccolor(value):
continue
elif self.is_FuncIRI(value):
continue
return False
return True
def is_percentage(self, value):
#percentage ::= number "%"
if self.is_number(value):
return True
elif is_string(value):
return pattern.percentage.match(value.strip()) is not None
return False
def is_time(self, value):
#time ::= <number> (~"ms" | ~"s")?
if self.is_number(value):
return True
elif is_string(value):
return pattern.time.match(value.strip()) is not None
return False
def is_transform_list(self, value):
if is_string(value):
return is_valid_transferlist(value)
else:
return False
def is_path_data(self, value):
if is_string(value):
return is_valid_pathdata(value)
else:
return False
def is_XML_Name(self, value):
# http://www.w3.org/TR/2006/REC-xml-20060816/#NT-Name
# Nmtoken
return bool(NMTOKEN_PATTERN.match(str(value).strip()))
def is_shape(self, value):
# shape ::= (<top> <right> <bottom> <left>)
# where <top>, <bottom> <right>, and <left> specify offsets from the
# respective sides of the box.
# <top>, <right>, <bottom>, and <left> are <length> values
# i.e. 'rect(5px, 10px, 10px, 5px)'
res = SHAPE_PATTERN.match(value.strip())
if res:
for arg in res.groups():
if arg.strip() == 'auto':
continue
if not self.is_length(arg):
return False
else:
return False
return True
def is_timing_value_list(self, value):
if is_string(value):
return is_valid_animation_timing(value)
else:
return False
def is_list_of_text_decoration_style(self, value):
return self.is_list_of_T(value, t='text_decoration_style')
def is_text_decoration_style(self, value):
return value in ('overline', 'underline', 'line-through', 'blink')
def get_func_by_name(self, funcname):
return getattr(self,
'is_'+funcname.replace('-', '_'),
self.is_anything)
def check(self, typename, value):
if typename.startswith('list-of-'):
t = typename[8:]
return self.is_list_of_T(value, t)
return self.get_func_by_name(typename)(value)
FOCUS_CONST = frozenset(['nav-next', 'nav-prev', 'nav-up', 'nav-down', 'nav-left',
'nav-right', 'nav-up-left', 'nav-up-right', 'nav-down-left',
'nav-down-right'])
class Tiny12TypeChecker(Full11TypeChecker):
def get_version(self):
return '1.2', 'tiny'
def is_boolean(self, value):
if isinstance(value, bool):
return True
if is_string(value):
return value.strip().lower() in ('true', 'false')
return False
def is_number(self, value):
try:
number = float(value)
if -32767.9999 <= number <= 32767.9999:
return True
else:
return False
except:
return False
def is_focus(self, value):
return str(value).strip() in FOCUS_CONST
@@ -0,0 +1,81 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: svg types
# Created: 30.09.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
class SVGAttribute(object):
def __init__(self, name, anim, types, const):
self.name = name
self._anim = anim
self._types = types
self._const = const
# 'elementname' is ignored, but necessary because of the signatures of
# the SVGMultiAttribute class methods get_...()
def get_anim(self, elementname='*'):
return self._anim
def get_types(self, elementname='*'):
return self._types
def get_const(self, elementname='*'):
return self._const
class SVGMultiAttribute(object):
# example: SVGMultiAttribute({'*':SVGAttribute(...), 'text tref':SVGAttribute(...)} )
# parametr is a dict-like object
# '*' is the default attribute definition
# 'text' and 'tref' share the same attribute definition
def __init__(self, attributes):
self.name = None
self._attributes = {}
for names, attribute in attributes.items():
for name in names.split():
name = name.strip()
self._attributes[name] = attribute
if not self.name:
self.name = attribute.name
elif self.name != attribute.name:
raise ValueError("Different attribute-names for SVGMultiAttribute "\
"(%s != %s)." % (self.name, attribute.name))
if '*' not in self._attributes and len(self._attributes):
# if no default attribute definition were given
# set the first attribute definition as the default attribute definition
firstkey = sorted(self._attributes.keys())[0]
self._attributes['*'] = self._attributes[firstkey]
def get_attribute(self, elementname):
if elementname in self._attributes:
return self._attributes[elementname]
else:
return self._attributes['*']
def get_anim(self, elementname='*'):
attribute = self.get_attribute(elementname)
return attribute.get_anim()
def get_types(self, elementname='*'):
attribute = self.get_attribute(elementname)
return attribute.get_types()
def get_const(self, elementname='*'):
attribute = self.get_attribute(elementname)
return attribute.get_const()
class SVGElement(object):
def __init__(self, name, attributes, properties, children):
self.name = name
s = set(attributes)
s.update(properties)
self.valid_attributes = frozenset(s)
self.valid_children = frozenset(children)
@@ -0,0 +1,140 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman
# Purpose: drawing
# Created: 10.09.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
"""
The *Drawing* object is the overall container for all SVG
elements. It provides the methods to store the drawing into a file or a
file-like object. If you want to use stylesheets, the reference links
to this stylesheets were also stored (`add_stylesheet`)
in the *Drawing* object.
set/get SVG attributes::
element['attribute'] = value
value = element['attribute']
The Drawing object also includes a defs section, add elements to the defs
section by::
drawing.defs.add(element)
"""
import io
from svgwrite.container import SVG, Defs
from svgwrite.elementfactory import ElementFactory
from svgwrite.utils import pretty_xml
class Drawing(SVG, ElementFactory):
""" This is the SVG drawing represented by the top level *svg* element.
A drawing consists of any number of SVG elements contained within the drawing
element, stored in the *elements* attribute.
A drawing can range from an empty drawing (i.e., no content inside of the drawing),
to a very simple drawing containing a single SVG element such as a *rect*,
to a complex, deeply nested collection of container elements and graphics elements.
"""
def __init__(self, filename="noname.svg", size=('100%', '100%'), **extra):
"""
:param string filename: filesystem filename valid for :func:`open`
:param 2-tuple size: width, height
:param keywords extra: additional svg-attributes for the *SVG* object
Important (and not SVG Attributes) **extra** parameters:
:param string profile: ``'tiny | full'`` - define the SVG baseProfile
:param bool debug: switch validation on/off
"""
super(Drawing, self).__init__(size=size, **extra)
self.filename = filename
self._stylesheets = [] # list of stylesheets appended
def get_xml(self):
""" Get the XML representation as `ElementTree` object.
:return: XML `ElementTree` of this object and all its subelements
"""
profile = self.profile
version = self.version
self.attribs['xmlns'] = "http://www.w3.org/2000/svg"
self.attribs['xmlns:xlink'] = "http://www.w3.org/1999/xlink"
self.attribs['xmlns:ev'] = "http://www.w3.org/2001/xml-events"
self.attribs['baseProfile'] = profile
self.attribs['version'] = version
return super(Drawing, self).get_xml()
def add_stylesheet(self, href, title, alternate="no", media="screen"):
""" Add a stylesheet reference.
:param string href: link to stylesheet <URI>
:param string title: name of stylesheet
:param string alternate: ``'yes'|'no'``
:param string media: ``'all | aureal | braille | embossed | handheld | print | projection | screen | tty | tv'``
"""
self._stylesheets.append((href, title, alternate, media))
def write(self, fileobj, pretty=False, indent=2):
""" Write XML string to `fileobj`.
:param fileobj: a file-like object
:param pretty: True for easy readable output
:param indent: how much to indent if pretty is enabled, by default 2 spaces
Python 3.x - set encoding at the open command::
open('filename', 'w', encoding='utf-8')
"""
# write xml header
fileobj.write('<?xml version="1.0" encoding="utf-8" ?>\n')
# don't use DOCTYPE. It's useless. see also:
# http://tech.groups.yahoo.com/group/svg-developers/message/48562
# write stylesheets
stylesheet_template = '<?xml-stylesheet href="%s" type="text/css" ' \
'title="%s" alternate="%s" media="%s"?>\n'
# removed map(), does not work with Python 3
for stylesheet in self._stylesheets:
fileobj.write(stylesheet_template % stylesheet)
xml_string = self.tostring()
if pretty: # write easy readable XML file
xml_string = pretty_xml(xml_string, indent=indent)
fileobj.write(xml_string)
def save(self, pretty=False, indent=2):
""" Write the XML string to `self.filename`.
:param pretty: True for easy readable output
:param indent: how much to indent if pretty is enabled, by default 2 spaces
"""
fileobj = io.open(self.filename, mode='w', encoding='utf-8')
self.write(fileobj, pretty=pretty, indent=indent)
fileobj.close()
def saveas(self, filename, pretty=False, indent=2):
""" Write the XML string to `filename`.
:param string filename: filesystem filename valid for :func:`open`
:param pretty: True for easy readable output
:param indent: how much to indent if pretty is enabled, by default 2 spaces
"""
self.filename = filename
self.save(pretty=pretty, indent=indent)
def _repr_svg_(self):
""" Show SVG in IPython, Jupyter Notebook, and Jupyter Lab
:return: unicode XML string of this object and all its subelements
"""
return self.tostring()
@@ -0,0 +1,76 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: element factory
# Created: 15.10.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
from svgwrite import container
from svgwrite import shapes
from svgwrite import path
from svgwrite import image
from svgwrite import text
from svgwrite import gradients
from svgwrite import pattern
from svgwrite import masking
from svgwrite import animate
from svgwrite import filters
from svgwrite import solidcolor
factoryelements = {
'g': container.Group,
'svg': container.SVG,
'defs': container.Defs,
'symbol': container.Symbol,
'marker': container.Marker,
'use': container.Use,
'a': container.Hyperlink,
'script': container.Script,
'style': container.Style,
'line': shapes.Line,
'rect': shapes.Rect,
'circle': shapes.Circle,
'ellipse': shapes.Ellipse,
'polyline': shapes.Polyline,
'polygon': shapes.Polygon,
'path': path.Path,
'image': image.Image,
'text': text.Text,
'tspan': text.TSpan,
'tref': text.TRef,
'textPath': text.TextPath,
'textArea': text.TextArea,
'linearGradient': gradients.LinearGradient,
'radialGradient': gradients.RadialGradient,
'pattern': pattern.Pattern,
'solidColor': solidcolor.SolidColor,
'clipPath': masking.ClipPath,
'mask': masking.Mask,
'animate': animate.Animate,
'set': animate.Set,
'animateColor': animate.AnimateColor,
'animateMotion': animate.AnimateMotion,
'animateTransform': animate.AnimateTransform,
'filter': filters.Filter,
}
class ElementBuilder(object):
def __init__(self, cls, factory):
self.cls = cls
self.factory = factory
def __call__(self, *args, **kwargs):
# inject creator object - inherit _parameter from factory
kwargs['factory'] = self.factory
# create an object of type 'cls'
return self.cls(*args, **kwargs)
class ElementFactory(object):
def __getattr__(self, name):
if name in factoryelements:
return ElementBuilder(factoryelements[name], self)
else:
raise AttributeError("'%s' has no attribute '%s'" % (self.__class__.__name__, name))
@@ -0,0 +1,49 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman
# Purpose: a hack to generate XML containing CDATA by ElementTree
# Created: 26.05.2012
# Copyright (C) 2012, Manfred Moitzi
# License: MIT License
# usage:
#
# from svgwrite.etree import etree, CDATA
#
# element = etree.Element('myTag')
# element.append(CDATA("< and >"))
#
# assert etree.tostring(element) == "<myTag><![CDATA[< and >]]></myTag>"
import sys
PY3 = sys.version_info[0] > 2
import xml.etree.ElementTree as etree
CDATA_TPL = "<![CDATA[%s]]>"
CDATA_TAG = CDATA_TPL
def CDATA(text):
element = etree.Element(CDATA_TAG)
element.text = text
return element
original_serialize_xml = etree._serialize_xml
if PY3:
def _serialize_xml_with_CDATA_support(write, elem, qnames, namespaces, **kwargs):
if elem.tag == CDATA_TAG:
write(CDATA_TPL % elem.text)
else:
original_serialize_xml(write, elem, qnames, namespaces, **kwargs)
else:
def _serialize_xml_with_CDATA_support(write, elem, encoding, qnames, namespaces):
if elem.tag == CDATA_TAG:
write(CDATA_TPL % elem.text.encode(encoding))
else:
original_serialize_xml(write, elem, encoding, qnames, namespaces)
# ugly, ugly, ugly patching
etree._serialize_xml = _serialize_xml_with_CDATA_support
@@ -0,0 +1,3 @@
# Copyright (c) 2018 Manfred Moitzi
# License: MIT License
from .inkscape import Inkscape
@@ -0,0 +1,89 @@
# Copyright (c) 2018 Manfred Moitzi
# License: MIT License
# based on work of:
# Copyright (c) 2018 Antonio Ospite <ao2@ao2.it>
from svgwrite.data.types import SVGAttribute
INKSCAPE_NAMESPACE = 'http://www.inkscape.org/namespaces/inkscape'
SODIPODI_NAMESPACE = 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd'
INKSCAPE_ATTRIBUTES = {
'xmlns:inkscape': SVGAttribute('xmlns:inkscape',
anim=False,
types=[],
const=frozenset([INKSCAPE_NAMESPACE])),
'xmlns:sodipodi': SVGAttribute('xmlns:sodipodi',
anim=False,
types=[],
const=frozenset([SODIPODI_NAMESPACE])),
'inkscape:groupmode': SVGAttribute('inkscape:groupmode',
anim=False,
types=[],
const=frozenset(['layer'])),
'inkscape:label': SVGAttribute('inkscape:label',
anim=False,
types=frozenset(['string']),
const=[]),
'sodipodi:insensitive': SVGAttribute('sodipodi:insensitive',
anim=False,
types=[],
const=frozenset(['true', 'false', '0', '1']))
}
def _setup_validator(validator):
# setup already done?
if 'xmlns:inkscape' in validator.attributes:
return
validator.attributes.update(INKSCAPE_ATTRIBUTES)
elements = validator.elements
# extend SVG attributes
elements['svg'].valid_attributes = \
{
'xmlns:inkscape',
'xmlns:sodipodi',
} | elements['svg'].valid_attributes
# extend group attributes
elements['g'].valid_attributes = \
{
'inkscape:groupmode',
'inkscape:label',
'sodipodi:insensitive',
} | elements['g'].valid_attributes
GROUP_MODE = 'inkscape:groupmode'
LABEL = 'inkscape:label'
INSENSITIVE = 'sodipodi:insensitive'
class Inkscape(object):
"""
Extension to support SOME Inkscape features.
"""
def __init__(self, drawing):
self.svg = drawing
_setup_validator(drawing.validator)
drawing['xmlns:inkscape'] = INKSCAPE_NAMESPACE
drawing['xmlns:sodipodi'] = SODIPODI_NAMESPACE
def layer(self, label=None, locked=False, **kwargs):
"""
Create new Inkscape layer.
Args:
label: layer name as string
locked: when set to True, make objects at this layer unselectable
"""
new_layer = self.svg.g(**kwargs)
new_layer[GROUP_MODE] = 'layer'
if label is not None:
new_layer[LABEL] = label
if locked:
new_layer[INSENSITIVE] = 1
return new_layer
@@ -0,0 +1,129 @@
"""
Extension to create and manipulate shapes
"""
# Copyright (c) 2019 Christof Hanke (christof.hanke@induhviduals.de)
# License: MIT License
import math
def ngon(num_corners, edge_length=None, radius=None, rotation=0.):
"""
Returns the corners of a regular polygon as iterable of (x, y) tuples. The polygon size is determined by the
`edge_length` or the `radius` argument. If both are given `edge_length` will be taken.
Args:
num_corners: count of polygon corners
edge_length: length of polygon side
radius: circum radius
rotation: rotation angle in radians
Returns: iterable of (x, y) tuples
"""
if num_corners < 3:
raise ValueError('Argument `num_corners` has to be greater than 2.')
if edge_length is not None:
radius = edge_length / 2 / math.sin(math.pi / num_corners)
elif radius is not None:
if radius <= 0.:
raise ValueError('Argument `radius` has to be greater than 0.')
else:
raise ValueError('Argument `edge_length` or `radius` required.')
delta = 2 * math.pi / num_corners
angle = rotation
for _ in range(num_corners):
yield (radius * math.cos(angle), radius * math.sin(angle))
angle += delta
def star(spikes, r1, r2, rotation=0.):
"""
Create a star shape as iterable of (x, y) vertices.
Argument `spikes` defines the count of star spikes, `r1` defines the radius of the "outer" vertices and `r2`
defines the radius of the "inner" vertices, but this does not mean that `r1` has to greater than `r2`.
Args:
spikes: spike count
r1: radius 1
r2: radius 2
rotation: rotation angle in radians
Returns: iterable of (x, y) tuples
"""
if spikes < 3:
raise ValueError('Argument `spikes` has to be greater than 2.')
if r1 <= 0.:
raise ValueError('Argument `r1` has to be greater than 0.')
if r2 <= 0.:
raise ValueError('Argument `r2` has to be greater than 0.')
corners1 = ngon(spikes, radius=r1, rotation=rotation)
corners2 = ngon(spikes, radius=r2, rotation=math.pi/spikes+rotation)
for s1, s2 in zip(corners1, corners2):
yield s1
yield s2
def translate(vertices, delta_x, delta_y):
"""
Translates `vertices` about `delta_x` and `delta_y`
Args:
vertices: iterable of (x, y) tuples
delta_x: translation in x axis
delta_y: translation in y axis
Returns: iterable of (x, y) tuples
"""
for x, y in vertices:
yield (x + delta_x, y + delta_y)
def scale(vertices, scale_x, scale_y):
"""
Scales `vertices` about `scale_x` and `scale_y`
Args:
vertices: iterable of (x, y) tuples
scale_x: scaling factor in x axis direction
scale_y: scaling factor in y axis direction
Returns: iterable of (x, y) tuples
"""
for x, y in vertices:
yield (x * scale_x, y * scale_y)
def rotate(vertices, delta):
"""
Rotates `vertices` about `delta` degrees around the origin (0, 0).
Args:
vertices: iterable of (x, y) tuples
delta: rotation angle in radians
Returns: iterable of (x, y) tuples
"""
for x, y in vertices:
r = math.hypot(x, y)
angle = math.atan2(y, x) + delta
yield (r * math.cos(angle), r * math.sin(angle))
def centroid(vertices):
"""
Returns the centroid of a series of `vertices`.
"""
k, c_x, c_y = 0, 0, 0
for x, y in vertices:
c_x += x
c_y += y
k += 1
return c_x / k, c_y / k
@@ -0,0 +1,273 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: filters module
# Created: 03.11.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
from svgwrite.base import BaseElement
from svgwrite.mixins import XLink, Presentation
from svgwrite.utils import strlist, is_string
__all__ = ['Filter']
class _feDistantLight(BaseElement):
elementname = 'feDistantLight'
def __init__(self, azimuth=0, elevation=0, **extra):
super(_feDistantLight, self).__init__(**extra)
if azimuth != 0:
self['azimuth'] = azimuth
if elevation != 0:
self['elevation'] = elevation
class _fePointLight(BaseElement):
elementname = 'fePointLight'
def __init__(self, source=(0, 0, 0), **extra):
super(_fePointLight, self).__init__(**extra)
x, y, z = source
if x != 0:
self['x'] = x
if y != 0:
self['y'] = y
if z != 0:
self['z'] = z
class _feSpotLight(_fePointLight):
elementname = 'feSpotLight'
def __init__(self, source=(0, 0, 0), target=(0, 0, 0), **extra):
super(_feSpotLight, self).__init__(source, **extra)
x, y, z = target
if x != 0:
self['pointsAtX'] = x
if y != 0:
self['pointsAtY'] = y
if z != 0:
self['pointsAtZ'] = z
class _FilterPrimitive(BaseElement, Presentation):
pass
class _FilterNoInput(_FilterPrimitive):
def __init__(self, start=None, size=None, **extra):
super(_FilterNoInput, self).__init__(**extra)
if start is not None:
self['x'] = start[0]
self['y'] = start[1]
if size is not None:
self['width'] = size[0]
self['height'] = size[1]
class _FilterRequireInput(_FilterNoInput):
def __init__(self, in_='SourceGraphic', **extra):
super(_FilterRequireInput, self).__init__(**extra)
self['in'] = in_
class _feBlend(_FilterRequireInput):
elementname = 'feBlend'
class _feColorMatrix(_FilterRequireInput):
elementname = 'feColorMatrix'
class _feComponentTransfer(_FilterRequireInput):
elementname = 'feComponentTransfer'
def feFuncR(self, type_, **extra):
return self.add(_feFuncR(type_, factory=self, **extra))
def feFuncG(self, type_, **extra):
return self.add(_feFuncG(type_, factory=self, **extra))
def feFuncB(self, type_, **extra):
return self.add(_feFuncB(type_, factory=self, **extra))
def feFuncA(self, type_, **extra):
return self.add(_feFuncA(type_, factory=self, **extra))
class _feFuncR(_FilterPrimitive):
elementname = 'feFuncR'
def __init__(self, type_, **extra):
super(_feFuncR, self).__init__(**extra)
self['type'] = type_
class _feFuncG(_feFuncR):
elementname = 'feFuncG'
class _feFuncB(_feFuncR):
elementname = 'feFuncB'
class _feFuncA(_feFuncR):
elementname = 'feFuncA'
class _feComposite(_FilterRequireInput):
elementname = 'feComposite'
class _feConvolveMatrix(_FilterRequireInput):
elementname = 'feConvolveMatrix'
class _feDiffuseLighting(_FilterRequireInput):
elementname = 'feDiffuseLighting'
def feDistantLight(self, azimuth=0, elevation=0, **extra):
return self.add(_feDistantLight(azimuth, elevation, **extra))
def fePointLight(self, source=(0, 0, 0), **extra):
return self.add(_fePointLight(source, **extra))
def feSpotLight(self, source=(0, 0, 0), target=(0, 0, 0), **extra):
return self.add(_feSpotLight(source, target, **extra))
class _feDisplacementMap(_FilterRequireInput):
elementname = 'feDisplacementMap'
class _feFlood(_FilterNoInput):
elementname = 'feFlood'
class _feGaussianBlur(_FilterRequireInput):
elementname = 'feGaussianBlur'
class _feImage(_FilterNoInput, XLink):
elementname = 'feImage'
def __init__(self, href, start=None, size=None, **extra):
super(_feImage, self).__init__(start, size, **extra)
self.set_href(href)
class _feMergeNode(_FilterPrimitive):
elementname = 'feMergeNode'
class _feMerge(_FilterNoInput):
elementname = 'feMerge'
def __init__(self, layernames, **extra):
super(_feMerge, self).__init__(**extra)
self.feMergeNode(layernames)
def feMergeNode(self, layernames):
for layername in layernames:
self.add(_feMergeNode(in_=layername, factory=self))
class _feMorphology(_FilterRequireInput):
elementname = 'feMorphology'
class _feOffset(_FilterRequireInput):
elementname = 'feOffset'
class _feSpecularLighting(_feDiffuseLighting):
elementname = 'feSpecularLighting'
class _feTile(_FilterRequireInput):
elementname = 'feTile'
class _feTurbulence(_FilterNoInput):
elementname = 'feTurbulence'
filter_factory = {
'feBlend': _feBlend,
'feColorMatrix': _feColorMatrix,
'feComponentTransfer': _feComponentTransfer,
'feComposite': _feComposite,
'feConvolveMatrix': _feConvolveMatrix,
'feDiffuseLighting': _feDiffuseLighting,
'feDisplacementMap': _feDisplacementMap,
'feFlood': _feFlood,
'feGaussianBlur': _feGaussianBlur,
'feImage': _feImage,
'feMerge': _feMerge,
'feMorphology': _feMorphology,
'feOffset': _feOffset,
'feSpecularLighting': _feSpecularLighting,
'feTile': _feTile,
'feTurbulence': _feTurbulence,
}
class _FilterBuilder(object):
def __init__(self, cls, parent):
self.cls = cls # primitive filter class to build
self.parent = parent # the parent Filter() object
def __call__(self, *args, **kwargs):
kwargs['factory'] = self.parent # to get the _paramters object
obj = self.cls(*args, **kwargs) # create an object of type 'cls'
self.parent.add(obj) # add primitive filter to parent Filter()
return obj
class Filter(BaseElement, XLink, Presentation):
"""
The filter element is a container element for filter primitives, and
also a **factory** for filter primitives.
"""
elementname = 'filter'
def __init__(self, start=None, size=None, resolution=None, inherit=None, **extra):
"""
:param 2-tuple start: defines the start point of the filter effects region (**x**, **y**)
:param 2-tuple size: defines the size of the filter effects region (**width**, **height**)
:param resolution: takes the form ``'x-pixels [y-pixels]'``, and indicates
the width and height of the intermediate images in pixels.
:param inherit: inherits properties from Filter `inherit` see: **xlink:href**
"""
super(Filter, self).__init__(**extra)
if start is not None:
self['x'] = start[0]
self['y'] = start[1]
if size is not None:
self['width'] = size[0]
self['height'] = size[1]
if resolution is not None:
if is_string(resolution):
self['filterRes'] = resolution
elif hasattr(resolution, '__iter__'):
self['filterRes'] = strlist(resolution, ' ')
else:
self['filterRes'] = str(resolution)
if inherit is not None:
self.href = inherit
self.update_id()
def get_xml(self):
self.update_id()
return super(Filter, self).get_xml()
def __getattr__(self, name):
# create primitive filters by Filter.<filtername>(...)
# and auto-add the new filter as subelement of Filter()
if name in filter_factory:
return _FilterBuilder(filter_factory[name], self)
else:
raise AttributeError("'%s' has no attribute '%s'" % (self.__class__.__name__, name))
@@ -0,0 +1,128 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: gradients module
# Created: 26.10.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
"""
Gradients consist of continuously smooth color transitions along a vector
from one color to another, possibly followed by additional transitions along
the same vector to other colors. SVG provides for two types of gradients:
linear gradients and radial gradients.
"""
from svgwrite.base import BaseElement
from svgwrite.mixins import Transform, XLink
from svgwrite.utils import is_string
class _GradientStop(BaseElement):
elementname = 'stop'
def __init__(self, offset=None, color=None, opacity=None, **extra):
super(_GradientStop, self).__init__(**extra)
if offset is not None:
self['offset'] = offset
if color is not None:
self['stop-color'] = color
if opacity is not None:
self['stop-opacity'] = opacity
class _AbstractGradient(BaseElement, Transform, XLink):
transformname = 'gradientTransform'
def __init__(self, inherit=None, **extra):
super(_AbstractGradient, self).__init__(**extra)
if inherit is not None:
if is_string(inherit):
self.set_href(inherit)
else:
self.set_href(inherit.get_iri())
def get_paint_server(self, default='none'):
""" Returns the <FuncIRI> of the gradient. """
return "%s %s" % (self.get_funciri(), default)
def add_stop_color(self, offset=None, color=None, opacity=None):
""" Adds a stop-color to the gradient.
:param offset: is either a <number> (usually ranging from 0 to 1) or
a `<percentage>` (usually ranging from 0% to 100%) which indicates where
the gradient stop is placed. Represents a location along the gradient
vector. For radial gradients, it represents a percentage distance from
(fx,fy) to the edge of the outermost/largest circle.
:param color: indicates what color to use at that gradient stop
:param opacity: defines the opacity of a given gradient stop
"""
self.add(_GradientStop(offset, color, opacity, factory=self))
return self
def add_colors(self, colors, sweep=(0., 1.), opacity=None):
""" Add stop-colors from colors with linear offset distributuion
from sweep[0] to sweep[1].
i.e. colors=['white', 'red', 'blue']
'white': offset = 0.0
'red': offset = 0.5
'blue': offset = 1.0
"""
delta = (sweep[1] - sweep[0]) / (len(colors) - 1)
offset = sweep[0]
for color in colors:
self.add_stop_color(round(offset, 3), color, opacity)
offset += delta
return self
def get_xml(self):
if hasattr(self, 'href'):
self.update_id()
return super(_AbstractGradient, self).get_xml()
class LinearGradient(_AbstractGradient):
""" Linear gradients are defined by a SVG <linearGradient> element.
"""
elementname = 'linearGradient'
def __init__(self, start=None, end=None, inherit=None, **extra):
"""
:param 2-tuple start: start point of the gradient (**x1**, **y1**)
:param 2-tuple end: end point of the gradient (**x2**, **y2**)
:param inherit: gradient inherits properties from `inherit` see: **xlink:href**
"""
super(LinearGradient, self).__init__(inherit=inherit, **extra)
if start is not None:
self['x1'] = start[0]
self['y1'] = start[1]
if end is not None:
self['x2'] = end[0]
self['y2'] = end[1]
class RadialGradient(_AbstractGradient):
""" Radial gradients are defined by a SVG <radialGradient> element.
"""
elementname = 'radialGradient'
def __init__(self, center=None, r=None, focal=None, inherit=None, **extra):
"""
:param 2-tuple center: center point for the gradient (**cx**, **cy**)
:param r: radius for the gradient
:param 2-tuple focal: focal point for the radial gradient (**fx**, **fy**)
:param inherit: gradient inherits properties from `inherit` see: **xlink:href**
"""
super(RadialGradient, self).__init__(inherit=inherit, **extra)
if center is not None:
self['cx'] = center[0]
self['cy'] = center[1]
if r is not None:
self['r'] = r
if focal is not None:
self['fx'] = focal[0]
self['fy'] = focal[1]
@@ -0,0 +1,61 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: svg image element
# Created: 09.10.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
from svgwrite.base import BaseElement
from svgwrite.mixins import Transform, _vert, _horiz, Clipping
class Image(BaseElement, Transform, Clipping):
""" The **image** element indicates that the contents of a complete file are
to be rendered into a given rectangle within the current user coordinate
system. The **image** element can refer to raster image files such as PNG
or JPEG or to files with MIME type of "image/svg+xml".
"""
elementname = 'image'
def __init__(self, href, insert=None, size=None, **extra):
"""
:param string href: hyperlink to the image resource
:param 2-tuple insert: insert point (**x**, **y**)
:param 2-tuple size: (**width**, **height**)
:param dict attribs: additional SVG attributes
:param extra: additional SVG attributes as keyword-arguments
"""
super(Image, self).__init__(**extra)
self['xlink:href'] = href
if insert is not None:
self['x'] = insert[0]
self['y'] = insert[1]
if size is not None:
self['width'] = size[0]
self['height'] = size[1]
def stretch(self):
""" Stretch viewBox in x and y direction to fill viewport, does not
preserve aspect ratio.
"""
self['preserveAspectRatio'] = 'none'
def fit(self, horiz="center", vert="middle", scale="meet"):
""" Set the preserveAspectRatio attribute.
:param string horiz: horizontal alignment ``'left'|'center'|'right'``
:param string vert: vertical alignment ``'top'|'middle'|'bottom'``
:param string scale: scale method ``'meet'|'slice'``
============= ===========
Scale methods Description
============= ===========
``meet`` preserve aspect ration and zoom to limits of viewBox
``slice`` preserve aspect ration and viewBox touch viewport on all bounds, viewBox will extend beyond the bounds of the viewport
============= ===========
"""
if self.debug and scale not in ('meet', 'slice'):
raise ValueError("Invalid scale parameter '%s'" % scale)
self.attribs['preserveAspectRatio'] = "%s%s %s" % (_horiz[horiz],_vert[vert], scale)
@@ -0,0 +1,50 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: masking module
# Created: 30.10.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
from svgwrite.base import BaseElement
from svgwrite.mixins import Transform
class ClipPath(BaseElement, Transform):
"""
The clipping path restricts the region to which paint can be applied.
Conceptually, any parts of the drawing that lie outside of the region
bounded by the currently active clipping path are not drawn. A clipping
path can be thought of as a mask wherein those pixels outside the clipping
path are black with an alpha value of zero and those pixels inside the
clipping path are white with an alpha value of one (with the possible
exception of anti-aliasing along the edge of the silhouette).
A **clipPath** element can contain **path** elements, **text** elements,
basic shapes (such as **circle**) or a **use** element. If a **use**
element is a child of a **clipPath** element, it must directly reference
**path**, **text** or basic shape elements. Indirect references are an
error.
"""
elementname = 'clipPath'
class Mask(BaseElement):
"""
In SVG, you can specify that any other graphics object or **g** element
can be used as an alpha mask for compositing the current object into the
background.
A **mask** can contain any graphical elements or container elements such
as a **g**.
"""
elementname = 'mask'
def __init__(self, start=None, size=None, **extra):
super(Mask, self).__init__(**extra)
if start is not None:
self['x'] = start[0]
self['y'] = start[1]
if size is not None:
self['width'] = size[0]
self['height'] = size[1]
@@ -0,0 +1,298 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: mixins
# Created: 19.10.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
from svgwrite.utils import strlist
from svgwrite.utils import is_string
_horiz = {'center': 'xMid', 'left': 'xMin', 'right': 'xMax'}
_vert = {'middle': 'YMid', 'top': 'YMin', 'bottom':'YMax'}
class ViewBox(object):
""" The **ViewBox** mixin provides the ability to specify that a
given set of graphics stretch to fit a particular container element.
The value of the **viewBox** attribute is a list of four numbers
**min-x**, **min-y**, **width** and **height**, separated by whitespace
and/or a comma, which specify a rectangle in **user space** which should
be mapped to the bounds of the viewport established by the given element,
taking into account attribute **preserveAspectRatio**.
"""
def viewbox(self, minx=0, miny=0, width=0, height=0):
""" Specify a rectangle in **user space** (no units allowed) which
should be mapped to the bounds of the viewport established by the
given element.
:param number minx: left border of the viewBox
:param number miny: top border of the viewBox
:param number width: width of the viewBox
:param number height: height of the viewBox
"""
self['viewBox'] = strlist( [minx, miny, width, height] )
def stretch(self):
""" Stretch viewBox in x and y direction to fill viewport, does not
preserve aspect ratio.
"""
self['preserveAspectRatio'] = 'none'
def fit(self, horiz="center", vert="middle", scale="meet"):
""" Set the **preserveAspectRatio** attribute.
:param string horiz: horizontal alignment ``'left | center | right'``
:param string vert: vertical alignment ``'top | middle | bottom'``
:param string scale: scale method ``'meet | slice'``
============= =======================================================
Scale methods Description
============= =======================================================
``'meet'`` preserve aspect ration and zoom to limits of viewBox
``'slice'`` preserve aspect ration and viewBox touch viewport on
all bounds, viewBox will extend beyond the bounds of
the viewport
============= =======================================================
"""
if self.debug and scale not in ('meet', 'slice'):
raise ValueError("Invalid scale parameter '%s'" % scale)
self['preserveAspectRatio'] = "%s%s %s" % (_horiz[horiz],_vert[vert], scale)
class Transform(object):
""" The **Transform** mixin operates on the **transform** attribute.
The value of the **transform** attribute is a `<transform-list>`, which
is defined as a list of transform definitions, which are applied in the
order provided. The individual transform definitions are separated by
whitespace and/or a comma. All coordinates are **user
space coordinates**.
"""
transformname = 'transform'
def translate(self, tx, ty=None):
"""
Specifies a translation by **tx** and **ty**. If **ty** is not provided,
it is assumed to be zero.
:param number tx: user coordinate - no units allowed
:param number ty: user coordinate - no units allowed
"""
self._add_transformation("translate(%s)" % strlist( [tx, ty] ))
def rotate(self, angle, center=None):
"""
Specifies a rotation by **angle** degrees about a given point.
If optional parameter **center** are not supplied, the rotate is
about the origin of the current user coordinate system.
:param number angle: rotate-angle in degrees
:param 2-tuple center: rotate-center as user coordinate - no units allowed
"""
self._add_transformation("rotate(%s)" % strlist( [angle, center] ))
def scale(self, sx, sy=None):
"""
Specifies a scale operation by **sx** and **sy**. If **sy** is not
provided, it is assumed to be equal to **sx**.
:param number sx: scalar factor x-axis, no units allowed
:param number sy: scalar factor y-axis, no units allowed
"""
self._add_transformation("scale(%s)" % strlist([sx, sy]))
def skewX(self, angle):
""" Specifies a skew transformation along the x-axis.
:param number angle: skew-angle in degrees, no units allowed
"""
self._add_transformation("skewX(%s)" % angle)
def skewY(self, angle):
""" Specifies a skew transformation along the y-axis.
:param number angle: skew-angle in degrees, no units allowed
"""
self._add_transformation("skewY(%s)" % angle)
def matrix(self, a, b, c, d, e, f):
self._add_transformation("matrix(%s)" % strlist( [a, b, c, d, e, f] ))
def _add_transformation(self, new_transform):
old_transform = self.attribs.get(self.transformname, '')
self[self.transformname] = ("%s %s" % (old_transform, new_transform)).strip()
class XLink(object):
""" XLink mixin """
def set_href(self, element):
"""
Create a reference to **element**.
:param element: if element is a `string` its the **id** name of the
referenced element, if element is a **BaseElement** class the **id**
SVG Attribute is used to create the reference.
"""
self.href = element
self.update_id()
def set_xlink(self, title=None, show=None, role=None, arcrole=None):
""" Set XLink attributes (for `href` use :meth:`set_href`).
"""
if role is not None:
self['xlink:role'] = role
if arcrole is not None:
self['xlink:arcrole'] = arcrole
if title is not None:
self['xlink:title'] = title
if show is not None:
self['xlink:show'] = show
def update_id(self):
if not hasattr(self, 'href'):
return
if is_string(self.href):
idstr = self.href
else:
idstr = self.href.get_iri()
self.attribs['xlink:href'] = idstr
class Presentation(object):
"""
Helper methods to set presentation attributes.
"""
def fill(self, color=None, rule=None, opacity=None):
"""
Set SVG Properties **fill**, **fill-rule** and **fill-opacity**.
"""
if color is not None:
if is_string(color):
self['fill'] = color
else:
self['fill'] = color.get_paint_server()
if rule is not None:
self['fill-rule'] = rule
if opacity is not None:
self['fill-opacity'] = opacity
return self
def stroke(self, color=None, width=None, opacity=None, linecap=None,
linejoin=None, miterlimit=None):
"""
Set SVG Properties **stroke**, **stroke-width**, **stroke-opacity**,
**stroke-linecap** and **stroke-miterlimit**.
"""
if color is not None:
if is_string(color):
self['stroke'] = color
else:
self['stroke'] = color.get_paint_server()
if width is not None:
self['stroke-width'] = width
if opacity is not None:
self['stroke-opacity'] = opacity
if linecap is not None:
self['stroke-linecap'] = linecap
if linejoin is not None:
self['stroke-linejoin'] = linejoin
if miterlimit is not None:
self['stroke-miterlimit'] = miterlimit
return self
def dasharray(self, dasharray=None, offset=None):
"""
Set SVG Properties **stroke-dashoffset** and **stroke-dasharray**.
Where *dasharray* specify the lengths of alternating dashes and gaps as
<list> of <int> or <float> values or a <string> of comma and/or white
space separated <lengths> or <percentages>. (e.g. as <list> dasharray=[1, 0.5]
or as <string> dasharray='1 0.5')
"""
if dasharray is not None:
self['stroke-dasharray'] = strlist(dasharray, ' ')
if offset is not None:
self['stroke-dashoffset'] = offset
return self
class MediaGroup(object):
"""
Helper methods to set media group attributes.
"""
def viewport_fill(self, color=None, opacity=None):
"""
Set SVG Properties **viewport-fill** and **viewport-fill-opacity**.
"""
if color is not None:
self['viewport-fill'] = color
if opacity is not None:
self['viewport-fill-opacity'] = opacity
return self
class Markers(object):
"""
Helper methods to set marker attributes.
"""
def set_markers(self, markers):
"""
Set markers for line elements (line, polygon, polyline, path) to
values specified by `markers`.
* if `markers` is a 3-tuple:
* attribute 'marker-start' = markers[0]
* attribute 'marker-mid' = markers[1]
* attribute 'marker-end' = markers[2]
* `markers` is a `string` or a `Marker` class:
* attribute 'marker' = `FuncIRI` of markers
"""
def get_funciri(value):
if is_string(value):
# strings has to be a valid reference including the '#'
return 'url(%s)' % value
else:
# else create a reference to the object '#id'
return 'url(#%s)' % value['id']
if is_string(markers):
self['marker'] = get_funciri(markers)
else:
try:
start_marker, mid_marker, end_marker = markers
if start_marker:
self['marker-start'] = get_funciri(start_marker)
if mid_marker:
self['marker-mid'] = get_funciri(mid_marker)
if end_marker:
self['marker-end'] = get_funciri(end_marker)
except (TypeError, KeyError):
self['marker'] = get_funciri(markers)
class Clipping(object):
def clip_rect(self, top='auto', right='auto', bottom='auto', left='auto'):
"""
Set SVG Property **clip**.
"""
self['clip'] = "rect(%s,%s,%s,%s)" % (top, right, bottom, left)
@@ -0,0 +1,71 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman
# Purpose: svgwrite package parameter
# Created: 10.09.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
from svgwrite.validator2 import get_validator
class Parameter(object):
"""
.. attribute:: Parameter.debug
*read/write* property
* *True* : debug mode is on, all SVG attributes are checked if valid
in the element context. Also the included SVG subelements will be
checked if they are valid for the parent SVG element.
* *False*: no validation checks will be done, but program execution is
faster.
.. attribute:: Parameter.profile
*read/write* property
name of the SVG profile, valid profiles are: ``'full|basic|tiny'``
"""
__slots__ = ['_debug', 'validator', '_profile']
def __init__(self, debug=True, profile='full'):
self._debug = debug
self.profile = profile
def _init_validator(self):
self.validator = get_validator(self.profile, self.debug)
@property
def debug(self):
return self._debug
@debug.setter
def debug(self, debug):
self._debug = debug
self._init_validator()
def get_version(self):
if self._profile == 'tiny':
return '1.2'
else:
return '1.1'
@property
def profile(self):
return self._profile
@profile.setter
def profile(self, profile):
"""
:param string profile: name of the SVG profile, valid profiles are:
``'full|basic|tiny'``
"""
profile = profile.lower()
if profile in ('tiny', 'basic', 'full'):
self._profile = profile
self._init_validator()
else:
raise ValueError("'%s' is not a valid profile." % profile)
@@ -0,0 +1,75 @@
#coding:utf-8
# Author: mozman
# Purpose: svg path element
# Created: 08.09.2010
# License: MIT License
from svgwrite.base import BaseElement
from svgwrite.utils import strlist
from svgwrite.mixins import Presentation, Markers, Transform
class Path(BaseElement, Transform, Presentation, Markers):
""" The <path> element represent the outline of a shape which can be filled,
stroked, used as a clipping path, or any combination of the three.
"""
elementname = 'path'
def __init__(self, d=None, **extra):
"""
:param `iterable` d: *coordinates*, *length* and *commands*
:param dict attribs: additional SVG attributes
:param extra: additional SVG attributes as keyword-arguments
"""
super(Path, self).__init__(**extra)
self.commands = []
self.push(d)
if self.debug:
self.validator.check_all_svg_attribute_values(self.elementname, self.attribs)
def push(self, *elements):
""" Push commands and coordinates onto the command stack.
:param `iterable` elements: *coordinates*, *length* and *commands*
"""
self.commands.extend(elements)
@staticmethod
def arc_flags(large_arc=True, angle_dir='+'):
large_arc_flag = int(large_arc)
sweep_flag = {'+': 1, '-': 0}[angle_dir]
return "%d,%d" % (large_arc_flag, sweep_flag)
def push_arc(self, target, rotation, r, large_arc=True, angle_dir='+', absolute=False):
""" Helper function for the elliptical-arc command.
see SVG-Reference: http://www.w3.org/TR/SVG11/paths.html#PathData
:param 2-tuple target: *coordinate* of the arc end point
:param number rotation: x-axis-rotation of the ellipse in degrees
:param number|2-tuple r: radii rx, ry when r is a *2-tuple* or rx=ry=r if r is a *number*
:param bool large_arc: draw the arc sweep of greater than or equal to 180 degrees (**large-arc-flag**)
:param angle_dir: ``'+|-'`` ``'+'`` means the arc will be drawn in a "positive-angle" direction (**sweep-flag**)
:param bool absolute: indicates that target *coordinates* are absolute else they are relative to the current point
"""
self.push({True: 'A', False: 'a'}[absolute])
if isinstance(r, (float, int)):
self.push(r, r)
else:
self.push(r)
self.push(rotation)
self.push(Path.arc_flags(large_arc, angle_dir))
self.push(target)
def get_xml(self):
""" Get the XML representation as `ElementTree` object.
:return: XML `ElementTree` of this object and all its subelements
"""
self.attribs['d'] = str(strlist(self.commands, ' '))
return super(Path, self).get_xml()
@@ -0,0 +1,50 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: pattern module
# Created: 29.10.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
from svgwrite.base import BaseElement
from svgwrite.mixins import XLink, ViewBox, Transform, Presentation
from svgwrite.utils import is_string
class Pattern(BaseElement, XLink, ViewBox, Transform, Presentation):
"""
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. Patterns are defined using a `pattern` element
and then referenced by properties `fill` and `stroke` on a given graphics
element to indicate that the given element shall be filled or stroked with
the referenced pattern.
"""
elementname = 'pattern'
transformname = 'patternTransform'
def __init__(self, insert=None, size=None, inherit=None, **extra):
"""
:param 2-tuple insert: base point of the pattern (**x**, **y**)
:param 2-tuple size: size of the pattern (**width**, **height**)
:param inherit: pattern inherits properties from `inherit` see: **xlink:href**
"""
super(Pattern, self).__init__(**extra)
if insert is not None:
self['x'] = insert[0]
self['y'] = insert[1]
if size is not None:
self['width'] = size[0]
self['height'] = size[1]
if inherit is not None:
if is_string(inherit):
self.set_href(inherit)
else:
self.set_href(inherit.get_iri())
if self.debug:
self.validator.check_all_svg_attribute_values(self.elementname, self.attribs)
def get_paint_server(self, default='none'):
""" Returns the <FuncIRI> of the gradient. """
return "%s %s" % (self.get_funciri(), default)
@@ -0,0 +1,159 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman <me@mozman.at>
# Purpose: svg shapes
# Created: 08.09.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
from svgwrite.base import BaseElement
from svgwrite.mixins import Presentation, Markers, Transform
class Line(BaseElement, Transform, Presentation, Markers):
""" The **line** element defines a line segment that starts at one point
and ends at another.
"""
elementname = 'line'
def __init__(self, start=(0, 0), end=(0, 0), **extra):
"""
:param 2-tuple start: start point (**x1**, **y1**)
:param 2-tuple end: end point (**x2**, **y2**)
:param extra: additional SVG attributes as keyword-arguments
"""
super(Line, self).__init__(**extra)
x1, y1 = start
x2, y2 = end
self['x1'] = x1
self['y1'] = y1
self['x2'] = x2
self['y2'] = y2
class Rect(BaseElement, Transform, Presentation):
""" The **rect** element defines a rectangle which is axis-aligned with the current
user coordinate system. Rounded rectangles can be achieved by setting appropriate
values for attributes **rx** and **ry**.
"""
elementname = 'rect'
def __init__(self, insert=(0, 0), size=(1, 1), rx=None, ry=None, **extra):
"""
:param 2-tuple insert: insert point (**x**, **y**), left-upper point
:param 2-tuple size: (**width**, **height**)
:param <length> rx: corner x-radius
:param <length> ry: corner y-radius
:param extra: additional SVG attributes as keyword-arguments
"""
super(Rect, self).__init__(**extra)
x, y = insert
width, height = size
self['x'] = x
self['y'] = y
self['width'] = width
self['height'] = height
if rx is not None:
self['rx'] = rx
if ry is not None:
self['ry'] = ry
class Circle(BaseElement, Transform, Presentation):
""" The **circle** element defines a circle based on a center point and a radius.
"""
elementname = 'circle'
def __init__(self, center=(0, 0), r=1, **extra):
"""
:param 2-tuple center: circle center point (**cx**, **cy**)
:param length r: circle-radius **r**
:param extra: additional SVG attributes as keyword-arguments
"""
super(Circle, self).__init__(**extra)
cx, cy = center
self['cx'] = cx
self['cy'] = cy
self['r'] = r
class Ellipse(BaseElement, Transform, Presentation):
""" The **ellipse** element defines an ellipse which is axis-aligned with the
current user coordinate system based on a center point and two radii.
"""
elementname = 'ellipse'
def __init__(self, center=(0, 0), r=(1, 1), **extra):
"""
:param 2-tuple center: ellipse center point (**cx**, **cy**)
:param 2-tuple r: ellipse radii (**rx**, **ry**)
:param extra: additional SVG attributes as keyword-arguments
"""
super(Ellipse, self).__init__(**extra)
cx, cy = center
rx, ry = r
self['cx'] = cx
self['cy'] = cy
self['rx'] = rx
self['ry'] = ry
class Polyline(BaseElement, Transform, Presentation, Markers):
""" The **polyline** element defines a set of connected straight line
segments. Typically, **polyline** elements define open shapes.
"""
elementname = 'polyline'
def __init__(self, points=[], **extra):
"""
:param `iterable` points: `iterable` of points (points are `2-tuples`)
:param extra: additional SVG attributes as keyword-arguments
"""
super(Polyline, self).__init__(**extra)
self.points = list(points)
if self.debug:
for point in self.points:
x, y = point
self.validator.check_svg_type(x, 'coordinate')
self.validator.check_svg_type(y, 'coordinate')
def get_xml(self):
self.attribs['points'] = self.points_to_string(self.points)
return super(Polyline, self).get_xml()
def points_to_string(self, points):
"""
Convert a `list` of points `2-tuples` to a `string` ``'p1x,p1y p2x,p2y ...'``.
"""
strings = []
for point in points:
if len(point) != 2:
raise TypeError('got %s values, but expected 2 values.' % len(point))
x, y = point
if self.debug:
self.validator.check_svg_type(x, 'coordinate')
self.validator.check_svg_type(y, 'coordinate')
if self.profile == 'tiny':
if isinstance(x, float):
x = round(x, 4)
if isinstance(y, float):
y = round(y, 4)
point = "%s,%s" % (x, y)
strings.append(point)
return ' '.join(strings)
class Polygon(Polyline):
""" The **polygon** element defines a closed shape consisting of a set of
connected straight line segments.
Same as :class:`~svgwrite.shapes.Polyline` but closed.
"""
elementname = 'polygon'
@@ -0,0 +1,41 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: solidColor paint serve (Tiny 1.2 profile)
# Created: 26.10.2016
# Copyright (C) 2016, Manfred Moitzi
# License: MIT License
from svgwrite.base import BaseElement
from svgwrite.mixins import XLink
class SolidColor(BaseElement, XLink):
"""
The `solidColor` element is a paint server that provides a single color with opacity.
It can be referenced like the other paint servers (i.e. gradients).
The `color` parameter specifies the color that shall be used for this `solidColor` element.
The keyword ``"currentColor"`` can be specified in the same manner as within a <paint> specification for the `fill`
and `stroke` properties. The `opacity` parameter defines the opacity of the `solidColor`.
"""
elementname = 'solidColor'
def __init__(self, color="currentColor", opacity=None, **extra):
"""
:param color: solid color like the other paint servers (i.e. gradients).
:param float opacity: opacity of the solid color in the range `0.0` (fully transparent) to `1.0` (fully opaque)
"""
super(SolidColor, self).__init__(**extra)
if self.profile != 'tiny':
raise TypeError("Paint server 'solidColor' requires the Tiny SVG profile.")
self['solid-color'] = color
if opacity is not None:
self['solid-opacity'] = opacity
if self.debug:
self.validator.check_all_svg_attribute_values(self.elementname, self.attribs)
def get_paint_server(self, default='none'):
""" Returns the <FuncIRI> of the gradient. """
return "%s %s" % (self.get_funciri(), default)
@@ -0,0 +1,218 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: text objects
# Created: 20.09.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
"""
Text that is to be rendered as part of an SVG document fragment is specified
using the **text** element. The characters to be drawn are expressed as XML
character data inside the **text** element.
"""
from svgwrite.base import BaseElement
from svgwrite.mixins import Presentation, Transform, XLink
from svgwrite.utils import iterflatlist, strlist, is_string
class TSpan(BaseElement, Presentation):
"""
Within a **Text** element, text and font properties
and the current text position can be adjusted with absolute or relative
coordinate values by using the **TSpan** element.
The characters to be drawn are expressed as XML character data inside the
**TSpan** element.
"""
elementname = 'tspan'
def __init__(self, text, insert=None, x=None, y=None, dx=None, dy=None, rotate=None,
**extra):
"""
:param string text: **tspan** content
:param 2-tuple insert: The **insert** parameter is the absolute insert point
of the text, don't use this parameter in combination
with the **x** or the **y** parameter.
:param list x: list of absolute x-axis values for characters
:param list y: list of absolute y-axis values for characters
:param list dx: list of relative x-axis values for characters
:param list dy: list of relative y-axis values for characters
:param list rotate: list of rotation-values for characters (in degrees)
"""
super(TSpan, self).__init__(**extra)
self.text = text
if insert is not None:
if is_string(insert):
raise TypeError("'insert' should be a <tuple> or a <list> with"
" at least two elements.")
if x or y:
raise ValueError("Use 'insert' and 'x' or 'y' parameter not"
" at the same time!")
x = [insert[0]]
y = [insert[1]]
if x is not None:
self['x'] = strlist(list(iterflatlist(x)), ' ')
if y is not None:
self['y'] = strlist(list(iterflatlist(y)), ' ')
if dx is not None:
self['dx'] = strlist(list(iterflatlist(dx)), ' ')
if dy is not None:
self['dy'] = strlist(list(iterflatlist(dy)), ' ')
if rotate is not None:
self['rotate'] = strlist(list(iterflatlist(rotate)), ' ')
def get_xml(self):
xml = super(TSpan, self).get_xml()
xml.text = str(self.text)
return xml
class Text(TSpan, Transform):
"""
The **Text** element defines a graphics element consisting of text.
The characters to be drawn are expressed as XML character data inside the
**Text** element.
"""
elementname = 'text'
class TRef(BaseElement, XLink, Presentation):
"""
The textual content for a **Text** can be either character data directly
embedded within the <text> element or the character data content of a
referenced element, where the referencing is specified with a **TRef**
element.
"""
elementname = 'tref'
def __init__(self, element, **extra):
"""
:param element: create a reference this element, if element is a \
`string` its the **id** name of the referenced element, \
if element is a :class:`~svgwrite.base.BaseElement` \
the **id** SVG Attribute is used to create the reference.
"""
super(TRef, self).__init__(**extra)
self.set_href(element)
def get_xml(self):
self.update_id() # if href is an object - 'id' - attribute may be changed!
return super(TRef, self).get_xml()
class TextPath(BaseElement, XLink, Presentation):
"""
In addition to text drawn in a straight line, SVG also includes the
ability to place text along the shape of a **path** element. To specify that
a block of text is to be rendered along the shape of a **path**, include
the given text within a **textPath** element which includes an **xlink:href**
attribute with a IRI reference to a **path** element.
"""
elementname = 'textPath'
def __init__(self, path, text, startOffset=None, method='align', spacing='exact',
**extra):
"""
:param path: link to **path**, **id** string or **Path** object
:param string text: **textPath** content
:param number startOffset: text starts with offset from begin of path.
:param string method: ``align|stretch``
:param string spacing: ``exact|auto``
"""
super(TextPath, self).__init__(**extra)
self.text = text
if method == 'stretch':
self['method'] = method
if spacing == 'auto':
self['spacing'] = spacing
if startOffset is not None:
self['startOffset'] = startOffset
self.set_href(path)
def get_xml(self):
self.update_id() # if href is an object - 'id' - attribute may be changed!
xml = super(TextPath, self).get_xml()
xml.text = str(self.text)
return xml
class TBreak(BaseElement):
elementname = 'tbreak'
def __init__(self, **extra):
super(TBreak, self).__init__(**extra)
def __getitem__(self, key):
raise NotImplementedError("__getitem__() not supported by TBreak class.")
def __setitem__(self, key, value):
raise NotImplementedError("__setitem__() not supported by TBreak class.")
def add(self, element):
raise NotImplementedError("add() not supported by TBreak class.")
class TextArea(BaseElement, Transform, Presentation):
"""
At this time **textArea** is only available for SVG 1.2 Tiny profile.
The **textArea** element allows simplistic wrapping of text content within a
given region. The `tiny` profile of SVG specifies a single rectangular region.
Other profiles may allow a sequence of arbitrary shapes.
Text wrapping via the **textArea** element is available as a lightweight and
convenient facility for simple text wrapping where a complete box model layout
engine is not required.
The layout of wrapped text is user agent dependent; thus, content developers
need to be aware that there might be different results, particularly with
regard to where line breaks occur.
The TextArea class wraps every text added by write() or writeline() as
**tspan** element.
"""
elementname = 'textArea'
def __init__(self, text=None, insert=None, size=None, **extra):
super(TextArea, self).__init__(**extra)
if text is not None:
self.write(text)
if insert is not None:
self['x'] = insert[0]
self['y'] = insert[1]
if size is not None:
self['width'] = size[0]
self['height'] = size[1]
def line_increment(self, value):
""" Set the line-spacing to *value*. """
self['line-increment'] = value
def write(self, text, **extra):
"""
Add text as **tspan** elements, with extra-params for the **tspan** element.
Use the '\\\\n' character for line breaks.
"""
if '\n' not in text:
self.add(TSpan(text, **extra))
else:
lines = text.split('\n')
for line in lines[:-1]:
if line: # no text between '\n'+
self.add(TSpan(line, **extra))
self.add(TBreak())
# case "text\n" : last element is ''
# case "texta\ntextb : last element is 'textb'
if lines[-1]:
self.add(TSpan(lines[-1], **extra))
@@ -0,0 +1,266 @@
#!/usr/bin/env python
# coding:utf-8
# Author: mozman
# Purpose: svg util functions and classes
# Created: 08.09.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
"""
.. autofunction:: rgb
.. autofunction:: iterflatlist
.. autofunction:: strlist
.. autofunction:: get_unit
.. autofunction:: split_coordinate
.. autofunction:: split_angle
.. autofunction:: rect_top_left_corner
.. autofunction:: pretty_xml
"""
import base64
from pathlib import Path
from svgwrite.data import pattern
def is_string(value):
return isinstance(value, str)
def rgb(r=0, g=0, b=0, mode='RGB'):
"""
Convert **r**, **g**, **b** values to a `string`.
:param r: red part
:param g: green part
:param b: blue part
:param string mode: ``'RGB | %'``
:rtype: string
========= =============================================================
mode Description
========= =============================================================
``'RGB'`` returns a rgb-string format: ``'rgb(r, g, b)'``
``'%'`` returns percent-values as rgb-string format: ``'rgb(r%, g%, b%)'``
========= =============================================================
"""
def percent(value):
value = float(value)
if value < 0:
value = 0
if value > 100:
value = 100
return value
if mode.upper() == 'RGB':
return "rgb(%d,%d,%d)" % (int(r) & 255, int(g) & 255, int(b) & 255)
elif mode == "%":
# see http://www.w3.org/TR/SVG11/types.html#DataTypeColor
# percentage is an 'number' value
return "rgb(%d%%,%d%%,%d%%)" % (percent(r), percent(g), percent(b))
else:
raise ValueError("Invalid mode '%s'" % mode)
def iterflatlist(values):
"""
Flatten nested *values*, returns an `iterator`.
"""
for element in values:
if hasattr(element, "__iter__") and not is_string(element):
for item in iterflatlist(element):
yield item
else:
yield element
def strlist(values, seperator=","):
"""
Concatenate **values** with **sepertator**, `None` values will be excluded.
:param values: `iterable` object
:returns: `string`
"""
if is_string(values):
return values
else:
return seperator.join([str(value) for value in iterflatlist(values) if value is not None])
def get_unit(coordinate):
"""
Get the `unit` identifier of **coordinate**, if **coordinate** has a valid
`unit` identifier appended, else returns `None`.
"""
if isinstance(coordinate, (int, float)):
return None
result = pattern.coordinate.match(coordinate)
if result:
return result.group(3)
else:
raise ValueError("Invalid format: '%s'" % coordinate)
def split_coordinate(coordinate):
"""
Split coordinate into `<number>` and 'unit` identifier.
:returns: <2-tuple> (number, unit-identifier) or (number, None) if no unit-identifier
is present or coordinate is an int or float.
"""
if isinstance(coordinate, (int, float)):
return (float(coordinate), None)
result = pattern.coordinate.match(coordinate)
if result:
return (float(result.group(1)), result.group(3))
else:
raise ValueError("Invalid format: '%s'" % coordinate)
def split_angle(angle):
"""
Split angle into `<number>` and `<angle>` identifier.
:returns: <2-tuple> (number, angle-identifier) or (number, None) if no angle-identifier
is present or angle is an int or float.
"""
if isinstance(angle, (int, float)):
return (float(angle), None)
result = pattern.angle.match(angle)
if result:
return (float(result.group(1)), result.group(3))
else:
raise ValueError("Invalid format: '%s'" % angle)
def rect_top_left_corner(insert, size, pos='top-left'):
"""
Calculate top-left corner of a rectangle.
**insert** and **size** must have the same units.
:param 2-tuple insert: insert point
:param 2-tuple size: (width, height)
:param string pos: insert position ``'vert-horiz'``
:return: ``'top-left'`` corner of the rect
:rtype: 2-tuple
========== ==============================
pos valid values
========== ==============================
**vert** ``'top | middle | bottom'``
**horiz** ``'left'|'center'|'right'``
========== ==============================
"""
vert, horiz = pos.lower().split('-')
x, xunit = split_coordinate(insert[0])
y, yunit = split_coordinate(insert[1])
width, wunit = split_coordinate(size[0])
height, hunit = split_coordinate(size[1])
if xunit != wunit:
raise ValueError("x-coordinate and width has to have the same unit")
if yunit != hunit:
raise ValueError("y-coordinate and height has to have the same unit")
if horiz == 'center':
x = x - width / 2.
elif horiz == 'right':
x = x - width
elif horiz != 'left':
raise ValueError("Invalid horizontal position: '%s'" % horiz)
if vert == 'middle':
y = y - height / 2.
elif vert == 'bottom':
y = y - height
elif vert != 'top':
raise ValueError("Invalid vertical position: '%s'" % vert)
if xunit:
x = "%s%s" % (x, xunit)
if yunit:
y = "%s%s" % (y, yunit)
return x, y
class AutoID(object):
_nextid = 1
def __init__(self, value=None):
self._set_value(value)
@classmethod
def _set_value(cls, value=None):
if value is not None:
cls._nextid = value
@classmethod
def next_id(cls, value=None):
cls._set_value(value)
retval = "id%d" % cls._nextid
cls._nextid += 1
return retval
def pretty_xml(xml_string, indent=2):
"""
Create human readable XML string.
:param xml_string: input xml string without line breaks and indentation
:indent int: how much to indent, by default 2 spaces
:return: xml_string with linebreaks and indentation
"""
import xml.dom.minidom as minidom
# check for empty string, len check avoids unnecessary string manipulation with large XML strings
if len(xml_string) < 20 and xml_string.strip() == "":
return ""
xml_tree = minidom.parseString(xml_string)
lines = xml_tree.toprettyxml(indent=' ' * indent).split('\n')
# remove 1. line = xml declaration
return '\n'.join(lines[1:])
FONT_MIMETYPES = {
'ttf': "application/x-font-ttf",
'otf': "application/x-font-opentype",
'woff': "application/font-woff",
'woff2': "application/font-woff2",
'eot': "application/vnd.ms-fontobject",
'sfnt': "application/font-sfnt",
}
def font_mimetype(name):
return FONT_MIMETYPES[Path(name.lower()).suffix[1:]]
def base64_data(data, mimetype):
data = base64.b64encode(data).decode()
return "data:{mimetype};charset=utf-8;base64,{data}".format(mimetype=mimetype, data=data)
def find_first_url(text):
import re
result = re.findall(r"url\((.*?)\)", text)
if result:
return result[0]
else:
return None
@@ -0,0 +1,170 @@
#!/usr/bin/env python
#coding:utf-8
# Author: mozman --<mozman@gmx.at>
# Purpose: validator2 module - new validator module
# Created: 01.10.2010
# Copyright (C) 2010, Manfred Moitzi
# License: MIT License
from svgwrite.data import full11
from svgwrite.data import tiny12
from svgwrite.data import pattern
validator_cache = {}
def cache_key(profile, debug):
return str(profile) + str(debug)
def get_validator(profile, debug=True):
""" Validator factory """
try:
return validator_cache[cache_key(profile, debug)]
except KeyError:
if profile == 'tiny':
validator = Tiny12Validator(debug)
elif profile in ('full', 'basic', 'none'):
validator = Full11Validator(debug)
else:
raise ValueError("Unsupported profile: '%s'" % profile)
validator_cache[cache_key(profile, debug)] = validator
return validator
class Tiny12Validator(object):
profilename = "Tiny 1.2"
def __init__(self, debug=True):
self.debug = debug
self.attributes = tiny12.attributes
self.elements = tiny12.elements
self.typechecker = tiny12.TypeChecker()
def check_all_svg_attribute_values(self, elementname, attributes):
"""
Check if attributes are valid for object 'elementname' and all svg
attributes have valid types and values.
Raises ValueError.
"""
for attributename, value in attributes.items():
self.check_svg_attribute_value(elementname, attributename, value)
def check_svg_attribute_value(self, elementname, attributename, value):
"""
Check if 'attributename' is valid for object 'elementname' and 'value'
is a valid svg type and value.
Raises ValueError.
"""
self._check_valid_svg_attribute_name(elementname, attributename)
self._check_svg_value(elementname, attributename, value)
def _check_svg_value(self, elementname, attributename, value):
"""
Checks if 'value' is a valid svg-type for svg-attribute
'attributename' at svg-element 'elementname'.
Raises TypeError.
"""
attribute = self.attributes[attributename]
# check if 'value' match a valid datatype
for typename in attribute.get_types(elementname):
if self.typechecker.check(typename, value):
return
# check if 'value' is a valid constant
valuestr = str(value)
if not valuestr in attribute.get_const(elementname):
raise TypeError("'%s' is not a valid value for attribute '%s' at svg-element <%s>." % (value, attributename, elementname))
def _check_valid_svg_attribute_name(self, elementname, attributename):
""" Check if 'attributename' is a valid svg-attribute for svg-element
'elementname'.
Raises ValueError.
"""
if not self.is_valid_svg_attribute(elementname, attributename):
raise ValueError("Invalid attribute '%s' for svg-element <%s>." % (attributename, elementname))
def _get_element(self, elementname):
try:
return self.elements[elementname]
except KeyError:
raise KeyError("<%s> is not valid for selected profile: '%s'." % (elementname, self.profilename))
def check_svg_type(self, value, typename='string'):
"""
Check if 'value' matches svg type 'typename'.
Raises TypeError.
"""
if self.typechecker.check(typename, value):
return value
else:
raise TypeError("%s is not of type '%s'." % (value, typename))
def is_valid_svg_type(self, value, typename):
return self.typechecker.check(typename, value)
def is_valid_elementname(self, elementname):
""" True if 'elementname' is a valid svg-element name. """
return elementname in self.elements
def is_valid_svg_attribute(self, elementname, attributename):
""" True if 'attributename' is a valid svg-attribute for svg-element
'elementname'.
"""
element = self._get_element(elementname)
return attributename in element.valid_attributes
def is_valid_children(self, elementname, childrenname):
""" True if svg-element 'childrenname' is a valid children of
svg-element 'elementname'.
"""
element = self._get_element(elementname)
return childrenname in element.valid_children
def check_valid_children(self, elementname, childrenname):
""" Checks if svg-element 'childrenname' is a valid children of
svg-element 'elementname'.
Raises ValueError.
"""
if not self.is_valid_children(elementname, childrenname):
raise ValueError("Invalid children '%s' for svg-element <%s>." % (childrenname, elementname))
def get_coordinate(self, value):
""" Split value in (number, unit) if value has an unit or (number, None).
Raises ValueError.
"""
if value is None:
raise TypeError("Invalid type 'None'.")
if isinstance(value, (int, float)):
result = (value, None)
else:
result = pattern.coordinate.match(value.strip())
if result:
number, tmp, unit = result.groups()
number = float(number)
else:
raise ValueError("'%s' is not a valid svg-coordinate." % value)
result = (number, unit)
if self.typechecker.is_number(result[0]):
return result
else:
version = "SVG %s %s" % self.typechecker.get_version()
raise ValueError("%s is not a valid number for: %s." % (value, version))
get_length = get_coordinate
class Full11Validator(Tiny12Validator):
profilename = "Full 1.1"
def __init__(self, debug=True):
self.debug = debug
self.attributes = full11.attributes
self.elements = full11.elements
self.typechecker = full11.TypeChecker()
@@ -0,0 +1,31 @@
# 2018-11-29: future consistent version numbers
# ---------------------------------------------
#
# version scheme for version: (major, minor, micro, release_level)
#
# major:
# 0 .. not all planned features done
# 1 .. all features available
# 2 .. if significant API change (2, 3, ...)
#
# minor:
# changes with new features or minor API changes
#
# micro:
# changes with bug fixes, maybe also minor changes
#
# release_state:
# a .. alpha: adding new features - non public development state
# b .. beta: testing new features - public development state
# rc .. release candidate: testing release - public testing
# release: public release
# examples
#
# major pre release alpha 2: VERSION = "0.9a2"; version = (0, 9, 0, 'a2')
# major release candidate 0: VERSION = "0.9rc0"; version = (0, 9, 0, 'rc0')
# major release: VERSION = "0.9"; version = (0, 9, 0, 'release')
# 1. bug fix release beta0: VERSION = "0.9.1b0"; version = (0, 9, 1, 'b0')
# 2. bug fix release: VERSION = "0.9.2"; version = (0, 9, 2, 'release')
version = (1, 4, 3, 'release')
__version__ = "1.4.3"