From a06f9743ab639b614418c381538e8c27dc864654 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Thu, 8 Sep 2022 18:13:01 +0200 Subject: [PATCH 01/10] improve explanations in test image page --- plone/namedfile/test.pt | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/plone/namedfile/test.pt b/plone/namedfile/test.pt index d15efb00..dd6e91fc 100644 --- a/plone/namedfile/test.pt +++ b/plone/namedfile/test.pt @@ -70,8 +70,22 @@

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. [from doc-string] +

+

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

+

Deprecated spellings: keep, thumbnail.

@@ -79,6 +93,11 @@

Mini direction=cover

+

+ Scales the relatively largest dimension up to the required size. + Despite the alternative spelling, I see no cropping happening. [from doc-string] +

+

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

@@ -86,7 +105,11 @@

Mini direction=contain

-
+ Starts by scaling the relatively smallest dimension to the required size and crops the other dimension if needed. [from doc-string] +

+

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

+

@@ -97,9 +120,7 @@

Picture tags

Temporary note: - Picture tags only work on Plone 6, with several other branches merged. - See coredev. - If not available (like on Plone 5.2), an ordinary image tag is created. + Picture tags only work on Plone 6. If not available (like on Plone 5.2), an ordinary image tag is created.

Picture Tag Large

From 6fec595532735ec98ee00e29563cf46892c81cc3 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Thu, 8 Sep 2022 18:13:17 +0200 Subject: [PATCH 02/10] use svg metadata modifier --- plone/namedfile/scaling.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index 21efd53f..74f8f227 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -2,6 +2,7 @@ from Acquisition import aq_base from DateTime import DateTime from io import BytesIO +from io import StringIO from plone.memoize import ram from plone.namedfile.file import FILECHUNK_CLASSES from plone.namedfile.interfaces import IAvailableSizes @@ -16,9 +17,10 @@ from plone.scale.interfaces import IImageScaleFactory from plone.scale.interfaces import IScaledImageQuality from plone.scale.scale import scaleImage +from plone.scale.scale import scale_svg_image from plone.scale.storage import IImageScaleStorage from Products.CMFCore.utils import getToolByName -from Products.CMFPlone.utils import safe_encode +from Products.CMFPlone.utils import safe_text from Products.Five import BrowserView from xml.sax.saxutils import quoteattr from zExceptions import Unauthorized @@ -284,14 +286,12 @@ def handle_image( ): """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_encode(orig_data) - if isinstance(orig_data, (bytes)): - orig_data = BytesIO(orig_data) - result = orig_data.read(), "svg+xml", (width, height) - return result + if isinstance(orig_data, bytes): + orig_data = StringIO(safe_text(orig_data)) + elif isinstance(orig_data, BytesIO): + orig_data = StringIO(safe_text(orig_data.read())) + scaled_data, size = scale_svg_image(orig_data, width, height, direction) + return scaled_data, "svg+xml", size try: result = self.create_scale( orig_data, direction=direction, height=height, width=width, **parameters From 9532bd07121eecbbac8476e476b9efd1bd8bed24 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Thu, 22 Sep 2022 22:27:13 +0200 Subject: [PATCH 03/10] fix problem with floats returned, but anyway be careful and handle floats also use textiowrapper --- plone/namedfile/scaling.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index 74f8f227..993ec03a 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -3,6 +3,7 @@ from DateTime import DateTime from io import BytesIO from io import StringIO +from io import TextIOWrapper from plone.memoize import ram from plone.namedfile.file import FILECHUNK_CLASSES from plone.namedfile.interfaces import IAvailableSizes @@ -54,7 +55,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") @@ -289,7 +290,7 @@ def handle_image( if isinstance(orig_data, bytes): orig_data = StringIO(safe_text(orig_data)) elif isinstance(orig_data, BytesIO): - orig_data = StringIO(safe_text(orig_data.read())) + orig_data = TextIOWrapper(orig_data, encoding="utf-8") scaled_data, size = scale_svg_image(orig_data, width, height, direction) return scaled_data, "svg+xml", size try: From 301448a5d2262054fe37790c334e842dfee94fa5 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Fri, 17 Jan 2025 11:43:13 +0100 Subject: [PATCH 04/10] Update tests.yml - no 3.7 any more --- .github/workflows/tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4f052910..abff7c66 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,6 @@ jobs: matrix: config: # [Python version, tox env] - - ["3.7", "plone52-py37"] - ["3.8", "plone52-py38"] - ["3.8", "plone60-py38"] - ["3.9", "plone60-py39"] From d6563b170ae625869e54c921e55e6a516a53bb22 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Wed, 2 Apr 2025 15:09:17 +0200 Subject: [PATCH 05/10] add news snippet --- news/133.feature | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 news/133.feature diff --git a/news/133.feature b/news/133.feature new file mode 100644 index 00000000..86e4ef53 --- /dev/null +++ b/news/133.feature @@ -0,0 +1,3 @@ +Scale/crop SVG images by using plone.scale new scale_svg_image. +This modifies the viewport of the SVG accordingly. +[@jensens] \ No newline at end of file From 413411c0e22127980f2ecf3e2541f76114c2f1f1 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 7 Apr 2025 13:24:48 +0200 Subject: [PATCH 06/10] fix a problem with encoding, lxml and SVGs --- plone/namedfile/scaling.py | 7 +++---- plone/namedfile/tests/test_scaling_functional.py | 6 +++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index 1fec0776..4a696342 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -3,7 +3,6 @@ from DateTime import DateTime from io import BytesIO from io import StringIO -from io import TextIOWrapper from plone.base.utils import safe_text from plone.memoize import ram from plone.namedfile.browser import ALLOWED_INLINE_MIMETYPES @@ -331,9 +330,9 @@ 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": if isinstance(orig_data, bytes): - orig_data = StringIO(safe_text(orig_data)) - elif isinstance(orig_data, BytesIO): - orig_data = TextIOWrapper(orig_data, encoding="utf-8") + orig_data = BytesIO(orig_data) + 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: 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() From f964aea36fa8bcc2ba174048f0fd7a614c10d155 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 7 Apr 2025 13:29:26 +0200 Subject: [PATCH 07/10] add news file 133 --- news/133.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/133.feature 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 From 4eca38ee64d80e1c6efbc7f162531f8d84d98b88 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 7 Apr 2025 13:32:33 +0200 Subject: [PATCH 08/10] remove unused imports --- plone/namedfile/scaling.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index 4a696342..bf3e1081 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -2,8 +2,6 @@ from Acquisition import aq_base from DateTime import DateTime from io import BytesIO -from io import StringIO -from plone.base.utils import safe_text from plone.memoize import ram from plone.namedfile.browser import ALLOWED_INLINE_MIMETYPES from plone.namedfile.browser import DISALLOWED_INLINE_MIMETYPES From 9f73d00cb943d803f539b4b0f442c0e90c1f9802 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Mon, 7 Apr 2025 13:34:59 +0200 Subject: [PATCH 09/10] revert accidiential commit meant for plone.namedfile --- plone/namedfile/utils.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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') From 3dd427dbc722c2f966cbb4f1446a74c7e66aaa9a Mon Sep 17 00:00:00 2001 From: Maurits van Rees Date: Thu, 19 Jun 2025 12:24:40 +0200 Subject: [PATCH 10/10] Undo the changes to test.pt. They are partially wrong, reintroducing the deprecated 'direction' instead of 'mode'. I want to merge the SVG improvements, as I already accidentally made a related change on the main branch. --- plone/namedfile/test.pt | 124 ++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 67 deletions(-) diff --git a/plone/namedfile/test.pt b/plone/namedfile/test.pt index 202b2b51..51c4b8c6 100644 --- a/plone/namedfile/test.pt +++ b/plone/namedfile/test.pt @@ -42,6 +42,10 @@ i18n:name="" i18n:translate="" >picture tags, + img tag with srcset,
-

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.

+

Examples with mode

-

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

+
+ +
+
-

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=cover

+
+ +
+
-

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.

-
- -
-
-
+

Mini mode=contain

+
+ +
+
+

Picture tags

@@ -232,6 +202,26 @@

+
+

img with srcset attributes

+

+ srcset allows the browser to select the correct image, depending on the space the image has on a page. +

+

+ To do so, the @@images view provides a srcset method, that will output the full srcset of this image, using all available image scales. It has as required parameter the value of the sizes attribute that the user of this method has to provide and will be output as is in the generated HTML. +

+

+ +

+

+ + + +

+
+