From 6395a017cb266b98adc46069ba084366570ae1b8 Mon Sep 17 00:00:00 2001 From: tatarize Date: Sat, 2 Nov 2019 17:07:07 +0000 Subject: [PATCH] Test Updates A few tests existed but were not ported. --- svg/elements/svg_elements.py | 2 + test/test_generation.py | 92 ++++++++++++++++++ test/test_parsing.py | 183 +++++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+) create mode 100644 test/test_generation.py create mode 100644 test/test_parsing.py diff --git a/svg/elements/svg_elements.py b/svg/elements/svg_elements.py index 7ea8bfb9..b87715e1 100644 --- a/svg/elements/svg_elements.py +++ b/svg/elements/svg_elements.py @@ -909,6 +909,7 @@ def blue(self): def hex(self): return '#%02x%02x%02x' % (self.red, self.green, self.blue) + def segment_length(curve, start, end, start_point, end_point, error, min_depth, depth): """Recursively approximates the length by straight lines""" mid = (start + end) / 2 @@ -2896,6 +2897,7 @@ def as_quad_curves(self): start_angle = self.get_start_angle() theta = self.get_rotation() p_start = self.start + p_end = self.end current_angle = start_angle - theta diff --git a/test/test_generation.py b/test/test_generation.py new file mode 100644 index 00000000..df59473d --- /dev/null +++ b/test/test_generation.py @@ -0,0 +1,92 @@ +from __future__ import division + +import unittest + +from svg.elements import * + +paths = [ + 'M 100,100 L 300,100 L 200,300 Z', + 'M 0,0 L 50,20 M 100,100 L 300,100 L 200,300 Z', + 'M 100,100 L 200,200', + 'M 100,200 L 200,100 L -100,-200', + 'M 100,200 C 100,100 250,100 250,200 S 400,300 400,200', + 'M 100,200 C 100,100 400,100 400,200', + 'M 100,500 C 25,400 475,400 400,500', + 'M 100,800 C 175,700 325,700 400,800', + 'M 600,200 C 675,100 975,100 900,200', + 'M 600,500 C 600,350 900,650 900,500', + 'M 600,800 C 625,700 725,700 750,800 S 875,900 900,800', + 'M 200,300 Q 400,50 600,300 T 1000,300', + 'M -3.4E+38,3.4E+38 L -3.4E-38,3.4E-38', + 'M 0,0 L 50,20 M 50,20 L 200,100 Z', + 'M 600,350 L 650,325 A 25,25 -30 0,1 700,300 L 750,275', +] + + +class TestGeneration(unittest.TestCase): + """Examples from the SVG spec""" + + def test_svg_examples(self): + for path in paths[15:]: + self.assertEqual(Path(path).d(), path) + + def test_svg_example0(self): + path = paths[0] + self.assertEqual(Path(path).d(), path) + + def test_svg_example1(self): + path = paths[1] + self.assertEqual(Path(path).d(), path) + + def test_svg_example2(self): + path = paths[2] + self.assertEqual(Path(path).d(), path) + + def test_svg_example3(self): + path = paths[3] + self.assertEqual(Path(path).d(), path) + + def test_svg_example4(self): + path = paths[4] + self.assertEqual(Path(path).d(), path) + + def test_svg_example5(self): + path = paths[5] + self.assertEqual(Path(path).d(), path) + + def test_svg_example6(self): + path = paths[6] + self.assertEqual(Path(path).d(), path) + + def test_svg_example7(self): + path = paths[7] + self.assertEqual(Path(path).d(), path) + + def test_svg_example8(self): + path = paths[8] + self.assertEqual(Path(path).d(), path) + + def test_svg_example9(self): + path = paths[9] + self.assertEqual(Path(path).d(), path) + + def test_svg_example10(self): + path = paths[10] + self.assertEqual(Path(path).d(), path) + + def test_svg_example11(self): + path = paths[11] + self.assertEqual(Path(path).d(), path) + + def test_svg_example12(self): + path = paths[12] + self.assertEqual(Path(path).d(), path) + + def test_svg_example13(self): + path = paths[13] + self.assertEqual(Path(path).d(), path) + + def test_svg_example14(self): + path = paths[14] + self.assertEqual(Path(path).d(), "M 600,350 L 650,325 A 27.9508,27.9508 -30 0,1 700,300 L 750,275") + # Too small arc forced increase rx,ry \ No newline at end of file diff --git a/test/test_parsing.py b/test/test_parsing.py new file mode 100644 index 00000000..4b2901ee --- /dev/null +++ b/test/test_parsing.py @@ -0,0 +1,183 @@ +from __future__ import division +import unittest + +from svg.elements import * + + +def parse_path(pathd): + return Path(pathd) + + +class TestParser(unittest.TestCase): + + def test_svg_examples(self): + """Examples from the SVG spec""" + path1 = Path('M 100 100 L 300 100 L 200 300 z') + self.assertEqual(path1, Path(Move(100 + 100j), + Line(100 + 100j, 300 + 100j), + Line(300 + 100j, 200 + 300j), + Close(200 + 300j, 100 + 100j))) # CHANGED. Closed object + self.assertTrue(path1.closed) + + # for Z command behavior when there is multiple subpaths + path1 = Path('M 0 0 L 50 20 M 100 100 L 300 100 L 200 300 z') + self.assertEqual(path1, Path( + Move(0j), + Line(0 + 0j, 50 + 20j), + Move(50 + 20j, 100+100j), # CHANGED. Move knows start position. + Line(100 + 100j, 300 + 100j), + Line(300 + 100j, 200 + 300j), + Close(200 + 300j, 100 + 100j))) # CHANGED. Closed object + + path1 = Path('M 100 100 L 200 200') + path2 = Path('M100 100L200 200') + self.assertEqual(path1, path2) + + path1 = Path('M 100 200 L 200 100 L -100 -200') + path2 = Path('M 100 200 L 200 100 -100 -200') + self.assertEqual(path1, path2) + + path1 = Path("""M100,200 C100,100 250,100 250,200 + S400,300 400,200""") + self.assertEqual(path1, + Path(Move(100 + 200j), + CubicBezier(100 + 200j, 100 + 100j, 250 + 100j, 250 + 200j), + CubicBezier(250 + 200j, 250 + 300j, 400 + 300j, 400 + 200j))) + + path1 = Path('M100,200 C100,100 400,100 400,200') + self.assertEqual(path1, + Path(Move(100 + 200j), + CubicBezier(100 + 200j, 100 + 100j, 400 + 100j, 400 + 200j))) + + path1 = Path('M100,500 C25,400 475,400 400,500') + self.assertEqual(path1, + Path(Move(100 + 500j), + CubicBezier(100 + 500j, 25 + 400j, 475 + 400j, 400 + 500j))) + + path1 = Path('M100,800 C175,700 325,700 400,800') + self.assertEqual(path1, + Path(Move(100+800j), + CubicBezier(100 + 800j, 175 + 700j, 325 + 700j, 400 + 800j))) + + path1 = Path('M600,200 C675,100 975,100 900,200') + self.assertEqual(path1, + Path(Move(600 + 200j), + CubicBezier(600 + 200j, 675 + 100j, 975 + 100j, 900 + 200j))) + + path1 = Path('M600,500 C600,350 900,650 900,500') + self.assertEqual(path1, + Path(Move(600 + 500j), + CubicBezier(600 + 500j, 600 + 350j, 900 + 650j, 900 + 500j))) + + path1 = Path("""M600,800 C625,700 725,700 750,800 + S875,900 900,800""") + self.assertEqual(path1, + Path(Move(600 + 800j), + CubicBezier(600 + 800j, 625 + 700j, 725 + 700j, 750 + 800j), + CubicBezier(750 + 800j, 775 + 900j, 875 + 900j, 900 + 800j))) + + path1 = Path('M200,300 Q400,50 600,300 T1000,300') + self.assertEqual(path1, + Path(Move(200 + 300j), + QuadraticBezier(200 + 300j, 400 + 50j, 600 + 300j), + QuadraticBezier(600 + 300j, 800 + 550j, 1000 + 300j))) + + path1 = Path('M300,200 h-150 a150,150 0 1,0 150,-150 z') + self.assertEqual(path1, + Path(Move(300 + 200j), + Line(300 + 200j, 150 + 200j), + Arc(150 + 200j, 150 + 150j, 0, 1, 0, 300 + 50j), + Close(300 + 50j, 300 + 200j))) # CHANGED. Closed object + + path1 = Path('M275,175 v-150 a150,150 0 0,0 -150,150 z') + self.assertEqual(path1, + Path(Move(275 + 175j), + Line(275 + 175j, 275 + 25j), + Arc(275 + 25j, 150 + 150j, 0, 0, 0, 125 + 175j), + Close(125 + 175j, 275 + 175j))) # CHANGED. Closed object + + path1 = Path('M275,175 v-150 a150,150 0 0,0 -150,150 L 275,175 z') + self.assertEqual(path1, + Path(Move(275 + 175j), + Line(275 + 175j, 275 + 25j), + Arc(275 + 25j, 150 + 150j, 0, 0, 0, 125 + 175j), + Line(125 + 175j, 275 + 175j), + Close(275 + 175j, 275 + 175j))) # CHANGED. Closed object + + path1 = Path("""M600,350 l 50,-25 + a25,25 -30 0,1 50,-25 l 50,-25 + a25,50 -30 0,1 50,-25 l 50,-25 + a25,75 -30 0,1 50,-25 l 50,-25 + a25,100 -30 0,1 50,-25 l 50,-25""") + self.assertEqual(path1, + Path(Move(600 + 350j), + Line(600 + 350j, 650 + 325j), + Arc(650 + 325j, 25 + 25j, -30, 0, 1, 700 + 300j), + Line(700 + 300j, 750 + 275j), + Arc(750 + 275j, 25 + 50j, -30, 0, 1, 800 + 250j), + Line(800 + 250j, 850 + 225j), + Arc(850 + 225j, 25 + 75j, -30, 0, 1, 900 + 200j), + Line(900 + 200j, 950 + 175j), + Arc(950 + 175j, 25 + 100j, -30, 0, 1, 1000 + 150j), + Line(1000 + 150j, 1050 + 125j))) + + def test_others(self): + # Other paths that need testing: + + # Relative moveto: + path1 = Path('M 0 0 L 50 20 m 50 80 L 300 100 L 200 300 z') + self.assertEqual(path1, Path( + Move(0j), + Line(0 + 0j, 50 + 20j), + Move(50 + 20j, 100 + 100j), # CHANGED. Path saves the start point if it knows it. + Line(100 + 100j, 300 + 100j), + Line(300 + 100j, 200 + 300j), + Close(200 + 300j, 100 + 100j))) # CHANGED. This is a Close object now. + + # Initial smooth and relative CubicBezier + path1 = Path("""M100,200 s 150,-100 150,0""") + self.assertEqual(path1, + Path(Move(100 + 200j), + CubicBezier(100 + 200j, 100 + 200j, 250 + 100j, 250 + 200j))) + + # Initial smooth and relative QuadraticBezier + path1 = Path("""M100,200 t 150,0""") + self.assertEqual(path1, + Path(Move(100 + 200j), + QuadraticBezier(100 + 200j, 100 + 200j, 250 + 200j))) + + # Relative QuadraticBezier + path1 = Path("""M100,200 q 0,0 150,0""") + self.assertEqual(path1, + Path(Move(100 + 200j), + QuadraticBezier(100 + 200j, 100 + 200j, 250 + 200j))) + + def test_negative(self): + """You don't need spaces before a minus-sign""" + path1 = Path('M100,200c10-5,20-10,30-20') + path2 = Path('M 100 200 c 10 -5 20 -10 30 -20') + self.assertEqual(path1, path2) + + def test_numbers(self): + """Exponents and other number format cases""" + # It can be e or E, the plus is optional, and a minimum of +/-3.4e38 must be supported. + path1 = Path('M-3.4e38 3.4E+38L-3.4E-38,3.4e-38') + path2 = Path(Move(-3.4e+38 + 3.4e+38j), Line(-3.4e+38 + 3.4e+38j, -3.4e-38 + 3.4e-38j)) + self.assertEqual(path1, path2) + + def test_errors(self): + self.assertRaises(ValueError, Path, 'M 100 100 L 200 200 Z 100 200') + + def test_non_path(self): + # It's possible in SVG to create paths that has zero length, + # we need to handle that. + + path = Path("M10.236,100.184") + self.assertEqual(path.d(), 'M 10.236,100.184') + + def test_issue_47(self): + arc_path_declared = Path(Move(0 + 25j), Arc(0 + 25j, 25 + 25j, 0.0, 0, 0, 0 - 25j)) + arc_path_parsed = Path('M 0 25 A25,25 0.0 0 0 0,-25') + arc_path_parsed_scaled = Path('M 0 25 A1,1 0.0 0 0 0,-25') + self.assertEqual(arc_path_declared, arc_path_parsed) + self.assertEqual(arc_path_parsed_scaled, arc_path_declared) \ No newline at end of file