diff options
Diffstat (limited to 'share/extensions/tests/test_inkex_elements.py')
-rw-r--r-- | share/extensions/tests/test_inkex_elements.py | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/share/extensions/tests/test_inkex_elements.py b/share/extensions/tests/test_inkex_elements.py new file mode 100644 index 0000000..c7a93be --- /dev/null +++ b/share/extensions/tests/test_inkex_elements.py @@ -0,0 +1,496 @@ +#!/usr/bin/env python +# coding=utf-8 +""" +Test specific elements API from svg xml lxml custom classes. +""" + +import pytest +import inkex + +from inkex import ( + Group, Layer, Pattern, Guide, Polyline, Use, Defs, + TextElement, TextPath, Tspan, FlowPara, FlowRoot, FlowRegion, FlowSpan, + PathElement, Rectangle, Circle, Ellipse, Anchor, Line as LineElement, + Transform, Style, LinearGradient, RadialGradient, Stop +) +from inkex.colors import Color +from inkex.paths import Move, Line +from inkex.utils import FragmentError, PY3 + +from .test_inkex_elements_base import SvgTestCase + +class ElementTestCase(SvgTestCase): + """Base element testing""" + tag = 'svg' + + def setUp(self): + super(ElementTestCase, self).setUp() + self.elem = self.svg.getElement('//svg:{}'.format(self.tag)) + + def test_print(self): + """Print element as string""" + self.assertEqual(str(self.elem), self.tag) + + def assertElement(self, elem, compare): # pylint: disable=invalid-name + """Assert an element""" + if PY3: + self.assertEqual(elem.tostring(), compare) + + +class PathElementTestCase(ElementTestCase): + """Test PathElements""" + source_file = 'with-lpe.svg' + tag = 'path' + + def test_new_path(self): + """Test new path element""" + path = PathElement.new(path=[Move(10, 10), Line(20, 20)]) + self.assertEqual(path.get('d'), 'M 10 10 L 20 20') + + def test_original_path(self): + """LPE paths can return their original paths""" + lpe = self.svg.getElementById('lpe') + nolpe = self.svg.getElementById('nolpe') + self.assertEqual(str(lpe.path), 'M 30 30 L -10 -10 Z') + self.assertEqual(str(lpe.original_path), 'M 20 20 L 10 10 Z') + self.assertEqual(str(nolpe.path), 'M 30 30 L -10 -10 Z') + self.assertEqual(str(nolpe.original_path), 'M 30 30 L -10 -10 Z') + + lpe.original_path = "M 60 60 L 5 5" + self.assertEqual(lpe.get('inkscape:original-d'), 'M 60 60 L 5 5') + self.assertEqual(lpe.get('d'), 'M 30 30 L -10 -10 Z') + + lpe.path = "M 60 60 L 15 15 Z" + self.assertEqual(lpe.get('d'), 'M 60 60 L 15 15 Z') + + nolpe.original_path = "M 60 60 L 5 5" + self.assertEqual(nolpe.get('inkscape:original-d', None), None) + self.assertEqual(nolpe.get('d'), 'M 60 60 L 5 5') + +class PolylineElementTestCase(ElementTestCase): + """Test the polyline elements support""" + tag = 'polyline' + + def test_type(self): + """Polyline have their own types""" + self.assertTrue(isinstance(self.elem, inkex.Polyline)) + + def test_polyline_points(self): + """Basic tests for points attribute as a path""" + pol = Polyline(points='10,10 50,50 10,15 15,10') + self.assertEqual(str(pol.path), 'M 10 10 L 50 50 L 10 15 L 15 10') + pol.path = "M 10 10 L 30 9 L 1 2 C 10 45 3 4 45 60 M 35 35" + self.assertEqual(pol.get('points'), '10,10 30,9 1,2 45,60 35,35') + +class PolygonElementTestCase(ElementTestCase): + """Test Polygon Elements""" + tag = 'polygon' + + def test_type(self): + """Polygons have their own types""" + self.assertTrue(isinstance(self.elem, inkex.Polygon)) + + def test_conversion(self): + """Polygones are converted to paths""" + pol = inkex.Polygon(points='10,10 50,50 10,15 15,10') + self.assertEqual(str(pol.path), 'M 10 10 L 50 50 L 10 15 L 15 10 Z') + +class LineElementTestCase(ElementTestCase): + """Test Line Elements""" + tag = 'line' + + def test_new_line(self): + """Line creation""" + line = LineElement.new((10, 10), (20, 20)) + self.assertElement(line, b'<line x1="10.0" y1="10.0" x2="20.0" y2="20.0"/>') + + def test_type(self): + """Lines have their own types""" + self.assertTrue(isinstance(self.elem, inkex.Line)) + + def test_conversion(self): + """Lines are converted to paths""" + pol = inkex.elements.Line(x1='2', y1='3', x2='4', y2='5') + self.assertEqual(str(pol.path), 'M 2 3 L 4 5 Z') + +class PatternTestCase(ElementTestCase): + """Test Pattern elements""" + tag = 'pattern' + + def test_pattern_transform(self): + """Patterns have a transformation of their own""" + pattern = Pattern() + self.assertEqual(pattern.patternTransform, Transform()) + pattern.patternTransform.add_translate(10, 10) + self.assertEqual(pattern.get('patternTransform'), 'translate(10, 10)') + +class GroupTest(ElementTestCase): + """Test extra functionality on a group element""" + tag = 'g' + + def test_new_group(self): + """Test creating groups""" + svg = Layer.new('layerA', Group.new('groupA', Rectangle())) + self.assertElement(svg,\ + b'<g inkscape:groupmode="layer" inkscape:label="layerA">'\ + b'<g inkscape:label="groupA"><rect/></g></g>') + + def test_transform_property(self): + """Test getting and setting a transform""" + self.assertEqual(str(self.elem.transform), 'matrix(1.44985 0 0 1.36417 -107.03 -167.362)') + self.elem.transform = 'translate(12, 14)' + self.assertEqual(self.elem.transform, Transform('translate(12, 14)')) + self.assertEqual(str(self.elem.transform), 'translate(12, 14)') + + def test_groupmode(self): + """Get groupmode is layer""" + self.assertEqual(self.svg.getElementById('A').groupmode, 'layer') + self.assertEqual(self.svg.getElementById('C').groupmode, 'group') + + def test_get_path(self): + """Group path is combined children""" + print(str(self.svg.getElementById('A').get_path())) + self.assertEqual( + str(self.svg.getElementById('A').get_path()), + 'M -108.539 517.61 L -87.6093 496.117 L -98.3066 492.768 L -69.9353 492.301 L -55.5172' + ' 506.163 L -66.2146 502.814 L -87.1446 524.307 M 60.0914 498.694 L 156.784 439.145 L' + ' 240.218 491.183 L 143.526 550.731 z M -176.909 458.816 a 64.2385 38.9175 -7.86457 1' + ' 0 88.3701 -19.0784 a 64.2385 38.9175 -7.86457 0 0 -88.3701 19.0784 z M -300.162' + ' 513.715 L -282.488 509.9 Z M -214.583 540.504 L -209.001 448.77 Z M -193.189 547.201 ' + 'L -238.536 486.266 L -185.049 503.008 L -230.396 442.073 M -193.189 547.201 L -238.536' + ' 486.266 L -185.049 503.008 L -230.396 442.073 Z M 15 15 L 15.5 20 Z') + + def test_bounding_box(self): + """A group returns a bounding box""" + empty = self.svg.add(Group(Group())) + self.assertEqual(empty.bounding_box(), None) + self.assertEqual(int(self.svg.getElementById('A').bounding_box().width), 783) + self.assertEqual(int(self.svg.getElementById('B').bounding_box().height), 114) + +class RectTest(ElementTestCase): + """Test extra functionality on a rectangle element""" + tag = 'rect' + + def test_parse(self): + """Test Rectangle parsed from XML""" + rect = Rectangle(attrib={ + "x": "10px", "y": "20px", + "width": "100px", "height": "200px", + "rx": "15px", "ry": "30px" }) + self.assertEqual(rect.left, 10) + self.assertEqual(rect.top, 20) + self.assertEqual(rect.right, 10+100) + self.assertEqual(rect.bottom, 20+200) + self.assertEqual(rect.width, 100) + self.assertEqual(rect.height, 200) + self.assertEqual(rect.rx, 15) + self.assertEqual(rect.ry, 30) + + def test_compose_transform(self): + """Composed transformation""" + self.assertEqual(self.elem.transform, Transform('rotate(16.097889)')) + self.assertEqual(str(self.elem.composed_transform()), + 'matrix(1.4019 -0.812338 1.20967 0.709877 -542.221 533.431)') + + def test_effetive_stylesheet(self): + """Test the non-parent combination of styles""" + self.assertEqual(str(self.elem.effective_style()),\ + 'fill:#0000ff;stroke-width:1px') + self.assertEqual(str(self.elem.getparent().effective_style()),\ + 'fill:#0000ff;stroke-width:1px;stroke:#f00') + + def test_compose_stylesheet(self): + """Test finding the composed stylesheet for the shape""" + self.assertEqual(str(self.elem.style), 'fill:#0000ff;stroke-width:1px') + self.assertEqual(str(self.elem.composed_style()), + 'fill:#0000ff;stroke:#d88;joker:url(#path1);stroke-width:1px') + + def test_path(self): + """Rectangle path""" + self.assertEqual(self.elem.get_path(), 'M 200.0,200.0 h100.0v100.0h-100.0 z') + self.assertEqual(str(self.elem.path), 'M 200 200 h 100 v 100 h -100 z') + +class PathTest(ElementTestCase): + """Test path extra functionality""" + tag = 'path' + + def test_apply_transform(self): + """Transformation can be applied to path""" + path = self.svg.getElementById('D') + path.transform = Transform(translate=(10, 10)) + self.assertEqual(path.get('d'), 'M30,130 L60,130 L60,120 L70,140 L60,160 L60,150 L30,150') + path.apply_transform() + self.assertEqual(path.get('d'), 'M 40 140 L 70 140 L 70 130 L 80 150 ' + 'L 70 170 L 70 160 L 40 160') + self.assertFalse(path.transform) + +class CircleTest(ElementTestCase): + """Test extra functionality on a circle element""" + tag = 'circle' + + def test_parse(self): + """Test Circle parsed from XML""" + circle = Circle(attrib={"cx": "10px", "cy": "20px", "r": "30px"}) + self.assertEqual(circle.center.x, 10) + self.assertEqual(circle.center.y, 20) + self.assertEqual(circle.radius, 30) + ellipse = Ellipse(attrib={"cx": "10px", "cy": "20px", "rx": "30px", "ry": "40px"}) + self.assertEqual(ellipse.center.x, 10) + self.assertEqual(ellipse.center.y, 20) + self.assertEqual(ellipse.radius.x, 30) + self.assertEqual(ellipse.radius.y, 40) + + def test_new(self): + """Test new circles""" + elem = Circle.new((10, 10), 50) + self.assertElement(elem, b'<circle cx="10.0" cy="10.0" r="50"/>') + elem = Ellipse.new((10, 10), (15, 10)) + self.assertElement(elem, b'<ellipse cx="10.0" cy="10.0" rx="15.0" ry="10.0"/>') + + def test_path(self): + """Circle path""" + self.assertEqual(self.elem.get_path(), + 'M 100.0,50.0 a 50.0,50.0 0 1 0 50.0, ' + '50.0 a 50.0,50.0 0 0 0 -50.0, -50.0 z') + +class AnchorTest(ElementTestCase): + """Test anchor tags""" + def test_new(self): + """Anchor tag creation""" + link = Anchor.new('https://inkscape.org', Rectangle()) + self.assertElement(link, b'<a xlink:href="https://inkscape.org"><rect/></a>') + +class NamedViewTest(ElementTestCase): + """Test the sodipodi namedview tag""" + def test_guides(self): + """Create a guide and see a list of them""" + self.svg.namedview.add(Guide().move_to(0, 0, 0)) + self.svg.namedview.add(Guide().move_to(0, 0, '90')) + self.assertEqual(len(self.svg.namedview.get_guides()), 2) + +class TextTest(ElementTestCase): + """Test all text functions""" + def test_append_superscript(self): + """Test adding superscript""" + tap = TextPath() + tap.append(Tspan.superscript('th')) + self.assertEqual(len(tap), 1) + + def test_path(self): + """Test getting paths""" + self.assertFalse(TextPath().get_path()) + self.assertFalse(TextElement().get_path()) + self.assertFalse(FlowRegion().get_path()) + self.assertFalse(FlowRoot().get_path()) + self.assertFalse(FlowPara().get_path()) + self.assertFalse(FlowSpan().get_path()) + self.assertFalse(Tspan().get_path()) + + +class UseTest(ElementTestCase): + """Test extra functionality on a use element""" + tag = 'use' + + def test_path(self): + """Use path follows ref""" + self.assertEqual(str(self.elem.path), 'M 0 0 L 10 10 Z') + + def test_empty_ref(self): + """An empty ref or None ref doesn't cause an error""" + use = Use() + use.set('xlink:href', 'something') + self.assertRaises(FragmentError, getattr, use, 'href') + elem = self.svg.add(Use()) + self.assertEqual(elem.href, None) + elem.set('xlink:href', '') + self.assertEqual(elem.href, None) + elem.set('xlink:href', '#badref') + self.assertEqual(elem.href, None) + elem.set('xlink:href', self.elem.get('xlink:href')) + self.assertEqual(elem.href.get('id'), 'path1') + + def test_unlink(self): + """Test use tag unlinking""" + elem = self.elem.unlink() + self.assertEqual(str(elem.path), 'M 0 0 L 10 10 Z') + self.assertEqual(elem.tag_name, 'path') + self.assertEqual(elem.getparent().get('id'), 'C') + +class StopTests(ElementTestCase): + """Color stop tests""" + black = Color('#000000') + grey50 = Color('#080808') + white = Color('#111111') + + def test_interpolate(self): + """Interpolate colours""" + stl1 = Style({'stop-color': self.black, 'stop-opacity': 0.0}) + stop1 = Stop(offset='0.0', style=str(stl1)) + stl2 = Style({'stop-color': self.white, 'stop-opacity': 1.0}) + stop2 = Stop(offset='1.0', style=str(stl2)) + stop3 = stop1.interpolate(stop2, 0.5) + assert stop3.style['stop-color'] == str(self.grey50) + assert float(stop3.style['stop-opacity']) == pytest.approx(0.5, 1e-3) + + +class GradientTests(ElementTestCase): + """Gradient testing""" + black = Color('#000000') + grey50 = Color('#080808') + white = Color('#111111') + + whiteop1 = Style({'stop-color': white, 'stop-opacity': 1.0}) + blackop1 = Style({'stop-color': black, 'stop-opacity': 1.0}) + whiteop0 = Style({'stop-color': white, 'stop-opacity': 0.0}) + blackop0 = Style({'stop-color': black, 'stop-opacity': 0.0}) + + translate11 = Transform('translate(1.0, 1.0)') + translate22 = Transform('translate(2.0, 2.0)') + + def test_parse(self): + """Gradients parsed from XML""" + values = [ + (LinearGradient, + {'x1': '0px', 'y1': '1px', 'x2': '2px', 'y2': '3px'}, + {'x1': 0.0, 'y1': 1.0, 'x2': 2.0, 'y2': 3.0}, + ), + (RadialGradient, + {'cx': '0px', 'cy': '1px', 'fx': '2px', 'fy': '3px', 'r': '4px'}, + {'cx': 0.0, 'cy': 1.0, 'fx': 2.0, 'fy': 3.0} + )] + for classname, attributes, expected in values: + grad = classname(attrib=attributes) + grad.apply_transform() # identity transform + for key, value in expected.items(): + assert float(grad.get(key)) == pytest.approx(value, 1e-3) + grad = classname(attrib=attributes) + grad = grad.interpolate(grad, 0.0) + for key, value in expected.items(): + assert float(grad.get(key)) == pytest.approx(value, 1e-3) + + def test_apply_transform(self): + """Transform gradients""" + values = [ + (LinearGradient, + {'x1': 0.0, 'y1': 0.0, 'x2': 1.0, 'y2': 1.0}, + {'x1': 1.0, 'y1': 1.0, 'x2': 2.0, 'y2': 2.0}), + (RadialGradient, + {'cx': 0.0, 'cy': 0.0, 'fx': 1.0, 'fy': 1.0, 'r': 1.0}, + {'cx': 1.0, 'cy': 1.0, 'fx': 2.0, 'fy': 2.0, 'r': 1.0} + )] + for classname, orientation, expected in values: + grad = classname().update(**orientation) + grad.gradientTransform = self.translate11 + grad.apply_transform() + val = grad.get('gradientTransform') + assert val is None + for key, value in expected.items(): + assert float(grad.get(key)) == pytest.approx(value, 1e-3) + + def test_stops(self): + """Gradients have stops""" + for classname in [LinearGradient, RadialGradient]: + grad = classname() + stops = [ + Stop().update(offset=0.0, style=self.whiteop0), + Stop().update(offset=1.0, style=self.blackop1)] + grad.add(*stops) + assert [s1.tostring() == s2.tostring() for s1, s2 in zip(grad.stops, stops)] + + def test_stop_styles(self): + """Gradients have styles""" + for classname in [LinearGradient, RadialGradient]: + grad = classname() + stops = [ + Stop().update(offset=0.0, style=self.whiteop0), + Stop().update(offset=1.0, style=self.blackop1)] + grad.add(*stops) + assert [str(s1) == str(s2.style) for s1, s2 in zip(grad.stop_styles, stops)] + + def test_get_stop_offsets(self): + """Gradients stop offsets""" + for classname in [LinearGradient, RadialGradient]: + grad = classname() + stops = [ + Stop().update(offset=0.0, style=self.whiteop0), + Stop().update(offset=1.0, style=self.blackop1)] + grad.add(*stops) + for stop1, stop2 in zip(grad.stop_offsets, stops): + self.assertEqual(float(stop1), pytest.approx(float(stop2.offset), 1e-3)) + + def test_interpolate(self): + """Gradients can be interpolated""" + values = [ + (LinearGradient, + {'x1': 0, 'y1': 0, 'x2': 1, 'y2': 1}, + {'x1': 2, 'y1': 2, 'x2': 1, 'y2': 1}, + {'x1': 1.0, 'y1': 1.0, 'x2': 1.0, 'y2': 1.0}), + (RadialGradient, + {'cx': 0, 'cy': 0, 'fx': 1, 'fy': 1, 'r': 0}, + {'cx': 2, 'cy': 2, 'fx': 1, 'fy': 1, 'r': 1}, + {'cx': 1.0, 'cy': 1.0, 'fx': 1.0, 'fy': 1.0, 'r': 0.5}) + ] + for classname, orientation1, orientation2, expected in values: + # gradient 1 + grad1 = classname() + stops1 = [ + Stop().update(offset=0.0, style=self.whiteop0), + Stop().update(offset=1.0, style=self.blackop1)] + grad1.add(*stops1) + grad1.update(gradientTransform=self.translate11) + grad1.update(**orientation1) + + # gradient 2 + grad2 = classname() + stops2 = [ + Stop().update(offset=0.0, style=self.blackop1), + Stop().update(offset=1.0, style=self.whiteop0)] + grad2.add(*stops2) + grad2.update(gradientTransform=self.translate22) + grad2.update(**orientation2) + grad = grad1.interpolate(grad2, 0.5) + comp = Style({'stop-color': self.grey50, 'stop-opacity': 0.5}) + self.assertEqual(str(grad.stops[0].style), str(Style(comp))) + self.assertEqual(str(grad.stops[1].style), str(Style(comp))) + self.assertEqual(str(grad.gradientTransform), 'translate(1.5, 1.5)') + for key, value in expected.items(): + self.assertEqual(float(grad.get(key)), pytest.approx(value, 1e-3)) + + +class SymbolTest(ElementTestCase): + """Test Symbol elements""" + source_file = 'symbol.svg' + tag = 'symbol' + + def test_unlink_symbol(self): + """Test unlink symbols""" + use = self.svg.getElementById('plane01') + self.assertEqual(use.tag_name, 'use') + self.assertEqual(use.href.tag_name, 'symbol') + # Unlinking should replace symbol with group + elem = use.unlink() + self.assertEqual(elem.tag_name, 'g') + self.assertEqual(str(elem.transform), 'translate(18, 16)') + self.assertEqual(elem[0].tag_name, 'title') + self.assertEqual(elem[1].tag_name, 'rect') + +class DefsTest(ElementTestCase): + """Test the definitions tag""" + source_file = 'shapes.svg' + tag = 'defs' + + def test_defs(self): + """Make sure defs can be seen in the nodes of an svg""" + self.assertTrue(isinstance(self.svg.defs, Defs)) + defs = self.svg.getElementById('defs33') + self.assertTrue(isinstance(defs, Defs)) + +class StyleTest(ElementTestCase): + """Test a style tag""" + source_file = 'css.svg' + tag = 'style' + + def test_style(self): + """Make sure style tags can be loaded and saved""" + css = self.svg.stylesheet + self.assertTrue(css) |