svg-ultralight 0.47.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 -201
  6. svg_ultralight/bounding_boxes/padded_text_initializers.py +207 -206
  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 -784
  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.47.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.47.0.dist-info/RECORD +0 -34
  36. svg_ultralight-0.47.0.dist-info/WHEEL +0 -5
  37. svg_ultralight-0.47.0.dist-info/top_level.txt +0 -1
@@ -0,0 +1,58 @@
1
+ """Read SVG file and extract text content.
2
+
3
+ Note about svg resolution: Despite the fact that vector graphics have effectively
4
+ infinite resolution, ePub apparently uses the actual geometry size to determine the
5
+ resolution of the image. For images ike a business card drawn in real-world inches
6
+ (3.5" width), an ePub will assume a size of 3.5 pixels. There may be some unit for
7
+ the width and height variables (for InDesign, it's pnt) that addresses this, but I
8
+ don't trust it to be consistent across ePub readers. I adjust the units to something
9
+ large, then use CSS to scale it down to the correct size.
10
+
11
+ :author: Shay Hill
12
+ :created: 2025-07-28
13
+ """
14
+
15
+ import os
16
+ from pathlib import Path
17
+
18
+ from lxml import etree
19
+ from lxml.etree import _Element as EtreeElement # pyright: ignore[reportPrivateUsage]
20
+
21
+ import svg_ultralight as su
22
+
23
+
24
+ def get_bounding_box_from_root(root: EtreeElement) -> su.BoundingBox:
25
+ """Extract bounding box from SVG root element.
26
+
27
+ :param root: the root element of the SVG file
28
+ :raise ValueError: if the viewBox attribute is not present
29
+
30
+ """
31
+ viewbox = root.get("viewBox", "")
32
+ try:
33
+ x, y, width, height = map(float, viewbox.split())
34
+ except ValueError as e:
35
+ msg = f"Invalid or missing viewBox attribute: '{viewbox}'"
36
+ raise ValueError(msg) from e
37
+ return su.BoundingBox(x, y, width, height)
38
+
39
+
40
+ def parse(svg_file: str | os.PathLike[str]) -> su.BoundElement:
41
+ """Import an SVG file and return an SVG object.
42
+
43
+ :param svg_file: Path to the SVG file.
44
+ :return: A BoundElement containing the SVG content and the svg viewBox as a
45
+ BoundingBox.
46
+
47
+ Near equivalent to `etree.parse(file).getroot()`, but returns a BoundElement
48
+ instance. This will only work with SVG files that have a viewBox attribute.
49
+ """
50
+ with Path(svg_file).open("r", encoding="utf-8") as f:
51
+ root = etree.parse(f).getroot()
52
+ if len(root) == 1:
53
+ elem = root[0]
54
+ else:
55
+ elem = su.new_element("g")
56
+ elem.extend(list(root))
57
+ bbox = get_bounding_box_from_root(root)
58
+ return su.BoundElement(elem, bbox)
@@ -1,87 +1,87 @@
1
- """Build root elements in various ways.
2
-
3
- :author: Shay Hill
4
- :created: 2023-09-23
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- from typing import TYPE_CHECKING
10
-
11
- from svg_ultralight.bounding_boxes import bound_helpers as bound
12
- from svg_ultralight.bounding_boxes.type_bounding_box import BoundingBox
13
- from svg_ultralight.main import new_svg_root
14
-
15
- if TYPE_CHECKING:
16
- from lxml.etree import (
17
- _Element as EtreeElement, # pyright: ignore[reportPrivateUsage]
18
- )
19
-
20
- from svg_ultralight.attrib_hints import ElemAttrib, OptionalElemAttribMapping
21
- from svg_ultralight.bounding_boxes.supports_bounds import SupportsBounds
22
- from svg_ultralight.layout import PadArg
23
-
24
-
25
- def _viewbox_args_from_bboxes(*bboxes: BoundingBox) -> dict[str, float]:
26
- """Create x_, y_, width_, height_ new_svg_root arguments from bounding boxes.
27
-
28
- :param bbox: bounding boxes to merge
29
- :return: dict of new_svg_root arguments
30
- """
31
- merged = BoundingBox.merged(*bboxes)
32
- return {
33
- "x_": merged.x,
34
- "y_": merged.y,
35
- "width_": merged.width,
36
- "height_": merged.height,
37
- }
38
-
39
-
40
- def new_svg_root_around_bounds(
41
- *bounded: SupportsBounds | EtreeElement,
42
- pad_: PadArg = 0,
43
- print_width_: float | str | None = None,
44
- print_height_: float | str | None = None,
45
- dpu_: float = 1,
46
- nsmap: dict[str | None, str] | None = None,
47
- attrib: OptionalElemAttribMapping = None,
48
- **attributes: ElemAttrib,
49
- ) -> EtreeElement:
50
- """Create svg root around BoundElements.
51
-
52
- :param bounded: BoundingBox istances, BoundElement instances, PaddedText
53
- instances, or any other EtreeElement instances. Anything that isn't a
54
- bounding box or SupportsBounds will be ignored.
55
- :param pad_: optionally increase viewBox by pad in all directions. Acceps a
56
- single value or a tuple of values applied to (cycled over) top, right,
57
- bottom, left. pad can be floats or dimension strings*
58
- :param print_width_: optionally explicitly set unpadded width in units
59
- (float) or a dimension string*
60
- :param print_height_: optionally explicitly set unpadded height in units
61
- (float) or a dimension string*
62
- :param dpu_: dots per unit. Scale the output by this factor. This is
63
- different from print_width_ and print_height_ in that dpu_ scales the
64
- *padded* output.
65
- :param nsmap: optionally pass a namespace map of your choosing
66
- :param attrib: optionally pass additional attributes as a mapping instead of as
67
- anonymous kwargs. This is useful for pleasing the linter when unpacking a
68
- dictionary into a function call.
69
- :param attributes: element attribute names and values
70
- :return: root svg element
71
- :raise ValueError: if no bounding boxes are found in bounded
72
- """
73
- attributes.update(attrib or {})
74
- bbox = bound.new_bbox_union(*bounded)
75
- viewbox = _viewbox_args_from_bboxes(bbox)
76
- return new_svg_root(
77
- x_=viewbox["x_"],
78
- y_=viewbox["y_"],
79
- width_=viewbox["width_"],
80
- height_=viewbox["height_"],
81
- pad_=pad_,
82
- print_width_=print_width_,
83
- print_height_=print_height_,
84
- dpu_=dpu_,
85
- nsmap=nsmap,
86
- attrib=attributes,
87
- )
1
+ """Build root elements in various ways.
2
+
3
+ :author: Shay Hill
4
+ :created: 2023-09-23
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING
10
+
11
+ from svg_ultralight.bounding_boxes import bound_helpers as bound
12
+ from svg_ultralight.bounding_boxes.type_bounding_box import BoundingBox
13
+ from svg_ultralight.main import new_svg_root
14
+
15
+ if TYPE_CHECKING:
16
+ from lxml.etree import (
17
+ _Element as EtreeElement, # pyright: ignore[reportPrivateUsage]
18
+ )
19
+
20
+ from svg_ultralight.attrib_hints import ElemAttrib, OptionalElemAttribMapping
21
+ from svg_ultralight.bounding_boxes.supports_bounds import SupportsBounds
22
+ from svg_ultralight.layout import PadArg
23
+
24
+
25
+ def _viewbox_args_from_bboxes(*bboxes: BoundingBox) -> dict[str, float]:
26
+ """Create x_, y_, width_, height_ new_svg_root arguments from bounding boxes.
27
+
28
+ :param bbox: bounding boxes to merge
29
+ :return: dict of new_svg_root arguments
30
+ """
31
+ merged = BoundingBox.merged(*bboxes)
32
+ return {
33
+ "x_": merged.x,
34
+ "y_": merged.y,
35
+ "width_": merged.width,
36
+ "height_": merged.height,
37
+ }
38
+
39
+
40
+ def new_svg_root_around_bounds(
41
+ *bounded: SupportsBounds | EtreeElement,
42
+ pad_: PadArg = 0,
43
+ print_width_: float | str | None = None,
44
+ print_height_: float | str | None = None,
45
+ dpu_: float = 1,
46
+ nsmap: dict[str | None, str] | None = None,
47
+ attrib: OptionalElemAttribMapping = None,
48
+ **attributes: ElemAttrib,
49
+ ) -> EtreeElement:
50
+ """Create svg root around BoundElements.
51
+
52
+ :param bounded: BoundingBox istances, BoundElement instances, PaddedText
53
+ instances, or any other EtreeElement instances. Anything that isn't a
54
+ bounding box or SupportsBounds will be ignored.
55
+ :param pad_: optionally increase viewBox by pad in all directions. Acceps a
56
+ single value or a tuple of values applied to (cycled over) top, right,
57
+ bottom, left. pad can be floats or dimension strings*
58
+ :param print_width_: optionally explicitly set unpadded width in units
59
+ (float) or a dimension string*
60
+ :param print_height_: optionally explicitly set unpadded height in units
61
+ (float) or a dimension string*
62
+ :param dpu_: dots per unit. Scale the output by this factor. This is
63
+ different from print_width_ and print_height_ in that dpu_ scales the
64
+ *padded* output.
65
+ :param nsmap: optionally pass a namespace map of your choosing
66
+ :param attrib: optionally pass additional attributes as a mapping instead of as
67
+ anonymous kwargs. This is useful for pleasing the linter when unpacking a
68
+ dictionary into a function call.
69
+ :param attributes: element attribute names and values
70
+ :return: root svg element
71
+ :raise ValueError: if no bounding boxes are found in bounded
72
+ """
73
+ attributes.update(attrib or {})
74
+ bbox = bound.new_bbox_union(*bounded)
75
+ viewbox = _viewbox_args_from_bboxes(bbox)
76
+ return new_svg_root(
77
+ x_=viewbox["x_"],
78
+ y_=viewbox["y_"],
79
+ width_=viewbox["width_"],
80
+ height_=viewbox["height_"],
81
+ pad_=pad_,
82
+ print_width_=print_width_,
83
+ print_height_=print_height_,
84
+ dpu_=dpu_,
85
+ nsmap=nsmap,
86
+ attrib=attributes,
87
+ )