svg-ultralight 0.37.0__py3-none-any.whl → 0.39.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/query.py CHANGED
@@ -21,7 +21,7 @@ from copy import deepcopy
21
21
  from pathlib import Path
22
22
  from subprocess import PIPE, Popen
23
23
  from tempfile import NamedTemporaryFile, TemporaryFile
24
- from typing import TYPE_CHECKING
24
+ from typing import TYPE_CHECKING, Literal
25
25
  from warnings import warn
26
26
 
27
27
  from lxml import etree
@@ -31,7 +31,11 @@ from svg_ultralight.bounding_boxes.type_padded_text import PaddedText
31
31
  from svg_ultralight.main import new_svg_root, write_svg
32
32
 
33
33
  if TYPE_CHECKING:
34
- from lxml.etree import _Element as EtreeElement # type: ignore
34
+ from collections.abc import Iterator
35
+
36
+ from lxml.etree import (
37
+ _Element as EtreeElement, # pyright: ignore[reportPrivateUsage]
38
+ )
35
39
 
36
40
 
37
41
  with TemporaryFile() as f:
@@ -39,24 +43,27 @@ with TemporaryFile() as f:
39
43
 
40
44
  _CACHE_DIR.mkdir(exist_ok=True)
41
45
 
46
+ _TEMP_ID_PREFIX = "svg_ultralight-temp_query_module-"
47
+
48
+
49
+ def _iter_elems(*elem_args: EtreeElement) -> Iterator[EtreeElement]:
50
+ """Yield element and sub-elements."""
51
+ for elem in elem_args:
52
+ yield from elem.iter()
53
+
42
54
 
43
55
  def _fill_ids(*elem_args: EtreeElement) -> None:
44
56
  """Set the id attribute of an element and all its children. Keep existing ids.
45
57
 
46
58
  :param elem: an etree element, accepts multiple arguments
47
59
  """
48
- if not elem_args:
49
- return
50
- elem = elem_args[0]
51
- for child in elem:
52
- _fill_ids(child)
53
- if elem.get("id") is None:
54
- elem.set("id", f"svg_ul-{uuid.uuid4()}")
55
- _fill_ids(*elem_args[1:])
60
+ for elem in _iter_elems(*elem_args):
61
+ if elem.get("id") is None:
62
+ elem.set("id", f"{_TEMP_ID_PREFIX}-{uuid.uuid4()}")
56
63
 
57
64
 
58
65
  def _normalize_views(elem: EtreeElement) -> None:
59
- """Create a square viewbox for any element with an svg tag.
66
+ """Create a square viewBox for any element with an svg tag.
60
67
 
61
68
  :param elem: an etree element
62
69
 
@@ -83,57 +90,82 @@ def _envelop_copies(*elem_args: EtreeElement) -> EtreeElement:
83
90
  return envelope
84
91
 
85
92
 
86
- def map_ids_to_bounding_boxes(
93
+ def _split_bb_string(bb_string: str) -> tuple[str, BoundingBox]:
94
+ """Split a bounding box string into id and BoundingBox instance.
95
+
96
+ :param bb_string: "id,x,y,width,height"
97
+ :return: (id, BoundingBox(x, y, width, height))
98
+ """
99
+ id_, *bounds = bb_string.split(",")
100
+ x, y, width, height = (float(x) for x in bounds)
101
+ return id_, BoundingBox(x, y, width, height)
102
+
103
+
104
+ def map_elems_to_bounding_boxes(
87
105
  inkscape: str | Path, *elem_args: EtreeElement
88
- ) -> dict[str, BoundingBox]:
106
+ ) -> dict[EtreeElement | Literal["svg"], BoundingBox]:
89
107
  r"""Query an svg file for bounding-box dimensions.
90
108
 
91
109
  :param inkscape: path to an inkscape executable on your local file system
92
110
  IMPORTANT: path cannot end with ``.exe``.
93
111
  Use something like ``"C:\\Program Files\\Inkscape\\inkscape"``
94
112
  :param elem_args: xml element (written to a temporary file then queried)
95
- :return: svg element ids (and a bounding box for the entire svg file as ``svg``)
96
- mapped to (x, y, width, height)
97
- :effects: adds an id attribute to any element without one. These will all have
98
- the prefix svg_ul-, so you can find and remove them later if desired.
99
-
100
- Bounding boxes are relative to svg viewbox. If viewbox x == -10,
113
+ :return: input svg elements and any descendents of those elements mapped
114
+ `BoundingBox(x, y, width, height)`
115
+ So return dict keys are the input elements themselves with one exception: a
116
+ string key, "svg", is mapped to a bounding box around all input elements.
117
+ :effects: temporarily adds an id attribute if any ids are missing. These are
118
+ removed if the function completes. Existing, non-unique ids will break this
119
+ function.
120
+
121
+ Bounding boxes are relative to svg viewBox. If, for instance, viewBox x == -10,
101
122
  all bounding-box x values will be offset -10. So, everything is wrapped in a root
102
- element with a "normalized" viewbox, (viewbox=(0, 0, 1, 1)) then any child root
103
- elements ("child root elements" sounds wrong, but it works) viewboxes are
104
- normalized as well. This works even with a root element around a root element, so
105
- input elem_args can be root elements or "normal" elements like "rect", "circle",
106
- or "text" or a mixture of both.
123
+ element, `envelope` with a "normalized" viewBox, `viewBox=(0, 0, 1, 1)`. That
124
+ way, any child root elements ("child root elements" sounds wrong, but it works)
125
+ viewBoxes are normalized as well. This works even with a root element around a
126
+ root element, so input elem_args can be root elements or "normal" elements like
127
+ "rect", "circle", or "text" or a mixture of both. Bounding boxes output here will
128
+ work as expected in any viewBox.
107
129
 
108
130
  The ``inkscape --query-all svg`` call will return a tuple:
109
131
 
110
132
  (b'svg1,x,y,width,height\\r\\elem1,x,y,width,height\\r\\n', None)
111
133
  where x, y, width, and height are strings of numbers.
112
134
 
113
- This calls the command and formats the output into a dictionary.
114
-
115
- Scaling arguments ("width", "height") to new_svg_root transform the bounding
116
- boxes in non-useful ways. This copies all elements except the root element in to
117
- a (0, 0, 1, 1) root. This will put the boxes where you'd expect them to be, no
118
- matter what root you use.
135
+ This calls the command and formats the output into a dictionary. There is a
136
+ little extra complexity to handle cases with duplicate elements. Inkscape will
137
+ map bounding boxes to element ids *if* those ids are unique. If Inkscape
138
+ encounters a duplicate ID, Inkscape will map the bounding box of that element to
139
+ a string like "rect1". If you pass unequal elements with the same id, I can't
140
+ help you, but you might pass the same element multiple times. If you do this,
141
+ Inkscape will find a bounding box for each occurrence, map the first occurrence
142
+ to the id, then map subsequent occurrences to a string like "rect1". This
143
+ function will handle that.
119
144
  """
120
145
  if not elem_args:
121
146
  return {}
122
147
  _fill_ids(*elem_args)
123
- envelope = _envelop_copies(*elem_args)
124
148
 
149
+ envelope = _envelop_copies(*elem_args)
125
150
  with NamedTemporaryFile(mode="wb", delete=False, suffix=".svg") as svg_file:
126
151
  svg = write_svg(svg_file, envelope)
127
152
  with Popen(f'"{inkscape}" --query-all {svg}', stdout=PIPE) as bb_process:
128
153
  bb_data = str(bb_process.communicate()[0])[2:-1]
129
- bb_strings = re.split(r"[\\r]*\\n", bb_data)[:-1]
130
154
  os.unlink(svg_file.name)
131
155
 
132
- id2bbox: dict[str, BoundingBox] = {}
133
- for id_, *bounds in (x.split(",") for x in bb_strings):
134
- x, y, width, height = (float(x) for x in bounds)
135
- id2bbox[id_] = BoundingBox(x, y, width, height)
136
- return id2bbox
156
+ bb_strings = re.split(r"[\\r]*\\n", bb_data)[:-1]
157
+ id2bbox = dict(map(_split_bb_string, bb_strings))
158
+
159
+ elem2bbox: dict[EtreeElement | Literal["svg"], BoundingBox] = {}
160
+ for elem in _iter_elems(*elem_args):
161
+ elem_id = elem.attrib.get("id")
162
+ if not (elem_id): # id removed in a previous loop
163
+ continue
164
+ elem2bbox[elem] = id2bbox[elem_id]
165
+ if elem_id.startswith(_TEMP_ID_PREFIX):
166
+ del elem.attrib["id"]
167
+ elem2bbox["svg"] = BoundingBox.merged(*id2bbox.values())
168
+ return elem2bbox
137
169
 
138
170
 
139
171
  def _hash_elem(elem: EtreeElement) -> str:
@@ -178,7 +210,7 @@ def get_bounding_boxes(
178
210
 
179
211
  This will work most of the time, but if you're missing an nsmap, you'll need to
180
212
  create an entire xml file with a custom nsmap (using
181
- `svg_ultralight.new_svg_root`) then call `map_ids_to_bounding_boxes` directly.
213
+ `svg_ultralight.new_svg_root`) then call `map_elems_to_bounding_boxes` directly.
182
214
  """
183
215
  elem2hash = {elem: _hash_elem(elem) for elem in elem_args}
184
216
  cached = [_try_bbox_cache(h) for h in elem2hash.values()]
@@ -187,10 +219,10 @@ def get_bounding_boxes(
187
219
 
188
220
  hash2bbox = {h: c for h, c in zip(elem2hash.values(), cached) if c is not None}
189
221
  remainder = [e for e, c in zip(elem_args, cached) if c is None]
190
- id2bbox = map_ids_to_bounding_boxes(inkscape, *remainder)
222
+ id2bbox = map_elems_to_bounding_boxes(inkscape, *remainder)
191
223
  for elem in remainder:
192
224
  hash_ = elem2hash[elem]
193
- hash2bbox[hash_] = id2bbox[elem.attrib["id"]]
225
+ hash2bbox[hash_] = id2bbox[elem]
194
226
  with (_CACHE_DIR / hash_).open("wb") as f:
195
227
  pickle.dump(hash2bbox[hash_], f)
196
228
  return tuple(hash2bbox[h] for h in elem2hash.values())
@@ -13,7 +13,9 @@ from svg_ultralight.bounding_boxes.type_bounding_box import BoundingBox
13
13
  from svg_ultralight.main import new_svg_root
14
14
 
15
15
  if TYPE_CHECKING:
16
- from lxml.etree import _Element as EtreeElement # type: ignore
16
+ from lxml.etree import (
17
+ _Element as EtreeElement, # pyright: ignore[reportPrivateUsage]
18
+ )
17
19
 
18
20
  from svg_ultralight.bounding_boxes.supports_bounds import SupportsBounds
19
21
  from svg_ultralight.layout import PadArg
@@ -22,7 +22,9 @@ from svg_ultralight.nsmap import NSMAP
22
22
  if TYPE_CHECKING:
23
23
  from collections.abc import Iterable
24
24
 
25
- from lxml.etree import _Element as EtreeElement # type: ignore
25
+ from lxml.etree import (
26
+ _Element as EtreeElement, # pyright: ignore[reportPrivateUsage]
27
+ )
26
28
 
27
29
 
28
30
  def format_number(num: float | str) -> str:
@@ -120,7 +122,9 @@ def _fix_key_and_format_val(key: str, val: str | float) -> tuple[str, str]:
120
122
  popular one will be 'class') can be passed with a trailing underscore (e.g.,
121
123
  class_='body_text').
122
124
  """
123
- if ":" in key:
125
+ if "http:" in key or "https:" in key:
126
+ key_ = key
127
+ elif ":" in key:
124
128
  namespace, tag = key.split(":")
125
129
  key_ = str(etree.QName(NSMAP[namespace], tag))
126
130
  else:
@@ -184,7 +188,7 @@ def svg_tostring(xml: EtreeElement, **tostring_kwargs: str | bool | None) -> byt
184
188
  value = tostring_kwargs.get(arg_name, default.value)
185
189
  tostring_kwargs[arg_name] = value
186
190
  as_bytes = etree.tostring(etree.ElementTree(xml), **tostring_kwargs) # type: ignore
187
- return cast(bytes, as_bytes)
191
+ return cast("bytes", as_bytes)
188
192
 
189
193
 
190
194
  def get_viewBox_str(
@@ -13,10 +13,12 @@ from typing import TYPE_CHECKING, cast
13
13
  from svg_ultralight.string_conversion import format_number
14
14
 
15
15
  if TYPE_CHECKING:
16
- from lxml.etree import _Element as EtreeElement # type: ignore
16
+ from lxml.etree import (
17
+ _Element as EtreeElement, # pyright: ignore[reportPrivateUsage]
18
+ )
17
19
 
18
20
 
19
- _RE_MATRIX = re.compile(r"matrix\(([^)]+)\)")
21
+ RE_MATRIX = re.compile(r"matrix\(([^)]+)\)")
20
22
 
21
23
  _Matrix = tuple[float, float, float, float, float, float]
22
24
 
@@ -50,13 +52,19 @@ def mat_dot(mat1: _Matrix, mat2: _Matrix) -> _Matrix:
50
52
  return (aa, bb, cc, dd, ee, ff)
51
53
 
52
54
 
53
- def mat_apply(mat1: _Matrix, mat2: tuple[float, float]) -> tuple[float, float]:
55
+ def mat_apply(matrix: _Matrix, point: tuple[float, float]) -> tuple[float, float]:
54
56
  """Apply an svg-style transformation matrix to a point.
55
57
 
56
- :param mat1: transformation matrix (sx, 0, 0, sy, tx, ty)
58
+ :param mat1: transformation matrix (a, b, c, d, e, f) describing a 3x3 matrix
59
+ with an implied third row of (0, 0, 1)
60
+ [[a, c, e], [b, d, f], [0, 0, 1]]
57
61
  :param mat2: point (x, y)
58
62
  """
59
- return mat1[0] * mat2[0] + mat1[4], mat1[3] * mat2[1] + mat1[5]
63
+ a, b, c, d, e, f = matrix
64
+ x, y = point
65
+ result_x = a * x + c * y + e
66
+ result_y = b * x + d * y + f
67
+ return result_x, result_y
60
68
 
61
69
 
62
70
  def mat_invert(tmat: _Matrix) -> _Matrix:
@@ -86,7 +94,7 @@ def get_transform_matrix(elem: EtreeElement) -> _Matrix:
86
94
  return (1, 0, 0, 1, 0, 0)
87
95
  values_str = ""
88
96
  with suppress(AttributeError):
89
- values_str = cast(re.Match[str], _RE_MATRIX.match(transform)).group(1)
97
+ values_str = cast("re.Match[str]", RE_MATRIX.match(transform)).group(1)
90
98
  with suppress(ValueError):
91
99
  aa, bb, cc, dd, ee, ff = (float(val) for val in values_str.split())
92
100
  return (aa, bb, cc, dd, ee, ff)
@@ -97,7 +105,7 @@ def get_transform_matrix(elem: EtreeElement) -> _Matrix:
97
105
  def new_transformation_matrix(
98
106
  transformation: _Matrix | None = None,
99
107
  *,
100
- scale: float | None = None,
108
+ scale: tuple[float, float] | None = None,
101
109
  dx: float | None = None,
102
110
  dy: float | None = None,
103
111
  ) -> _Matrix:
@@ -107,10 +115,10 @@ def new_transformation_matrix(
107
115
  svg-style transformation matrix.
108
116
  """
109
117
  transformation = transformation or (1, 0, 0, 1, 0, 0)
110
- scale = scale or 1
118
+ scale = scale or (1, 1)
111
119
  dx = dx or 0
112
120
  dy = dy or 0
113
- return mat_dot((scale, 0, 0, scale, dx, dy), transformation)
121
+ return mat_dot((scale[0], 0, 0, scale[1], dx, dy), transformation)
114
122
 
115
123
 
116
124
  def transform_element(elem: EtreeElement, matrix: _Matrix) -> EtreeElement:
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: svg-ultralight
3
- Version: 0.37.0
3
+ Version: 0.39.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,6 +10,7 @@ Requires-Dist: lxml
10
10
  Requires-Dist: pillow
11
11
  Requires-Dist: paragraphs
12
12
  Requires-Dist: types-lxml
13
+ Requires-Dist: typing-extensions
13
14
  Provides-Extra: dev
14
15
  Requires-Dist: pytest; extra == "dev"
15
16
  Requires-Dist: commitizen; extra == "dev"
@@ -187,7 +188,7 @@ Another way to add params through the new_element name / float translator. Again
187
188
 
188
189
  ## Extras:
189
190
 
190
- ### query.map_ids_to_bounding_boxes
191
+ ### query.map_elems_to_bounding_boxes
191
192
 
192
193
  Python cannot parse an svg file. Python can *create* an svg file, and Inkscape can parse (and inspect) it. Inkscape has a command-line interface capable of reading an svg file and returning some limited information. This is the only way I know for a Python program to:
193
194
 
@@ -197,7 +198,7 @@ Python cannot parse an svg file. Python can *create* an svg file, and Inkscape c
197
198
 
198
199
  This would be necessary for, e.g., algorithmically fitting text in a box.
199
200
 
200
- from svg_ultralight.queries import map_ids_to_bounding_boxes
201
+ from svg_ultralight.queries import map_elems_to_bounding_boxes
201
202
 
202
203
  You can get a tiny bit more sophisticated with Inkscape bounding-box queries, but not much. This will give you pretty much all you can get out of it.
203
204
 
@@ -0,0 +1,29 @@
1
+ svg_ultralight/__init__.py,sha256=wUc79mKsG6lGZ1xaYijyJ4Sm9lG5-5XgRArVsCI0niY,2554
2
+ svg_ultralight/animate.py,sha256=JSrBm-59BcNXDF0cGgl4-C89eBunjevZnwZxIWt48TU,1112
3
+ svg_ultralight/image_ops.py,sha256=PXN_p5GX91UTvhnwwU-bPuj6WzM9wCx1SqfzR5icNnQ,4686
4
+ svg_ultralight/inkscape.py,sha256=tVFPtwsUcGKGODmYhC9IdGLyT7OAeNCVsDN0hPBSSgg,9194
5
+ svg_ultralight/layout.py,sha256=7LV2I3u4EhqSc6ASvgwDtTZyV-Y1qt2wtvRtH2uKVAE,12799
6
+ svg_ultralight/main.py,sha256=VA7tVMO7fiRI_JkEGaH7UFgzJ5YIbHKx4VHfMnT50hI,7446
7
+ svg_ultralight/metadata.py,sha256=xaIfqhKu52Dl4JOrRlpUsWkkE7Umw8j5Z4waFTli-kI,4234
8
+ svg_ultralight/nsmap.py,sha256=y63upO78Rr-JJT56RWWZuyrsILh6HPoY4GhbYnK1A0g,1244
9
+ svg_ultralight/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ svg_ultralight/query.py,sha256=wR8oixRYA489k03RU5apXaoami2KvYl8BKmwypQGTi0,10972
11
+ svg_ultralight/root_elements.py,sha256=E_H7HXk0M5F3IyFVOxO8PQmhww1-sHTzJhx8hBJPZvg,2911
12
+ svg_ultralight/string_conversion.py,sha256=7gaUWEJptAVZawlvoAJ7JoP996nALoJrAByM3o219Tc,7462
13
+ svg_ultralight/transformations.py,sha256=h1GUD4hvo10ugnSwShDmrwzF048yfug7lCWqCzOTxWs,4296
14
+ svg_ultralight/unit_conversion.py,sha256=g07nhzXdjPvGcJmkhLdFbeDLrSmbI8uFoVgPo7G62Bg,9258
15
+ svg_ultralight/bounding_boxes/__init__.py,sha256=qUEn3r4s-1QNHaguhWhhaNfdP4tl_B6YEqxtiTFuzhQ,78
16
+ svg_ultralight/bounding_boxes/bound_helpers.py,sha256=LFkVsdYFKYCnEL6vLvEa_5cfu8D44ZGYeEEb7_0MnC0,7146
17
+ svg_ultralight/bounding_boxes/supports_bounds.py,sha256=Yi696oJ393y29vql0U6kLMR3soSggLB3_wSACHppoJo,4505
18
+ svg_ultralight/bounding_boxes/type_bound_collection.py,sha256=yW-gwehTrLhgG4Rk-wd6XWP0oLj7Yh_Njrudm8Dn1fk,2637
19
+ svg_ultralight/bounding_boxes/type_bound_element.py,sha256=v-XwW1bxA3tT0CzLWOrRO5EHtLA3ivds8DyA5KFju6w,2196
20
+ svg_ultralight/bounding_boxes/type_bounding_box.py,sha256=XsjM6nR5bKRLFa8z1U84GIC-YbleKoXa0s-UScz9Ewo,13621
21
+ svg_ultralight/bounding_boxes/type_padded_text.py,sha256=fiDZoHIaQbGK8ETrPRa2wa5ykbdFBeeDHw9zGqKtD6E,10674
22
+ svg_ultralight/constructors/__init__.py,sha256=XLOInLhzMERWNnFAs-itMs-OZrBOpvQthZJ2T5duqBE,327
23
+ svg_ultralight/constructors/new_element.py,sha256=hRUW2hR_BTkthEqPClYV7-IeFe9iv2zwb6ehp1k1xDk,3475
24
+ svg_ultralight/strings/__init__.py,sha256=BMGhF1pulscIgkiYvZLr6kPRR0L4lW0jUNFxkul4_EM,295
25
+ svg_ultralight/strings/svg_strings.py,sha256=FQNxNmMkR2M-gCFo_woQKXLgCHi3ncUlRMiaRR_a9nQ,1978
26
+ svg_ultralight-0.39.0.dist-info/METADATA,sha256=--QD3_OuduOUZx2fsCx6FsgTaDAuPAFHkkeKxCKY5Dw,8971
27
+ svg_ultralight-0.39.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
28
+ svg_ultralight-0.39.0.dist-info/top_level.txt,sha256=se-6yqM_0Yg5orJKvKWdjQZ4iR4G_EjhL7oRgju-fdY,15
29
+ svg_ultralight-0.39.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,29 +0,0 @@
1
- svg_ultralight/__init__.py,sha256=wUc79mKsG6lGZ1xaYijyJ4Sm9lG5-5XgRArVsCI0niY,2554
2
- svg_ultralight/animate.py,sha256=JSrBm-59BcNXDF0cGgl4-C89eBunjevZnwZxIWt48TU,1112
3
- svg_ultralight/image_ops.py,sha256=PXN_p5GX91UTvhnwwU-bPuj6WzM9wCx1SqfzR5icNnQ,4686
4
- svg_ultralight/inkscape.py,sha256=M8yTxXOu4NlXnhsMycvEJiIDpnDeiZ_bZakJBM38ZoU,9152
5
- svg_ultralight/layout.py,sha256=FgR45FsHax4xDjGkk9HEVW4OcwhtM8aqw2JUdZs_m7Q,12326
6
- svg_ultralight/main.py,sha256=6oNkZfD27UMdP-oYqp5agS_IGcYb8NkUZwM9Zdyb3SA,7287
7
- svg_ultralight/metadata.py,sha256=xR3ObM0QV7OQ90IKvfigR5B6e0JW6GGVGvTlL5NswWI,4211
8
- svg_ultralight/nsmap.py,sha256=y63upO78Rr-JJT56RWWZuyrsILh6HPoY4GhbYnK1A0g,1244
9
- svg_ultralight/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- svg_ultralight/query.py,sha256=SufFumN11VTWloqjMpSuQ5eghAYx4ABQRIMXRJGs70o,9428
11
- svg_ultralight/root_elements.py,sha256=pt9J6mPrnoTAZVF6vKTZoM_o947I8UCj6MbGcD2JUCk,2869
12
- svg_ultralight/string_conversion.py,sha256=WEmpf75RJmJ2lfJluagAz2wPsz6wM8XvTEwkq4U0vEc,7353
13
- svg_ultralight/transformations.py,sha256=7-6NNh6xZ45mM_933fMuQfRGpI7q9Qrt5qse9e6FUek,4036
14
- svg_ultralight/unit_conversion.py,sha256=g07nhzXdjPvGcJmkhLdFbeDLrSmbI8uFoVgPo7G62Bg,9258
15
- svg_ultralight/bounding_boxes/__init__.py,sha256=qUEn3r4s-1QNHaguhWhhaNfdP4tl_B6YEqxtiTFuzhQ,78
16
- svg_ultralight/bounding_boxes/bound_helpers.py,sha256=V0BlBHDZ9xksVG5dGLPfcyRwPP3jSj6ODdtIpqT-FcI,7273
17
- svg_ultralight/bounding_boxes/supports_bounds.py,sha256=fbHV6mGdeIVV3lS15vBKSrHiHKR7DMg4K5X__4LLmCE,4569
18
- svg_ultralight/bounding_boxes/type_bound_collection.py,sha256=b89TM2UsdaeApyjTQeMbx_FG_WcCiLAImEiHiZ6EWPI,2600
19
- svg_ultralight/bounding_boxes/type_bound_element.py,sha256=9RdxH8osOlAvPdWR0Ww9NsasHLPYFDs-MbydoV48x4E,2239
20
- svg_ultralight/bounding_boxes/type_bounding_box.py,sha256=cDrMp6uwaA--KJIQS2puG10qh8n3TBmiscg-cfk1f3w,16149
21
- svg_ultralight/bounding_boxes/type_padded_text.py,sha256=QA6PfeO_sQYc5pEXuyfyQ3lRUcZAc4B2BthWXpdt3qQ,14848
22
- svg_ultralight/constructors/__init__.py,sha256=XLOInLhzMERWNnFAs-itMs-OZrBOpvQthZJ2T5duqBE,327
23
- svg_ultralight/constructors/new_element.py,sha256=8nqmOEgt3j-aOVeRaMLFHqrwKg2Dm5w0AfuK9MP4ak8,3433
24
- svg_ultralight/strings/__init__.py,sha256=BMGhF1pulscIgkiYvZLr6kPRR0L4lW0jUNFxkul4_EM,295
25
- svg_ultralight/strings/svg_strings.py,sha256=FQNxNmMkR2M-gCFo_woQKXLgCHi3ncUlRMiaRR_a9nQ,1978
26
- svg_ultralight-0.37.0.dist-info/METADATA,sha256=wWTDiiOScZrgr_Np0_XCqyJ9WKEAEKHJRXvPU5Qw5-Y,8933
27
- svg_ultralight-0.37.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
28
- svg_ultralight-0.37.0.dist-info/top_level.txt,sha256=se-6yqM_0Yg5orJKvKWdjQZ4iR4G_EjhL7oRgju-fdY,15
29
- svg_ultralight-0.37.0.dist-info/RECORD,,