svg-ultralight 0.33.0__tar.gz → 0.34.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of svg-ultralight might be problematic. Click here for more details.

Files changed (48) hide show
  1. {svg_ultralight-0.33.0/src/svg_ultralight.egg-info → svg_ultralight-0.34.0}/PKG-INFO +1 -1
  2. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/pyproject.toml +2 -2
  3. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/__init__.py +8 -0
  4. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/bounding_boxes/bound_helpers.py +74 -0
  5. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/layout.py +4 -1
  6. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0/src/svg_ultralight.egg-info}/PKG-INFO +1 -1
  7. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/test_bounding.py +68 -1
  8. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/test_layout.py +21 -0
  9. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/.gitignore +0 -0
  10. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/.pre-commit-config.yaml +0 -0
  11. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/README.md +0 -0
  12. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/setup.cfg +0 -0
  13. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/animate.py +0 -0
  14. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/bounding_boxes/__init__.py +0 -0
  15. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/bounding_boxes/supports_bounds.py +0 -0
  16. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/bounding_boxes/type_bound_collection.py +0 -0
  17. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/bounding_boxes/type_bound_element.py +0 -0
  18. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/bounding_boxes/type_bounding_box.py +0 -0
  19. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/bounding_boxes/type_padded_text.py +0 -0
  20. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/constructors/__init__.py +0 -0
  21. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/constructors/new_element.py +0 -0
  22. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/inkscape.py +0 -0
  23. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/main.py +0 -0
  24. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/metadata.py +0 -0
  25. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/nsmap.py +0 -0
  26. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/py.typed +0 -0
  27. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/query.py +0 -0
  28. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/root_elements.py +0 -0
  29. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/string_conversion.py +0 -0
  30. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/strings/__init__.py +0 -0
  31. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/strings/svg_strings.py +0 -0
  32. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/transformations.py +0 -0
  33. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight/unit_conversion.py +0 -0
  34. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight.egg-info/SOURCES.txt +0 -0
  35. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight.egg-info/dependency_links.txt +0 -0
  36. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight.egg-info/requires.txt +0 -0
  37. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/src/svg_ultralight.egg-info/top_level.txt +0 -0
  38. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/__init__.py +0 -0
  39. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/conftest.py +0 -0
  40. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/test_inkscape.py +0 -0
  41. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/test_matrices.py +0 -0
  42. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/test_metadata.py +0 -0
  43. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/test_new_element.py +0 -0
  44. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/test_queries.py +0 -0
  45. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/test_root_elements.py +0 -0
  46. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/test_string_conversion.py +0 -0
  47. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tests/test_svg_ultralight.py +0 -0
  48. {svg_ultralight-0.33.0 → svg_ultralight-0.34.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: svg-ultralight
3
- Version: 0.33.0
3
+ Version: 0.34.0
4
4
  Summary: a sensible way to create svg files with Python
5
5
  Author-email: Shay Hill <shay_public@hotmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "svg-ultralight"
3
- version = "0.33.0"
3
+ version = "0.34.0"
4
4
  description = "a sensible way to create svg files with Python"
5
5
  authors = [{ name = "Shay Hill", email = "shay_public@hotmail.com" }]
6
6
  license = { text = "MIT" }
@@ -37,7 +37,7 @@ legacy_tox_ini = """
37
37
 
38
38
  [tool.commitizen]
39
39
  name = "cz_conventional_commits"
40
- version = "0.33.0"
40
+ version = "0.34.0"
41
41
  tag_format = "$version"
42
42
  version_files = ["pyproject.toml:^version"]
43
43
  annotated_tag = true
@@ -8,6 +8,10 @@ from svg_ultralight.bounding_boxes.bound_helpers import (
8
8
  new_bbox_union,
9
9
  new_bound_union,
10
10
  new_element_union,
11
+ cut_bbox,
12
+ pad_bbox,
13
+ bbox_dict,
14
+ new_bbox_rect,
11
15
  )
12
16
  from svg_ultralight.bounding_boxes.supports_bounds import SupportsBounds
13
17
  from svg_ultralight.bounding_boxes.type_bound_collection import BoundCollection
@@ -57,7 +61,9 @@ __all__ = [
57
61
  "NSMAP",
58
62
  "PaddedText",
59
63
  "SupportsBounds",
64
+ "bbox_dict",
60
65
  "clear_svg_ultralight_cache",
66
+ "cut_bbox",
61
67
  "deepcopy_element",
62
68
  "format_attr_dict",
63
69
  "format_number",
@@ -68,6 +74,7 @@ __all__ = [
68
74
  "mat_apply",
69
75
  "mat_dot",
70
76
  "mat_invert",
77
+ "new_bbox_rect",
71
78
  "new_bbox_union",
72
79
  "new_bound_union",
73
80
  "new_element",
@@ -77,6 +84,7 @@ __all__ = [
77
84
  "new_sub_element",
78
85
  "new_svg_root",
79
86
  "new_svg_root_around_bounds",
87
+ "pad_bbox",
80
88
  "pad_text",
81
89
  "transform_element",
82
90
  "update_element",
@@ -95,3 +95,77 @@ def new_bound_union(*blems: SupportsBounds | EtreeElement) -> BoundElement:
95
95
  group = new_element_union(*blems)
96
96
  bbox = new_bbox_union(*blems)
97
97
  return BoundElement(group, bbox)
98
+
99
+
100
+ def _expand_pad(pad: float | tuple[float, ...]) -> tuple[float, float, float, float]:
101
+ """Expand a float pad argument into a 4-tuple."""
102
+ if isinstance(pad, (int, float)):
103
+ return pad, pad, pad, pad
104
+ if len(pad) == 1:
105
+ return pad[0], pad[0], pad[0], pad[0]
106
+ if len(pad) == 2:
107
+ return pad[0], pad[1], pad[0], pad[1]
108
+ if len(pad) == 3:
109
+ return pad[0], pad[1], pad[2], pad[1]
110
+ return pad[0], pad[1], pad[2], pad[3]
111
+
112
+
113
+ def cut_bbox(
114
+ bbox: SupportsBounds,
115
+ *,
116
+ x: float | None = None,
117
+ y: float | None = None,
118
+ x2: float | None = None,
119
+ y2: float | None = None,
120
+ ) -> BoundingBox:
121
+ """Return a new bounding box with updated limits.
122
+
123
+ :param bbox: the original bounding box or bounded element.
124
+ :param x: the new x-coordinate.
125
+ :param y: the new y-coordinate.
126
+ :param x2: the new x2-coordinate.
127
+ :param y2: the new y2-coordinate.
128
+ :return: a new bounding box with the updated limits.
129
+ """
130
+ x = bbox.x if x is None else x
131
+ y = bbox.y if y is None else y
132
+ x2 = bbox.x2 if x2 is None else x2
133
+ y2 = bbox.y2 if y2 is None else y2
134
+ width = x2 - x
135
+ height = y2 - y
136
+ return BoundingBox(x, y, width, height)
137
+
138
+
139
+ def pad_bbox(bbox: SupportsBounds, pad: float | tuple[float, ...]) -> BoundingBox:
140
+ """Return a new bounding box with padding.
141
+
142
+ :param bbox: the original bounding box or bounded element.
143
+ :param pad: the padding to apply.
144
+ If a single number, the same padding will be applied to all sides.
145
+ If a tuple, will be applied per css rules.
146
+ len = 1 : 0, 0, 0, 0
147
+ len = 2 : 0, 1, 0, 1
148
+ len = 3 : 0, 1, 2, 1
149
+ len = 4 : 0, 1, 2, 3
150
+ :return: a new bounding box with padding applied.
151
+ """
152
+ t, r, b, l = _expand_pad(pad)
153
+ return cut_bbox(bbox, x=bbox.x - l, y=bbox.y - t, x2=bbox.x2 + r, y2=bbox.y2 + b)
154
+
155
+
156
+ def bbox_dict(bbox: SupportsBounds) -> dict[str, float]:
157
+ """Return a dictionary representation of a bounding box.
158
+
159
+ :param bbox: the bounding box or bound element from which to extract dimensions.
160
+ :return: a dictionary with keys x, y, width, and height.
161
+ """
162
+ return {"x": bbox.x, "y": bbox.y, "width": bbox.width, "height": bbox.height}
163
+
164
+
165
+ def new_bbox_rect(bbox: BoundingBox, **kwargs: float | str) -> EtreeElement:
166
+ """Return a new rect element with the same dimensions as the bounding box.
167
+
168
+ :param bbox: the bounding box or bound element from which to extract dimensions.
169
+ :param kwargs: additional attributes for the rect element.
170
+ """
171
+ return new_element("rect", **bbox_dict(bbox), **kwargs)
@@ -43,7 +43,10 @@ def expand_pad_arg(pad: PadArg) -> tuple[float, float, float, float]:
43
43
  return expand_pad_arg([pad])
44
44
  as_ms = [m if isinstance(m, Measurement) else Measurement(m) for m in pad]
45
45
  as_units = [m.value for m in as_ms]
46
- as_units = [as_units[i % len(as_units)] for i in range(4)]
46
+ if len(as_units) == 3:
47
+ as_units = [*as_units, as_units[1]]
48
+ else:
49
+ as_units = [as_units[i % len(as_units)] for i in range(4)]
47
50
  return as_units[0], as_units[1], as_units[2], as_units[3]
48
51
 
49
52
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: svg-ultralight
3
- Version: 0.33.0
3
+ Version: 0.34.0
4
4
  Summary: a sensible way to create svg files with Python
5
5
  Author-email: Shay Hill <shay_public@hotmail.com>
6
6
  License: MIT
@@ -10,9 +10,16 @@ from svg_ultralight.bounding_boxes.type_bound_element import BoundElement
10
10
  from svg_ultralight.bounding_boxes.type_padded_text import PaddedText
11
11
  from svg_ultralight.bounding_boxes.type_bounding_box import BoundingBox
12
12
  from svg_ultralight.bounding_boxes.type_bound_collection import BoundCollection
13
+ from svg_ultralight.bounding_boxes.bound_helpers import (
14
+ pad_bbox,
15
+ cut_bbox,
16
+ bbox_dict,
17
+ new_bbox_rect,
18
+ )
13
19
  import copy
14
20
  from svg_ultralight.constructors import new_element
15
21
 
22
+
16
23
  class TestBoundElement:
17
24
  @pytest.fixture
18
25
  def bound_element(self) -> BoundElement:
@@ -84,7 +91,6 @@ class TestBoundElement:
84
91
  assert bound_element.y2 == 250.0
85
92
 
86
93
 
87
-
88
94
  class TestPaddedText:
89
95
  @pytest.fixture
90
96
  def bound_element(self) -> PaddedText:
@@ -155,6 +161,7 @@ class TestPaddedText:
155
161
  assert math.isclose(bound_element.height, 252.76)
156
162
  assert bound_element.y2 == 203.0
157
163
 
164
+
158
165
  class TestBoundCollection:
159
166
 
160
167
  @pytest.fixture
@@ -178,3 +185,63 @@ class TestBoundCollection:
178
185
  blem_trans = blem.elem.attrib["transform"]
179
186
  elem_trans = elem.attrib["transform"]
180
187
  assert blem_trans == elem_trans
188
+
189
+
190
+ class TestBoundHelpers:
191
+ def test_pad_bbox(self):
192
+ bbox = BoundingBox(0, 0, 4, 4)
193
+ padded = pad_bbox(bbox, 1)
194
+ assert padded.x == -1
195
+ assert padded.y == -1
196
+ assert padded.width == 6
197
+ assert padded.height == 6
198
+
199
+ def test_pad_bbox_t1(self):
200
+ bbox = BoundingBox(0, 0, 4, 4)
201
+ padded = pad_bbox(bbox, (1,))
202
+ assert padded.x == -1
203
+ assert padded.y == -1
204
+ assert padded.width == 6
205
+ assert padded.height == 6
206
+
207
+ def test_pad_bbox_t2(self):
208
+ bbox = BoundingBox(0, 0, 4, 4)
209
+ padded = pad_bbox(bbox, (1, 2))
210
+ assert padded.x == -2
211
+ assert padded.y == -1
212
+ assert padded.width == 8
213
+ assert padded.height == 6
214
+
215
+ def test_pad_bbox_t3(self):
216
+ bbox = BoundingBox(0, 0, 4, 4)
217
+ padded = pad_bbox(bbox, (1, 2, 3))
218
+ assert padded.x == -2
219
+ assert padded.y == -1
220
+ assert padded.width == 8
221
+ assert padded.height == 8
222
+
223
+ def test_pad_bbox_t4(self):
224
+ bbox = BoundingBox(0, 0, 4, 4)
225
+ padded = pad_bbox(bbox, (1, 2, 3, 4))
226
+ assert padded.x == -4
227
+ assert padded.y == -1
228
+ assert padded.width == 10
229
+ assert padded.height == 8
230
+
231
+ def test_cut_bbox(self):
232
+ bbox = BoundingBox(0, 0, 4, 4)
233
+ cut = cut_bbox(bbox, x=1)
234
+ assert cut.x == 1
235
+ assert cut.y == 0
236
+ assert cut.width == 3
237
+ assert cut.height == 4
238
+
239
+ def test_bbox_dict(self):
240
+ bbox = BoundingBox(0, 1, 2, 3)
241
+ assert bbox_dict(bbox) == {"x": 0, "y": 1, "width": 2, "height": 3}
242
+
243
+ def test_new_bbox_rect(self):
244
+ bbox = BoundingBox(0, 1, 2, 3)
245
+ elem = new_bbox_rect(bbox)
246
+ assert elem.attrib == {"x": "0", "y": "1", "width": "2", "height": "3"}
247
+
@@ -123,6 +123,27 @@ class TestMeasurement:
123
123
  assert (Measurement((1, unit)) / 4).value == Measurement((1 / 4, unit)).value
124
124
 
125
125
 
126
+ class TestExpandPadArg:
127
+ def test_expand_val(self):
128
+ """Test that a single value is expanded to a 4-tuple."""
129
+ assert layout.expand_pad_arg(1) == (1, 1, 1, 1)
130
+
131
+ def test_expand_1tuple(self):
132
+ """Test that a single value is expanded to a 4-tuple."""
133
+ assert layout.expand_pad_arg(1) == (1, 1, 1, 1)
134
+
135
+ def test_expand_2tuple(self):
136
+ """Test that a single value is expanded to a 4-tuple."""
137
+ assert layout.expand_pad_arg((1, 2)) == (1, 2, 1, 2)
138
+
139
+ def test_expand_3tuple(self):
140
+ """Test that a single value is expanded to a 4-tuple per css rules."""
141
+ assert layout.expand_pad_arg((1, 2, 3)) == (1, 2, 3, 2)
142
+
143
+ def test_expand_4tuple(self):
144
+ """Test that a single value is expanded to a 4-tuple per css rules."""
145
+ assert layout.expand_pad_arg((1, 2, 3, 4)) == (1, 2, 3, 4)
146
+
126
147
  class TestLayout:
127
148
  def test_standard(self):
128
149
  """No print dimensions give expanded pad argument
File without changes