svg-ultralight 0.50.2__tar.gz → 0.52.0__tar.gz
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-0.50.2 → svg_ultralight-0.52.0}/PKG-INFO +1 -1
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/pyproject.toml +2 -2
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/bounding_boxes/padded_text_initializers.py +61 -9
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/bounding_boxes/supports_bounds.py +1 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/bounding_boxes/type_bound_collection.py +6 -3
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/bounding_boxes/type_bound_element.py +5 -2
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/bounding_boxes/type_bounding_box.py +7 -1
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/bounding_boxes/type_padded_text.py +15 -2
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/font_tools/font_info.py +20 -5
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/transformations.py +15 -3
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/README.md +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/__init__.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/animate.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/attrib_hints.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/bounding_boxes/__init__.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/bounding_boxes/bound_helpers.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/constructors/__init__.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/constructors/new_element.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/font_tools/__init__.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/font_tools/comp_results.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/image_ops.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/inkscape.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/layout.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/main.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/metadata.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/nsmap.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/py.typed +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/query.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/read_svg.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/root_elements.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/string_conversion.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/strings/__init__.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/strings/svg_strings.py +0 -0
- {svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/unit_conversion.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "svg-ultralight"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.52.0"
|
|
4
4
|
description = "a sensible way to create svg files with Python"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -33,7 +33,7 @@ dev = [
|
|
|
33
33
|
|
|
34
34
|
[tool.commitizen]
|
|
35
35
|
name = "cz_conventional_commits"
|
|
36
|
-
version = "0.
|
|
36
|
+
version = "0.52.0"
|
|
37
37
|
tag_format = "$version"
|
|
38
38
|
major-version-zero = true
|
|
39
39
|
version_files = ["pyproject.toml:^version"]
|
|
@@ -23,11 +23,12 @@ to 16px.
|
|
|
23
23
|
from __future__ import annotations
|
|
24
24
|
|
|
25
25
|
from copy import deepcopy
|
|
26
|
-
from typing import TYPE_CHECKING
|
|
26
|
+
from typing import TYPE_CHECKING, overload
|
|
27
27
|
|
|
28
28
|
from svg_ultralight.bounding_boxes.type_padded_text import PaddedText
|
|
29
29
|
from svg_ultralight.constructors import new_element, update_element
|
|
30
30
|
from svg_ultralight.font_tools.font_info import (
|
|
31
|
+
FTFontInfo,
|
|
31
32
|
get_padded_text_info,
|
|
32
33
|
get_svg_font_attributes,
|
|
33
34
|
)
|
|
@@ -99,6 +100,7 @@ def pad_text(
|
|
|
99
100
|
return PaddedText(text_elem, bbox, tpad, rpad, bpad, lpad)
|
|
100
101
|
|
|
101
102
|
|
|
103
|
+
@overload
|
|
102
104
|
def pad_text_ft(
|
|
103
105
|
font: str | os.PathLike[str],
|
|
104
106
|
text: str,
|
|
@@ -109,11 +111,38 @@ def pad_text_ft(
|
|
|
109
111
|
y_bounds_reference: str | None = None,
|
|
110
112
|
attrib: OptionalElemAttribMapping = None,
|
|
111
113
|
**attributes: ElemAttrib,
|
|
112
|
-
) -> PaddedText:
|
|
114
|
+
) -> PaddedText: ...
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@overload
|
|
118
|
+
def pad_text_ft(
|
|
119
|
+
font: str | os.PathLike[str],
|
|
120
|
+
text: list[str],
|
|
121
|
+
font_size: float | None = None,
|
|
122
|
+
ascent: float | None = None,
|
|
123
|
+
descent: float | None = None,
|
|
124
|
+
*,
|
|
125
|
+
y_bounds_reference: str | None = None,
|
|
126
|
+
attrib: OptionalElemAttribMapping = None,
|
|
127
|
+
**attributes: ElemAttrib,
|
|
128
|
+
) -> list[PaddedText]: ...
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def pad_text_ft(
|
|
132
|
+
font: str | os.PathLike[str],
|
|
133
|
+
text: str | list[str],
|
|
134
|
+
font_size: float | None = None,
|
|
135
|
+
ascent: float | None = None,
|
|
136
|
+
descent: float | None = None,
|
|
137
|
+
*,
|
|
138
|
+
y_bounds_reference: str | None = None,
|
|
139
|
+
attrib: OptionalElemAttribMapping = None,
|
|
140
|
+
**attributes: ElemAttrib,
|
|
141
|
+
) -> PaddedText | list[PaddedText]:
|
|
113
142
|
"""Create a new PaddedText instance using fontTools.
|
|
114
143
|
|
|
115
144
|
:param font: path to a font file.
|
|
116
|
-
:param text: the text of the text element.
|
|
145
|
+
:param text: the text of the text element or a list of text strings.
|
|
117
146
|
:param font_size: the font size to use.
|
|
118
147
|
:param ascent: the ascent of the font. If not provided, it will be calculated
|
|
119
148
|
from the font file.
|
|
@@ -130,7 +159,8 @@ def pad_text_ft(
|
|
|
130
159
|
:param attributes: additional attributes to set on the text element. There is a
|
|
131
160
|
chance these will cause the font element to exceed the BoundingBox of the
|
|
132
161
|
PaddedText instance.
|
|
133
|
-
:return: a PaddedText instance with a line_gap defined.
|
|
162
|
+
:return: a PaddedText instance with a line_gap defined. If a list of strings is
|
|
163
|
+
given for parameter `text`, a list of PaddedText instances is returned.
|
|
134
164
|
"""
|
|
135
165
|
attributes.update(attrib or {})
|
|
136
166
|
attributes_ = format_attr_dict(**attributes)
|
|
@@ -142,11 +172,33 @@ def pad_text_ft(
|
|
|
142
172
|
_ = attributes_.pop("font-weight", None)
|
|
143
173
|
_ = attributes_.pop("font-stretch", None)
|
|
144
174
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
175
|
+
font_info = FTFontInfo(font)
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
input_one_text_item = False
|
|
179
|
+
if isinstance(text, str):
|
|
180
|
+
input_one_text_item = True
|
|
181
|
+
text = [text]
|
|
182
|
+
|
|
183
|
+
elems: list[PaddedText] = []
|
|
184
|
+
for text_item in text:
|
|
185
|
+
text_info = get_padded_text_info(
|
|
186
|
+
font_info,
|
|
187
|
+
text_item,
|
|
188
|
+
font_size,
|
|
189
|
+
ascent,
|
|
190
|
+
descent,
|
|
191
|
+
y_bounds_reference=y_bounds_reference,
|
|
192
|
+
)
|
|
193
|
+
elem = text_info.new_element(**attributes_)
|
|
194
|
+
elems.append(
|
|
195
|
+
PaddedText(elem, text_info.bbox, *text_info.padding, text_info.line_gap)
|
|
196
|
+
)
|
|
197
|
+
finally:
|
|
198
|
+
font_info.font.close()
|
|
199
|
+
if input_one_text_item:
|
|
200
|
+
return elems[0]
|
|
201
|
+
return elems
|
|
150
202
|
|
|
151
203
|
|
|
152
204
|
def pad_text_mix(
|
|
@@ -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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
582
|
+
return bbox
|
|
571
583
|
|
|
572
584
|
@property
|
|
573
585
|
def ascent(self) -> float:
|
|
@@ -644,7 +656,7 @@ def get_font_size_given_height(font: str | os.PathLike[str], height: float) -> f
|
|
|
644
656
|
|
|
645
657
|
|
|
646
658
|
def get_padded_text_info(
|
|
647
|
-
font: str | os.PathLike[str],
|
|
659
|
+
font: str | os.PathLike[str] | FTFontInfo,
|
|
648
660
|
text: str,
|
|
649
661
|
font_size: float | None = None,
|
|
650
662
|
ascent: float | None = None,
|
|
@@ -669,7 +681,10 @@ def get_padded_text_info(
|
|
|
669
681
|
:return: A FTTextInfo object with the information necessary to create a
|
|
670
682
|
PaddedText instance: bbox, tpad, rpad, bpad, lpad.
|
|
671
683
|
"""
|
|
672
|
-
|
|
684
|
+
if isinstance(font, FTFontInfo):
|
|
685
|
+
font_info = font
|
|
686
|
+
else:
|
|
687
|
+
font_info = FTFontInfo(font)
|
|
673
688
|
if y_bounds_reference:
|
|
674
689
|
capline_info = FTTextInfo(font_info, y_bounds_reference, font_size)
|
|
675
690
|
ascent = -capline_info.bbox.y
|
|
@@ -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(
|
|
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
|
-
:
|
|
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
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/bounding_boxes/__init__.py
RENAMED
|
File without changes
|
{svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/bounding_boxes/bound_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/constructors/new_element.py
RENAMED
|
File without changes
|
|
File without changes
|
{svg_ultralight-0.50.2 → svg_ultralight-0.52.0}/src/svg_ultralight/font_tools/comp_results.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|