svg-ultralight 0.27.0__py3-none-any.whl → 0.28.0__py3-none-any.whl
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.
- svg_ultralight/bounding_boxes/type_bounding_box.py +73 -38
- svg_ultralight/query.py +2 -1
- {svg_ultralight-0.27.0.dist-info → svg_ultralight-0.28.0.dist-info}/METADATA +1 -1
- {svg_ultralight-0.27.0.dist-info → svg_ultralight-0.28.0.dist-info}/RECORD +6 -6
- {svg_ultralight-0.27.0.dist-info → svg_ultralight-0.28.0.dist-info}/WHEEL +0 -0
- {svg_ultralight-0.27.0.dist-info → svg_ultralight-0.28.0.dist-info}/top_level.txt +0 -0
|
@@ -6,13 +6,51 @@
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
import dataclasses
|
|
10
10
|
|
|
11
11
|
from svg_ultralight.bounding_boxes.supports_bounds import SupportsBounds
|
|
12
12
|
from svg_ultralight.string_conversion import format_number
|
|
13
13
|
|
|
14
|
+
Matrix = tuple[float, float, float, float, float, float]
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
|
|
17
|
+
def mat_dot(mat1: Matrix, mat2: Matrix) -> Matrix:
|
|
18
|
+
"""Matrix multiplication for svg-style matrices.
|
|
19
|
+
|
|
20
|
+
:param mat1: transformation matrix (sx, 0, 0, sy, tx, ty)
|
|
21
|
+
:param mat2: transformation matrix (sx, 0, 0, sy, tx, ty)
|
|
22
|
+
|
|
23
|
+
Svg uses an unusual matrix format. For 3x3 transformation matrix
|
|
24
|
+
|
|
25
|
+
[[00, 01, 02],
|
|
26
|
+
[10, 11, 12],
|
|
27
|
+
[20, 21, 22]]
|
|
28
|
+
|
|
29
|
+
The svg matrix is
|
|
30
|
+
(00, 10, 01, 11, 02, 12)
|
|
31
|
+
|
|
32
|
+
Values 10 and 01 are only used for skewing, which is not supported by a bounding
|
|
33
|
+
box, but they're here if this function is used in other ways.
|
|
34
|
+
"""
|
|
35
|
+
aa = sum(mat1[x] * mat2[y] for x, y in ((0, 0), (2, 1)))
|
|
36
|
+
bb = sum(mat1[x] * mat2[y] for x, y in ((1, 0), (3, 1)))
|
|
37
|
+
cc = sum(mat1[x] * mat2[y] for x, y in ((0, 2), (2, 3)))
|
|
38
|
+
dd = sum(mat1[x] * mat2[y] for x, y in ((1, 2), (3, 3)))
|
|
39
|
+
ee = sum(mat1[x] * mat2[y] for x, y in ((0, 4), (2, 5))) + mat1[4]
|
|
40
|
+
ff = sum(mat1[x] * mat2[y] for x, y in ((1, 4), (3, 5))) + mat1[5]
|
|
41
|
+
return (aa, bb, cc, dd, ee, ff)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def mat_apply(mat1: Matrix, mat2: tuple[float, float]) -> tuple[float, float]:
|
|
45
|
+
"""Apply an svg-style transformation matrix to a point.
|
|
46
|
+
|
|
47
|
+
:param mat1: transformation matrix (sx, 0, 0, sy, tx, ty)
|
|
48
|
+
:param mat2: point (x, y)
|
|
49
|
+
"""
|
|
50
|
+
return mat1[0] * mat2[0] + mat1[4], mat1[3] * mat2[1] + mat1[5]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclasses.dataclass
|
|
16
54
|
class BoundingBox(SupportsBounds):
|
|
17
55
|
"""Mutable bounding box object for svg_ultralight.
|
|
18
56
|
|
|
@@ -21,14 +59,12 @@ class BoundingBox(SupportsBounds):
|
|
|
21
59
|
:param width: width of the bounding box
|
|
22
60
|
:param height: height of the bounding box
|
|
23
61
|
|
|
24
|
-
The below optional
|
|
25
|
-
the entire state of a BoundingBox instance.
|
|
62
|
+
The below optional parameter, in addition to the required parameters, captures
|
|
63
|
+
the entire state of a BoundingBox instance. It could be used to make a copy or
|
|
26
64
|
to initialize a transformed box with the same transform_string as another box.
|
|
27
|
-
Under most circumstances,
|
|
65
|
+
Under most circumstances, it will not be used.
|
|
28
66
|
|
|
29
|
-
:param
|
|
30
|
-
:param translation_x: x translation of the bounding box
|
|
31
|
-
:param translation_y: y translation of the bounding box
|
|
67
|
+
:param _transform: transformation matrix
|
|
32
68
|
|
|
33
69
|
Functions that return a bounding box will return a BoundingBox instance. This
|
|
34
70
|
instance can be transformed (uniform scale and translate only). Transformations
|
|
@@ -37,8 +73,8 @@ class BoundingBox(SupportsBounds):
|
|
|
37
73
|
Define the bbox with x=, y=, width=, height=
|
|
38
74
|
|
|
39
75
|
Transform the BoundingBox by setting these variables. Each time you set x, cx,
|
|
40
|
-
x2, y, cy, y2, width, or height, private transformation
|
|
41
|
-
|
|
76
|
+
x2, y, cy, y2, width, or height, private transformation value _transform will be
|
|
77
|
+
updated.
|
|
42
78
|
|
|
43
79
|
The ultimate transformation can be accessed through ``.transformation_string``.
|
|
44
80
|
So the workflow will look like :
|
|
@@ -78,13 +114,19 @@ class BoundingBox(SupportsBounds):
|
|
|
78
114
|
_y: float
|
|
79
115
|
_width: float
|
|
80
116
|
_height: float
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
117
|
+
_transform: Matrix = (1, 0, 0, 1, 0, 0)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def transform(self) -> Matrix:
|
|
121
|
+
"""Get read only tranformation matrix.
|
|
122
|
+
|
|
123
|
+
:return: transformation matrix of the bounding box
|
|
124
|
+
"""
|
|
125
|
+
return self._transform
|
|
84
126
|
|
|
85
127
|
@property
|
|
86
128
|
def scale(self) -> float:
|
|
87
|
-
"""
|
|
129
|
+
"""Get scale of the bounding box.
|
|
88
130
|
|
|
89
131
|
:return: uniform scale of the bounding box
|
|
90
132
|
|
|
@@ -95,7 +137,7 @@ class BoundingBox(SupportsBounds):
|
|
|
95
137
|
width*scale, height => height*scale, scale => scale*scale. This matches how
|
|
96
138
|
scale works in almost every other context.
|
|
97
139
|
"""
|
|
98
|
-
return self.
|
|
140
|
+
return self._transform[0]
|
|
99
141
|
|
|
100
142
|
@scale.setter
|
|
101
143
|
def scale(self, value: float) -> None:
|
|
@@ -110,7 +152,7 @@ class BoundingBox(SupportsBounds):
|
|
|
110
152
|
`scale = 2` -> ignore whatever scale was previously defined and set scale to 2
|
|
111
153
|
`scale *= 2` -> make it twice as big as it was.
|
|
112
154
|
"""
|
|
113
|
-
self.
|
|
155
|
+
self._add_transform(value / self.scale, 0, 0)
|
|
114
156
|
|
|
115
157
|
@property
|
|
116
158
|
def x(self) -> float:
|
|
@@ -118,7 +160,7 @@ class BoundingBox(SupportsBounds):
|
|
|
118
160
|
|
|
119
161
|
:return: internal _x value transformed by scale and translation
|
|
120
162
|
"""
|
|
121
|
-
return (self.
|
|
163
|
+
return mat_apply(self._transform, (self._x, 0))[0]
|
|
122
164
|
|
|
123
165
|
@x.setter
|
|
124
166
|
def x(self, value: float) -> None:
|
|
@@ -142,7 +184,7 @@ class BoundingBox(SupportsBounds):
|
|
|
142
184
|
|
|
143
185
|
:param value: new center x value after transformation
|
|
144
186
|
"""
|
|
145
|
-
self.
|
|
187
|
+
self.x += value - self.cx
|
|
146
188
|
|
|
147
189
|
@property
|
|
148
190
|
def x2(self) -> float:
|
|
@@ -158,7 +200,7 @@ class BoundingBox(SupportsBounds):
|
|
|
158
200
|
|
|
159
201
|
:param value: new x2 value after transformation
|
|
160
202
|
"""
|
|
161
|
-
self.
|
|
203
|
+
self.x += value - self.x2
|
|
162
204
|
|
|
163
205
|
@property
|
|
164
206
|
def y(self) -> float:
|
|
@@ -166,7 +208,7 @@ class BoundingBox(SupportsBounds):
|
|
|
166
208
|
|
|
167
209
|
:return: internal _y value transformed by scale and translation
|
|
168
210
|
"""
|
|
169
|
-
return (self.
|
|
211
|
+
return mat_apply(self._transform, (0, self._y))[1]
|
|
170
212
|
|
|
171
213
|
@y.setter
|
|
172
214
|
def y(self, value: float) -> None:
|
|
@@ -190,7 +232,7 @@ class BoundingBox(SupportsBounds):
|
|
|
190
232
|
|
|
191
233
|
:param value: new center y value after transformation
|
|
192
234
|
"""
|
|
193
|
-
self.
|
|
235
|
+
self.y += value - self.cy
|
|
194
236
|
|
|
195
237
|
@property
|
|
196
238
|
def y2(self) -> float:
|
|
@@ -206,7 +248,7 @@ class BoundingBox(SupportsBounds):
|
|
|
206
248
|
|
|
207
249
|
:param value: new y2 value after transformation
|
|
208
250
|
"""
|
|
209
|
-
self.y
|
|
251
|
+
self.y += value - self.y2
|
|
210
252
|
|
|
211
253
|
@property
|
|
212
254
|
def width(self) -> float:
|
|
@@ -214,7 +256,7 @@ class BoundingBox(SupportsBounds):
|
|
|
214
256
|
|
|
215
257
|
:return: internal _width value transformed by scale
|
|
216
258
|
"""
|
|
217
|
-
return self._width * self.
|
|
259
|
+
return self._width * self.scale
|
|
218
260
|
|
|
219
261
|
@width.setter
|
|
220
262
|
def width(self, value: float) -> None:
|
|
@@ -227,7 +269,7 @@ class BoundingBox(SupportsBounds):
|
|
|
227
269
|
"""
|
|
228
270
|
current_x = self.x
|
|
229
271
|
current_y = self.y
|
|
230
|
-
self.
|
|
272
|
+
self.scale *= value / self.width
|
|
231
273
|
self.x = current_x
|
|
232
274
|
self.y = current_y
|
|
233
275
|
|
|
@@ -237,7 +279,7 @@ class BoundingBox(SupportsBounds):
|
|
|
237
279
|
|
|
238
280
|
:return: internal _height value transformed by scale
|
|
239
281
|
"""
|
|
240
|
-
return self._height * self.
|
|
282
|
+
return self._height * self.scale
|
|
241
283
|
|
|
242
284
|
@height.setter
|
|
243
285
|
def height(self, value: float) -> None:
|
|
@@ -250,18 +292,15 @@ class BoundingBox(SupportsBounds):
|
|
|
250
292
|
"""
|
|
251
293
|
self.width = value * self.width / self.height
|
|
252
294
|
|
|
253
|
-
def _add_transform(self, scale: float,
|
|
295
|
+
def _add_transform(self, scale: float, dx: float, dy: float):
|
|
254
296
|
"""Transform the bounding box by updating the transformation attributes.
|
|
255
297
|
|
|
256
298
|
:param scale: scale factor
|
|
257
|
-
:param
|
|
258
|
-
:param
|
|
259
|
-
|
|
260
|
-
Transformation attributes are _translation_x, _translation_y, and _scale
|
|
299
|
+
:param dx: x translation
|
|
300
|
+
:param dy: y translation
|
|
261
301
|
"""
|
|
262
|
-
|
|
263
|
-
self.
|
|
264
|
-
self._scale *= scale
|
|
302
|
+
tmat = (scale, 0, 0, scale, dx, dy)
|
|
303
|
+
self._transform = mat_dot(tmat, self._transform)
|
|
265
304
|
|
|
266
305
|
@property
|
|
267
306
|
def transform_string(self) -> str:
|
|
@@ -272,11 +311,7 @@ class BoundingBox(SupportsBounds):
|
|
|
272
311
|
Use with
|
|
273
312
|
``update_element(elem, transform=bbox.transform_string)``
|
|
274
313
|
"""
|
|
275
|
-
|
|
276
|
-
format_number(x)
|
|
277
|
-
for x in (self._scale, self._translation_x, self._translation_y)
|
|
278
|
-
)
|
|
279
|
-
return f"scale({scale}) translate({trans_x} {trans_y})"
|
|
314
|
+
return f"matrix({' '.join(map(format_number, self._transform))})"
|
|
280
315
|
|
|
281
316
|
def merge(self, *others: BoundingBox) -> BoundingBox:
|
|
282
317
|
"""Create a bounding box around all other bounding boxes.
|
svg_ultralight/query.py
CHANGED
|
@@ -118,7 +118,8 @@ def map_ids_to_bounding_boxes(
|
|
|
118
118
|
|
|
119
119
|
id2bbox: dict[str, BoundingBox] = {}
|
|
120
120
|
for id_, *bounds in (x.split(",") for x in bb_strings):
|
|
121
|
-
|
|
121
|
+
x, y, width, height = (float(x) for x in bounds)
|
|
122
|
+
id2bbox[id_] = BoundingBox(x, y, width, height)
|
|
122
123
|
return id2bbox
|
|
123
124
|
|
|
124
125
|
|
|
@@ -6,7 +6,7 @@ svg_ultralight/main.py,sha256=6oNkZfD27UMdP-oYqp5agS_IGcYb8NkUZwM9Zdyb3SA,7287
|
|
|
6
6
|
svg_ultralight/metadata.py,sha256=Mxgxrxe1Ar4kp2wTT29aadxgHNFaaNLABo29jStiWDg,4201
|
|
7
7
|
svg_ultralight/nsmap.py,sha256=y63upO78Rr-JJT56RWWZuyrsILh6HPoY4GhbYnK1A0g,1244
|
|
8
8
|
svg_ultralight/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
svg_ultralight/query.py,sha256=
|
|
9
|
+
svg_ultralight/query.py,sha256=_KQuk4IwhVVDgTT0GZm_gbqNUGp7lMEDM32b_CTTSUA,7320
|
|
10
10
|
svg_ultralight/root_elements.py,sha256=pt9J6mPrnoTAZVF6vKTZoM_o947I8UCj6MbGcD2JUCk,2869
|
|
11
11
|
svg_ultralight/string_conversion.py,sha256=WEmpf75RJmJ2lfJluagAz2wPsz6wM8XvTEwkq4U0vEc,7353
|
|
12
12
|
svg_ultralight/unit_conversion.py,sha256=g07nhzXdjPvGcJmkhLdFbeDLrSmbI8uFoVgPo7G62Bg,9258
|
|
@@ -14,13 +14,13 @@ svg_ultralight/bounding_boxes/__init__.py,sha256=qUEn3r4s-1QNHaguhWhhaNfdP4tl_B6
|
|
|
14
14
|
svg_ultralight/bounding_boxes/bound_helpers.py,sha256=D-Qp8yDmn5vIBdaSlBvaWR0F_iRCdotgQqjaRHXBq-8,3397
|
|
15
15
|
svg_ultralight/bounding_boxes/supports_bounds.py,sha256=1yqmZ7PH1bBH-LTIUDzSvKFXkPLXfJM7jJNz0bXurZ4,3926
|
|
16
16
|
svg_ultralight/bounding_boxes/type_bound_element.py,sha256=VKiN4UnC2XlPKapWRHxgtqhO4BuVoe6YkLrirlEP09w,5714
|
|
17
|
-
svg_ultralight/bounding_boxes/type_bounding_box.py,sha256=
|
|
17
|
+
svg_ultralight/bounding_boxes/type_bounding_box.py,sha256=8c9aNTVCjyZpHQMW5mFsxTkgOi_2yH7TyGxlmJPxUR0,11444
|
|
18
18
|
svg_ultralight/bounding_boxes/type_padded_text.py,sha256=FnwHD54qPMlyOB0yhRgZShQWA_riaaTS9GwD9HnbPm4,14119
|
|
19
19
|
svg_ultralight/constructors/__init__.py,sha256=YcnO0iBQc19aL8Iemw0Y452MBMBIT2AN5nZCnoGxpn0,327
|
|
20
20
|
svg_ultralight/constructors/new_element.py,sha256=VtMz9sPn9rMk6rui5Poysy3vezlOaS-tGIcGbu-SXmY,3406
|
|
21
21
|
svg_ultralight/strings/__init__.py,sha256=Zalrf-ThFz7b7xKELx5lb2gOlBgV-6jk_k_EeSdVCVk,295
|
|
22
22
|
svg_ultralight/strings/svg_strings.py,sha256=RYKMxOHq9abbZyGcFqsElBGLrBX-EjjNxln3s_ibi30,1296
|
|
23
|
-
svg_ultralight-0.
|
|
24
|
-
svg_ultralight-0.
|
|
25
|
-
svg_ultralight-0.
|
|
26
|
-
svg_ultralight-0.
|
|
23
|
+
svg_ultralight-0.28.0.dist-info/METADATA,sha256=ybepa8hEhsBKXUT_CtnqUoKBh5Eg5nQReYFyQwdNW60,8871
|
|
24
|
+
svg_ultralight-0.28.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
25
|
+
svg_ultralight-0.28.0.dist-info/top_level.txt,sha256=se-6yqM_0Yg5orJKvKWdjQZ4iR4G_EjhL7oRgju-fdY,15
|
|
26
|
+
svg_ultralight-0.28.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|