Commits

Manfred Moitzi committed 84bd723

added Marker class, Markers mixin and test

Comments (0)

Files changed (14)

 NEWS
 ====
 
+Version 0.3.0 - End of November 2010
+
+  * alpha version
+  * new elements:
+
+    * Marker
+
 Version 0.2.0 - 24 October 2010
 
   * alpha version
   * validator rewritten as validator2.py
   * debug and profile options separated for each drawing object
-  * important change: create objects with factory functions of the Drawing class: drawing.<svg-elementname>(...)
+  * important change: create objects with factory functions of the
+    *Drawing* class: drawing.<svg-elementname>(...)
   * added mixins for setting stroke and fill properties
   * new elements:
 
-    * Hyperlink <a>
+    * Hyperlink (is the SVG element: <a>)
     * Image
     * TextArea
 
   * alpha version
   * classes:
 
-    * basic shapes: Line, Rect, Circle, Ellipse, Polyline, Polygon
+    * basic shapes:
+
+      * Line
+      * Rect
+      * Circle
+      * Ellipse
+      * Polyline
+      * Polygon
+
     * Path element
-    * text elements: Text, TSpan, TRef, TextPath
-    * container elements: Group, Symbol, SVG, Use, Defs
+    * text elements:
+
+      * Text
+      * TSpan
+      * TRef
+      * TextPath
+
+    * container elements:
+
+      * Group
+      * Symbol
+      * SVG
+      * Use
+      * Defs
 
   * for examples see: examples.py
 

doc/classes/marker.rst

+:class:`Marker` objects --- <marker>
+====================================
+
+.. autoclass:: svgwrite.container.Marker
+
+.. automethod:: svgwrite.container.Marker.__init__(insert=None, size=None, orient='auto', attribs=None, \*\*extra)
+
+Inherited Attributes
+--------------------
+
+.. attribute:: Marker.attribs
+
+   `dict` of SVG attributes
+
+.. attribute:: Marker.elements
+
+   `list` of SVG subelements
+
+Inherited Methods
+-----------------
+
+.. automethod:: svgwrite.container.Marker.add(element)
+
+.. automethod:: svgwrite.container.Marker.tostring()
+
+.. automethod:: svgwrite.container.Marker.get_xml()
+
+Supported Interfaces
+--------------------
+
+:class:`svgwrite.interface.IViewBox`
+
+    :meth:`viewbox`, :meth:`stretch`, :meth:`fit`
+
+Used Mixins
+-----------
+
+:class:`svgwrite.mixins.Presentation`
+
+    :meth:`fill`, :meth:`stroke`, :meth:`dasharray`
+
+SVG Attributes
+--------------
+
+ .. seealso:: http://www.w3.org/TR/SVG11/painting.html#MarkerElement
+
+* **class** -- `string` assigns one or more css-class-names to an element
+* **style** -- `string` allows per-element css-style rules to be specified
+  directly on a given element
+* **externalResourcesRequired** -- `bool` *False*: if document rendering
+  can proceed even if external resources are unavailable else: *True*
+* **viewBox** -- use :class:`svgwrite.interface.IViewBox` interface
+* **preserveAspectRatio** -- use :class:`svgwrite.interface.IViewBox`
+  interface
+
+* **markerUnits** -- ``'strokeWidth|userSpaceOnUse'``
+  Defines the coordinate system for attributes **markerWidth**, **markerHeight**
+  and the contents of the **marker**.
+
+  If markerUnits -- ``'strokeWidth'``, **markerWidth**,
+  **markerHeight** and the contents of the **marker** represent values in a coordinate
+  system which has a single unit equal the size in user units of the current
+  stroke width in place for the graphic object referencing the marker.
+
+  If markerUnits -- ``'userSpaceOnUse'``, **markerWidth**, **markerHeight** and the
+  contents of the **marker** represent values in the current user coordinate
+  system in place for the graphic object referencing the marker (i.e., the
+  user coordinate system for the element referencing the **marker** element via
+  a **marker**, **marker-start**, **marker-mid** or **marker-end** property).
+
+* **refX** -- `coordinate`
+
+  The x-axis coordinate of the reference point which is to be aligned exactly
+  at the marker position. The coordinate is defined in the coordinate system
+  after application of the **viewBox** and **preserveAspectRatio** attributes.
+  (default = "0")
+
+* **refY** -- `coordinate`
+
+  The y-axis coordinate of the reference point which is to be aligned exactly
+  at the marker position. The coordinate is defined in the coordinate system
+  after application of the **viewBox** and **preserveAspectRatio** attributes.
+  (default = "0")
+
+* **markerWidth** -- `length`
+
+  Represents the width of the viewport into which the marker is to be fitted
+  when it is rendered. (default = "3")
+
+* **markerHeight** -- `length`
+
+  Represents the height of the viewport into which the marker is to be fitted
+  when it is rendered. A value of zero disables rendering of the element.
+  (default = "3")
+
+* **orient** -- ``'auto'`` | `angle`
+
+  Indicates how the marker is rotated. (SVG default = "0", but for :meth:`__init__`
+  ``'auto'`` is the default value)
+
+  .. seealso:: http://www.w3.org/TR/SVG11/painting.html#OrientAttribute
+
+Standard SVG Attributes
+-----------------------
+
+* :doc:`Core Attributes </attributes/core>`
+* :doc:`Presentation Attributes </attributes/presentation>`

doc/classes/mixins.rst

    * http://www.w3.org/TR/SVG11/painting.html#StrokeDashoffsetProperty
 
 :class:`MediaGroup` mixin
-===========================
+=========================
 
 .. autoclass:: svgwrite.mixins.MediaGroup
 
    * http://www.w3.org/TR/SVGMobile12/painting.html#viewport-fill-property
    * http://www.w3.org/TR/SVGMobile12/painting.html#viewport-fill-opacity-property
 
+:class:`Markers` mixin
+======================
+
+.. autoclass:: svgwrite.mixins.Markers
+
+Methods
+-------
+
+.. automethod:: svgwrite.mixins.Markers.set_markers(markers)
+
+.. seealso::
+
+   * http://www.w3.org/TR/SVG11/painting.html#MarkerProperty
+   * http://www.w3.org/TR/SVG11/painting.html#MarkerStartProperty
+   * http://www.w3.org/TR/SVG11/painting.html#MarkerMidProperty
+   * http://www.w3.org/TR/SVG11/painting.html#MarkerEndProperty

doc/classes/path.rst

 
     :meth:`fill`, :meth:`stroke`, :meth:`dasharray`
 
+:class:`svgwrite.mixins.Markers`
+
+    :meth:`set_markers`
+
 Path Commands
 -------------
 

doc/classes/shapes.rst

 
 .. autoclass:: svgwrite.shapes.Line
 
+Used Mixins
+~~~~~~~~~~~
+
+:class:`svgwrite.mixins.Markers`
+
+    :meth:`set_markers`
+
 :class:`Rect` objects --- <rect>
 ================================
 
 
 .. autoclass:: svgwrite.shapes.Polyline
 
+Used Mixins
+~~~~~~~~~~~
+
+:class:`svgwrite.mixins.Markers`
+
+    :meth:`set_markers`
+
 :class:`Polygon` objects --- <polygon>
 ======================================
 
 .. autoclass:: svgwrite.shapes.Polygon
 
+Used Mixins
+~~~~~~~~~~~
+
+:class:`svgwrite.mixins.Markers`
+
+    :meth:`set_markers`
+
 Common for: Line, Rect, Circle, Ellipse, Polyline, Polygon
 ----------------------------------------------------------
 

doc/classes/symbol.rst

 
     :meth:`fill`, :meth:`stroke`, :meth:`dasharray`
 
-SVG attributes
+SVG Attributes
 --------------
 
+  .. seealso:: http://www.w3.org/TR/SVG11/struct.html#SymbolElement
+
 * **class** -- `string` assigns one or more css-class-names to an element
 * **style** -- `string` allows per-element css-style rules to be specified
   directly on a given element

svgwrite/container.py

     value = element['attribute']
 
 .. seealso::
+
    :ref:`Common SVG Attributs <Common-SVG-Attributs>`
 """
 
     or <polygon> element.
     """
     elementname = 'marker'
+    def __init__(self, insert=None, size=None, orient='auto', attribs=None, **extra):
+        """
+        :param 2-tuple insert: reference point
+        :param 2-tuple size: width, height
+        :param dict attribs: additional SVG attributes
+        :param extra: additional SVG attributs as keyword-arguments
+        """
+        super(Marker, self).__init__(attribs=attribs, **extra)
+        if insert:
+            self['refX'] = insert[0]
+            self['refY'] = insert[1]
+        if size:
+            self['markerWidth'] = size[0]
+            self['markerHeight'] = size[1]
+
+        self['orient'] = orient
+        if 'id' not in self.attribs: # an 'id' is necessary
+            self['id'] = self.nextid()
 
 class SVG(Symbol):
     """ An SVG document fragment consists of any number of SVG elements contained

svgwrite/data/full11.py

     types=frozenset([u'string']),
     const=empty_list),
 'marker': SVGAttribute('marker', anim=True,
-    types=frozenset([u'string']),
-    const=empty_list),
+    types=frozenset([u'FuncIRI']),
+    const=frozenset([u'none', u'inherit'])),
 'marker-end': SVGAttribute('marker-end', anim=True,
-    types=frozenset([u'IRI']),
+    types=frozenset([u'FuncIRI']),
     const=frozenset([u'none', u'inherit'])),
 'marker-mid': SVGAttribute('marker-mid', anim=True,
-    types=frozenset([u'IRI']),
+    types=frozenset([u'FuncIRI']),
     const=frozenset([u'none', u'inherit'])),
 'marker-start': SVGAttribute('marker-start', anim=True,
-    types=frozenset([u'IRI']),
+    types=frozenset([u'FuncIRI']),
     const=frozenset([u'none', u'inherit'])),
 'markerHeight': SVGAttribute('markerHeight', anim=True,
     types=frozenset([u'length']),
     "flood-opacity", "font-family", "font-size", "font-size-adjust",
     "font-stretch", "font-style", "font-variant", "font-weight",
     "glyph-orientation-horizontal", "glyph-orientation-vertical",
-    "image-rendering", "kerning", "letter-spacing", "lighting-color",
+    "image-rendering", "kerning", "letter-spacing", "lighting-color", "marker",
     "marker-end", "marker-mid", "marker-start", "mask", "opacity",
     "overflow", "pointer-events", "shape-rendering", "stop-color",
     "stop-opacity", "stroke", "stroke-dasharray", "stroke-dashoffset",

svgwrite/elementfactory.py

     'svg': container.SVG,
     'defs': container.Defs,
     'symbol': container.Symbol,
+    'marker': container.Marker,
     'use': container.Use,
     'a': container.Hyperlink,
     'line': shapes.Line,

svgwrite/mixins.py

         if opacity:
             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 as `string` or a `Marker` class:
+
+          * attribute 'marker' = markers
+
+        """
+        def get_funciri(value):
+            if isinstance(value, basestring):
+                # 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 isinstance(markers, basestring):
+            self['marker'] = get_funciri(markers)
+        else:
+            try:
+                markerstart, markermid, markerend = markers
+                self['marker-start'] = get_funciri(markerstart)
+                self['marker-mid'] = get_funciri(markermid)
+                self['marker-end'] = get_funciri(markerend)
+            except (TypeError, KeyError):
+                self['marker'] = get_funciri(markers)
 from base import BaseElement
 from utils import strlist
 from interface import ITransform
-from mixins import Presentation
+from mixins import Presentation, Markers
 
-class Path(BaseElement, ITransform, Presentation):
+class Path(BaseElement, ITransform, 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.
 

svgwrite/shapes.py

 
 from base import BaseElement
 from interface import ITransform
-from mixins import Presentation
+from mixins import Presentation, Markers
 
-class Line(BaseElement, ITransform, Presentation):
+class Line(BaseElement, ITransform, Presentation, Markers):
     """ The <line> element defines a line segment that starts at one point and ends
     at another.
 
         self['rx'] = rx
         self['ry'] = ry
 
-class Polyline(BaseElement, ITransform, Presentation):
+class Polyline(BaseElement, ITransform, Presentation, Markers):
     """ The <polyline> element defines a set of connected straight line segments.
     Typically, <polyline> elements define open shapes.
 

tests/test_marker_class.py

+#!/usr/bin/env python
+#coding:utf-8
+# Author:  mozman --<mozman@gmx.at>
+# Purpose: test marker element
+# Created: 24.10.2010
+# Copyright (C) 2010, Manfred Moitzi
+# License: GPLv3
+
+import sys
+import unittest
+
+from svgwrite.container import Marker, Group
+
+class TestMarker(unittest.TestCase):
+    def test_constructor(self):
+        marker = Marker(id='test', debug=True, profile='full')
+        self.assertEqual(marker.tostring(), '<marker id="test" orient="auto" />')
+
+    def test_add_subelement(self):
+        marker = Marker(id='test', debug=True, profile='full')
+        marker.add(Group())
+        self.assertEqual(marker.tostring(), '<marker id="test" orient="auto"><g /></marker>')
+
+    def test_add_subelement_with_autoid(self):
+        marker = Marker(debug=True, profile='full')
+        marker.add(Group())
+        self.assertEqual(marker.tostring(), '<marker id="id1" orient="auto"><g /></marker>')
+
+    def test_insert(self):
+        marker = Marker(id='test', insert=(1, 2), debug=True, profile='full')
+        self.assertEqual(marker.tostring(), '<marker id="test" orient="auto" refX="1" refY="2" />')
+
+    def test_size(self):
+        marker = Marker(id='test', size=(1, 2), debug=True, profile='full')
+        self.assertEqual(marker.tostring(), '<marker id="test" markerHeight="2" markerWidth="1" orient="auto" />')
+
+    def test_orient_rad(self):
+        marker = Marker(id='test', orient='3.1415rad', debug=True, profile='full')
+        self.assertEqual(marker.tostring(), '<marker id="test" orient="3.1415rad" />')
+
+    def test_orient_number(self):
+        marker = Marker(id='test', orient=30, debug=True, profile='full')
+        self.assertEqual(marker.tostring(), '<marker id="test" orient="30" />')
+
+if __name__=='__main__':
+    unittest.main()

tests/test_markers_mixin.py

+#!/usr/bin/env python
+#coding:utf-8
+# Author:  mozman --<mozman@gmx.at>
+# Purpose: test markers mixin
+# Created: 24.10.2010
+# Copyright (C) 2010, Manfred Moitzi
+# License: GPLv3
+
+import unittest
+
+from svgwrite.base import BaseElement
+from svgwrite.mixins import Markers
+
+class MarkerMock(BaseElement, Markers):
+    elementname = 'line' # has valid marker properties
+
+class TestMarkers(unittest.TestCase):
+
+    def test_with_one_string(self):
+        e = MarkerMock(debug=True, profile='full')
+        e.set_markers('#mozman')
+        self.assertEqual(e.tostring(), '<line marker="url(#mozman)" />')
+
+    def test_with_three_strings(self):
+        e = MarkerMock(debug=True, profile='full')
+        e.set_markers(('#mozman1', '#mozman2', '#mozman3'))
+        self.assertEqual(e.tostring(), '<line marker-end="url(#mozman3)" ' \
+                         'marker-mid="url(#mozman2)" marker-start="url(#mozman1)" />')
+
+    def test_with_one_obj(self):
+        marker = MarkerMock(id='mozman', debug=True, profile='full')
+        e = MarkerMock(debug=True, profile='full')
+        e.set_markers(marker)
+        self.assertEqual(e.tostring(), '<line marker="url(#mozman)" />')
+
+    def test_with_three_obj(self):
+        m1 = MarkerMock(id='mozman1', debug=True, profile='full')
+        m2 = MarkerMock(id='mozman2', debug=True, profile='full')
+        m3 = MarkerMock(id='mozman3', debug=True, profile='full')
+        e = MarkerMock(debug=True, profile='full')
+
+        e.set_markers((m1, m2, m3))
+        self.assertEqual(e.tostring(), '<line marker-end="url(#mozman3)" ' \
+                         'marker-mid="url(#mozman2)" marker-start="url(#mozman1)" />')
+
+    def test_unpack_error(self):
+        e = MarkerMock(debug=True, profile='full')
+        # one or three values not two
+        self.assertRaises(ValueError, e.set_markers, (1, 2))
+
+    def test_id_error(self):
+        e = MarkerMock(debug=True, profile='full')
+        # intergers not valid
+        self.assertRaises(TypeError, e.set_markers, (1, 2, 3))
+
+if __name__=='__main__':
+    unittest.main()