From 57f52cf46ae5d7b9b3e5439377a0a6f856c4302c Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Fri, 13 Aug 2021 12:23:52 +0100 Subject: [PATCH 01/15] Improve __repr__ and Viewbox 1. Viewbox `init` now handles various forms of arguments so it can recreate from a repr: * "x y w h" * x=x etc. 2. Viewbox creates a correct better `repr`. 3. SVGImage adds missing attributes, puts the href (which is potentially huge) last and (I hope this is correct) puts the href attribute in quotations so that the end is easily parseable. --- svgelements/svgelements.py | 58 ++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/svgelements/svgelements.py b/svgelements/svgelements.py index 0152683f..fea7299a 100644 --- a/svgelements/svgelements.py +++ b/svgelements/svgelements.py @@ -43,7 +43,7 @@ and the Arc can do exact arc calculations if scipy is installed. """ -SVGELEMENTS_VERSION = "1.5.5" +SVGELEMENTS_VERSION = "1.5.6" MIN_DEPTH = 5 ERROR = 1e-12 @@ -3030,24 +3030,36 @@ def matrix_multiply(m, s): class Viewbox: - def __init__(self, viewbox, preserve_aspect_ratio=None): + def __init__(self, *args, x=None, y=None, width=None, height=None, preserve_aspect_ratio=None): """ Viewbox controls the scaling between the drawing size view that is observing that drawing. :param viewbox: either values or viewbox attribute or a Viewbox object :param preserve_aspect_ratio: preserveAspectRatio """ - self.x = None - self.y = None - self.width = None - self.height = None - self.preserve_aspect_ratio = preserve_aspect_ratio - if isinstance(viewbox, dict): - self.property_by_values(viewbox) - elif isinstance(viewbox, Viewbox): - self.property_by_object(viewbox) + self.x = x + self.y = y + self.width = width + self.height = height + if len(args) <= 2: + viewbox = args[0] + if isinstance(viewbox, dict): + self.property_by_values(viewbox) + elif isinstance(viewbox, Viewbox): + self.property_by_object(viewbox) + else: + self.set_viewbox(viewbox) + if len(args) == 2: + preserve_aspect_ratio == args[1] + elif len(args) == 4: + self.x = float(args[0]) + self.y = float(args[1]) + self.width = float(args[2]) + self.height = float(args[3]) + if preserve_aspect_ratio == SVG_VALUE_NONE: + self.preserve_aspect_ratio = None else: - self.set_viewbox(viewbox) + self.preserve_aspect_ratio = preserve_aspect_ratio def __eq__(self, other): if not isinstance(other, Viewbox): @@ -3071,7 +3083,19 @@ def __str__(self): ) def __repr__(self): - return "%s('%s')" % (self.__class__.__name__, str(self)) + values = [] + if self.x is not None: + values.append("x=%s" % Length.str(self.x)) + if self.y is not None: + values.append("y=%s" % Length.str(self.y)) + if self.width is not None: + values.append("width=%s" % Length.str(self.width)) + if self.height is not None: + values.append("height=%s" % Length.str(self.height)) + if self.preserve_aspect_ratio is not None: + values.append("%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserve_aspect_ratio)) + params = ", ".join(values) + return "Viewbox(%s)" % params def property_by_object(self, obj): self.x = obj.x @@ -7703,8 +7727,6 @@ def __init__(self, *args, **kwargs): def __repr__(self): values = [] - if self.url is not None: - values.append("%s=%s" % (SVG_HREF, self.url)) if self.x != 0: values.append("%s=%s" % (SVG_ATTR_X, Length.str(self.x))) if self.y != 0: @@ -7713,12 +7735,18 @@ def __repr__(self): values.append("%s=%s" % (SVG_ATTR_WIDTH, Length.str(self.width))) if self.height != "100%": values.append("%s=%s" % (SVG_ATTR_HEIGHT, Length.str(self.height))) + if self.image_width != 0: + values.append("image_width=%s" % Length.str(self.image_width)) + if self.image_height != 0: + values.append("image_height=%s" % Length.str(self.image_height)) if self.preserve_aspect_ratio is not None: values.append( "%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserve_aspect_ratio) ) if self.viewbox is not None: values.append("%s=%s" % (SVG_ATTR_VIEWBOX, repr(self.viewbox))) + if self.url is not None: + values.append("%s='%s'" % (SVG_HREF, self.url)) params = ", ".join(values) return "SVGImage(%s)" % params From 9b3241e74043e2cd9d9a6c65b320d37e05e0b4ca Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Fri, 13 Aug 2021 13:13:04 +0100 Subject: [PATCH 02/15] Correct attribute name in Viewbox test --- test/test_copy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_copy.py b/test/test_copy.py index bfb5364f..88210d09 100644 --- a/test/test_copy.py +++ b/test/test_copy.py @@ -31,7 +31,7 @@ def test_copy_objects(self): self.assertEqual(matrix, matrix_copy) # SVG OBJECTS - viewbox = Viewbox('0 0 103 109', preserve_aspect_ratio="xMaxyMin slice") + viewbox = Viewbox('0 0 103 109', preserveAspectRatio="xMaxyMin slice") viewbox_copy = copy(viewbox) # self.assertEqual(viewbox, viewbox_copy) @@ -143,4 +143,4 @@ def test_copy_objects(self): svg = SVG.parse(q) svg_copy = copy(svg) self.assertEqual(svg, svg_copy) - self.assertIsNotNone(svg_copy.values) \ No newline at end of file + self.assertIsNotNone(svg_copy.values) From 0e29472891b7b29abd9dd4f17b9cd420c05dc114 Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Fri, 13 Aug 2021 13:18:57 +0100 Subject: [PATCH 03/15] SVG attribute is preserveAspectRatio Since `**kwargs` are often converted using attribute name, we need to use `preserveAspectRatio` everywhere instead of `preserve_aspect_ratio`. We also need to convert "none" to `None`. --- svgelements/svgelements.py | 46 +++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/svgelements/svgelements.py b/svgelements/svgelements.py index fea7299a..1ba1d6e8 100644 --- a/svgelements/svgelements.py +++ b/svgelements/svgelements.py @@ -3030,12 +3030,12 @@ def matrix_multiply(m, s): class Viewbox: - def __init__(self, *args, x=None, y=None, width=None, height=None, preserve_aspect_ratio=None): + def __init__(self, *args, x=None, y=None, width=None, height=None, preserveAspectRatio=None): """ Viewbox controls the scaling between the drawing size view that is observing that drawing. :param viewbox: either values or viewbox attribute or a Viewbox object - :param preserve_aspect_ratio: preserveAspectRatio + :param preserveAspectRatio: preserveAspectRatio """ self.x = x self.y = y @@ -3050,16 +3050,16 @@ def __init__(self, *args, x=None, y=None, width=None, height=None, preserve_aspe else: self.set_viewbox(viewbox) if len(args) == 2: - preserve_aspect_ratio == args[1] + preserveAspectRatio = args[1] elif len(args) == 4: self.x = float(args[0]) self.y = float(args[1]) self.width = float(args[2]) self.height = float(args[3]) - if preserve_aspect_ratio == SVG_VALUE_NONE: - self.preserve_aspect_ratio = None + if preserveAspectRatio == SVG_VALUE_NONE: + self.preserveAspectRatio = None else: - self.preserve_aspect_ratio = preserve_aspect_ratio + self.preserveAspectRatio = preserveAspectRatio def __eq__(self, other): if not isinstance(other, Viewbox): @@ -3072,7 +3072,7 @@ def __eq__(self, other): return False if self.height != other.height: return False - return self.preserve_aspect_ratio == other.preserve_aspect_ratio + return self.preserveAspectRatio == other.preserveAspectRatio def __str__(self): return "%s %s %s %s" % ( @@ -3092,8 +3092,9 @@ def __repr__(self): values.append("width=%s" % Length.str(self.width)) if self.height is not None: values.append("height=%s" % Length.str(self.height)) - if self.preserve_aspect_ratio is not None: - values.append("%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserve_aspect_ratio)) + print("__repr__",self.preserveAspectRatio) + if self.preserveAspectRatio is not None: + values.append("%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserveAspectRatio)) params = ", ".join(values) return "Viewbox(%s)" % params @@ -3102,14 +3103,14 @@ def property_by_object(self, obj): self.y = obj.y self.width = obj.width self.height = obj.height - self.preserve_aspect_ratio = obj.preserve_aspect_ratio + self.preserveAspectRatio = obj.preserveAspectRatio def property_by_values(self, values): viewbox = values.get(SVG_ATTR_VIEWBOX) if viewbox is not None: self.set_viewbox(viewbox) if SVG_ATTR_PRESERVEASPECTRATIO in values: - self.preserve_aspect_ratio = values[SVG_ATTR_PRESERVEASPECTRATIO] + self.preserveAspectRatio = values[SVG_ATTR_PRESERVEASPECTRATIO] def set_viewbox(self, viewbox): if viewbox is not None: @@ -3132,7 +3133,7 @@ def transform(self, element): self.y, self.width, self.height, - self.preserve_aspect_ratio, + self.preserveAspectRatio, ) @staticmethod @@ -7416,7 +7417,7 @@ def property_by_values(self, values): class Pattern(SVGElement, list): def __init__(self, *args, **kwargs): self.viewbox = None - self.preserve_aspect_ratio = None + self.preserveAspectRatio = None self.x = None self.y = None self.width = None @@ -7439,7 +7440,7 @@ def viewbox_transform(self): def property_by_object(self, s): SVGElement.property_by_object(self, s) self.viewbox = s.viewbox - self.preserve_aspect_ratio = s.preserve_aspect_ratio + self.preserveAspectRatio = s.preserveAspectRatio self.x = s.x self.y = s.y @@ -7462,7 +7463,7 @@ def property_by_values(self, values): if viewbox is not None: self.viewbox = Viewbox(viewbox) if SVG_ATTR_PRESERVEASPECTRATIO in values: - self.preserve_aspect_ratio = values[SVG_ATTR_PRESERVEASPECTRATIO] + self.preserveAspectRatio = values[SVG_ATTR_PRESERVEASPECTRATIO] self.x = Length(values.get(SVG_ATTR_X, 0)).value() self.y = Length(values.get(SVG_ATTR_Y, 0)).value() self.width = Length(values.get(SVG_ATTR_WIDTH, "100%")).value() @@ -7696,7 +7697,7 @@ def __init__(self, *args, **kwargs): self.url = None self.data = None self.viewbox = None - self.preserve_aspect_ratio = None + self.preserveAspectRatio = None self.x = None self.y = None self.width = None @@ -7739,9 +7740,9 @@ def __repr__(self): values.append("image_width=%s" % Length.str(self.image_width)) if self.image_height != 0: values.append("image_height=%s" % Length.str(self.image_height)) - if self.preserve_aspect_ratio is not None: + if self.preserveAspectRatio is not None: values.append( - "%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserve_aspect_ratio) + "%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserveAspectRatio) ) if self.viewbox is not None: values.append("%s=%s" % (SVG_ATTR_VIEWBOX, repr(self.viewbox))) @@ -7757,7 +7758,7 @@ def property_by_object(self, s): self.url = s.url self.data = s.data self.viewbox = s.viewbox - self.preserve_aspect_ratio = s.preserve_aspect_ratio + self.preserveAspectRatio = s.preserveAspectRatio self.x = s.x self.y = s.y @@ -7780,7 +7781,10 @@ def property_by_values(self, values): if viewbox is not None: self.viewbox = Viewbox(viewbox) if SVG_ATTR_PRESERVEASPECTRATIO in values: - self.preserve_aspect_ratio = values[SVG_ATTR_PRESERVEASPECTRATIO] + if values[SVG_ATTR_PRESERVEASPECTRATIO] == SVG_VALUE_NONE: + self.preserveAspectRatio = None + else: + self.preserveAspectRatio = values[SVG_ATTR_PRESERVEASPECTRATIO] self.x = Length(values.get(SVG_ATTR_X, 0)).value() self.y = Length(values.get(SVG_ATTR_Y, 0)).value() self.width = Length(values.get(SVG_ATTR_WIDTH, "100%")).value() @@ -7877,7 +7881,7 @@ def set_values_by_image(self): self.image_height = self.image.height self.viewbox = Viewbox( "0 0 %d %d" % (self.image_width, self.image_height), - self.preserve_aspect_ratio, + self.preserveAspectRatio, ) self.render(width=self.image_width, height=self.image_height) self.transform = Matrix(self.viewbox_transform) * self.transform From f7ddac2d2bb36ab4c3df283edd3ebf3328f1b84d Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Fri, 13 Aug 2021 16:15:20 +0100 Subject: [PATCH 04/15] Fix Group test with rounding error --- test/test_group.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_group.py b/test/test_group.py index 10828312..d1f48459 100644 --- a/test/test_group.py +++ b/test/test_group.py @@ -48,8 +48,8 @@ def test_group_2rect(self): r1 = rects[1] self.assertEqual(r1.implicit_x, 0) self.assertEqual(r1.implicit_y, 0) - self.assertEqual(r1.implicit_width, 200) - self.assertEqual(r1.implicit_height, 200) + self.assertEqual(round(r1.implicit_width,12), 200) + self.assertEqual(round(r1.implicit_height,12), 200) print(r1.bbox()) def test_issue_107(self): @@ -68,4 +68,4 @@ def test_issue_107(self): m *= "translate(100,100)" # Test __imul__ n = m * 'scale(2)' # Test __mult__ self.assertEqual(n[0][0].transform, Matrix("matrix(2,0,0,2,200,200)")) - self.assertEqual(m[0][0].transform, Matrix("matrix(1,0,0,1,100,100)")) \ No newline at end of file + self.assertEqual(m[0][0].transform, Matrix("matrix(1,0,0,1,100,100)")) From 9bb81492d4b1b0accd04feb7b429f6236da63cad Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Fri, 13 Aug 2021 16:44:25 +0100 Subject: [PATCH 05/15] Fix bug if args is empty --- svgelements/svgelements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/svgelements/svgelements.py b/svgelements/svgelements.py index 1ba1d6e8..d9167a10 100644 --- a/svgelements/svgelements.py +++ b/svgelements/svgelements.py @@ -3041,7 +3041,7 @@ def __init__(self, *args, x=None, y=None, width=None, height=None, preserveAspec self.y = y self.width = width self.height = height - if len(args) <= 2: + if args and len(args) <= 2: viewbox = args[0] if isinstance(viewbox, dict): self.property_by_values(viewbox) From b5e2d7b3bc4e6ad42b54cff3beb85d20e73d9bd5 Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Fri, 13 Aug 2021 16:51:11 +0100 Subject: [PATCH 06/15] Add unit test for new ways of creating Viewbox --- test/test_viewbox.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/test_viewbox.py b/test/test_viewbox.py index a04037a5..b8608b5e 100644 --- a/test/test_viewbox.py +++ b/test/test_viewbox.py @@ -6,6 +6,15 @@ class TestElementViewbox(unittest.TestCase): + def test_viewbox_init(self): + # Test various ways of creating a viewbox are equal. + v1 = Viewbox('0 0 100 100') + v2 = Viewbox(x=0, y=0, width=100, height=100) + v3 = Viewbox(v1) + self.assertEqual(v1, v2) + self.assertEqual(v1, v3) + self.assertEqual(v2, v3) + def test_viewbox_incomplete_transform(self): q = io.StringIO(u''' ''') From 997e09cb0aa16be62ce8ce8c8444b58943f09c4b Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Wed, 18 Aug 2021 09:59:41 +0100 Subject: [PATCH 07/15] Remove debug print statement --- svgelements/svgelements.py | 1 - 1 file changed, 1 deletion(-) diff --git a/svgelements/svgelements.py b/svgelements/svgelements.py index d9167a10..95e9159d 100644 --- a/svgelements/svgelements.py +++ b/svgelements/svgelements.py @@ -3092,7 +3092,6 @@ def __repr__(self): values.append("width=%s" % Length.str(self.width)) if self.height is not None: values.append("height=%s" % Length.str(self.height)) - print("__repr__",self.preserveAspectRatio) if self.preserveAspectRatio is not None: values.append("%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserveAspectRatio)) params = ", ".join(values) From e1ef4f1872bbc48d46aef4880e81626eac8493cd Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Fri, 20 Aug 2021 14:05:32 +0100 Subject: [PATCH 08/15] Revert preserveAspectRatio to preserve_aspect_ratio --- svgelements/svgelements.py | 70 ++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/svgelements/svgelements.py b/svgelements/svgelements.py index 95e9159d..5f714b3c 100644 --- a/svgelements/svgelements.py +++ b/svgelements/svgelements.py @@ -543,14 +543,14 @@ class Length(object): """ SVGLength as used in SVG - Length are lazy solving values. Several conversion values are typically unknown by default and length simply + Length class is lazy when solving values. Several conversion values are unknown by default and length simply stores that ambiguity. So we can have a length of 50% and without calling .value(relative_length=3000) it will - simply store as 50%. Likewise you can have discrete values like 30cm or 20in which have knowable discrete values - but are not knowable in pixels unless a PPI value is supplied. We can say .value(relative_length=30cm, PPI=96) and - solve this for a value like 12%. We can also convert values between knowable lengths. So 30cm in 300mm regardless - whether we know how to convert this to pixels. 0% is 0 in any units or relative values. We can convert pixels to - pc and pt without issue. We can convert vh, vw, vmax, vmin values if we know viewbox values. We can convert em - values if we know the font_size. We can add values together if they are convertible units. Length("20in") + "3cm". + simply store as 50%. Likewise you can have absolute values like 30cm or 20in which are not knowable in pixels + unless a PPI value is supplied. We can say .value(relative_length=30cm, PPI=96) and solve this for a value like + 12%. We can also convert values between knowable lengths. So 30cm is 300mm regardless whether we know how to + convert this to pixels. 0% is 0 in any units or relative values. We can convert pixels to pc and pt without issue. + We can convert vh, vw, vmax, vmin values if we know viewbox values. We can convert em values if we know the font_size. + We can add values together if they are convertible units e.g. Length("20in") + Length("3cm"). If .value() cannot solve for the value with the given information then it will return a Length value. If it can be solved it will return a float. @@ -3030,12 +3030,12 @@ def matrix_multiply(m, s): class Viewbox: - def __init__(self, *args, x=None, y=None, width=None, height=None, preserveAspectRatio=None): + def __init__(self, *args, x=None, y=None, width=None, height=None, preserveAspectRatio=None, preserve_aspect_ratio=None): """ Viewbox controls the scaling between the drawing size view that is observing that drawing. :param viewbox: either values or viewbox attribute or a Viewbox object - :param preserveAspectRatio: preserveAspectRatio + :param preserveAspectRatio or preserve_aspect_ratio: preserveAspectRatio """ self.x = x self.y = y @@ -3056,10 +3056,12 @@ def __init__(self, *args, x=None, y=None, width=None, height=None, preserveAspec self.y = float(args[1]) self.width = float(args[2]) self.height = float(args[3]) - if preserveAspectRatio == SVG_VALUE_NONE: - self.preserveAspectRatio = None + if preserveAspectRatio != SVG_VALUE_NONE: + self.preserve_aspect_ratio = preserveAspectRatio + elif preserve_aspect_ratio != SVG_VALUE_NONE: + self.preserve_aspect_ratio = preserve_aspect_ratio else: - self.preserveAspectRatio = preserveAspectRatio + self.preserve_aspect_ratio = None def __eq__(self, other): if not isinstance(other, Viewbox): @@ -3072,7 +3074,7 @@ def __eq__(self, other): return False if self.height != other.height: return False - return self.preserveAspectRatio == other.preserveAspectRatio + return self.preserve_aspect_ratio == other.preserve_aspect_ratio def __str__(self): return "%s %s %s %s" % ( @@ -3092,8 +3094,8 @@ def __repr__(self): values.append("width=%s" % Length.str(self.width)) if self.height is not None: values.append("height=%s" % Length.str(self.height)) - if self.preserveAspectRatio is not None: - values.append("%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserveAspectRatio)) + if self.preserve_aspect_ratio is not None: + values.append("%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserve_aspect_ratio)) params = ", ".join(values) return "Viewbox(%s)" % params @@ -3102,14 +3104,22 @@ def property_by_object(self, obj): self.y = obj.y self.width = obj.width self.height = obj.height - self.preserveAspectRatio = obj.preserveAspectRatio + self.preserve_aspect_ratio = obj.preserve_aspect_ratio def property_by_values(self, values): viewbox = values.get(SVG_ATTR_VIEWBOX) if viewbox is not None: self.set_viewbox(viewbox) + if SVG_ATTR_X in values: + self.x = values[SVG_ATTR_X] + if SVG_ATTR_Y in values: + self.y = values[SVG_ATTR_Y] + if SVG_ATTR_WIDTH in values: + self.width = values[SVG_ATTR_WIDTH] + if SVG_ATTR_HEIGHT in values: + self.height = values[SVG_ATTR_HEIGHT] if SVG_ATTR_PRESERVEASPECTRATIO in values: - self.preserveAspectRatio = values[SVG_ATTR_PRESERVEASPECTRATIO] + self.preserve_aspect_ratio = values[SVG_ATTR_PRESERVEASPECTRATIO] def set_viewbox(self, viewbox): if viewbox is not None: @@ -3132,7 +3142,7 @@ def transform(self, element): self.y, self.width, self.height, - self.preserveAspectRatio, + self.preserve_aspect_ratio, ) @staticmethod @@ -6976,8 +6986,8 @@ def _init_points(self, points): def __repr__(self): values = [] if self.points is not None: - s = ", ".join(map(str, self.points)) - values.append("points=(%s)" % repr(s)) + s = " ".join(map(str, self.points)) + values.append("points='%s'" % s) self._repr_shape(values) params = ", ".join(values) return "%s(%s)" % (self.__class__.__name__, params) @@ -7416,7 +7426,7 @@ def property_by_values(self, values): class Pattern(SVGElement, list): def __init__(self, *args, **kwargs): self.viewbox = None - self.preserveAspectRatio = None + self.preserve_aspect_ratio = None self.x = None self.y = None self.width = None @@ -7439,7 +7449,7 @@ def viewbox_transform(self): def property_by_object(self, s): SVGElement.property_by_object(self, s) self.viewbox = s.viewbox - self.preserveAspectRatio = s.preserveAspectRatio + self.preserve_aspect_ratio = s.preserve_aspect_ratio self.x = s.x self.y = s.y @@ -7462,7 +7472,7 @@ def property_by_values(self, values): if viewbox is not None: self.viewbox = Viewbox(viewbox) if SVG_ATTR_PRESERVEASPECTRATIO in values: - self.preserveAspectRatio = values[SVG_ATTR_PRESERVEASPECTRATIO] + self.preserve_aspect_ratio = values[SVG_ATTR_PRESERVEASPECTRATIO] self.x = Length(values.get(SVG_ATTR_X, 0)).value() self.y = Length(values.get(SVG_ATTR_Y, 0)).value() self.width = Length(values.get(SVG_ATTR_WIDTH, "100%")).value() @@ -7696,7 +7706,7 @@ def __init__(self, *args, **kwargs): self.url = None self.data = None self.viewbox = None - self.preserveAspectRatio = None + self.preserve_aspect_ratio = None self.x = None self.y = None self.width = None @@ -7739,9 +7749,9 @@ def __repr__(self): values.append("image_width=%s" % Length.str(self.image_width)) if self.image_height != 0: values.append("image_height=%s" % Length.str(self.image_height)) - if self.preserveAspectRatio is not None: + if self.preserve_aspect_ratio is not None: values.append( - "%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserveAspectRatio) + "%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserve_aspect_ratio) ) if self.viewbox is not None: values.append("%s=%s" % (SVG_ATTR_VIEWBOX, repr(self.viewbox))) @@ -7757,7 +7767,7 @@ def property_by_object(self, s): self.url = s.url self.data = s.data self.viewbox = s.viewbox - self.preserveAspectRatio = s.preserveAspectRatio + self.preserve_aspect_ratio = s.preserve_aspect_ratio self.x = s.x self.y = s.y @@ -7781,9 +7791,9 @@ def property_by_values(self, values): self.viewbox = Viewbox(viewbox) if SVG_ATTR_PRESERVEASPECTRATIO in values: if values[SVG_ATTR_PRESERVEASPECTRATIO] == SVG_VALUE_NONE: - self.preserveAspectRatio = None + self.preserve_aspect_ratio = None else: - self.preserveAspectRatio = values[SVG_ATTR_PRESERVEASPECTRATIO] + self.preserve_aspect_ratio = values[SVG_ATTR_PRESERVEASPECTRATIO] self.x = Length(values.get(SVG_ATTR_X, 0)).value() self.y = Length(values.get(SVG_ATTR_Y, 0)).value() self.width = Length(values.get(SVG_ATTR_WIDTH, "100%")).value() @@ -7880,7 +7890,7 @@ def set_values_by_image(self): self.image_height = self.image.height self.viewbox = Viewbox( "0 0 %d %d" % (self.image_width, self.image_height), - self.preserveAspectRatio, + self.preserve_aspect_ratio, ) self.render(width=self.image_width, height=self.image_height) self.transform = Matrix(self.viewbox_transform) * self.transform From 6df48b6a8d38357809d73cf932183c94ae44cf2e Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Fri, 20 Aug 2021 14:09:10 +0100 Subject: [PATCH 09/15] Use assertAlmostEqual instead of explicit round. --- test/test_group.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_group.py b/test/test_group.py index d1f48459..50d89fab 100644 --- a/test/test_group.py +++ b/test/test_group.py @@ -48,8 +48,8 @@ def test_group_2rect(self): r1 = rects[1] self.assertEqual(r1.implicit_x, 0) self.assertEqual(r1.implicit_y, 0) - self.assertEqual(round(r1.implicit_width,12), 200) - self.assertEqual(round(r1.implicit_height,12), 200) + self.assertAlmostEqual(r1.implicit_width, 200) + self.assertAlmostEqual(r1.implicit_height, 200) print(r1.bbox()) def test_issue_107(self): From 9e595fab4b8c2bd31166803d9749c8f3f6f9bf9c Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Fri, 20 Aug 2021 14:21:11 +0100 Subject: [PATCH 10/15] Remove image_width/height from repr These are NOT part of the SVG 2.0 spec. --- svgelements/svgelements.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/svgelements/svgelements.py b/svgelements/svgelements.py index 5f714b3c..8918833d 100644 --- a/svgelements/svgelements.py +++ b/svgelements/svgelements.py @@ -7745,10 +7745,6 @@ def __repr__(self): values.append("%s=%s" % (SVG_ATTR_WIDTH, Length.str(self.width))) if self.height != "100%": values.append("%s=%s" % (SVG_ATTR_HEIGHT, Length.str(self.height))) - if self.image_width != 0: - values.append("image_width=%s" % Length.str(self.image_width)) - if self.image_height != 0: - values.append("image_height=%s" % Length.str(self.image_height)) if self.preserve_aspect_ratio is not None: values.append( "%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserve_aspect_ratio) From 40cfbf5b4bbe8a6e25d8ec089ba1a91faf8c543f Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Fri, 20 Aug 2021 14:38:26 +0100 Subject: [PATCH 11/15] Fix Shape tests for polygon/polyline points --- test/test_shape.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_shape.py b/test/test_shape.py index 419b34e2..071ac0b5 100644 --- a/test/test_shape.py +++ b/test/test_shape.py @@ -297,9 +297,9 @@ def test_shapes_repr(self): s = SimpleLine(fill='red') self.assertEqual(repr(s), "SimpleLine(x1=0.0, y1=0.0, x2=0.0, y2=0.0, fill='#ff0000')") s = Polygon(fill='red') - self.assertEqual(repr(s), "Polygon(points=(''), fill='#ff0000')") + self.assertEqual(repr(s), "Polygon(points='', fill='#ff0000')") s = Polyline(fill='red') - self.assertEqual(repr(s), "Polyline(points=(''), fill='#ff0000')") + self.assertEqual(repr(s), "Polyline(points='', fill='#ff0000')") s = Path(fill='red') self.assertEqual(repr(s), "Path(fill='#ff0000')") From 7d0defeb2e7a0863e6e68ca5444462dea0463071 Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Sat, 21 Aug 2021 20:06:11 +0100 Subject: [PATCH 12/15] Address review comments --- svgelements/svgelements.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/svgelements/svgelements.py b/svgelements/svgelements.py index 8918833d..704f2f07 100644 --- a/svgelements/svgelements.py +++ b/svgelements/svgelements.py @@ -3030,17 +3030,18 @@ def matrix_multiply(m, s): class Viewbox: - def __init__(self, *args, x=None, y=None, width=None, height=None, preserveAspectRatio=None, preserve_aspect_ratio=None): + def __init__(self, *args, **kwargs): """ Viewbox controls the scaling between the drawing size view that is observing that drawing. :param viewbox: either values or viewbox attribute or a Viewbox object :param preserveAspectRatio or preserve_aspect_ratio: preserveAspectRatio """ - self.x = x - self.y = y - self.width = width - self.height = height + self.x = None + self.y = None + self.width = None + self.height = None + self.preserve_aspect_ratio = None if args and len(args) <= 2: viewbox = args[0] if isinstance(viewbox, dict): @@ -3050,18 +3051,14 @@ def __init__(self, *args, x=None, y=None, width=None, height=None, preserveAspec else: self.set_viewbox(viewbox) if len(args) == 2: - preserveAspectRatio = args[1] + self.preserve_aspect_ratio = args[1] elif len(args) == 4: self.x = float(args[0]) self.y = float(args[1]) self.width = float(args[2]) self.height = float(args[3]) - if preserveAspectRatio != SVG_VALUE_NONE: - self.preserve_aspect_ratio = preserveAspectRatio - elif preserve_aspect_ratio != SVG_VALUE_NONE: - self.preserve_aspect_ratio = preserve_aspect_ratio else: - self.preserve_aspect_ratio = None + self.property_by_values(dict(kwargs)) def __eq__(self, other): if not isinstance(other, Viewbox): @@ -3095,7 +3092,7 @@ def __repr__(self): if self.height is not None: values.append("height=%s" % Length.str(self.height)) if self.preserve_aspect_ratio is not None: - values.append("%s=%s" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserve_aspect_ratio)) + values.append("%s='%s'" % (SVG_ATTR_PRESERVEASPECTRATIO, self.preserve_aspect_ratio)) params = ", ".join(values) return "Viewbox(%s)" % params @@ -3107,9 +3104,8 @@ def property_by_object(self, obj): self.preserve_aspect_ratio = obj.preserve_aspect_ratio def property_by_values(self, values): - viewbox = values.get(SVG_ATTR_VIEWBOX) - if viewbox is not None: - self.set_viewbox(viewbox) + if SVG_ATTR_VIEWBOX in values: + self.set_viewbox(values[SVG_ATTR_VIEWBOX]) if SVG_ATTR_X in values: self.x = values[SVG_ATTR_X] if SVG_ATTR_Y in values: @@ -3118,6 +3114,8 @@ def property_by_values(self, values): self.width = values[SVG_ATTR_WIDTH] if SVG_ATTR_HEIGHT in values: self.height = values[SVG_ATTR_HEIGHT] + if "preserve_aspect_ratio" in values: + self.preserve_aspect_ratio = values["preserve_aspect_ratio"] if SVG_ATTR_PRESERVEASPECTRATIO in values: self.preserve_aspect_ratio = values[SVG_ATTR_PRESERVEASPECTRATIO] From 1bd62fd137ae029a9a3d095d5bab8369bf442714 Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Sat, 21 Aug 2021 20:14:24 +0100 Subject: [PATCH 13/15] Improve Viewbox initialisation tests --- test/test_viewbox.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/test/test_viewbox.py b/test/test_viewbox.py index b8608b5e..fe1d2db3 100644 --- a/test/test_viewbox.py +++ b/test/test_viewbox.py @@ -6,14 +6,23 @@ class TestElementViewbox(unittest.TestCase): - def test_viewbox_init(self): + def test_viewbox_creation(self): # Test various ways of creating a viewbox are equal. - v1 = Viewbox('0 0 100 100') - v2 = Viewbox(x=0, y=0, width=100, height=100) - v3 = Viewbox(v1) + v1 = Viewbox('0 0 100 100', 'xMid') + v2 = Viewbox(viewBox="0 0 100 100", preserve_aspect_ratio="xMid") + v3 = Viewbox(x=0, y=0, width=100, height=100, preserveAspectRatio="xMid") + v4 = Viewbox(v1) + v5 = Viewbox({"x":0, "y":0, "width":100, "height":100, "preserveAspectRatio":"xMid"}) self.assertEqual(v1, v2) self.assertEqual(v1, v3) + self.assertEqual(v1, v4) + self.assertEqual(v1, v5) self.assertEqual(v2, v3) + self.assertEqual(v2, v4) + self.assertEqual(v2, v5) + self.assertEqual(v3, v4) + self.assertEqual(v3, v5) + self.assertEqual(v4, v5) def test_viewbox_incomplete_transform(self): q = io.StringIO(u''' From 13a94383b7af063de8744eb9b9cbf6f908339be0 Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Sat, 21 Aug 2021 20:15:35 +0100 Subject: [PATCH 14/15] Fig bug that improved test coverage identified. --- svgelements/svgelements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/svgelements/svgelements.py b/svgelements/svgelements.py index 704f2f07..83d34870 100644 --- a/svgelements/svgelements.py +++ b/svgelements/svgelements.py @@ -3057,7 +3057,7 @@ def __init__(self, *args, **kwargs): self.y = float(args[1]) self.width = float(args[2]) self.height = float(args[3]) - else: + if kwargs: self.property_by_values(dict(kwargs)) def __eq__(self, other): From 0ec0d65210ab8085b3f3dae87e3c70614d8a192c Mon Sep 17 00:00:00 2001 From: Sophist <3001893+Sophist-UK@users.noreply.github.com> Date: Sat, 21 Aug 2021 20:17:13 +0100 Subject: [PATCH 15/15] Improve repr tests to include preserveAspectRatio --- test/test_repr.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/test_repr.py b/test/test_repr.py index cc248f2f..6e520a02 100644 --- a/test/test_repr.py +++ b/test/test_repr.py @@ -32,8 +32,7 @@ def test_repr_matrix(self): self.assertFalse(obj != eval(repr(obj))) def test_repr_viewbox(self): - obj = Viewbox("0 0 100 60") - print(repr(obj)) + obj = Viewbox("0 0 100 60", "xMid") self.assertTrue(obj == eval(repr(obj))) self.assertFalse(obj != eval(repr(obj))) @@ -146,4 +145,3 @@ def test_repr_title(self): obj = Title(title="SVG Description") self.assertTrue(obj == eval(repr(obj))) self.assertFalse(obj != eval(repr(obj))) -