svg-ultralight 0.64.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.
Files changed (35) hide show
  1. svg_ultralight/__init__.py +112 -0
  2. svg_ultralight/animate.py +40 -0
  3. svg_ultralight/attrib_hints.py +14 -0
  4. svg_ultralight/bounding_boxes/__init__.py +5 -0
  5. svg_ultralight/bounding_boxes/bound_helpers.py +200 -0
  6. svg_ultralight/bounding_boxes/padded_text_initializers.py +442 -0
  7. svg_ultralight/bounding_boxes/supports_bounds.py +167 -0
  8. svg_ultralight/bounding_boxes/type_bound_collection.py +74 -0
  9. svg_ultralight/bounding_boxes/type_bound_element.py +68 -0
  10. svg_ultralight/bounding_boxes/type_bounding_box.py +432 -0
  11. svg_ultralight/bounding_boxes/type_padded_list.py +208 -0
  12. svg_ultralight/bounding_boxes/type_padded_text.py +502 -0
  13. svg_ultralight/constructors/__init__.py +14 -0
  14. svg_ultralight/constructors/new_element.py +117 -0
  15. svg_ultralight/font_tools/__init__.py +5 -0
  16. svg_ultralight/font_tools/comp_results.py +291 -0
  17. svg_ultralight/font_tools/font_info.py +849 -0
  18. svg_ultralight/image_ops.py +156 -0
  19. svg_ultralight/inkscape.py +261 -0
  20. svg_ultralight/layout.py +291 -0
  21. svg_ultralight/main.py +183 -0
  22. svg_ultralight/metadata.py +122 -0
  23. svg_ultralight/nsmap.py +36 -0
  24. svg_ultralight/py.typed +5 -0
  25. svg_ultralight/query.py +254 -0
  26. svg_ultralight/read_svg.py +58 -0
  27. svg_ultralight/root_elements.py +96 -0
  28. svg_ultralight/string_conversion.py +244 -0
  29. svg_ultralight/strings/__init__.py +21 -0
  30. svg_ultralight/strings/svg_strings.py +106 -0
  31. svg_ultralight/transformations.py +152 -0
  32. svg_ultralight/unit_conversion.py +247 -0
  33. svg_ultralight-0.64.0.dist-info/METADATA +208 -0
  34. svg_ultralight-0.64.0.dist-info/RECORD +35 -0
  35. svg_ultralight-0.64.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,208 @@
1
+ """A list of padded text elements that transform as one.
2
+
3
+ This class simplifies working with multiple PaddedText elements as a group.
4
+
5
+ In addition to the usual PaddedText getters and setters (x, cx, x2, y, cy, y2, width,
6
+ height), it also provides tight versions of those (tx, tcx, tx2, ty, tcy, ty2,
7
+ twidth, theight) that operate on the unpadded bounding box. Get and set these
8
+ dimension attributes with the get_dim and set_dim methods.
9
+
10
+ Will additionally stack and align the elements. Stack (any method with a `name`
11
+ argument) will expect a one of x, cx, x2, y, cy, or y2 (or their tight equivalents
12
+ tx, tcx, tx2, ty, tcy, ty2).
13
+
14
+ A PaddedList instance contains no information except pointers to the PaddedText
15
+ elements, so you could, for instance, create a jagged arrangements of text elements
16
+ with
17
+
18
+ ```python
19
+ padded_list.align('x')
20
+ padded_list.stack()
21
+ padded_list[::2].transform(dx=10)
22
+ ```
23
+
24
+ :author: Shay Hill
25
+ :created: 2025-11-17
26
+ """
27
+
28
+ import itertools as it
29
+ from typing import cast, overload
30
+
31
+ from svg_ultralight.attrib_hints import ElemAttrib
32
+ from svg_ultralight.bounding_boxes.bound_helpers import new_bound_union
33
+ from svg_ultralight.bounding_boxes.type_bound_element import BoundElement
34
+ from svg_ultralight.bounding_boxes.type_bounding_box import BoundingBox
35
+ from svg_ultralight.bounding_boxes.type_padded_text import PaddedText
36
+ from svg_ultralight.constructors import update_element
37
+ from svg_ultralight.transformations import new_transformation_matrix
38
+
39
+ _Matrix = tuple[float, float, float, float, float, float]
40
+
41
+ # fmt: off
42
+ _BBOX_DIMS = {
43
+ "x", "cx", "x2", "y", "cy", "y2", "width", "height",
44
+ "tx", "tcx", "tx2", "ty", "tcy", "ty2", "twidth", "theight",
45
+ }
46
+ # fmt: on
47
+
48
+
49
+ class PaddedList:
50
+ """A list of padded text elements that transform as one."""
51
+
52
+ def __init__(self, *plems: PaddedText) -> None:
53
+ """Initialize with a list of padded text elements."""
54
+ self.plems = list(plems)
55
+
56
+ @overload
57
+ def __getitem__(self, idx: slice) -> "PaddedList": ...
58
+
59
+ @overload
60
+ def __getitem__(self, idx: int) -> PaddedText: ...
61
+
62
+ def __getitem__(self, idx: slice | int) -> "PaddedList | PaddedText":
63
+ """Get one or more padded text elements."""
64
+ if isinstance(idx, int):
65
+ return self.plems[idx]
66
+ return PaddedList(*self.plems[idx])
67
+
68
+ def append(self, ptext: PaddedText) -> None:
69
+ """Append a padded text element to the list."""
70
+ self.plems.append(ptext)
71
+
72
+ @property
73
+ def bbox(self) -> BoundingBox:
74
+ """The bounding box of the padded text elements."""
75
+ return BoundingBox.union(*(x.bbox for x in self.plems))
76
+
77
+ @property
78
+ def tbox(self) -> BoundingBox:
79
+ """The unpadded bounding box of the padded text elements.
80
+
81
+ t for true or tight.
82
+ """
83
+ return BoundingBox.union(*(x.tbox for x in self.plems))
84
+
85
+ def transform(
86
+ self,
87
+ transformation: _Matrix | None = None,
88
+ *,
89
+ scale: tuple[float, float] | float | None = None,
90
+ dx: float | None = None,
91
+ dy: float | None = None,
92
+ ) -> None:
93
+ """Apply a transformation to all the padded text elements."""
94
+ for p in self.plems:
95
+ p.transform(transformation, scale=scale, dx=dx, dy=dy)
96
+
97
+ def transform_preserve_sidebearings(
98
+ self,
99
+ transformation: _Matrix | None = None,
100
+ *,
101
+ scale: tuple[float, float] | float | None = None,
102
+ dx: float | None = None,
103
+ dy: float | None = None,
104
+ ) -> None:
105
+ """Apply a transformation to all the padded text elements."""
106
+ for p in self.plems:
107
+ p.transform_preserve_sidebearings(transformation, scale=scale, dx=dx, dy=dy)
108
+
109
+ def union(self, **attribs: ElemAttrib) -> BoundElement:
110
+ """Return a single bound element containing all the padded text elements."""
111
+ union = new_bound_union(*self.plems)
112
+ _ = update_element(union.elem, **attribs)
113
+ return union
114
+
115
+ def tunion(self, **attribs: ElemAttrib) -> BoundElement:
116
+ """Return a single bound element containing all the unpadded text elements.
117
+
118
+ This version uses the unpadded bounding boxes of the padded text elements.
119
+ """
120
+ union = self.union(**attribs)
121
+ union.bbox = self.tbox
122
+ return union
123
+
124
+ def padded_union(self, **attribs: ElemAttrib) -> PaddedText:
125
+ """Return a PaddedText inst where the elem is a `g` of all the padded text."""
126
+ union = self.tunion(**attribs)
127
+ tpad = self.tbox.y - self.bbox.y
128
+ rpad = self.bbox.x2 - self.tbox.x2
129
+ bpad = self.bbox.y2 - self.tbox.y2
130
+ lpad = self.tbox.x - self.bbox.x
131
+ return PaddedText(union.elem, union.bbox, tpad, rpad, bpad, lpad)
132
+
133
+ def get_dim(self, dim: str) -> float:
134
+ """Get a dimension from bbox or tbox."""
135
+ if dim not in _BBOX_DIMS:
136
+ msg = "Invalid bbox dimension '{dim}'"
137
+ raise ValueError(msg)
138
+ box = self.tbox if dim.startswith("t") else self.bbox
139
+ dim = dim.removeprefix("t")
140
+ if dim not in ("x", "cx", "x2", "y", "cy", "y2", "width", "height"):
141
+ msg = f"Cannot get dimension '{dim}'"
142
+ raise AttributeError(msg)
143
+ return cast("float", getattr(box, dim))
144
+
145
+ def new_tmat(self, dim: str, value: float) -> _Matrix:
146
+ """Create a transformation matrix to set a bbox dimension to a value.
147
+
148
+ :param dim: One of 'x', 'cx', 'x2', 'y', 'cy', 'y2', 'width', or 'height'
149
+ or any of the same prefixed with 't'.
150
+ """
151
+ if dim not in _BBOX_DIMS:
152
+ msg = "Invalid bbox dimension '{dim}'"
153
+ raise ValueError(msg)
154
+ current_value = self.get_dim(dim)
155
+ dim = dim.removeprefix("t")
156
+ if dim in ("x", "cx", "x2"):
157
+ return new_transformation_matrix(dx=value - current_value)
158
+ if dim in ("y", "cy", "y2"):
159
+ return new_transformation_matrix(dy=value - current_value)
160
+ if dim in ("width", "height"):
161
+ return new_transformation_matrix(scale=value / current_value)
162
+ msg = f"Cannot set dimension '{dim}'"
163
+ raise AttributeError(msg)
164
+
165
+ def set_dim(self, **dims: float) -> None:
166
+ """Set a dimension on bbox or tbox."""
167
+ for dim, value in dims.items():
168
+ if dim not in _BBOX_DIMS:
169
+ msg = "Invalid bbox dim '{dim}'"
170
+ raise ValueError(msg)
171
+ tmat = self.new_tmat(dim, value)
172
+ self.transform(transformation=tmat)
173
+
174
+ def set(self, **attribs: ElemAttrib) -> None:
175
+ """Set an attribute on all padded text elements."""
176
+ for p in self.plems:
177
+ _ = update_element(p.elem, **attribs)
178
+
179
+ def align(self, dimension: str, value: float | None = None) -> None:
180
+ """Align the specified edges or centers of the padded text elements.
181
+
182
+ :param dimension: One of 'x', 'cx', 'x2', 'y', 'cy', or 'y2' or any of the
183
+ same prefixed with 't'.
184
+ :param value: If provided, align to this value. Otherwise, align to the
185
+ corresponding edge or center of the bounding box of all the padded
186
+ text elements.
187
+ """
188
+ if value is None:
189
+ value = self.get_dim(dimension)
190
+ for i, plem in enumerate(self.plems):
191
+ tmat = self[i : i + 1].new_tmat(dimension, value)
192
+ plem.transform(transformation=tmat)
193
+
194
+ def stack(self, scale: float = 1, gap: float | None = None) -> None:
195
+ """Stack the gapded text elements vertically with a gap.
196
+
197
+ :param scale: If provided, scale the native line height (ascent + descent)
198
+ of the text elements by this factor.
199
+ :param gap: If provided, add this much space between the text elements. This
200
+ is an alternate strategy for when you are using fonts of different sizes.
201
+ If the gap parameter is passed, the scale parameter is ignored.
202
+ """
203
+ if gap is not None:
204
+ for above, below in it.pairwise(self.plems):
205
+ below.y = above.y2 + gap
206
+ return
207
+ for above, below in it.pairwise(self.plems):
208
+ below.y = above.y + above.height * scale