svg-ultralight 0.34.0__tar.gz → 0.36.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.

Files changed (51) hide show
  1. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/.pre-commit-config.yaml +13 -25
  2. {svg_ultralight-0.34.0/src/svg_ultralight.egg-info → svg_ultralight-0.36.0}/PKG-INFO +3 -1
  3. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/pyproject.toml +7 -2
  4. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/__init__.py +7 -5
  5. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/bounding_boxes/bound_helpers.py +38 -2
  6. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/constructors/__init__.py +1 -1
  7. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/constructors/new_element.py +2 -1
  8. svg_ultralight-0.36.0/src/svg_ultralight/image_ops.py +134 -0
  9. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/query.py +0 -1
  10. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/strings/__init__.py +1 -1
  11. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0/src/svg_ultralight.egg-info}/PKG-INFO +3 -1
  12. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight.egg-info/SOURCES.txt +3 -1
  13. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight.egg-info/requires.txt +3 -0
  14. svg_ultralight-0.36.0/tests/conftest.py +16 -0
  15. svg_ultralight-0.36.0/tests/resources/arrow.svg +3 -0
  16. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tests/test_bounding.py +8 -0
  17. svg_ultralight-0.34.0/tests/conftest.py +0 -11
  18. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/.gitignore +0 -0
  19. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/README.md +0 -0
  20. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/setup.cfg +0 -0
  21. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/animate.py +0 -0
  22. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/bounding_boxes/__init__.py +0 -0
  23. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/bounding_boxes/supports_bounds.py +0 -0
  24. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/bounding_boxes/type_bound_collection.py +0 -0
  25. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/bounding_boxes/type_bound_element.py +0 -0
  26. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/bounding_boxes/type_bounding_box.py +0 -0
  27. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/bounding_boxes/type_padded_text.py +0 -0
  28. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/inkscape.py +0 -0
  29. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/layout.py +0 -0
  30. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/main.py +0 -0
  31. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/metadata.py +0 -0
  32. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/nsmap.py +0 -0
  33. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/py.typed +0 -0
  34. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/root_elements.py +0 -0
  35. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/string_conversion.py +0 -0
  36. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/strings/svg_strings.py +0 -0
  37. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/transformations.py +0 -0
  38. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight/unit_conversion.py +0 -0
  39. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight.egg-info/dependency_links.txt +0 -0
  40. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/src/svg_ultralight.egg-info/top_level.txt +0 -0
  41. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tests/__init__.py +0 -0
  42. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tests/test_inkscape.py +0 -0
  43. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tests/test_layout.py +0 -0
  44. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tests/test_matrices.py +0 -0
  45. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tests/test_metadata.py +0 -0
  46. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tests/test_new_element.py +0 -0
  47. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tests/test_queries.py +0 -0
  48. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tests/test_root_elements.py +0 -0
  49. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tests/test_string_conversion.py +0 -0
  50. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tests/test_svg_ultralight.py +0 -0
  51. {svg_ultralight-0.34.0 → svg_ultralight-0.36.0}/tox.ini +0 -0
@@ -42,12 +42,12 @@ repos:
42
42
  # files: .pre-commit-config.yaml
43
43
 
44
44
  - repo: https://github.com/pre-commit/mirrors-mypy
45
- rev: v1.13.0
45
+ rev: v1.14.0
46
46
  hooks:
47
47
  - id: mypy
48
48
  name: mypy
49
49
  language: python
50
- language_version: python 3.12
50
+ language_version: python 3.11
51
51
  types: [python]
52
52
  require_serial: true
53
53
  verbose: true
@@ -57,21 +57,8 @@ repos:
57
57
  # - --ignore-missing-imports
58
58
  # files: ^(src/|tests/)
59
59
 
60
- - repo: https://github.com/PyCQA/isort
61
- rev: 5.13.2
62
- hooks:
63
- - id: isort
64
- args: ["--profile", "black", "--filter-files", "--combine-as"]
65
-
66
- - repo: https://github.com/psf/black
67
- rev: 24.10.0
68
- hooks:
69
- - id: black
70
- language_version: python3.9
71
- args: ["--skip-magic-trailing-comma"]
72
-
73
60
  - repo: https://github.com/asottile/pyupgrade
74
- rev: v3.19.0
61
+ rev: v3.19.1
75
62
  hooks:
76
63
  - args:
77
64
  - --py39-plus
@@ -84,8 +71,6 @@ repos:
84
71
 
85
72
  - repo: https://github.com/charliermarsh/ruff-pre-commit
86
73
  # ignores
87
- # ANN101 Missing type annotation for self in method
88
- # ANN102 Missing type annotation for cls in classmethod
89
74
  # ANN201 Missing return type annotation for public function
90
75
  # ANN202 Missing return type annotation for private function (wants -> None everywhere)
91
76
  # B905 zip() without an explicit strict= parameter
@@ -103,25 +88,28 @@ repos:
103
88
  # S101 Use of `assert` detected
104
89
  # S603 `subprocess` call: check for execution of untrusted input
105
90
  # PLR2004 Magic value used in comparison
106
- # D413 [*] Missing blank line after last section ("Attributes")
107
- # D407 [*] Missing dashed underline after section ("Attributes")
108
- # D406 [*] Section name should end with a newline ("Attributes")
109
91
  # S320 Using `lxml` to parse untrusted data is known to be vulnerable to XML attacks
110
92
  # S301 don't use pickle
111
93
  # B028 wants explicit stacklevel on warn
112
94
  # BLE001 Use of `except Exception:` detected
113
- rev: 'v0.7.4'
95
+ rev: 'v0.8.4'
114
96
  hooks:
115
97
  - id: ruff
98
+ name: "ruff-lint"
116
99
  exclude: "tests"
117
100
  args:
118
101
  - --target-version=py39
119
102
  - --select=ALL
120
- - --ignore=ANN101,ANN102,ANN201,ANN202,B905,COM812,D203,D213,I001,ISC003,N802,N806,PGH003,PLR0913,PTH108,S101,S603,PLR2004,D413,D407,D406,S320,S301,B028,BLE001
121
- # - --fix
103
+ - --ignore=ANN201,ANN202,B905,COM812,D203,D213,I001,ISC003,N802,N806,PGH003,PLR0913,PTH108,S101,S603,PLR2004,S320,S301,B028,BLE001
104
+ - --fix
105
+ - --fixable=RUF022
106
+ - id: ruff
107
+ args: ["check", "--select", "I", "--fix"]
108
+ - id: ruff-format
109
+ name: "ruff-format"
122
110
 
123
111
  # reads pyproject.toml for additional config
124
112
  - repo: https://github.com/RobertCraigie/pyright-python
125
- rev: v1.1.389
113
+ rev: v1.1.391
126
114
  hooks:
127
115
  - id: pyright
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: svg-ultralight
3
- Version: 0.34.0
3
+ Version: 0.36.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
@@ -15,6 +15,8 @@ Requires-Dist: pytest; extra == "dev"
15
15
  Requires-Dist: commitizen; extra == "dev"
16
16
  Requires-Dist: pre-commit; extra == "dev"
17
17
  Requires-Dist: tox; extra == "dev"
18
+ Provides-Extra: images
19
+ Requires-Dist: pillow; extra == "images"
18
20
 
19
21
  # svg_ultralight
20
22
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "svg-ultralight"
3
- version = "0.34.0"
3
+ version = "0.36.0"
4
4
  description = "a sensible way to create svg files with Python"
5
5
  authors = [{ name = "Shay Hill", email = "shay_public@hotmail.com" }]
6
6
  license = { text = "MIT" }
@@ -10,6 +10,7 @@ dependencies = ["lxml", "pillow", "paragraphs", "types-lxml"]
10
10
 
11
11
  [project.optional-dependencies]
12
12
  dev = ["pytest", "commitizen", "pre-commit", "tox"]
13
+ images = ["pillow"]
13
14
 
14
15
  [build-system]
15
16
  requires = ["setuptools", "setuptools-scm"]
@@ -18,6 +19,8 @@ build-backend = "setuptools.build_meta"
18
19
 
19
20
  [tool.pytest.ini_options]
20
21
  addopts = "--doctest-modules"
22
+ pythonpath = ["tests"]
23
+ log_cli = 1
21
24
 
22
25
 
23
26
  [tool.isort]
@@ -34,10 +37,12 @@ legacy_tox_ini = """
34
37
  commands = pytest tests
35
38
  """
36
39
 
40
+ [tool.ruff.lint.pydocstyle]
41
+ convention = "pep257"
37
42
 
38
43
  [tool.commitizen]
39
44
  name = "cz_conventional_commits"
40
- version = "0.34.0"
45
+ version = "0.36.0"
41
46
  tag_format = "$version"
42
47
  version_files = ["pyproject.toml:^version"]
43
48
  annotated_tag = true
@@ -5,13 +5,14 @@
5
5
  """
6
6
 
7
7
  from svg_ultralight.bounding_boxes.bound_helpers import (
8
+ bbox_dict,
9
+ cut_bbox,
10
+ new_bbox_rect,
8
11
  new_bbox_union,
9
12
  new_bound_union,
10
13
  new_element_union,
11
- cut_bbox,
12
14
  pad_bbox,
13
- bbox_dict,
14
- new_bbox_rect,
15
+ parse_bound_element,
15
16
  )
16
17
  from svg_ultralight.bounding_boxes.supports_bounds import SupportsBounds
17
18
  from svg_ultralight.bounding_boxes.type_bound_collection import BoundCollection
@@ -35,10 +36,10 @@ from svg_ultralight.main import new_svg_root, write_svg
35
36
  from svg_ultralight.metadata import new_metadata
36
37
  from svg_ultralight.nsmap import NSMAP, new_qname
37
38
  from svg_ultralight.query import (
39
+ clear_svg_ultralight_cache,
38
40
  get_bounding_box,
39
41
  get_bounding_boxes,
40
42
  pad_text,
41
- clear_svg_ultralight_cache,
42
43
  )
43
44
  from svg_ultralight.root_elements import new_svg_root_around_bounds
44
45
  from svg_ultralight.string_conversion import (
@@ -55,10 +56,10 @@ from svg_ultralight.transformations import (
55
56
  )
56
57
 
57
58
  __all__ = [
59
+ "NSMAP",
58
60
  "BoundCollection",
59
61
  "BoundElement",
60
62
  "BoundingBox",
61
- "NSMAP",
62
63
  "PaddedText",
63
64
  "SupportsBounds",
64
65
  "bbox_dict",
@@ -86,6 +87,7 @@ __all__ = [
86
87
  "new_svg_root_around_bounds",
87
88
  "pad_bbox",
88
89
  "pad_text",
90
+ "parse_bound_element",
89
91
  "transform_element",
90
92
  "update_element",
91
93
  "write_pdf",
@@ -16,7 +16,10 @@ from svg_ultralight.bounding_boxes.type_padded_text import PaddedText
16
16
  from svg_ultralight.constructors import new_element
17
17
 
18
18
  if TYPE_CHECKING:
19
+ import os
20
+
19
21
  from svg_ultralight.bounding_boxes.supports_bounds import SupportsBounds
22
+ from lxml import etree
20
23
 
21
24
  _Matrix = tuple[float, float, float, float, float, float]
22
25
 
@@ -149,8 +152,10 @@ def pad_bbox(bbox: SupportsBounds, pad: float | tuple[float, ...]) -> BoundingBo
149
152
  len = 4 : 0, 1, 2, 3
150
153
  :return: a new bounding box with padding applied.
151
154
  """
152
- t, r, b, l = _expand_pad(pad)
153
- return cut_bbox(bbox, x=bbox.x - l, y=bbox.y - t, x2=bbox.x2 + r, y2=bbox.y2 + b)
155
+ top, right, bottom, left = _expand_pad(pad)
156
+ return cut_bbox(
157
+ bbox, x=bbox.x - left, y=bbox.y - top, x2=bbox.x2 + right, y2=bbox.y2 + bottom
158
+ )
154
159
 
155
160
 
156
161
  def bbox_dict(bbox: SupportsBounds) -> dict[str, float]:
@@ -169,3 +174,34 @@ def new_bbox_rect(bbox: BoundingBox, **kwargs: float | str) -> EtreeElement:
169
174
  :param kwargs: additional attributes for the rect element.
170
175
  """
171
176
  return new_element("rect", **bbox_dict(bbox), **kwargs)
177
+
178
+
179
+ def _get_view_box(elem: EtreeElement) -> tuple[float, float, float, float]:
180
+ """Return the view box of an element as a tuple of floats.
181
+
182
+ :param elem: the element from which to extract the view box.
183
+ :return: a tuple of floats representing the view box.
184
+
185
+ This will work on svg files created by this library and some others. Not all svg
186
+ files have a viewBox attribute.
187
+ """
188
+ view_box = elem.get("viewBox")
189
+ if view_box is None:
190
+ msg = "Element does not have a viewBox attribute."
191
+ raise ValueError(msg)
192
+ x, y, width, height = map(float, view_box.split())
193
+ return x, y, width, height
194
+
195
+
196
+ def parse_bound_element(svg_fil: str | os.PathLike[str]) -> BoundElement:
197
+ """Import an element as a BoundElement.
198
+
199
+ :param elem: the element to import.
200
+ :return: a BoundElement instance.
201
+ """
202
+ tree = etree.parse(svg_fil)
203
+ root = tree.getroot()
204
+ elem = new_element("g")
205
+ elem.extend(list(root))
206
+ bbox = BoundingBox(*_get_view_box(root))
207
+ return BoundElement(elem, bbox)
@@ -11,4 +11,4 @@ from svg_ultralight.constructors.new_element import (
11
11
  update_element,
12
12
  )
13
13
 
14
- __all__ = ["new_element", "new_sub_element", "update_element", "deepcopy_element"]
14
+ __all__ = ["deepcopy_element", "new_element", "new_sub_element", "update_element"]
@@ -20,7 +20,8 @@ from lxml import etree
20
20
  from svg_ultralight.string_conversion import set_attributes
21
21
 
22
22
  if TYPE_CHECKING:
23
- from lxml.etree import QName, _Element as EtreeElement # type: ignore
23
+ from lxml.etree import QName
24
+ from lxml.etree import _Element as EtreeElement # type: ignore
24
25
 
25
26
 
26
27
  def new_element(tag: str | QName, **attributes: str | float) -> EtreeElement:
@@ -0,0 +1,134 @@
1
+ """Crop an image before converting to binary and including in the svg file.
2
+
3
+ This optional module requires the Pillow library. Create an svg image element with a
4
+ rasterized image positioned inside a bounding box.
5
+
6
+ :author: Shay Hill
7
+ :created: 2024-11-20
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import TYPE_CHECKING
13
+
14
+ from paragraphs import par
15
+
16
+ try:
17
+ from PIL import Image
18
+
19
+ if TYPE_CHECKING:
20
+ from PIL.Image import Image as ImageType
21
+ except ImportError as err:
22
+ msg = par(
23
+ """PIL is not installed. Install it using 'pip install Pillow' to use
24
+ svg_ultralight.image_ops module."""
25
+ )
26
+ raise ImportError(msg) from err
27
+
28
+ import base64
29
+ import io
30
+
31
+ from lxml import etree
32
+
33
+ from svg_ultralight import NSMAP
34
+ from svg_ultralight.bounding_boxes.bound_helpers import bbox_dict
35
+ from svg_ultralight.constructors import new_element
36
+
37
+ if TYPE_CHECKING:
38
+ from pathlib import Path
39
+
40
+ from lxml.etree import (
41
+ _Element as EtreeElement, # pyright: ignore [reportPrivateUsage]
42
+ )
43
+
44
+ from svg_ultralight.bounding_boxes.type_bounding_box import BoundingBox
45
+
46
+
47
+ def _symmetric_crop(
48
+ image: ImageType, center: tuple[float, float] | None = None
49
+ ) -> ImageType:
50
+ """Crop an image symmetrically around a center point.
51
+
52
+ :param image: PIL.Image instance
53
+ :param center: optional center point for cropping. Proportions of image with and
54
+ image height, so the default value, (0.5, 0.5), is the true center of the
55
+ image. (0.4, 0.5) would crop 20% off the right side of the image.
56
+ :return: PIL.Image instance
57
+ """
58
+ if center is None:
59
+ return image
60
+
61
+ if not all(0 < x < 1 for x in center):
62
+ msg = "Center must be between (0, 0) and (1, 1)"
63
+ raise ValueError(msg)
64
+
65
+ xd, yd = (min(x, 1 - x) for x in center)
66
+ left, right = sorted(x * image.width for x in (center[0] - xd, center[0] + xd))
67
+ top, bottom = sorted(x * image.height for x in (center[1] - yd, center[1] + yd))
68
+
69
+ return image.crop((left, top, right, bottom))
70
+
71
+
72
+ def _crop_image_to_bbox_ratio(
73
+ image: ImageType, bbox: BoundingBox, center: tuple[float, float] | None = None
74
+ ) -> ImageType:
75
+ """Crop an image to the ratio of a bounding box.
76
+
77
+ :param image: PIL.Image instance
78
+ :param bbox: BoundingBox instance
79
+ :param center: optional center point for cropping. Proportions of image with and
80
+ image height, so the default value, (0.5, 0.5), is the true center of the
81
+ image. (0.4, 0.5) would crop 20% off the right side of the image.
82
+ :return: PIL.Image instance
83
+
84
+ This crops the image to the specified ratio. It's not a resize, so it will cut
85
+ off the top and bottom or the sides of the image to fit the ratio.
86
+ """
87
+ image = _symmetric_crop(image, center)
88
+ width, height = image.size
89
+
90
+ ratio = bbox.width / bbox.height
91
+ if width / height > ratio:
92
+ new_width = height * ratio
93
+ left = (width - new_width) / 2
94
+ right = width - left
95
+ return image.crop((left, 0, right, height))
96
+ new_height = width / ratio
97
+ top = (height - new_height) / 2
98
+ bottom = height - top
99
+ return image.crop((0, top, width, bottom))
100
+
101
+
102
+ def _get_svg_embedded_image_str(image: ImageType) -> str:
103
+ """Return the string you'll need to embed an image in an svg.
104
+
105
+ :param image: PIL.Image instance
106
+ :return: argument for xlink:href
107
+ """
108
+ in_mem_file = io.BytesIO()
109
+ image.save(in_mem_file, format="PNG")
110
+ _ = in_mem_file.seek(0)
111
+ img_bytes = in_mem_file.read()
112
+ base64_encoded_result_bytes = base64.b64encode(img_bytes)
113
+ base64_encoded_result_str = base64_encoded_result_bytes.decode("ascii")
114
+ return "data:image/png;base64," + base64_encoded_result_str
115
+
116
+
117
+ def new_image_elem_in_bbox(
118
+ filename: Path | str, bbox: BoundingBox, center: tuple[float, float] | None
119
+ ) -> EtreeElement:
120
+ """Create a new svg image element inside a bounding box.
121
+
122
+ :param filename: filename of source image
123
+ :param bbox: bounding box for the image
124
+ :param center: center point for cropping. Proportions of image width and image
125
+ height, so the default value, (0.5, 0.5), is the true center of the image.
126
+ (0.4, 0.5) would crop 20% off the right side of the image.
127
+ :return: an etree image element with the cropped image embedded
128
+ """
129
+ image = _crop_image_to_bbox_ratio(Image.open(filename), bbox, center)
130
+ svg_image = new_element("image", **bbox_dict(bbox))
131
+ svg_image.set(
132
+ etree.QName(NSMAP["xlink"], "href"), _get_svg_embedded_image_str(image)
133
+ )
134
+ return svg_image
@@ -31,7 +31,6 @@ 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
-
35
34
  from lxml.etree import _Element as EtreeElement # type: ignore
36
35
 
37
36
 
@@ -10,4 +10,4 @@ from svg_ultralight.strings.svg_strings import (
10
10
  svg_ints,
11
11
  )
12
12
 
13
- __all__ = ["svg_color_tuple", "svg_ints", "svg_float_tuples"]
13
+ __all__ = ["svg_color_tuple", "svg_float_tuples", "svg_ints"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: svg-ultralight
3
- Version: 0.34.0
3
+ Version: 0.36.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
@@ -15,6 +15,8 @@ Requires-Dist: pytest; extra == "dev"
15
15
  Requires-Dist: commitizen; extra == "dev"
16
16
  Requires-Dist: pre-commit; extra == "dev"
17
17
  Requires-Dist: tox; extra == "dev"
18
+ Provides-Extra: images
19
+ Requires-Dist: pillow; extra == "images"
18
20
 
19
21
  # svg_ultralight
20
22
 
@@ -5,6 +5,7 @@ pyproject.toml
5
5
  tox.ini
6
6
  src/svg_ultralight/__init__.py
7
7
  src/svg_ultralight/animate.py
8
+ src/svg_ultralight/image_ops.py
8
9
  src/svg_ultralight/inkscape.py
9
10
  src/svg_ultralight/layout.py
10
11
  src/svg_ultralight/main.py
@@ -43,4 +44,5 @@ tests/test_new_element.py
43
44
  tests/test_queries.py
44
45
  tests/test_root_elements.py
45
46
  tests/test_string_conversion.py
46
- tests/test_svg_ultralight.py
47
+ tests/test_svg_ultralight.py
48
+ tests/resources/arrow.svg
@@ -8,3 +8,6 @@ pytest
8
8
  commitizen
9
9
  pre-commit
10
10
  tox
11
+
12
+ [images]
13
+ pillow
@@ -0,0 +1,16 @@
1
+ """Test configuration for pytest.
2
+
3
+ :author: Shay Hill
4
+ :created: 7/2/2019
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ def pytest_assertrepr_compare(config: Any, op: str, left: str, right: str):
11
+ """See full error diffs"""
12
+ if op in ("==", "!="):
13
+ return ["{0} {1} {2}".format(left, op, right)]
14
+
15
+ TEST_RESOURCES = Path(__file__).parent / "resources"
16
+
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="0 0 10 10">
2
+ <rect x="0" y="0" width="10" height="10"/>
3
+ </svg>
@@ -6,13 +6,16 @@
6
6
 
7
7
  import pytest
8
8
  import math
9
+ from conftest import TEST_RESOURCES
9
10
  from svg_ultralight.bounding_boxes.type_bound_element import BoundElement
10
11
  from svg_ultralight.bounding_boxes.type_padded_text import PaddedText
12
+ from lxml import etree
11
13
  from svg_ultralight.bounding_boxes.type_bounding_box import BoundingBox
12
14
  from svg_ultralight.bounding_boxes.type_bound_collection import BoundCollection
13
15
  from svg_ultralight.bounding_boxes.bound_helpers import (
14
16
  pad_bbox,
15
17
  cut_bbox,
18
+ parse_bound_element,
16
19
  bbox_dict,
17
20
  new_bbox_rect,
18
21
  )
@@ -245,3 +248,8 @@ class TestBoundHelpers:
245
248
  elem = new_bbox_rect(bbox)
246
249
  assert elem.attrib == {"x": "0", "y": "1", "width": "2", "height": "3"}
247
250
 
251
+ def test_import_bound_element():
252
+ blem = parse_bound_element(TEST_RESOURCES / "arrow.svg")
253
+ assert blem.bbox == BoundingBox(_x=0, _y=0, _width=10, _height=10, _transformation=(1, 0, 0, 1, 0, 0))
254
+ assert etree.tostring(blem.elem) == b'<g><ns0:rect xmlns:ns0="http://www.w3.org/2000/svg"' + b' x="0" y="0" width="10" height="10"/>\n</g>'
255
+
@@ -1,11 +0,0 @@
1
- """Test configuration for pytest.
2
-
3
- :author: Shay Hill
4
- :created: 7/2/2019
5
- """
6
-
7
-
8
- def pytest_assertrepr_compare(config, op, left, right):
9
- """See full error diffs."""
10
- if op in ("==", "!="):
11
- return [f"{left} {op} {right}"]
File without changes