File indexing completed on 2025-04-27 04:01:22

0001 import math
0002 from xml.etree import ElementTree
0003 from unittest.mock import MagicMock
0004 
0005 from .. import base
0006 from lottie.parsers.svg.importer import SvgParser, parse_color
0007 from lottie import NVector
0008 from lottie import objects
0009 from lottie.utils.transform import TransformMatrix
0010 
0011 
0012 class TestUnit(base.TestCase):
0013     def test_naked(self):
0014         parser = SvgParser()
0015         parser.dpi = 96
0016 
0017         self.assertEqual(parser._parse_unit(123), 123)
0018         self.assertEqual(parser._parse_unit(12.3), 12.3)
0019         self.assertEqual(parser._parse_unit("123"), 123)
0020         self.assertEqual(parser._parse_unit("12.3"), 12.3)
0021 
0022     def test_px(self):
0023         parser = SvgParser()
0024         parser.dpi = 96
0025 
0026         self.assertEqual(parser._parse_unit("123px"), 123)
0027         self.assertEqual(parser._parse_unit("12.3px"), 12.3)
0028 
0029     def test_in(self):
0030         parser = SvgParser()
0031         parser.dpi = 96
0032 
0033         self.assertEqual(parser._parse_unit("1in"), 96)
0034         self.assertEqual(parser._parse_unit("1.5in"), 96*1.5)
0035 
0036         parser.dpi = 100
0037         self.assertEqual(parser._parse_unit("1in"), 100)
0038 
0039     def test_pc(self):
0040         parser = SvgParser()
0041         parser.dpi = 96
0042 
0043         self.assertAlmostEqual(parser._parse_unit("1pc"), 96/6)
0044         self.assertAlmostEqual(parser._parse_unit("1.5pc"), 96*1.5/6)
0045 
0046         parser.dpi = 100
0047         self.assertAlmostEqual(parser._parse_unit("1pc"), 100/6)
0048 
0049     def test_pt(self):
0050         parser = SvgParser()
0051         parser.dpi = 96
0052 
0053         self.assertAlmostEqual(parser._parse_unit("1pt"), 96/72)
0054         self.assertAlmostEqual(parser._parse_unit("1.5pt"), 96*1.5/72)
0055 
0056         parser.dpi = 100
0057         self.assertAlmostEqual(parser._parse_unit("1pt"), 100/72)
0058 
0059     def test_cm(self):
0060         parser = SvgParser()
0061         parser.dpi = 96
0062 
0063         self.assertAlmostEqual(parser._parse_unit("1cm"), 96/2.54)
0064         self.assertAlmostEqual(parser._parse_unit("1.5cm"), 96*1.5/2.54)
0065 
0066         parser.dpi = 100
0067         self.assertAlmostEqual(parser._parse_unit("1cm"), 100/2.54)
0068 
0069     def test_mm(self):
0070         parser = SvgParser()
0071         parser.dpi = 96
0072 
0073         self.assertAlmostEqual(parser._parse_unit("1mm"), 96/25.4)
0074         self.assertAlmostEqual(parser._parse_unit("1.5mm"), 96*1.5/25.4)
0075 
0076         parser.dpi = 100
0077         self.assertAlmostEqual(parser._parse_unit("1mm"), 100/25.4)
0078 
0079     def test_Q(self):
0080         parser = SvgParser()
0081         parser.dpi = 96
0082 
0083         self.assertAlmostEqual(parser._parse_unit("1Q"), 96/25.4/4)
0084         self.assertAlmostEqual(parser._parse_unit("1.5Q"), 96*1.5/25.4/4)
0085 
0086         parser.dpi = 100
0087         self.assertAlmostEqual(parser._parse_unit("1Q"), 100/25.4/4)
0088 
0089     def test_vw(self):
0090         parser = SvgParser()
0091         parser.animation = MagicMock()
0092         parser.animation.width = 512
0093         parser.animation.height = 256
0094 
0095         self.assertAlmostEqual(parser._parse_unit("1vw"), 5.12)
0096         self.assertAlmostEqual(parser._parse_unit("1.5vw"), 5.12*1.5)
0097 
0098     def test_vh(self):
0099         parser = SvgParser()
0100         parser.animation = MagicMock()
0101         parser.animation.width = 512
0102         parser.animation.height = 256
0103 
0104         self.assertAlmostEqual(parser._parse_unit("1vh"), 2.56)
0105         self.assertAlmostEqual(parser._parse_unit("1.5vh"), 2.56*1.5)
0106 
0107     def test_vmax(self):
0108         parser = SvgParser()
0109         parser.animation = MagicMock()
0110         parser.animation.width = 512
0111         parser.animation.height = 256
0112 
0113         self.assertAlmostEqual(parser._parse_unit("1vmax"), 5.12)
0114         self.assertAlmostEqual(parser._parse_unit("1.5vmax"), 5.12*1.5)
0115 
0116     def test_vmin(self):
0117         parser = SvgParser()
0118         parser.animation = MagicMock()
0119         parser.animation.width = 512
0120         parser.animation.height = 256
0121 
0122         self.assertAlmostEqual(parser._parse_unit("1vmin"), 2.56)
0123         self.assertAlmostEqual(parser._parse_unit("1.5vmin"), 2.56*1.5)
0124 
0125 
0126 class TestColor(base.TestCase):
0127     def test_hex_long(self):
0128         self.assert_nvector_equal(
0129             parse_color("#abcdef"),
0130             NVector(0xab/0xff, 0xcd/0xff, 0xef/0xff, 1)
0131         )
0132         self.assert_nvector_equal(
0133             parse_color("#ABCDEF"),
0134             NVector(0xab/0xff, 0xcd/0xff, 0xef/0xff, 1)
0135         )
0136 
0137     def test_hex_short(self):
0138         self.assert_nvector_equal(
0139             parse_color("#abc"),
0140             NVector(0xa/0xf, 0xb/0xf, 0xc/0xf, 1)
0141         )
0142         self.assert_nvector_equal(
0143             parse_color("#abc"),
0144             NVector(0xa/0xf, 0xb/0xf, 0xc/0xf, 1)
0145         )
0146 
0147     def test_rgb(self):
0148         self.assert_nvector_equal(
0149             parse_color("rgb(12, 34, 56)"),
0150             NVector(12/0xff, 34/0xff, 56/0xff, 1)
0151         );
0152         self.assert_nvector_equal(
0153             parse_color("rgb(12%, 34%, 56%)"),
0154             NVector(0.12, 0.34, 0.56, 1)
0155         );
0156 
0157     def test_rgba(self):
0158         self.assert_nvector_equal(
0159             parse_color("rgba(12, 34, 56, 0.7)"),
0160             NVector(12/0xff, 34/0xff, 56/0xff, 0.7)
0161         );
0162         self.assert_nvector_equal(
0163             parse_color("rgba(12%, 34%, 56%, 0.7)"),
0164             NVector(0.12, 0.34, 0.56, 0.7)
0165         );
0166 
0167     def test_named(self):
0168         self.assert_nvector_equal(
0169             parse_color("transparent"),
0170             NVector(0, 0, 0, 0)
0171         );
0172         self.assert_nvector_equal(
0173             parse_color("red"),
0174             NVector(1, 0, 0, 1)
0175         );
0176         self.assert_nvector_equal(
0177             parse_color("lime"),
0178             NVector(0, 1, 0, 1)
0179         );
0180         self.assert_nvector_equal(
0181             parse_color("blue"),
0182             NVector(0, 0, 1, 1)
0183         );
0184 
0185     def test_hsl(self):
0186         self.assert_nvector_equal(
0187             parse_color("hsl(0, 100%, 50%)"),
0188             NVector(1, 0, 0, 1)
0189         );
0190         self.assert_nvector_equal(
0191             parse_color("hsl(60, 100%, 50%)"),
0192             NVector(1, 1, 0, 1)
0193         );
0194         self.assert_nvector_equal(
0195             parse_color("hsl(120, 100%, 50%)"),
0196             NVector(0, 1, 0, 1)
0197         );
0198         self.assert_nvector_equal(
0199             parse_color("hsl(180, 100%, 50%)"),
0200             NVector(0, 1, 1, 1)
0201         );
0202         self.assert_nvector_equal(
0203             parse_color("hsl(240, 100%, 50%)"),
0204             NVector(0, 0, 1, 1)
0205         );
0206         self.assert_nvector_equal(
0207             parse_color("hsl(300, 100%, 50%)"),
0208             NVector(1, 0, 1, 1)
0209         );
0210         self.assert_nvector_equal(
0211             parse_color("hsl(360, 100%, 50%)"),
0212             NVector(1, 0, 0, 1)
0213         );
0214 
0215         self.assert_nvector_equal(
0216             parse_color("hsl(120, 100%, 0%)"),
0217             NVector(0, 0, 0, 1)
0218         );
0219         self.assert_nvector_equal(
0220             parse_color("hsl(120, 100%, 100%)"),
0221             NVector(1, 1, 1, 1)
0222         );
0223         self.assert_nvector_equal(
0224             parse_color("hsl(120, 100%, 25%)"),
0225             NVector(0, 0.5, 0, 1)
0226         );
0227         self.assert_nvector_equal(
0228             parse_color("hsl(120, 75%, 75%)"),
0229             NVector(0.5625, 0.9375, 0.5625, 1)
0230         );
0231 
0232     def test_hsla(self):
0233         self.assert_nvector_equal(
0234             parse_color("hsla(0, 100%, 50%, 0.7)"),
0235             NVector(1, 0, 0, 0.7)
0236         );
0237         self.assert_nvector_equal(
0238             parse_color("hsla(60, 100%, 50%, 0.7)"),
0239             NVector(1, 1, 0, 0.7)
0240         );
0241         self.assert_nvector_equal(
0242             parse_color("hsla(120, 100%, 50%, 0.7)"),
0243             NVector(0, 1, 0, 0.7)
0244         );
0245         self.assert_nvector_equal(
0246             parse_color("hsla(180, 100%, 50%, 0.7)"),
0247             NVector(0, 1, 1, 0.7)
0248         );
0249         self.assert_nvector_equal(
0250             parse_color("hsla(240, 100%, 50%, 0.7)"),
0251             NVector(0, 0, 1, 0.7)
0252         );
0253         self.assert_nvector_equal(
0254             parse_color("hsla(300, 100%, 50%, 0.7)"),
0255             NVector(1, 0, 1, 0.7)
0256         );
0257         self.assert_nvector_equal(
0258             parse_color("hsla(360, 100%, 50%, 0.7)"),
0259             NVector(1, 0, 0, 0.7)
0260         );
0261 
0262         self.assert_nvector_equal(
0263             parse_color("hsla(120, 100%, 0%, 0.7)"),
0264             NVector(0, 0, 0, 0.7)
0265         );
0266         self.assert_nvector_equal(
0267             parse_color("hsla(120, 100%, 100%, 0.7)"),
0268             NVector(1, 1, 1, 0.7)
0269         );
0270         self.assert_nvector_equal(
0271             parse_color("hsla(120, 100%, 25%, 0.7)"),
0272             NVector(0, 0.5, 0, 0.7)
0273         );
0274         self.assert_nvector_equal(
0275             parse_color("hsla(120, 75%, 75%, 0.7)"),
0276             NVector(0.5625, 0.9375, 0.5625, 0.7)
0277         );
0278 
0279     def test_current(self):
0280         self.assert_nvector_equal(
0281             parse_color("currentColor", NVector(0.1, 0.2, 0.3, 0.4)),
0282             NVector(0.1, 0.2, 0.3, 0.4)
0283         );
0284         self.assert_nvector_equal(
0285             parse_color("inherit", NVector(0.1, 0.2, 0.3, 0.4)),
0286             NVector(0.1, 0.2, 0.3, 0.4)
0287         );
0288 
0289 
0290 class TestStyle(base.TestCase):
0291     def test_style(self):
0292         parser = SvgParser()
0293         et = ElementTree.Element("g")
0294         et.attrib["style"] = "fill: red; stroke:green;stroke-width:3px"
0295         self.assertDictEqual(
0296             parser.parse_style(et, {}),
0297             {
0298                 "fill": "red",
0299                 "stroke": "green",
0300                 "stroke-width": "3px",
0301             }
0302         )
0303 
0304     def test_attrs(self):
0305         parser = SvgParser()
0306         et = ElementTree.Element("g")
0307         et.attrib["fill"] = "red"
0308         et.attrib["stroke"] = "green"
0309         et.attrib["stroke-width"] = "3px"
0310         self.assertDictEqual(
0311             parser.parse_style(et, {}),
0312             {
0313                 "fill": "red",
0314                 "stroke": "green",
0315                 "stroke-width": "3px",
0316             }
0317         )
0318 
0319     def test_mixed(self):
0320         parser = SvgParser()
0321         et = ElementTree.Element("g")
0322         et.attrib["style"] = "stroke:green;stroke-width:3px"
0323         et.attrib["fill"] = "red"
0324         self.assertDictEqual(
0325             parser.parse_style(et, {}),
0326             {
0327                 "fill": "red",
0328                 "stroke": "green",
0329                 "stroke-width": "3px",
0330             }
0331         )
0332 
0333     def test_inherit(self):
0334         parser = SvgParser()
0335         et = ElementTree.Element("g")
0336         et.attrib["style"] = "stroke:green;stroke-width:3px"
0337         base = { "fill": "red" }
0338         self.assertDictEqual(
0339             parser.parse_style(et, base),
0340             {
0341                 "fill": "red",
0342                 "stroke": "green",
0343                 "stroke-width": "3px",
0344             }
0345         )
0346         self.assertDictEqual(base, { "fill": "red" })
0347 
0348     def test_apply_common_style(self):
0349         parser = SvgParser()
0350         transform = MagicMock()
0351         parser.apply_common_style({"fill": "red"}, transform)
0352         self.assertEqual(transform.opacity.value, 100)
0353         parser.apply_common_style({"fill": "red", "opacity": "0.7"}, transform)
0354         self.assertEqual(transform.opacity.value, 70)
0355 
0356 
0357     def test_apply_visibility(self):
0358         parser = SvgParser()
0359         object = MagicMock()
0360         object.hidden = False
0361         parser.apply_visibility({"fill": "red"}, object)
0362         self.assertFalse(object.hidden)
0363         parser.apply_visibility({"fill": "red", "display": "block"}, object)
0364         self.assertFalse(object.hidden)
0365         parser.apply_visibility({"fill": "red", "display": "inline"}, object)
0366         self.assertFalse(object.hidden)
0367         parser.apply_visibility({"fill": "red", "visibility": "visible"}, object)
0368         self.assertFalse(object.hidden)
0369         parser.apply_visibility({"fill": "red", "display": "none"}, object)
0370         self.assertTrue(object.hidden)
0371         parser.apply_visibility({"fill": "red", "visibility": "hidden"}, object)
0372         self.assertTrue(object.hidden)
0373 
0374 
0375 class TestParseTransform(base.TestCase):
0376     def assert_transform(self, transf, **kwargs):
0377         for k, v in kwargs.items():
0378             tv = getattr(transf, k).value
0379             if isinstance(v, NVector):
0380                 self.assert_nvector_equal(tv, v)
0381             else:
0382                 self.assertEqual(tv, v)
0383 
0384     def test_inkscape_center(self):
0385         parser = SvgParser()
0386         element = ElementTree.Element("g")
0387         group = objects.Rect(NVector(100, 200), NVector(300, 400))
0388         dest_transform = objects.Transform()
0389 
0390         element.attrib[parser.qualified("inkscape", "transform-center-x")] = 0
0391         element.attrib[parser.qualified("inkscape", "transform-center-y")] = 0
0392         parser.parse_transform(element, group, dest_transform)
0393         self.assert_transform(
0394             dest_transform,
0395             anchor_point=NVector(100, 200),
0396             position=NVector(100, 200),
0397             scale=NVector(100, 100),
0398             rotation=0,
0399             skew_axis=0,
0400             skew=0,
0401         )
0402 
0403         element.attrib[parser.qualified("inkscape", "transform-center-x")] = 20
0404         element.attrib[parser.qualified("inkscape", "transform-center-y")] = -30
0405         parser.parse_transform(element, group, dest_transform)
0406         self.assert_transform(
0407             dest_transform,
0408             anchor_point=NVector(120, 230),
0409             position=NVector(120, 230),
0410             scale=NVector(100, 100),
0411             rotation=0,
0412             skew_axis=0,
0413             skew=0,
0414         )
0415 
0416     def test_translate(self):
0417         parser = SvgParser()
0418         element = ElementTree.Element("g")
0419         group = objects.Rect(NVector(100, 200), NVector(300, 400))
0420         dest_transform = objects.Transform()
0421 
0422         element.attrib["transform"] = "translate(12, 34)"
0423         parser.parse_transform(element, group, dest_transform)
0424         self.assert_transform(
0425             dest_transform,
0426             anchor_point=NVector(0, 0),
0427             position=NVector(12, 34),
0428             scale=NVector(100, 100),
0429             rotation=0,
0430             skew_axis=0,
0431             skew=0,
0432         )
0433 
0434     def test_translate_multi(self):
0435         parser = SvgParser()
0436         element = ElementTree.Element("g")
0437         group = objects.Rect(NVector(100, 200), NVector(300, 400))
0438         dest_transform = objects.Transform()
0439 
0440         element.attrib["transform"] = "translate(12, 34) translate(500, 600)"
0441         parser.parse_transform(element, group, dest_transform)
0442         self.assert_transform(
0443             dest_transform,
0444             anchor_point=NVector(0, 0),
0445             position=NVector(512, 634),
0446             scale=NVector(100, 100),
0447             rotation=0,
0448             skew_axis=0,
0449             skew=0,
0450         )
0451 
0452     def test_rotate(self):
0453         parser = SvgParser()
0454         element = ElementTree.Element("g")
0455         group = objects.Rect(NVector(100, 200), NVector(300, 400))
0456         dest_transform = objects.Transform()
0457 
0458         element.attrib["transform"] = "rotate(45)"
0459         parser.parse_transform(element, group, dest_transform)
0460         self.assert_transform(
0461             dest_transform,
0462             anchor_point=NVector(0, 0),
0463             position=NVector(0, 0),
0464             scale=NVector(100, 100),
0465             rotation=45,
0466             skew_axis=0,
0467             skew=0,
0468         )
0469 
0470     def test_rotate_around(self):
0471         parser = SvgParser()
0472         element = ElementTree.Element("g")
0473         group = objects.Rect(NVector(100, 200), NVector(300, 400))
0474         dest_transform = objects.Transform()
0475 
0476         element.attrib["transform"] = "rotate(45, 12, 34)"
0477         parser.parse_transform(element, group, dest_transform)
0478         self.assert_transform(
0479             dest_transform,
0480             anchor_point=NVector(12, 34),
0481             position=NVector(12, 34),
0482             scale=NVector(100, 100),
0483             rotation=45,
0484             skew_axis=0,
0485             skew=0,
0486         )
0487 
0488     def test_scale_1(self):
0489         parser = SvgParser()
0490         element = ElementTree.Element("g")
0491         group = objects.Rect(NVector(100, 200), NVector(300, 400))
0492         dest_transform = objects.Transform()
0493 
0494         element.attrib["transform"] = "scale(0.7)"
0495         parser.parse_transform(element, group, dest_transform)
0496         self.assert_transform(
0497             dest_transform,
0498             anchor_point=NVector(0, 0),
0499             position=NVector(0, 0),
0500             scale=NVector(70, 70),
0501             rotation=0,
0502             skew_axis=0,
0503             skew=0,
0504         )
0505 
0506     def test_scale_2(self):
0507         parser = SvgParser()
0508         element = ElementTree.Element("g")
0509         group = objects.Rect(NVector(100, 200), NVector(300, 400))
0510         dest_transform = objects.Transform()
0511 
0512         element.attrib["transform"] = "scale(0.7, 0.5)"
0513         parser.parse_transform(element, group, dest_transform)
0514         self.assert_transform(
0515             dest_transform,
0516             anchor_point=NVector(0, 0),
0517             position=NVector(0, 0),
0518             scale=NVector(70, 50),
0519             rotation=0,
0520             skew_axis=0,
0521             skew=0,
0522         )
0523 
0524     def test_multi_trans(self):
0525         parser = SvgParser()
0526         element = ElementTree.Element("g")
0527         group = objects.Rect(NVector(100, 200), NVector(300, 400))
0528         dest_transform = objects.Transform()
0529 
0530         element.attrib["transform"] = "scale(0.7, 0.5) rotate(45) translate(12, 34)"
0531         parser.parse_transform(element, group, dest_transform)
0532         self.assert_transform(
0533             dest_transform,
0534             anchor_point=NVector(0, 0),
0535             position=NVector(12, 34),
0536             scale=NVector(70, 50),
0537             rotation=45,
0538             skew_axis=0,
0539             skew=0,
0540         )
0541 
0542     def test_matrix(self):
0543         parser = SvgParser()
0544         element = ElementTree.Element("g")
0545         group = objects.Rect(NVector(100, 200), NVector(300, 400))
0546         dest_transform = objects.Transform()
0547 
0548         m = TransformMatrix()
0549         m.scale(0.7, 0.5)
0550         m.rotate(-math.pi/4)
0551         m.translate(12, 34)
0552         element.attrib["transform"] = m.to_css_2d()
0553         parser.parse_transform(element, group, dest_transform)
0554         self.assert_transform(
0555             dest_transform,
0556             anchor_point=NVector(0, 0),
0557             position=NVector(12, 34),
0558             scale=NVector(70, 50),
0559             rotation=45,
0560             skew_axis=0,
0561             skew=0,
0562         )