svg-ultralight 0.51.0__py3-none-any.whl → 0.52.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.

@@ -52,6 +52,7 @@ class SupportsBounds(Protocol):
52
52
  scale: tuple[float, float] | float | None = None,
53
53
  dx: float | None = None,
54
54
  dy: float | None = None,
55
+ reverse: bool = False,
55
56
  ) -> None:
56
57
  """Apply a transformation to the object."""
57
58
  ...
@@ -47,6 +47,7 @@ class BoundCollection(HasBoundingBox):
47
47
  scale: tuple[float, float] | float | None = None,
48
48
  dx: float | None = None,
49
49
  dy: float | None = None,
50
+ reverse: bool = False,
50
51
  ) -> None:
51
52
  """Transform each bound element in self.blems.
52
53
 
@@ -54,9 +55,11 @@ class BoundCollection(HasBoundingBox):
54
55
  :param scale: optional scale factor
55
56
  :param dx: optional x translation
56
57
  :param dy: optional y translation
58
+ :param reverse: Transform the element as if it were in a <g> element
59
+ transformed by tmat.
57
60
 
58
61
  Keep track of all compounding transformations in order to have a value for
59
- self.scale (required for membersh and to provide access to cumulative
62
+ self.scale (required for members and to provide access to cumulative
60
63
  transforms should this be useful for any reason. This means all
61
64
  transformations must be applied to two bounding boxes: a persistant bbox to
62
65
  keep track of the scale property and a temporary bbox to isolate each
@@ -66,6 +69,6 @@ class BoundCollection(HasBoundingBox):
66
69
  self.bbox.transform(tmat)
67
70
  for blem in self.blems:
68
71
  if isinstance(blem, EtreeElement):
69
- _ = transform_element(blem, tmat)
72
+ _ = transform_element(blem, tmat, reverse=reverse)
70
73
  else:
71
- blem.transform(tmat)
74
+ blem.transform(tmat, reverse=reverse)
@@ -52,6 +52,7 @@ class BoundElement(HasBoundingBox):
52
52
  scale: tuple[float, float] | float | None = None,
53
53
  dx: float | None = None,
54
54
  dy: float | None = None,
55
+ reverse: bool = False,
55
56
  ) -> None:
56
57
  """Transform the element and bounding box.
57
58
 
@@ -59,7 +60,9 @@ class BoundElement(HasBoundingBox):
59
60
  :param scale: a scaling factor
60
61
  :param dx: the x translation
61
62
  :param dy: the y translation
63
+ :param reverse: Transform the element as if it were in a <g> element
64
+ transformed by tmat.
62
65
  """
63
66
  tmat = new_transformation_matrix(transformation, scale=scale, dx=dx, dy=dy)
64
- self.bbox.transform(tmat)
65
- _ = transform_element(self.elem, tmat)
67
+ self.bbox.transform(tmat, reverse=reverse)
68
+ _ = transform_element(self.elem, tmat, reverse=reverse)
@@ -78,6 +78,7 @@ class HasBoundingBox(SupportsBounds):
78
78
  scale: tuple[float, float] | float | None = None,
79
79
  dx: float | None = None,
80
80
  dy: float | None = None,
81
+ reverse: bool = False,
81
82
  ) -> None:
82
83
  """Transform the bounding box by updating the transformation attribute.
83
84
 
@@ -85,6 +86,8 @@ class HasBoundingBox(SupportsBounds):
85
86
  :param scale: scale factor
86
87
  :param dx: x translation
87
88
  :param dy: y translation
89
+ :param reverse: Transform the element as if it were in a <g> element
90
+ transformed by tmat.
88
91
 
89
92
  All parameters are optional. Scale, dx, and dy are optional and applied after
90
93
  the transformation matrix if both are given. This shouldn't be necessary in
@@ -94,7 +97,10 @@ class HasBoundingBox(SupportsBounds):
94
97
  when applying a transformation from another bounding box instance.
95
98
  """
96
99
  tmat = new_transformation_matrix(transformation, scale=scale, dx=dx, dy=dy)
97
- self.bbox.transformation = mat_dot(tmat, self.bbox.transformation)
100
+ if reverse:
101
+ self.bbox.transformation = mat_dot(self.bbox.transformation, tmat)
102
+ else:
103
+ self.bbox.transformation = mat_dot(tmat, self.bbox.transformation)
98
104
 
99
105
  @property
100
106
  def scale(self) -> tuple[float, float]:
@@ -119,6 +119,16 @@ class PaddedText(BoundElement):
119
119
  self.lpad = lpad
120
120
  self._line_gap = line_gap
121
121
 
122
+ @property
123
+ def tbox(self) -> BoundingBox:
124
+ """Return the unpadded BoundingBox around the text element.
125
+
126
+ Tight bbox or True bbox. An alias for unpadded_bbox.
127
+
128
+ :return: The unpadded BoundingBox around the text element.
129
+ """
130
+ return self.unpadded_bbox
131
+
122
132
  @property
123
133
  def bbox(self) -> BoundingBox:
124
134
  """Return a BoundingBox around the margins and cap/baseline.
@@ -154,6 +164,7 @@ class PaddedText(BoundElement):
154
164
  scale: tuple[float, float] | float | None = None,
155
165
  dx: float | None = None,
156
166
  dy: float | None = None,
167
+ reverse: bool = False,
157
168
  ) -> None:
158
169
  """Transform the element and bounding box.
159
170
 
@@ -161,10 +172,12 @@ class PaddedText(BoundElement):
161
172
  :param scale: a scaling factor
162
173
  :param dx: the x translation
163
174
  :param dy: the y translation
175
+ :param reverse: Transform the element as if it were in a <g> element
176
+ transformed by tmat.
164
177
  """
165
178
  tmat = new_transformation_matrix(transformation, scale=scale, dx=dx, dy=dy)
166
- self.unpadded_bbox.transform(tmat)
167
- _ = transform_element(self.elem, tmat)
179
+ self.unpadded_bbox.transform(tmat, reverse=reverse)
180
+ _ = transform_element(self.elem, tmat, reverse=reverse)
168
181
 
169
182
  @property
170
183
  def line_gap(self) -> float:
@@ -425,7 +425,13 @@ class FTFontInfo:
425
425
  coordinates (+y is down).
426
426
  """
427
427
  min_x, min_y, max_x, max_y = self.get_char_bounds(char)
428
- return BoundingBox(min_x, -max_y, max_x - min_x, max_y - min_y)
428
+ return BoundingBox(
429
+ min_x,
430
+ min_y,
431
+ max_x - min_x,
432
+ max_y - min_y,
433
+ transformation=(1, 0, 0, -1, 0, 0),
434
+ )
429
435
 
430
436
  def get_text_bounds(self, text: str) -> tuple[int, int, int, int]:
431
437
  """Return bounds of a string as xmin, ymin, xmax, ymax.
@@ -484,7 +490,13 @@ class FTFontInfo:
484
490
  coordinates (+y is down).
485
491
  """
486
492
  min_x, min_y, max_x, max_y = self.get_text_bounds(text)
487
- return BoundingBox(min_x, -max_y, max_x - min_x, max_y - min_y)
493
+ return BoundingBox(
494
+ min_x,
495
+ min_y,
496
+ max_x - min_x,
497
+ max_y - min_y,
498
+ transformation=(1, 0, 0, -1, 0, 0),
499
+ )
488
500
 
489
501
  def get_lsb(self, char: str) -> float:
490
502
  """Return the left side bearing of a character."""
@@ -567,7 +579,7 @@ class FTTextInfo:
567
579
  """
568
580
  bbox = self.font.get_text_bbox(self.text)
569
581
  bbox.transform(scale=self.scale)
570
- return BoundingBox(*bbox.values())
582
+ return bbox
571
583
 
572
584
  @property
573
585
  def ascent(self) -> float:
@@ -129,12 +129,24 @@ def new_transformation_matrix(
129
129
  return mat_dot((float(scale_x), 0, 0, float(scale_y), dx, dy), transformation)
130
130
 
131
131
 
132
- def transform_element(elem: EtreeElement, matrix: _Matrix) -> EtreeElement:
132
+ def transform_element(
133
+ elem: EtreeElement, matrix: _Matrix, *, reverse: bool = False
134
+ ) -> EtreeElement:
133
135
  """Apply a transformation matrix to an svg element.
134
136
 
135
137
  :param elem: svg element
136
- :param matrix: transformation matrix
138
+ :par m matrix: transformation matrix
139
+
140
+ :param reverse: If you have a transformation matrix, A, and wish to apply an
141
+ additional transform, B, the result is B @ A. This is how an element can be
142
+ cumulatively transformed in svg.
143
+
144
+ If the element is transformed by A and is a part of a GROUP transformed by B,
145
+ then the result is the reverse: A @ B.
137
146
  """
138
147
  current = get_transform_matrix(elem)
139
- elem.attrib["transform"] = svg_matrix(mat_dot(matrix, current))
148
+ if reverse:
149
+ elem.attrib["transform"] = svg_matrix(mat_dot(current, matrix))
150
+ else:
151
+ elem.attrib["transform"] = svg_matrix(mat_dot(matrix, current))
140
152
  return elem
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: svg-ultralight
3
- Version: 0.51.0
3
+ Version: 0.52.0
4
4
  Summary: a sensible way to create svg files with Python
5
5
  Author: Shay Hill
6
6
  Author-email: Shay Hill <shay_public@hotmail.com>
@@ -4,16 +4,16 @@ svg_ultralight/attrib_hints.py,sha256=_p85b77mcmj15IUnsDtgbYJ3FFd-Q1qM2OERv2UEZ2
4
4
  svg_ultralight/bounding_boxes/__init__.py,sha256=YNCwgN-Ja2MFoCxIYxC3KZTCx_gFvPfRQ-8zBR5Q9mk,73
5
5
  svg_ultralight/bounding_boxes/bound_helpers.py,sha256=o_NlH3bBU6lvn8KPTvp7hrEDb5OLqwO3t-gc51d44Rk,6583
6
6
  svg_ultralight/bounding_boxes/padded_text_initializers.py,sha256=7LD7VzmDSaNA2S9QTW0sNzMwLpjjReWGV0e5f4uglk8,10002
7
- svg_ultralight/bounding_boxes/supports_bounds.py,sha256=zoJr7goYEbiVsr5Vl2RlqCsbyBe5RHZGrU0B5WMBRbY,4427
8
- svg_ultralight/bounding_boxes/type_bound_collection.py,sha256=vzZPJoUg4Joaj7nTl4VpDFFHihIoBfBNvu6IaIprYKM,2582
9
- svg_ultralight/bounding_boxes/type_bound_element.py,sha256=CdbL48NMauCX5qZGlJes3aYFZHyVE3rby99HN-Kr7GI,2147
10
- svg_ultralight/bounding_boxes/type_bounding_box.py,sha256=81tyPpCHxRH1CXELO_qSCtueCyHYWBeEJO_JIxpl88Q,13101
11
- svg_ultralight/bounding_boxes/type_padded_text.py,sha256=1IV0DGjxem8celN4GAT8l60fLYhJwsES3O4tkHG7v60,14248
7
+ svg_ultralight/bounding_boxes/supports_bounds.py,sha256=8rIklGICIx-DXELN7FjDwrzOi8iUrlstGrCIuN9ew3o,4458
8
+ svg_ultralight/bounding_boxes/type_bound_collection.py,sha256=ct8BLjqyHSCNhOGG0eYuubdOajI2KVo1nbTP3JxXZ00,2756
9
+ svg_ultralight/bounding_boxes/type_bound_element.py,sha256=gXsHCSJ6lxIGODm1oJ5yYAPzuIi7NkBIIzD_GX-cgo8,2322
10
+ svg_ultralight/bounding_boxes/type_bounding_box.py,sha256=r3XcdW_U8VvRauylw54aMwPsmC6A3HY72rz_sTcd1Uo,13359
11
+ svg_ultralight/bounding_boxes/type_padded_text.py,sha256=WFpz9ebHn3NIMdqO3-Qowbvq0w227jNKW6y_Y6Usg-g,14717
12
12
  svg_ultralight/constructors/__init__.py,sha256=fb-A50G3YTZNMQXpxcCl_QAcfssS0Us065_kdJRybDQ,313
13
13
  svg_ultralight/constructors/new_element.py,sha256=hVQG7WBHQoTUmGiZNtVadGOitdTFBrHOQeVKKVOWGaA,3526
14
14
  svg_ultralight/font_tools/__init__.py,sha256=b_VSvk5aODzS2wv48EMU2sey_mxq1o1SL8ecWTdy4kc,78
15
15
  svg_ultralight/font_tools/comp_results.py,sha256=KR6RtVGWjGGfHX2d_-XRx_Jr9CQ36kPbQxLVK1yzgTI,10421
16
- svg_ultralight/font_tools/font_info.py,sha256=9nSj8qjzbT3bEe2Qgr-Rtfzb6RyFFCiXHbvv4Oyfxw0,29332
16
+ svg_ultralight/font_tools/font_info.py,sha256=RkxhxMdhuIDNDn8OUfEOTRPf9Y2PoYdxoBRFp70KQes,29521
17
17
  svg_ultralight/image_ops.py,sha256=cwR038ECUuiceIrk-9Peh5Ijp4tzjuaV5Y1cvYOt4TI,5454
18
18
  svg_ultralight/inkscape.py,sha256=_aQ42ZQ1JP9TFdHmtxgCRsXvxfZPpdZWTGKEea9BgIU,9336
19
19
  svg_ultralight/layout.py,sha256=yiTAYtSsYywdCObIdjkK3A4fiEYLNPqD3BOQKxfj6gQ,12514
@@ -27,8 +27,8 @@ svg_ultralight/root_elements.py,sha256=N4BBO1j2iUDIGbk70FRrtfElZo0AG-3Olqugxz7LD
27
27
  svg_ultralight/string_conversion.py,sha256=pzqkbGb-wXdhGd8ojEx4Hy-HBCC8fOfcloaLrNMh8TM,8607
28
28
  svg_ultralight/strings/__init__.py,sha256=iaRwr9AF9bPDkG3XvgGRSf8JPAS2GQ8ds9yCNpPN4ng,365
29
29
  svg_ultralight/strings/svg_strings.py,sha256=XlXQ5RqueGrROXBI4VzR2cK7e1NdNhYx5S84bgyqFUQ,3132
30
- svg_ultralight/transformations.py,sha256=fnPMdMQFgT6OEUIDwb_A7tvuILraPFW1jR9I1RaI1cA,4325
30
+ svg_ultralight/transformations.py,sha256=YyhehH0Hlui2U9t7hjwgHgRyHzUR7UCMSSo-G85J-bo,4784
31
31
  svg_ultralight/unit_conversion.py,sha256=Y32GZ5TLkvjDHM2kBw52M8ZxDNlpVSlDkSdRa1S_X-A,8985
32
- svg_ultralight-0.51.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
33
- svg_ultralight-0.51.0.dist-info/METADATA,sha256=qvOlGkyOA8KxGifWkMETlzsu--3Nf8qFCyqhet7VoyM,8691
34
- svg_ultralight-0.51.0.dist-info/RECORD,,
32
+ svg_ultralight-0.52.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
33
+ svg_ultralight-0.52.0.dist-info/METADATA,sha256=q03HJlJG2pVKkQLNbLhG1pv0aK9FckO1NARKhF4ZNzQ,8691
34
+ svg_ultralight-0.52.0.dist-info/RECORD,,