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:
@@ -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"
|
||||
Reference in New Issue
Block a user