svg-ultralight 0.48.0__py3-none-any.whl → 0.50.1__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.

Files changed (37) hide show
  1. svg_ultralight/__init__.py +108 -105
  2. svg_ultralight/animate.py +40 -40
  3. svg_ultralight/attrib_hints.py +13 -14
  4. svg_ultralight/bounding_boxes/__init__.py +5 -5
  5. svg_ultralight/bounding_boxes/bound_helpers.py +189 -189
  6. svg_ultralight/bounding_boxes/padded_text_initializers.py +207 -207
  7. svg_ultralight/bounding_boxes/supports_bounds.py +166 -166
  8. svg_ultralight/bounding_boxes/type_bound_collection.py +71 -71
  9. svg_ultralight/bounding_boxes/type_bound_element.py +65 -65
  10. svg_ultralight/bounding_boxes/type_bounding_box.py +396 -396
  11. svg_ultralight/bounding_boxes/type_padded_text.py +411 -411
  12. svg_ultralight/constructors/__init__.py +14 -14
  13. svg_ultralight/constructors/new_element.py +115 -115
  14. svg_ultralight/font_tools/__init__.py +5 -5
  15. svg_ultralight/font_tools/comp_results.py +295 -293
  16. svg_ultralight/font_tools/font_info.py +793 -792
  17. svg_ultralight/image_ops.py +156 -156
  18. svg_ultralight/inkscape.py +261 -261
  19. svg_ultralight/layout.py +290 -291
  20. svg_ultralight/main.py +183 -198
  21. svg_ultralight/metadata.py +122 -122
  22. svg_ultralight/nsmap.py +36 -36
  23. svg_ultralight/py.typed +5 -0
  24. svg_ultralight/query.py +254 -249
  25. svg_ultralight/read_svg.py +58 -0
  26. svg_ultralight/root_elements.py +87 -87
  27. svg_ultralight/string_conversion.py +244 -244
  28. svg_ultralight/strings/__init__.py +21 -13
  29. svg_ultralight/strings/svg_strings.py +106 -67
  30. svg_ultralight/transformations.py +140 -141
  31. svg_ultralight/unit_conversion.py +247 -248
  32. {svg_ultralight-0.48.0.dist-info → svg_ultralight-0.50.1.dist-info}/METADATA +208 -214
  33. svg_ultralight-0.50.1.dist-info/RECORD +34 -0
  34. svg_ultralight-0.50.1.dist-info/WHEEL +4 -0
  35. svg_ultralight-0.48.0.dist-info/RECORD +0 -34
  36. svg_ultralight-0.48.0.dist-info/WHEEL +0 -5
  37. svg_ultralight-0.48.0.dist-info/top_level.txt +0 -1
@@ -1,207 +1,207 @@
1
- """Functions that create PaddedText instances.
2
-
3
- Three variants:
4
-
5
- - `pad_text`: uses Inkscape to measure text bounds
6
-
7
- - `pad_text_ft`: uses fontTools to measure text bounds (faster, and you get line_gap)
8
-
9
- - `pad_text_mix`: uses Inkscape and fontTools to give true ascent, descent, and
10
- line_gap while correcting some of the layout differences between fontTools and
11
- Inkscape.
12
-
13
- There is a default font size for pad_text if an element is passed. There is also a
14
- default for the other pad_text_ functions, but it taken from the font file and is
15
- usually 1024, so it won't be easy to miss. The default for standard pad_text is to
16
- prevent surprises if Inksape defaults to font-size 12pt while your browser defaults
17
- to 16px.
18
-
19
- :author: Shay Hill
20
- :created: 2025-06-09
21
- """
22
-
23
- from __future__ import annotations
24
-
25
- from copy import deepcopy
26
- from typing import TYPE_CHECKING
27
-
28
- from svg_ultralight.bounding_boxes.type_padded_text import PaddedText
29
- from svg_ultralight.constructors import new_element, update_element
30
- from svg_ultralight.font_tools.font_info import (
31
- get_padded_text_info,
32
- get_svg_font_attributes,
33
- )
34
- from svg_ultralight.query import get_bounding_boxes
35
- from svg_ultralight.string_conversion import format_attr_dict, format_number
36
-
37
- if TYPE_CHECKING:
38
- import os
39
-
40
- from lxml.etree import (
41
- _Element as EtreeElement, # pyright: ignore[reportPrivateUsage]
42
- )
43
-
44
- from svg_ultralight.attrib_hints import ElemAttrib, OptionalElemAttribMapping
45
-
46
- DEFAULT_Y_BOUNDS_REFERENCE = "{[|gjpqyf"
47
-
48
- # A default font size for pad_text if font-size is not specified in the reference
49
- # element.
50
- DEFAULT_FONT_SIZE_FOR_PAD_TEXT = 12.0 # Default font size for pad_text if not specified
51
-
52
-
53
- def pad_text(
54
- inkscape: str | os.PathLike[str],
55
- text_elem: EtreeElement,
56
- y_bounds_reference: str | None = None,
57
- *,
58
- font: str | os.PathLike[str] | None = None,
59
- ) -> PaddedText:
60
- r"""Create a PaddedText instance from a text element.
61
-
62
- :param inkscape: path to an inkscape executable on your local file system
63
- IMPORTANT: path cannot end with ``.exe``.
64
- Use something like ``"C:\\Program Files\\Inkscape\\inkscape"``
65
- :param text_elem: an etree element with a text tag
66
- :param y_bounds_reference: an optional string to use to determine the ascent and
67
- capline of the font. The default is a good choice, which approaches or even
68
- meets the ascent of descent of most fonts without using utf-8 characters. You
69
- might want to use a letter like "M" or even "x" if you are using an all-caps
70
- string and want to center between the capline and baseline or if you'd like
71
- to center between the baseline and x-line.
72
- :param font: optionally add a path to a font file to use for the text element.
73
- This is going to conflict with any font-family, font-style, or other
74
- font-related attributes *except* font-size. You likely want to use
75
- `font_tools.new_padded_text` if you're going to pass a font path, but you can
76
- use it here to compare results between `pad_text` and `new_padded_text`.
77
- :return: a PaddedText instance
78
- """
79
- if y_bounds_reference is None:
80
- y_bounds_reference = DEFAULT_Y_BOUNDS_REFERENCE
81
- if font is not None:
82
- _ = update_element(text_elem, **get_svg_font_attributes(font))
83
- if "font-size" not in text_elem.attrib:
84
- text_elem.attrib["font-size"] = format_number(DEFAULT_FONT_SIZE_FOR_PAD_TEXT)
85
- rmargin_ref = deepcopy(text_elem)
86
- capline_ref = deepcopy(text_elem)
87
- _ = rmargin_ref.attrib.pop("id", None)
88
- _ = capline_ref.attrib.pop("id", None)
89
- rmargin_ref.attrib["text-anchor"] = "end"
90
- capline_ref.text = y_bounds_reference
91
-
92
- bboxes = get_bounding_boxes(inkscape, text_elem, rmargin_ref, capline_ref)
93
- bbox, rmargin_bbox, capline_bbox = bboxes
94
-
95
- tpad = bbox.y - capline_bbox.y
96
- rpad = -rmargin_bbox.x2
97
- bpad = capline_bbox.y2 - bbox.y2
98
- lpad = bbox.x
99
- return PaddedText(text_elem, bbox, tpad, rpad, bpad, lpad)
100
-
101
-
102
- def pad_text_ft(
103
- font: str | os.PathLike[str],
104
- text: str,
105
- font_size: float | None = None,
106
- ascent: float | None = None,
107
- descent: float | None = None,
108
- *,
109
- y_bounds_reference: str | None = None,
110
- attrib: OptionalElemAttribMapping = None,
111
- **attributes: ElemAttrib,
112
- ) -> PaddedText:
113
- """Create a new PaddedText instance using fontTools.
114
-
115
- :param font: path to a font file.
116
- :param text: the text of the text element.
117
- :param font_size: the font size to use.
118
- :param ascent: the ascent of the font. If not provided, it will be calculated
119
- from the font file.
120
- :param descent: the descent of the font. If not provided, it will be calculated
121
- from the font file.
122
- :param y_bounds_reference: optional character or string to use as a reference
123
- for the ascent and descent. If provided, the ascent and descent will be the y
124
- extents of the capline reference. This argument is provided to mimic the
125
- behavior of the query module's `pad_text` function. `pad_text` does no
126
- inspect font files and relies on Inkscape to measure reference characters.
127
- :param attrib: optionally pass additional attributes as a mapping instead of as
128
- anonymous kwargs. This is useful for pleasing the linter when unpacking a
129
- dictionary into a function call.
130
- :param attributes: additional attributes to set on the text element. There is a
131
- chance these will cause the font element to exceed the BoundingBox of the
132
- PaddedText instance.
133
- :return: a PaddedText instance with a line_gap defined.
134
- """
135
- attributes.update(attrib or {})
136
- attributes_ = format_attr_dict(**attributes)
137
- attributes_.update(get_svg_font_attributes(font))
138
-
139
- _ = attributes_.pop("font-size", None)
140
- _ = attributes_.pop("font-family", None)
141
- _ = attributes_.pop("font-style", None)
142
- _ = attributes_.pop("font-weight", None)
143
- _ = attributes_.pop("font-stretch", None)
144
-
145
- info = get_padded_text_info(
146
- font, text, font_size, ascent, descent, y_bounds_reference=y_bounds_reference
147
- )
148
- elem = info.new_element(**attributes_)
149
- return PaddedText(elem, info.bbox, *info.padding, info.line_gap)
150
-
151
-
152
- def pad_text_mix(
153
- inkscape: str | os.PathLike[str],
154
- font: str | os.PathLike[str],
155
- text: str,
156
- font_size: float | None = None,
157
- ascent: float | None = None,
158
- descent: float | None = None,
159
- *,
160
- y_bounds_reference: str | None = None,
161
- attrib: OptionalElemAttribMapping = None,
162
- **attributes: ElemAttrib,
163
- ) -> PaddedText:
164
- """Use Inkscape text bounds and fill missing with fontTools.
165
-
166
- :param font: path to a font file.
167
- :param text: the text of the text element.
168
- :param font_size: the font size to use.
169
- :param ascent: the ascent of the font. If not provided, it will be calculated
170
- from the font file.
171
- :param descent: the descent of the font. If not provided, it will be calculated
172
- from the font file.
173
- :param y_bounds_reference: optional character or string to use as a reference
174
- for the ascent and descent. If provided, the ascent and descent will be the y
175
- extents of the capline reference. This argument is provided to mimic the
176
- behavior of the query module's `pad_text` function. `pad_text` does no
177
- inspect font files and relies on Inkscape to measure reference characters.
178
- :param attrib: optionally pass additional attributes as a mapping instead of as
179
- anonymous kwargs. This is useful for pleasing the linter when unpacking a
180
- dictionary into a function call.
181
- :param attributes: additional attributes to set on the text element. There is a
182
- chance these will cause the font element to exceed the BoundingBox of the
183
- PaddedText instance.
184
- :return: a PaddedText instance with a line_gap defined.
185
- """
186
- attributes.update(attrib or {})
187
- elem = new_element("text", text=text, **attributes)
188
- padded_inkscape = pad_text(inkscape, elem, y_bounds_reference, font=font)
189
- padded_fonttools = pad_text_ft(
190
- font,
191
- text,
192
- font_size,
193
- ascent,
194
- descent,
195
- y_bounds_reference=y_bounds_reference,
196
- attrib=attributes,
197
- )
198
- bbox = padded_inkscape.unpadded_bbox
199
- rpad = padded_inkscape.rpad
200
- lpad = padded_inkscape.lpad
201
- if y_bounds_reference is None:
202
- tpad = padded_fonttools.tpad
203
- bpad = padded_fonttools.bpad
204
- else:
205
- tpad = padded_inkscape.tpad
206
- bpad = padded_inkscape.bpad
207
- return PaddedText(elem, bbox, tpad, rpad, bpad, lpad, padded_fonttools.line_gap)
1
+ """Functions that create PaddedText instances.
2
+
3
+ Three variants:
4
+
5
+ - `pad_text`: uses Inkscape to measure text bounds
6
+
7
+ - `pad_text_ft`: uses fontTools to measure text bounds (faster, and you get line_gap)
8
+
9
+ - `pad_text_mix`: uses Inkscape and fontTools to give true ascent, descent, and
10
+ line_gap while correcting some of the layout differences between fontTools and
11
+ Inkscape.
12
+
13
+ There is a default font size for pad_text if an element is passed. There is also a
14
+ default for the other pad_text_ functions, but it taken from the font file and is
15
+ usually 1024, so it won't be easy to miss. The default for standard pad_text is to
16
+ prevent surprises if Inksape defaults to font-size 12pt while your browser defaults
17
+ to 16px.
18
+
19
+ :author: Shay Hill
20
+ :created: 2025-06-09
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from copy import deepcopy
26
+ from typing import TYPE_CHECKING
27
+
28
+ from svg_ultralight.bounding_boxes.type_padded_text import PaddedText
29
+ from svg_ultralight.constructors import new_element, update_element
30
+ from svg_ultralight.font_tools.font_info import (
31
+ get_padded_text_info,
32
+ get_svg_font_attributes,
33
+ )
34
+ from svg_ultralight.query import get_bounding_boxes
35
+ from svg_ultralight.string_conversion import format_attr_dict, format_number
36
+
37
+ if TYPE_CHECKING:
38
+ import os
39
+
40
+ from lxml.etree import (
41
+ _Element as EtreeElement, # pyright: ignore[reportPrivateUsage]
42
+ )
43
+
44
+ from svg_ultralight.attrib_hints import ElemAttrib, OptionalElemAttribMapping
45
+
46
+ DEFAULT_Y_BOUNDS_REFERENCE = "{[|gjpqyf"
47
+
48
+ # A default font size for pad_text if font-size is not specified in the reference
49
+ # element.
50
+ DEFAULT_FONT_SIZE_FOR_PAD_TEXT = 12.0 # Default font size for pad_text if not specified
51
+
52
+
53
+ def pad_text(
54
+ inkscape: str | os.PathLike[str],
55
+ text_elem: EtreeElement,
56
+ y_bounds_reference: str | None = None,
57
+ *,
58
+ font: str | os.PathLike[str] | None = None,
59
+ ) -> PaddedText:
60
+ r"""Create a PaddedText instance from a text element.
61
+
62
+ :param inkscape: path to an inkscape executable on your local file system
63
+ IMPORTANT: path cannot end with ``.exe``.
64
+ Use something like ``"C:\\Program Files\\Inkscape\\inkscape"``
65
+ :param text_elem: an etree element with a text tag
66
+ :param y_bounds_reference: an optional string to use to determine the ascent and
67
+ capline of the font. The default is a good choice, which approaches or even
68
+ meets the ascent of descent of most fonts without using utf-8 characters. You
69
+ might want to use a letter like "M" or even "x" if you are using an all-caps
70
+ string and want to center between the capline and baseline or if you'd like
71
+ to center between the baseline and x-line.
72
+ :param font: optionally add a path to a font file to use for the text element.
73
+ This is going to conflict with any font-family, font-style, or other
74
+ font-related attributes *except* font-size. You likely want to use
75
+ `font_tools.new_padded_text` if you're going to pass a font path, but you can
76
+ use it here to compare results between `pad_text` and `new_padded_text`.
77
+ :return: a PaddedText instance
78
+ """
79
+ if y_bounds_reference is None:
80
+ y_bounds_reference = DEFAULT_Y_BOUNDS_REFERENCE
81
+ if font is not None:
82
+ _ = update_element(text_elem, **get_svg_font_attributes(font))
83
+ if "font-size" not in text_elem.attrib:
84
+ text_elem.attrib["font-size"] = format_number(DEFAULT_FONT_SIZE_FOR_PAD_TEXT)
85
+ rmargin_ref = deepcopy(text_elem)
86
+ capline_ref = deepcopy(text_elem)
87
+ _ = rmargin_ref.attrib.pop("id", None)
88
+ _ = capline_ref.attrib.pop("id", None)
89
+ rmargin_ref.attrib["text-anchor"] = "end"
90
+ capline_ref.text = y_bounds_reference
91
+
92
+ bboxes = get_bounding_boxes(inkscape, text_elem, rmargin_ref, capline_ref)
93
+ bbox, rmargin_bbox, capline_bbox = bboxes
94
+
95
+ tpad = bbox.y - capline_bbox.y
96
+ rpad = -rmargin_bbox.x2
97
+ bpad = capline_bbox.y2 - bbox.y2
98
+ lpad = bbox.x
99
+ return PaddedText(text_elem, bbox, tpad, rpad, bpad, lpad)
100
+
101
+
102
+ def pad_text_ft(
103
+ font: str | os.PathLike[str],
104
+ text: str,
105
+ font_size: float | None = None,
106
+ ascent: float | None = None,
107
+ descent: float | None = None,
108
+ *,
109
+ y_bounds_reference: str | None = None,
110
+ attrib: OptionalElemAttribMapping = None,
111
+ **attributes: ElemAttrib,
112
+ ) -> PaddedText:
113
+ """Create a new PaddedText instance using fontTools.
114
+
115
+ :param font: path to a font file.
116
+ :param text: the text of the text element.
117
+ :param font_size: the font size to use.
118
+ :param ascent: the ascent of the font. If not provided, it will be calculated
119
+ from the font file.
120
+ :param descent: the descent of the font. If not provided, it will be calculated
121
+ from the font file.
122
+ :param y_bounds_reference: optional character or string to use as a reference
123
+ for the ascent and descent. If provided, the ascent and descent will be the y
124
+ extents of the capline reference. This argument is provided to mimic the
125
+ behavior of the query module's `pad_text` function. `pad_text` does no
126
+ inspect font files and relies on Inkscape to measure reference characters.
127
+ :param attrib: optionally pass additional attributes as a mapping instead of as
128
+ anonymous kwargs. This is useful for pleasing the linter when unpacking a
129
+ dictionary into a function call.
130
+ :param attributes: additional attributes to set on the text element. There is a
131
+ chance these will cause the font element to exceed the BoundingBox of the
132
+ PaddedText instance.
133
+ :return: a PaddedText instance with a line_gap defined.
134
+ """
135
+ attributes.update(attrib or {})
136
+ attributes_ = format_attr_dict(**attributes)
137
+ attributes_.update(get_svg_font_attributes(font))
138
+
139
+ _ = attributes_.pop("font-size", None)
140
+ _ = attributes_.pop("font-family", None)
141
+ _ = attributes_.pop("font-style", None)
142
+ _ = attributes_.pop("font-weight", None)
143
+ _ = attributes_.pop("font-stretch", None)
144
+
145
+ info = get_padded_text_info(
146
+ font, text, font_size, ascent, descent, y_bounds_reference=y_bounds_reference
147
+ )
148
+ elem = info.new_element(**attributes_)
149
+ return PaddedText(elem, info.bbox, *info.padding, info.line_gap)
150
+
151
+
152
+ def pad_text_mix(
153
+ inkscape: str | os.PathLike[str],
154
+ font: str | os.PathLike[str],
155
+ text: str,
156
+ font_size: float | None = None,
157
+ ascent: float | None = None,
158
+ descent: float | None = None,
159
+ *,
160
+ y_bounds_reference: str | None = None,
161
+ attrib: OptionalElemAttribMapping = None,
162
+ **attributes: ElemAttrib,
163
+ ) -> PaddedText:
164
+ """Use Inkscape text bounds and fill missing with fontTools.
165
+
166
+ :param font: path to a font file.
167
+ :param text: the text of the text element.
168
+ :param font_size: the font size to use.
169
+ :param ascent: the ascent of the font. If not provided, it will be calculated
170
+ from the font file.
171
+ :param descent: the descent of the font. If not provided, it will be calculated
172
+ from the font file.
173
+ :param y_bounds_reference: optional character or string to use as a reference
174
+ for the ascent and descent. If provided, the ascent and descent will be the y
175
+ extents of the capline reference. This argument is provided to mimic the
176
+ behavior of the query module's `pad_text` function. `pad_text` does no
177
+ inspect font files and relies on Inkscape to measure reference characters.
178
+ :param attrib: optionally pass additional attributes as a mapping instead of as
179
+ anonymous kwargs. This is useful for pleasing the linter when unpacking a
180
+ dictionary into a function call.
181
+ :param attributes: additional attributes to set on the text element. There is a
182
+ chance these will cause the font element to exceed the BoundingBox of the
183
+ PaddedText instance.
184
+ :return: a PaddedText instance with a line_gap defined.
185
+ """
186
+ attributes.update(attrib or {})
187
+ elem = new_element("text", text=text, **attributes)
188
+ padded_inkscape = pad_text(inkscape, elem, y_bounds_reference, font=font)
189
+ padded_fonttools = pad_text_ft(
190
+ font,
191
+ text,
192
+ font_size,
193
+ ascent,
194
+ descent,
195
+ y_bounds_reference=y_bounds_reference,
196
+ attrib=attributes,
197
+ )
198
+ bbox = padded_inkscape.unpadded_bbox
199
+ rpad = padded_inkscape.rpad
200
+ lpad = padded_inkscape.lpad
201
+ if y_bounds_reference is None:
202
+ tpad = padded_fonttools.tpad
203
+ bpad = padded_fonttools.bpad
204
+ else:
205
+ tpad = padded_inkscape.tpad
206
+ bpad = padded_inkscape.bpad
207
+ return PaddedText(elem, bbox, tpad, rpad, bpad, lpad, padded_fonttools.line_gap)