From 14405dcfd91b068de7edc01e5da68435372d5004 Mon Sep 17 00:00:00 2001 From: Jonathan Neuhauser Date: Fri, 16 May 2025 22:11:53 +0200 Subject: [PATCH 1/3] Fix .path for groups with multiple path element children ... if they start with a relative path command Fixes #604 --- inkex/elements/_groups.py | 7 +++++-- tests/test_inkex_bounding_box.py | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/inkex/elements/_groups.py b/inkex/elements/_groups.py index 62ea2c695..0688dc749 100644 --- a/inkex/elements/_groups.py +++ b/inkex/elements/_groups.py @@ -26,7 +26,7 @@ Interface for all group based elements such as Groups, Use, Markers etc. from lxml import etree # pylint: disable=unused-import from ..paths import Path -from ..transforms import BoundingBox, Transform +from ..transforms import BoundingBox, Transform, Vector2d from ._utils import addNS from ._base import ShapeElement, ViewboxMixin @@ -45,7 +45,10 @@ class GroupBase(ShapeElement): ret = Path() for child in self: if isinstance(child, ShapeElement): - ret += child.path.transform(child.transform) + child_path = child.path.transform(child.transform) + if child_path and child_path[0].is_relative: + child_path[0] = child_path[0].to_absolute(Vector2d(0, 0)) + ret += child_path return ret def bounding_box(self, transform=None): diff --git a/tests/test_inkex_bounding_box.py b/tests/test_inkex_bounding_box.py index d854eb9cc..1eda8696e 100644 --- a/tests/test_inkex_bounding_box.py +++ b/tests/test_inkex_bounding_box.py @@ -681,6 +681,15 @@ class BoundingBoxTest(TestCase): pe.path = path self.assert_bounding_box_is_equal(pe, (10, 50), (10, 17.5)) + def test_path_combined_relative(self): + g = Group() + g.append(PathElement.new(path="m -32,-16 h 16")) + g.append(PathElement.new(path="m -32,0 h 16")) + bbx = g.bounding_box() + assert bbx.width == bbx.height == 16 + pbx = g.path.bounding_box() + assert pbx.width == pbx.height == 16 + @requires_inkscape def test_path_combined_1(self): path = Path("M 0 0 C 11 14 33 3 85 98 H 84 V 91 L 13 78 C 26 83 65 24 94 77") -- GitLab From 3cc3622bc40d8ca824c654c51ac5bcd98618f235 Mon Sep 17 00:00:00 2001 From: Jonathan Neuhauser Date: Fri, 16 May 2025 22:13:39 +0200 Subject: [PATCH 2/3] Support path += "path commands as string" --- inkex/paths/path.py | 13 +++++++++++-- tests/test_inkex_paths.py | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/inkex/paths/path.py b/inkex/paths/path.py index 15f3f8945..a9fcf227d 100644 --- a/inkex/paths/path.py +++ b/inkex/paths/path.py @@ -603,10 +603,19 @@ class Path(list): def __str__(self): return " ".join([str(seg) for seg in self]) - def __add__(self, other): - acopy = copy.deepcopy(self) + @staticmethod + def __add_helper__(other): + """Prepare a path for adding (either add or iadd)""" if isinstance(other, str): other = Path(other) + return other + + def __iadd__(self, value): + return super().__iadd__(self.__add_helper__(value)) + + def __add__(self, other): + acopy = copy.deepcopy(self) + other = self.__add_helper__(other) if isinstance(other, list): acopy.extend(other) return acopy diff --git a/tests/test_inkex_paths.py b/tests/test_inkex_paths.py index cbd8a4b75..76bbe1e4e 100644 --- a/tests/test_inkex_paths.py +++ b/tests/test_inkex_paths.py @@ -485,6 +485,11 @@ class PathTest(TestCase): self.assertEqual(type(ret), Path) self._assertPath(ret, "M 20 20 C 40 40 9 10 10 10") + ret = Path("M 20 20") + ret += "C 40 40 9 10 10 10" + self.assertEqual(type(ret), Path) + self._assertPath(ret, "M 20 20 C 40 40 9 10 10 10") + def test_subtracting_from_path(self): """Paths can be translated using addition""" ret = Path("M 20,20 L 90,90 l 10,10 Z").translate(-10, -10) -- GitLab From 6d18ba7ec92b8bd277b66605cfb6d9dfd59760f7 Mon Sep 17 00:00:00 2001 From: Jonathan Neuhauser Date: Fri, 16 May 2025 22:20:04 +0200 Subject: [PATCH 3/3] QRCode: Use symbol.bounding_box instead of symbol.path.bounding_box --- render_barcode_qrcode.py | 2 +- ...-symbolid__AirTransportation_Inv__--modulesize__10.out | 2 +- tests/test_inkex_bounding_box.py | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/render_barcode_qrcode.py b/render_barcode_qrcode.py index ac139fe61..b61eb1443 100755 --- a/render_barcode_qrcode.py +++ b/render_barcode_qrcode.py @@ -1236,7 +1236,7 @@ class QrCode(inkex.GenerateExtension): raise inkex.AbortExtension( _("Can't find symbol {}").format(self.options.symbolid) ) - bbox = symbol.path.bounding_box() + bbox = symbol.bounding_box() transform = inkex.Transform( scale=( float(self.boxsize) / bbox.width, diff --git a/tests/data/refs/render_barcode_qrcode__--text__ThingOne__--drawtype__symbol__--correctionlevel__2__--symbolid__AirTransportation_Inv__--modulesize__10.out b/tests/data/refs/render_barcode_qrcode__--text__ThingOne__--drawtype__symbol__--correctionlevel__2__--symbolid__AirTransportation_Inv__--modulesize__10.out index 1576a05b2..f2e8afd8a 100644 --- a/tests/data/refs/render_barcode_qrcode__--text__ThingOne__--drawtype__symbol__--correctionlevel__2__--symbolid__AirTransportation_Inv__--modulesize__10.out +++ b/tests/data/refs/render_barcode_qrcode__--text__ThingOne__--drawtype__symbol__--correctionlevel__2__--symbolid__AirTransportation_Inv__--modulesize__10.out @@ -1,6 +1,6 @@ - + Air Transportation diff --git a/tests/test_inkex_bounding_box.py b/tests/test_inkex_bounding_box.py index 1eda8696e..21bd678c7 100644 --- a/tests/test_inkex_bounding_box.py +++ b/tests/test_inkex_bounding_box.py @@ -22,6 +22,7 @@ from tempfile import TemporaryDirectory from inkex.command import is_inkscape_available from inkex.tester.decorators import requires_inkscape from inkex.tester.svg import svg_file +from inkex.paths import Line, Move try: from typing import Optional, Tuple @@ -682,9 +683,16 @@ class BoundingBoxTest(TestCase): self.assert_bounding_box_is_equal(pe, (10, 50), (10, 17.5)) def test_path_combined_relative(self): + """Test concatenation of two paths where the second path has a lower-case + first move command""" g = Group() g.append(PathElement.new(path="m -32,-16 h 16")) g.append(PathElement.new(path="m -32,0 h 16")) + + # Two relative paths can not be concatenated, but must be converted to absolute + # (at least the first command) before concatenation. + assert g.path[2] == Move(-32, 0) + bbx = g.bounding_box() assert bbx.width == bbx.height == 16 pbx = g.path.bounding_box() -- GitLab