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 )