diff --git a/news/133.feature b/news/133.feature new file mode 100644 index 00000000..5145a308 --- /dev/null +++ b/news/133.feature @@ -0,0 +1 @@ +- feature: "scale" SVGs by setting the correct height and width for the given scale in its metadata. \ No newline at end of file diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index f79fc6a7..bf3e1081 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -2,7 +2,6 @@ from Acquisition import aq_base from DateTime import DateTime from io import BytesIO -from plone.base.utils import safe_bytes from plone.memoize import ram from plone.namedfile.browser import ALLOWED_INLINE_MIMETYPES from plone.namedfile.browser import DISALLOWED_INLINE_MIMETYPES @@ -20,6 +19,7 @@ from plone.rfc822.interfaces import IPrimaryFieldInfo from plone.scale.interfaces import IImageScaleFactory from plone.scale.interfaces import IScaledImageQuality +from plone.scale.scale import scale_svg_image from plone.scale.scale import scaleImage from plone.scale.storage import IImageScaleStorage from Products.CMFCore.utils import getToolByName @@ -58,7 +58,7 @@ def _image_tag_from_values(*values): for k, v in values: if v is None: continue - if isinstance(v, int): + if isinstance(v, (int, float)): v = str(v) elif isinstance(v, bytes): v = str(v, "utf8") @@ -327,14 +327,12 @@ def create_scale(self, data, mode, height, width, **parameters): def handle_image(self, orig_value, orig_data, mode, height, width, **parameters): """Return a scaled image, its mimetype format, and width and height.""" if getattr(orig_value, "contentType", "") == "image/svg+xml": - # No need to scale, we can simply use the original data, - # but report a different width and height. - if isinstance(orig_data, (str)): - orig_data = safe_bytes(orig_data) - if isinstance(orig_data, (bytes)): + if isinstance(orig_data, bytes): orig_data = BytesIO(orig_data) - result = orig_data.read(), "svg+xml", (width, height) - return result + if isinstance(orig_data, str): + orig_data = BytesIO(orig_data.encode("utf-8")) + scaled_data, size = scale_svg_image(orig_data, width, height, mode) + return scaled_data, "svg+xml", size try: result = self.create_scale( orig_data, mode=mode, height=height, width=width, **parameters diff --git a/plone/namedfile/test.pt b/plone/namedfile/test.pt index 5568f1d5..202b2b51 100644 --- a/plone/namedfile/test.pt +++ b/plone/namedfile/test.pt @@ -112,42 +112,76 @@
-

Examples with mode

+

Examples with direction/mode

+

+ There are three modes to scale an image: + scale, + cover + and + contain. +

+

Scaling methods do never stretch/distort the image in one direction only.

-

Mini

-
- -
-
+

Mini direction=scale +

+ Scales to the requested dimensions without cropping. + The resulting image may have a different size than requested. + This option requires both, width and height, to be specified. + Does not scale up. +

+

+ Here direction is not explicit set, it uses by default + direction="scale". +

+

Deprecated spellings: + keep, + thumbnail.

+
+ +
+
-

Mini mode=cover

-
- -
-
+

Mini direction=cover

+

+ Scales the relatively largest dimension up to the required size. + Despite the alternative spelling, I see no cropping happening. +

+

Deprecated spellings: + scale-crop-to-fill, + up.

+
+ +
+
-

Mini mode=contain

-
- -
-
-
+

Mini direction=contain

+

+ Starts by scaling the relatively smallest dimension to the required size and crops the other dimension if needed. +

+

Deprecated spellings: + scale-crop-to-fit, + down.

+
+ +
+
+

Picture tags

diff --git a/plone/namedfile/tests/test_scaling_functional.py b/plone/namedfile/tests/test_scaling_functional.py index bf8f1fd7..f20175fa 100644 --- a/plone/namedfile/tests/test_scaling_functional.py +++ b/plone/namedfile/tests/test_scaling_functional.py @@ -214,7 +214,11 @@ def testSVGPublishThumbViaName(self): self.layer["app"].absolute_url() + "/svg/@@images/image/thumb" ) self.assertEqual("image/svg+xml", self.browser.headers["content-type"]) - self.assertEqual(self.browser.contents, data) + self.assertIn(b'width="128" height="32"', self.browser.contents) + self.assertEqual(self.browser.contents[:20], data[:20].replace(b'"', b"'")) + self.assertEqual( + self.browser.contents[-50:], data.replace(b"\r", b"").strip()[-50:] + ) def testImagesViewWithNoSubpath(self): transaction.commit() diff --git a/plone/namedfile/utils.rst b/plone/namedfile/utils.rst index c5833cac..8c4c26c2 100644 --- a/plone/namedfile/utils.rst +++ b/plone/namedfile/utils.rst @@ -13,7 +13,7 @@ Used in the widget itself to strip off any path, regardless of platform:: >>> safe_basename('/farmyard/cows/daisy') 'daisy' - >>> safe_basename('F:\FARMYARD\COWS\DAISY.TXT') + >>> safe_basename(r'F:\FARMYARD\COWS\DAISY.TXT') 'DAISY.TXT' >>> safe_basename('Macintosh Farmyard:Cows:Daisy Text File')