#!/usr/bin/env python # coding=utf-8 """ Test the element API base classes and basic functionality """ from lxml import etree from inkex.elements import ( load_svg, ShapeElement, Group, Rectangle, Tspan, TextElement, Line, ) from inkex.transforms import Transform from inkex.styles import Style from inkex.utils import FragmentError from inkex.tester import TestCase from inkex.tester.svg import svg_file class FakeShape(ShapeElement): # pylint: disable=abstract-method """A protend shape""" tag_name = 'fake' class SvgTestCase(TestCase): """Test SVG""" source_file = 'complextransform.test.svg' def setUp(self): super(SvgTestCase, self).setUp() self.svg = svg_file(self.data_file('svg', self.source_file)) class OverridenElementTestCase(SvgTestCase): """Test element overriding functionality""" def test_tag_names(self): """ Test tag names for custom and unknown tags """ doc = load_svg(""" Unknown SVG tag """) svg = doc.getroot() good = svg.getElementById("good") self.assertEqual(good.TAG, "g") bad = svg.getElementById("bad") self.assertEqual(bad.TAG, "badsvg") ugly = svg.getElementById("ugly") self.assertEqual(ugly.TAG, "othertag") def test_reference_count(self): """ Test inkex.element.BaseElement-derived object type is preserved on adding to group See https://gitlab.com/inkscape/extensions/-/issues/81 for details """ grp = Group() for _ in range(10): rect = Rectangle() grp.add(rect) for elem in grp: self.assertEqual(type(elem), Rectangle) def test_abstract_raises(self): """Abstract classes cannot be instantiated""" self.assertRaises(NotImplementedError, FakeShape().get_path) self.assertRaises(AttributeError, FakeShape().set_path, None) def test_add(self): """Can add single or multiple elements with passthrough""" elem = self.svg.getElementById('D') group = elem.add(Group(id='foo')) self.assertEqual(group.get('id'), 'foo') groups = elem.add(Group(id='f1'), Group(id='f2')) self.assertEqual(len(groups), 2) self.assertEqual(groups[0].get('id'), 'f1') self.assertEqual(groups[1].get('id'), 'f2') def test_creation(self): """Create elements with attributes""" group = Group().update(inkscape__label='Foo') self.assertEqual(group.get('inkscape:label'), 'Foo') group = Group().update(inkscape__label='Bar') self.assertEqual(group.label, 'Bar') def test_tostring(self): """Elements can be printed as strings""" self.assertEqual(Group().tostring(), b'') elem = Group(id='bar') path = elem.add(Tspan(id='foo')) elem.transform.add_translate(50, 50) path.style['fill'] = 'red' self.assertEqual(elem.tostring(), \ b'') class AttributeHandelingTestCase(SvgTestCase): """Test how attributes are handled""" def test_chained_multiple_attrs(self): """Set multiple attributes at a time""" group = Group().update( attr1='A', attr2='B' ).update( attr3='C', attr4='D' ) self.assertEqual(group.get('attr1'), 'A') self.assertEqual(group.get('attr2'), 'B') self.assertEqual(group.get('attr3'), 'C') self.assertEqual(group.get('attr4'), 'D') # remove attributes, setting them to None group.update( attr1=None, attr4=None ) self.assertEqual(group.get('attr1'), None) self.assertEqual(group.get('attr2'), 'B') self.assertEqual(group.get('attr3'), 'C') self.assertEqual(group.get('attr4'), None) self.assertEqual(group.pop('attr2'), 'B') self.assertEqual(group.pop('attr3'), 'C') def test_set_wrapped_attribute(self): """Remove wrapped attribute using .set()""" group = Group().update( transform=Transform(scale=2) ) self.assertEqual(group.transform.matrix[0][0], 2) self.assertEqual(group.transform.matrix[1][1], 2) group.update( transform=None ) self.assertEqual(group.transform, Transform()) def test_pop_wrapped_attribute(self): """Remove wrapped attribute using .pop()""" group = Group() self.assertEqual(group.pop('transform'), Transform()) group.update( transform=Transform(scale=2) ) self.assertEqual(group.pop('transform'), Transform(scale=2)) self.assertEqual(group.pop('transform'), Transform()) self.assertRaises(AttributeError, getattr, group, 'foo') def test_pop_regular_attribute(self): """Remove wrapped attribute using .pop()""" group = Group() self.assertEqual(group.get('attr1'), None) group.update( attr1="42" ) self.assertEqual(group.pop('attr1'), "42") self.assertEqual(group.pop('attr1'), None) def test_update_consistant(self): """Update doesn't keep callbacks around""" elem = self.svg.getElementById('D') tr_a = Transform(translate=(10, 10)) tr_b = Transform(translate=(-20, 15)) elem.transform = tr_a elem.transform = tr_b self.assertEqual(str(elem.transform), 'translate(-20, 15)') tr_a.add_translate(10, 10) self.assertEqual(str(elem.transform), 'translate(-20, 15)') elem.set('transform', None) self.assertEqual(elem.get('transform'), None) def test_in_place_style(self): """Do styles update when we set them""" elem = self.svg.getElementById('D') elem.style['fill'] = 'purpleberry' self.assertEqual(elem.get('style'), 'fill:purpleberry') elem.style = {'marker': 'flag'} self.assertEqual(elem.get('style'), 'marker:flag') elem.style = Style(stroke='gammon') self.assertEqual(elem.get('style'), 'stroke:gammon') elem.style.update('grape:2;strawberry:nice;') self.assertEqual(elem.get('style'), 'stroke:gammon;grape:2;strawberry:nice') def test_random_id(self): """Test setting a random id""" elem = self.svg.getElementById('D') elem.set_random_id('Thing') self.assertEqual(elem.get('id'), 'Thing5815') elem.set_random_id('Thing', size=2) self.assertEqual(elem.get('id'), 'Thing85') elem.set_random_id() self.assertEqual(elem.get('id'), 'path5392') # No document root, no random id allowed self.assertRaises(FragmentError, elem.copy().set_random_id) def test_random_ids(self): """Test setting a tree of ids""" elem = self.svg.getElementById('D') self.svg.set_random_ids(prefix='TreeItem') self.assertEqual(self.svg.get('id'), 'TreeItem5815') self.assertEqual(self.svg[0].get('id'), 'TreeItem8555') self.assertEqual(elem.get('id'), 'TreeItem2036') def test_set_id_backlinks(self): """Changing an id can update backlinks""" elem = self.svg.getElementById('path1') elem.set_id('plant54', True) self.assertEqual(self.svg.getElementById('G').get('xlink:href'), '#plant54') self.assertEqual(self.svg.getElementById('G').href, elem) self.assertEqual(str(self.svg.getElementById('B').style), 'fill:#eee;joker:url(#plant54)') def test_get_element_by_name(self): """Get elements by name""" self.assertEqual(self.svg.getElementByName('Key').get('id'), 'K') self.assertEqual(self.svg.getElementByName('Elm', 'svg:g').get('id'), 'L') self.assertEqual(self.svg.getElementByName('Mine').get('id'), 'M') self.assertEqual(self.svg.getElementByName('doesntexist'), None) self.assertEqual(self.svg.getElementByName('Key', 'rect'), None) class TransformationTestCase(SvgTestCase): """Test transformative functions""" def test_bounding_box(self): """Elements can have bounding boxes""" elem = self.svg.getElementById('D') self.assertEqual(tuple(elem.bounding_box()), ((60.0, 100.0), (130.0, 170.00))) self.assertTrue(elem.bounding_box().center.is_close((80.0, 150.0))) self.assertEqual(tuple(TextElement(x='10', y='5').bounding_box()), ((10, 10), (5, 5))) group = Group(elem) self.assertEqual(elem.bounding_box(), group.bounding_box()) def test_transform(self): """In-place modified transforms are retained""" elem = self.svg.getElementById('D') self.assertEqual(str(elem.transform), 'translate(30, 10)') elem.transform.add_translate(-10, 10) self.assertEqual(str(elem.transform), 'translate(20, 20)') def test_scale(self): """In-place scaling from blank transform""" elem = self.svg.getElementById('F') self.assertEqual(elem.transform, Transform()) self.assertEqual(elem.get('transform'), None) elem.transform.add_scale(1.0666666666666667, 1.0666666666666667) self.assertEqual(elem.get('transform'), Transform(scale=1.06667)) self.assertIn(b'transform', etree.tostring(elem)) def test_in_place_transforms(self): """Do style and transforms update correctly""" elem = self.svg.getElementById('D') self.assertEqual(type(elem.transform), Transform) self.assertEqual(type(elem.style), Style) self.assertTrue(elem.transform) elem.transform = Transform() self.assertEqual(elem.transform, Transform()) self.assertEqual(elem.get('transform'), None) self.assertNotIn(b'transform', etree.tostring(elem)) elem.transform.add_translate(10, 10) self.assertIn(b'transform', etree.tostring(elem)) elem.transform.add_translate(-10, -10) self.assertNotIn(b'transform', etree.tostring(elem)) class RelationshipTestCase(SvgTestCase): """Test relationships between elements""" def test_findall(self): """Findall elements in svg""" groups = self.svg.findall('svg:g') self.assertEqual(len(groups), 1) def test_copy(self): """Test copying elements""" elem = self.svg.getElementById('D') cpy = elem.copy() self.assertFalse(cpy.getparent()) self.assertFalse(cpy.get('id')) def test_duplicate(self): """Test duplicating elements""" elem = self.svg.getElementById('D') dup = elem.duplicate() self.assertTrue(dup.get('id')) self.assertNotEqual(elem.get('id'), dup.get('id')) self.assertEqual(elem.getparent(), dup.getparent()) def test_replace_with(self): """Replacing nodes in a tree""" rect = self.svg.getElementById('E') path = rect.to_path_element() rect.replace_with(path) self.assertEqual(rect.getparent(), None) self.assertEqual(path.getparent(), self.svg.getElementById('C')) def test_descendants(self): """Elements can walk their descendants""" self.assertEqual(tuple(self.svg.descendants().ids), ( 'mydoc', 'path1', 'base', 'metadata7', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', )) get = self.svg.getElementById self.assertEqual(tuple(get('L').descendants().ids), ('L', 'M')) self.assertEqual(tuple(get('M').descendants().ids), ('M',)) def test_ancestors(self): """Element descendants of elements""" get = self.svg.getElementById self.assertEqual(tuple(get('M').ancestors().ids), ('L', 'K', 'A', 'mydoc')) self.assertEqual(tuple(get('M').ancestors(stop_at=[None]).ids), ('L', 'K', 'A', 'mydoc')) self.assertEqual(tuple(get('M').ancestors(stop_at=[get('K')]).ids), ('L', 'K')) self.assertEqual(tuple(get('M').ancestors(stop_at=[get('L')]).ids), ('L',)) def test_luca(self): """Test last common ancestor""" get = self.svg.getElementById self.assertEqual(tuple(get('M').ancestors(get('M')).ids), ('L',)) self.assertEqual(tuple(get('G').ancestors(get('H')).ids), ('C',)) self.assertEqual(tuple(get('M').ancestors(get('H')).ids), ('L', 'K', 'A'))