From b4b394c99cf81c36f0a35f89ee4d7349fe344a50 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 17 Mar 2025 12:18:39 +0100 Subject: [PATCH 01/24] fix: fix rendering unrenderable values --- plone/namedfile/test.pt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/plone/namedfile/test.pt b/plone/namedfile/test.pt index 5568f1d..266395e 100644 --- a/plone/namedfile/test.pt +++ b/plone/namedfile/test.pt @@ -198,6 +198,7 @@

+<<<<<<< HEAD
@@ -239,6 +240,28 @@ +======= +
+

Stored scales

+
+
+

+ +

+
+
+ +
+ + : + +
+
+ Open in new tab +>>>>>>> 4fe955e (fix: fix rendering unrenderable values)
From 0e47b897489fed32cb7fb1dd2f650cc8599fbf2c Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 17 Mar 2025 12:19:16 +0100 Subject: [PATCH 02/24] feat: add a method to render a img tag with srcset --- plone/namedfile/scaling.py | 61 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index f79fc6a..821e3da 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -739,6 +739,67 @@ def picture( fieldname=fieldname, ).prettify() + def srcset( + self, + fieldname=None, + scale_in_src="huge", + sizes="", + title=_marker, + alt=_marker, + **kwargs, + ): + if fieldname is None: + try: + primary = IPrimaryFieldInfo(self.context, None) + except TypeError: + return + if primary is None: + return # 404 + fieldname = primary.fieldname + + original_width, original_height = self.getImageSize(fieldname) + + storage = getMultiAdapter( + (self.context, functools.partial(self.modified, fieldname)), + IImageScaleStorage, + ) + + srcset_urls = [] + for width, height in self.available_sizes.values(): + if width <= original_width: + scale = storage.scale( + fieldname=fieldname, width=width, height=height, mode="scale" + ) + extension = scale["data"].contentType.split("/")[-1].lower() + srcset_urls.append( + f'{self.context.absolute_url()}/@@images/{scale["uid"]}.{extension} {scale["width"]}w' + ) + attributes = {} + if title is _marker: + attributes["title"] = self.context.Title() + elif title: + attributes["title"] = title + if alt is not _marker: + attributes["alt"] = alt + + attributes.update(**kwargs) + + attributes["sizes"] = sizes + + srcset_string = ", ".join(srcset_urls) + attributes["srcset"] = srcset_string + + if scale_in_src not in self.available_sizes: + for key, (width, height) in self.available_sizes.items(): + if width <= original_width: + scale_in_src = key + break + + scale = self.scale(fieldname=fieldname, scale=scale_in_src) + attributes["src"] = scale.url + + return _image_tag_from_values(*attributes.items()) + class NavigationRootScaling(ImageScaling): @lazy_property From 1a2c0bb3cb4a63d7e0582fafee2d42212d21e4c4 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 17 Mar 2025 12:19:59 +0100 Subject: [PATCH 03/24] add tests --- plone/namedfile/tests/test_scaling.py | 105 ++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/plone/namedfile/tests/test_scaling.py b/plone/namedfile/tests/test_scaling.py index aa34934..79dbc3c 100644 --- a/plone/namedfile/tests/test_scaling.py +++ b/plone/namedfile/tests/test_scaling.py @@ -813,6 +813,111 @@ def testOversizedHighPixelDensityScale(self): self.assertEqual(len(foo.srcset), 1) self.assertEqual(foo.srcset[0]["scale"], 2) + def testImgSrcSet(self): + """ test rendered srcset values """ + self.scaling.available_sizes = { + "huge": (1600, 65536), + "great": (1200, 65536), + "larger": (1000, 65536), + "large": (800, 65536), + "teaser": (600, 65536), + "preview": (400, 65536), + "mini": (200, 65536), + "thumb": (128, 128), + "tile": (64, 64), + "icon": (32, 32), + "listing": (16, 16), + } + tag = self.scaling.srcset( + "image", + sizes="50vw" + ) + base = self.item.absolute_url() + expected = f"""""" + self.assertTrue(_ellipsis_match(expected, tag.strip())) + + def testImgSrcSetCustomSrc(self): + """ test that we can select a custom scale in the src attribute """ + self.scaling.available_sizes = { + "huge": (1600, 65536), + "great": (1200, 65536), + "larger": (1000, 65536), + "large": (800, 65536), + "teaser": (600, 65536), + "preview": (400, 65536), + "mini": (200, 65536), + "thumb": (128, 128), + "tile": (64, 64), + "icon": (32, 32), + "listing": (16, 16), + } + tag = self.scaling.srcset("image", sizes="50vw", scale_in_src="mini") + base = self.item.absolute_url() + expected = f"""""" + self.assertTrue(_ellipsis_match(expected, tag.strip())) + + def testImgSrcSetInexistentScale(self): + """test that when requesting an inexistent scale for the src attribute + we provide the biggest scale we can produce + """ + self.scaling.available_sizes = { + "huge": (1600, 65536), + "great": (1200, 65536), + "larger": (1000, 65536), + "large": (800, 65536), + "teaser": (600, 65536), + "preview": (400, 65536), + "mini": (200, 65536), + "thumb": (128, 128), + "tile": (64, 64), + "icon": (32, 32), + "listing": (16, 16), + } + tag = self.scaling.srcset("image", sizes="50vw", scale_in_src="inexistent-scale-name") + base = self.item.absolute_url() + expected = f"""""" + self.assertTrue(_ellipsis_match(expected, tag.strip())) + + def testImgSrcSetCustomTitle(self): + """test passing a custom title to the srcset method""" + self.scaling.available_sizes = { + "huge": (1600, 65536), + "great": (1200, 65536), + "larger": (1000, 65536), + "large": (800, 65536), + "teaser": (600, 65536), + "preview": (400, 65536), + "mini": (200, 65536), + "thumb": (128, 128), + "tile": (64, 64), + "icon": (32, 32), + "listing": (16, 16), + } + tag = self.scaling.srcset("image", sizes="50vw", title="My Custom Title") + base = self.item.absolute_url() + expected = f"""""" + self.assertTrue(_ellipsis_match(expected, tag.strip())) + + def testImgSrcSetAdditionalAttributes(self): + """test that additional parameters are outputed as is, like alt, loading, ...""" + self.scaling.available_sizes = { + "huge": (1600, 65536), + "great": (1200, 65536), + "larger": (1000, 65536), + "large": (800, 65536), + "teaser": (600, 65536), + "preview": (400, 65536), + "mini": (200, 65536), + "thumb": (128, 128), + "tile": (64, 64), + "icon": (32, 32), + "listing": (16, 16), + } + tag = self.scaling.srcset("image", sizes="50vw", title="My Custom Title", alt="This image shows nothing", loading="lazy") + base = self.item.absolute_url() + expected = f"""This image shows nothing""" + self.assertTrue(_ellipsis_match(expected, tag.strip())) + class ImageTraverseTests(unittest.TestCase): From bb7047c7d1e04752f25b632cd7e648c56c687708 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 17 Mar 2025 12:20:11 +0100 Subject: [PATCH 04/24] show the srcset example in the image-test view --- plone/namedfile/test.pt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/plone/namedfile/test.pt b/plone/namedfile/test.pt index 266395e..f138a85 100644 --- a/plone/namedfile/test.pt +++ b/plone/namedfile/test.pt @@ -198,6 +198,7 @@

+<<<<<<< HEAD <<<<<<< HEAD
======= +======= +
+

img with srcset attributes

+

+ srcst allows the browser selecting 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 a required paramter the value of the sizes attribute that the user of this method has to provide and will be outputed as is in the generated HTML. +

+

+ +

+

+ + + +

+
+ + +>>>>>>> 8c6eb8d (show the srcset example in the image-test view)

Stored scales

From d46112b0a2d75544de25dd2657a23633e348d17d Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 17 Mar 2025 12:26:06 +0100 Subject: [PATCH 05/24] changelog --- news/170.feature | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 news/170.feature diff --git a/news/170.feature b/news/170.feature new file mode 100644 index 0000000..a6c729d --- /dev/null +++ b/news/170.feature @@ -0,0 +1,2 @@ +Add a srcset method to the @@images view +[erral] From 4634b00712f7580c9a3107a2a1de36d686cc8ba3 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 17 Mar 2025 12:29:54 +0100 Subject: [PATCH 06/24] add link in the images test --- plone/namedfile/test.pt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/plone/namedfile/test.pt b/plone/namedfile/test.pt index f138a85..74ff935 100644 --- a/plone/namedfile/test.pt +++ b/plone/namedfile/test.pt @@ -26,6 +26,7 @@ >

Go to: +<<<<<<< HEAD +======= + examples, + cover, + contain, + picture tags, + img tag with srcset, + stored scales, + clear +

+
+

Thumb with info

+

Standard display of Image content type, but using thumb scale

+
+ + + +
+>>>>>>> 14cb618 (add link in the images test)
-======= -======= -
-

img with srcset attributes

-

- srcst allows the browser selecting 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 a required paramter the value of the sizes attribute that the user of this method has to provide and will be outputed as is in the generated HTML. -

-

- -

-

- - - -

-
- - ->>>>>>> 8c6eb8d (show the srcset example in the image-test view) -
-

Stored scales

-
-
-

- -

-
-
- -
- - : - -
-
-
Open in new tab ->>>>>>> 4fe955e (fix: fix rendering unrenderable values)
From f5133fa2a79041f3fdf07a42fb37143b29a1cfa7 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 17 Mar 2025 15:21:22 +0100 Subject: [PATCH 08/24] lint --- plone/namedfile/test.pt | 39 +++++++++++++++------------ plone/namedfile/tests/test_scaling.py | 25 ++++++++++------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/plone/namedfile/test.pt b/plone/namedfile/test.pt index 6aca89a..6775619 100644 --- a/plone/namedfile/test.pt +++ b/plone/namedfile/test.pt @@ -42,7 +42,10 @@ i18n:name="" i18n:translate="" >picture tags, - img tag with srcset, + img tag with srcset,
-
-

img with srcset attributes

-

+

+

img with srcset attributes

+

srcst allows the browser selecting 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 a required paramter the value of the sizes attribute that the user of this method has to provide and will be outputed as is in the generated HTML. -

-

- -

-

- - - -

-
+

+

+ 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 a required parameter the value of the sizes attribute that the user of this method has to provide and will be outputted as is in the generated HTML. +

+

+ +

+

+ + + +

+
""" self.assertTrue(_ellipsis_match(expected, tag.strip())) def testImgSrcSetCustomSrc(self): - """ test that we can select a custom scale in the src attribute """ + """test that we can select a custom scale in the src attribute""" self.scaling.available_sizes = { "huge": (1600, 65536), "great": (1200, 65536), @@ -858,7 +855,7 @@ def testImgSrcSetCustomSrc(self): def testImgSrcSetInexistentScale(self): """test that when requesting an inexistent scale for the src attribute - we provide the biggest scale we can produce + we provide the biggest scale we can produce """ self.scaling.available_sizes = { "huge": (1600, 65536), @@ -873,7 +870,9 @@ def testImgSrcSetInexistentScale(self): "icon": (32, 32), "listing": (16, 16), } - tag = self.scaling.srcset("image", sizes="50vw", scale_in_src="inexistent-scale-name") + tag = self.scaling.srcset( + "image", sizes="50vw", scale_in_src="inexistent-scale-name" + ) base = self.item.absolute_url() expected = f"""""" self.assertTrue(_ellipsis_match(expected, tag.strip())) @@ -899,7 +898,7 @@ def testImgSrcSetCustomTitle(self): self.assertTrue(_ellipsis_match(expected, tag.strip())) def testImgSrcSetAdditionalAttributes(self): - """test that additional parameters are outputed as is, like alt, loading, ...""" + """test that additional parameters are outputted as is, like alt, loading, ...""" self.scaling.available_sizes = { "huge": (1600, 65536), "great": (1200, 65536), @@ -913,7 +912,13 @@ def testImgSrcSetAdditionalAttributes(self): "icon": (32, 32), "listing": (16, 16), } - tag = self.scaling.srcset("image", sizes="50vw", title="My Custom Title", alt="This image shows nothing", loading="lazy") + tag = self.scaling.srcset( + "image", + sizes="50vw", + title="My Custom Title", + alt="This image shows nothing", + loading="lazy", + ) base = self.item.absolute_url() expected = f"""This image shows nothing""" self.assertTrue(_ellipsis_match(expected, tag.strip())) From 00166e8704107a6a87a5f388873b3080afc996ba Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Wed, 19 Mar 2025 09:53:16 +0100 Subject: [PATCH 09/24] Update plone/namedfile/test.pt Co-authored-by: Maurits van Rees --- plone/namedfile/test.pt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plone/namedfile/test.pt b/plone/namedfile/test.pt index 6775619..95e3b71 100644 --- a/plone/namedfile/test.pt +++ b/plone/namedfile/test.pt @@ -210,7 +210,7 @@ srcst allows the browser selecting 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 a required parameter the value of the sizes attribute that the user of this method has to provide and will be outputted as is in the generated HTML. + 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.

From 08fe009827d9900589fcd337fe2b4ba3f261eb05 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Wed, 19 Mar 2025 09:53:25 +0100 Subject: [PATCH 10/24] Update plone/namedfile/tests/test_scaling.py Co-authored-by: Maurits van Rees --- plone/namedfile/tests/test_scaling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plone/namedfile/tests/test_scaling.py b/plone/namedfile/tests/test_scaling.py index aec23ff..e600f51 100644 --- a/plone/namedfile/tests/test_scaling.py +++ b/plone/namedfile/tests/test_scaling.py @@ -898,7 +898,7 @@ def testImgSrcSetCustomTitle(self): self.assertTrue(_ellipsis_match(expected, tag.strip())) def testImgSrcSetAdditionalAttributes(self): - """test that additional parameters are outputted as is, like alt, loading, ...""" + """test that additional parameters are output as is, like alt, loading, ...""" self.scaling.available_sizes = { "huge": (1600, 65536), "great": (1200, 65536), From ac8b949beadaac05075c87f2d7ae3ac5ff432922 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Fri, 21 Mar 2025 08:22:06 +0100 Subject: [PATCH 11/24] Update plone/namedfile/test.pt Co-authored-by: Maurits van Rees --- plone/namedfile/test.pt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plone/namedfile/test.pt b/plone/namedfile/test.pt index 95e3b71..f202549 100644 --- a/plone/namedfile/test.pt +++ b/plone/namedfile/test.pt @@ -207,7 +207,7 @@ >

img with srcset attributes

- srcst allows the browser selecting the correct image depending on the space the image has on a page. + 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. From 681facdda1384119bb0c250ed3428165037b7b88 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Fri, 21 Mar 2025 08:22:14 +0100 Subject: [PATCH 12/24] Update plone/namedfile/test.pt Co-authored-by: Maurits van Rees --- plone/namedfile/test.pt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plone/namedfile/test.pt b/plone/namedfile/test.pt index f202549..51c4b8c 100644 --- a/plone/namedfile/test.pt +++ b/plone/namedfile/test.pt @@ -209,7 +209,7 @@

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.

From 85ecca37d38d583ce543b34ac2c72152b56c8f32 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Thu, 3 Apr 2025 09:24:55 +0200 Subject: [PATCH 13/24] do not generate the scale itself, but use pre_scale to pre_create it, it will effectively be created when rendering the image --- plone/namedfile/scaling.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index 821e3da..13d2586 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -767,10 +767,10 @@ def srcset( srcset_urls = [] for width, height in self.available_sizes.values(): if width <= original_width: - scale = storage.scale( + scale = storage.pre_scale( fieldname=fieldname, width=width, height=height, mode="scale" ) - extension = scale["data"].contentType.split("/")[-1].lower() + extension = scale["mimetype"].split("/")[-1].lower() srcset_urls.append( f'{self.context.absolute_url()}/@@images/{scale["uid"]}.{extension} {scale["width"]}w' ) From abee1ff8f602d09d75a239fea1d309da7e8dd3da Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Thu, 3 Apr 2025 10:30:07 +0200 Subject: [PATCH 14/24] support passing custom CSS classes --- plone/namedfile/scaling.py | 6 +++++- plone/namedfile/tests/test_scaling.py | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index 13d2586..d6d68d8 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -744,8 +744,9 @@ def srcset( fieldname=None, scale_in_src="huge", sizes="", - title=_marker, alt=_marker, + css_class=None, + title=_marker, **kwargs, ): if fieldname is None: @@ -782,6 +783,9 @@ def srcset( if alt is not _marker: attributes["alt"] = alt + if css_class is not None: + attributes["class"] = css_class + attributes.update(**kwargs) attributes["sizes"] = sizes diff --git a/plone/namedfile/tests/test_scaling.py b/plone/namedfile/tests/test_scaling.py index e600f51..e6abc78 100644 --- a/plone/namedfile/tests/test_scaling.py +++ b/plone/namedfile/tests/test_scaling.py @@ -915,12 +915,13 @@ def testImgSrcSetAdditionalAttributes(self): tag = self.scaling.srcset( "image", sizes="50vw", - title="My Custom Title", alt="This image shows nothing", + css_class="my-personal-class", + title="My Custom Title", loading="lazy", ) base = self.item.absolute_url() - expected = f"""This image shows nothing""" + expected = f"""This image shows nothing""" self.assertTrue(_ellipsis_match(expected, tag.strip())) From 10a4ac34b003c0ba53b296cf93b6ef524165dc78 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 27 May 2025 09:17:49 +0200 Subject: [PATCH 15/24] documentation --- plone/namedfile/usage.rst | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/plone/namedfile/usage.rst b/plone/namedfile/usage.rst index c18cf07..9127721 100644 --- a/plone/namedfile/usage.rst +++ b/plone/namedfile/usage.rst @@ -457,3 +457,43 @@ several ways that you may reference scales from page templates. 5. and lastly, the short-cut can also be used to render the unscaled image:: + + +Image scales and srcset +----------------------- + +Nowadays modern browsers are able to render different images depending on their width +if urls and widths are correctly provided in an attribute called `srcset` and the rendered +space is provided in the attribute `sizes`. + +So one can do the following: + + + + +This will render the `img` with the urls of all scales configured in Plone, calculating the width +of each of the scales and will add the `sizes="90vw"` attribute which instructs the browser to "render +the image that best fits as it will take the 90% of the current viewport-width" whichever is the current +viewport. + +This will mean that for bigger screens the browser will download a bigger image while in small screens +a smaller scale is enough. + +This also means that the developer does not need to worry on creating a specific scale, they only need to +provide the correct media query to signal the required width. + +The `scrset` method of the `@@images` view takes also all other parameters that can be rendered in the `img` +tag such as `title`, `alt` or `loading`: + + + + + +*NOTE*: while using this approach may be useful for projects, using it in reusable addons is not recomended +because it may require overriding it to your needs in a project. For such cases, we recomend using configurable +picture variants. From 2dc84ba56c9b63688583d337855bdd8e3901e394 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 27 May 2025 09:31:26 +0200 Subject: [PATCH 16/24] fix spelling --- plone/namedfile/usage.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plone/namedfile/usage.rst b/plone/namedfile/usage.rst index 9127721..3061b7d 100644 --- a/plone/namedfile/usage.rst +++ b/plone/namedfile/usage.rst @@ -494,6 +494,6 @@ tag such as `title`, `alt` or `loading`: css_class='rounded-img')" /> -*NOTE*: while using this approach may be useful for projects, using it in reusable addons is not recomended -because it may require overriding it to your needs in a project. For such cases, we recomend using configurable +*NOTE*: while using this approach may be useful for projects, using it in reusable addons is not recommended +because it may require overriding it to your needs in a project. For such cases, we recommend using configurable picture variants. From 0fc3c04adaf78fc498fc88c5e24842d86ada8bf0 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 27 May 2025 12:13:43 +0200 Subject: [PATCH 17/24] Update plone/namedfile/usage.rst Co-authored-by: Steve Piercy --- plone/namedfile/usage.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plone/namedfile/usage.rst b/plone/namedfile/usage.rst index 3061b7d..f22410a 100644 --- a/plone/namedfile/usage.rst +++ b/plone/namedfile/usage.rst @@ -463,8 +463,8 @@ Image scales and srcset ----------------------- Nowadays modern browsers are able to render different images depending on their width -if urls and widths are correctly provided in an attribute called `srcset` and the rendered -space is provided in the attribute `sizes`. +if URLs and widths are correctly provided in an attribute called ``srcset``, and the rendered +space is provided in the attribute ``sizes``. So one can do the following: From de4c763b10f2fda7a9aa4e65e9792f651c1e1072 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 27 May 2025 12:14:05 +0200 Subject: [PATCH 18/24] Update plone/namedfile/usage.rst Co-authored-by: Steve Piercy --- plone/namedfile/usage.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plone/namedfile/usage.rst b/plone/namedfile/usage.rst index f22410a..450f34f 100644 --- a/plone/namedfile/usage.rst +++ b/plone/namedfile/usage.rst @@ -472,8 +472,9 @@ So one can do the following: tal:replace="structure python:images.srcset(sizes='90vw')" /> -This will render the `img` with the urls of all scales configured in Plone, calculating the width -of each of the scales and will add the `sizes="90vw"` attribute which instructs the browser to "render +This will render the ``img`` with the URLs of all scales configured in Plone, calculating the width +of each of the scales and will add the ``sizes="90vw"`` attribute. +The ``sizes`` attribute instructs the browser to render the image that best fits as it will take the 90% of the current viewport-width" whichever is the current viewport. From 41be735a8662619e0cc72086a329c56159741647 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 27 May 2025 12:15:24 +0200 Subject: [PATCH 19/24] Apply suggestions from code review Co-authored-by: Steve Piercy --- plone/namedfile/usage.rst | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/plone/namedfile/usage.rst b/plone/namedfile/usage.rst index 450f34f..c1aca57 100644 --- a/plone/namedfile/usage.rst +++ b/plone/namedfile/usage.rst @@ -468,6 +468,8 @@ space is provided in the attribute ``sizes``. So one can do the following: +.. code-block:: xml + @@ -475,17 +477,16 @@ So one can do the following: This will render the ``img`` with the URLs of all scales configured in Plone, calculating the width of each of the scales and will add the ``sizes="90vw"`` attribute. The ``sizes`` attribute instructs the browser to render -the image that best fits as it will take the 90% of the current viewport-width" whichever is the current -viewport. +the image that best fits, as it will take 90% of the current viewport width. + +This means that, for bigger screens, the browser will download a bigger scaled image, while for smaller screens, a smaller scaled image is enough. -This will mean that for bigger screens the browser will download a bigger image while in small screens -a smaller scale is enough. +This also means that the developer does not need to worry about creating a specific scale. +They only need to provide the correct media query to signal the required width. -This also means that the developer does not need to worry on creating a specific scale, they only need to -provide the correct media query to signal the required width. +The ``scrset`` method of the ``@@images`` view also takes all other parameters that can be rendered in the ``img`` tag, such as ``title``, ``alt``, or ``loading``: -The `scrset` method of the `@@images` view takes also all other parameters that can be rendered in the `img` -tag such as `title`, `alt` or `loading`: +.. code-block:: xml -*NOTE*: while using this approach may be useful for projects, using it in reusable addons is not recommended -because it may require overriding it to your needs in a project. For such cases, we recommend using configurable -picture variants. +.. note:: + + While using this approach may be useful for projects, using it in reusable add-ons is not recommended, because it may require overriding it to your needs in a project. + For such cases, use configurable picture variants instead. From 0a6f4e0aa2962dc4847e75cafc748a8bd12ee70e Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 27 May 2025 12:32:24 +0200 Subject: [PATCH 20/24] point documentation of this package to Plone docs --- README.rst | 5 +- plone/namedfile/usage.rst | 97 --------------------------------------- 2 files changed, 3 insertions(+), 99 deletions(-) diff --git a/README.rst b/README.rst index 8314989..98a9464 100644 --- a/README.rst +++ b/README.rst @@ -12,13 +12,14 @@ and BLOBs to be configured in zope.conf. plone.supermodel handlers are registered. -See the ``usage.rst`` doctest for more details. +See the `image handling section of Plone documentation `_ to learn how to +use the features provided by this package. Source Code =========== - Note: This packages is licensed under a *BSD license*. + Note: This packages is licensed under a *BSD license*. Please do not add dependencies on GPL code! Contributors please read the document `Process for Plone core's development `_ diff --git a/plone/namedfile/usage.rst b/plone/namedfile/usage.rst index c1aca57..8dbb027 100644 --- a/plone/namedfile/usage.rst +++ b/plone/namedfile/usage.rst @@ -403,100 +403,3 @@ We will test this with a dummy request, faking traversal:: 'text/plain' >>> request.response.getHeader('Content-Disposition') "attachment; filename*=UTF-8''test.txt" - - -Image scales ------------- - -This package can handle the creation, storage, and retrieval of arbitrarily -sized scaled versions of images stored in NamedImage or NamedBlobImage fields. - -Image scales are accessed via an @@images view that is available for any item -providing ``plone.namedfile.interfaces.IImageScaleTraversable``. There are -several ways that you may reference scales from page templates. - -1. for full control you may do the tag generation explicitly:: - - - - This would create an up to 64 by 64 pixel scaled down version of the image - stored in the "image" field. It also allows for passing in additional - parameters supported by the ``scaleImage`` function from ``plone.scale``, - e.g. ``mode`` or ``quality``. - - .. _`plone.scale`: https://pypi.org/project/plone.scale/ - -2. for automatic tag generation with extra parameters you would use:: - - - -3. It is possible to access scales via predefined named scale sizes, rather - than hardcoding the dimensions every time you access a scale. The scale - sizes are found via calling a utility providing - ``plone.namedfile.interfaces.IAvailableSizes``, which should return a dict of - scale name => (width, height). A scale called 'mini' could then be accessed - like this:: - - - - This would use the predefined scale size "mini" to determine the desired - image dimensions, but still allow to pass in extra parameters. - -4. a convenience short-cut for option 3 can be used:: - - - -5. and lastly, the short-cut can also be used to render the unscaled image:: - - - - -Image scales and srcset ------------------------ - -Nowadays modern browsers are able to render different images depending on their width -if URLs and widths are correctly provided in an attribute called ``srcset``, and the rendered -space is provided in the attribute ``sizes``. - -So one can do the following: - -.. code-block:: xml - - - - -This will render the ``img`` with the URLs of all scales configured in Plone, calculating the width -of each of the scales and will add the ``sizes="90vw"`` attribute. -The ``sizes`` attribute instructs the browser to render -the image that best fits, as it will take 90% of the current viewport width. - -This means that, for bigger screens, the browser will download a bigger scaled image, while for smaller screens, a smaller scaled image is enough. - -This also means that the developer does not need to worry about creating a specific scale. -They only need to provide the correct media query to signal the required width. - -The ``scrset`` method of the ``@@images`` view also takes all other parameters that can be rendered in the ``img`` tag, such as ``title``, ``alt``, or ``loading``: - -.. code-block:: xml - - - - - -.. note:: - - While using this approach may be useful for projects, using it in reusable add-ons is not recommended, because it may require overriding it to your needs in a project. - For such cases, use configurable picture variants instead. From 60e3642d22e2ca8a63e5a6768d1547a5a8e30c93 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 27 May 2025 16:18:43 +0200 Subject: [PATCH 21/24] Update plone/namedfile/scaling.py Co-authored-by: Alessandro Pisa --- plone/namedfile/scaling.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index d6d68d8..5f57188 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -777,7 +777,7 @@ def srcset( ) attributes = {} if title is _marker: - attributes["title"] = self.context.Title() + attributes["title"] = self.title elif title: attributes["title"] = title if alt is not _marker: From 30108e72ecac0eff7736897e1dfa248194b940b7 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 27 May 2025 16:19:17 +0200 Subject: [PATCH 22/24] make attribute generation consistent with the method --- plone/namedfile/scaling.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index 5f57188..c4b2a6a 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -780,7 +780,9 @@ def srcset( attributes["title"] = self.title elif title: attributes["title"] = title - if alt is not _marker: + if alt is _marker: + attributes["alt"] = self.title + else: attributes["alt"] = alt if css_class is not None: From 0850c09f238f3f0a2562670c5827875a16559d91 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 27 May 2025 16:40:18 +0200 Subject: [PATCH 23/24] self.title does not work --- plone/namedfile/scaling.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index c4b2a6a..ddf140c 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -777,11 +777,11 @@ def srcset( ) attributes = {} if title is _marker: - attributes["title"] = self.title + attributes["title"] = self.context.Title() elif title: attributes["title"] = title if alt is _marker: - attributes["alt"] = self.title + attributes["alt"] = self.context.Title() else: attributes["alt"] = alt From 68c3e06b30891f246ffad1a9aef08627c1a9daa0 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Tue, 27 May 2025 16:55:22 +0200 Subject: [PATCH 24/24] fix testing after the alt/title changes --- plone/namedfile/tests/test_scaling.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plone/namedfile/tests/test_scaling.py b/plone/namedfile/tests/test_scaling.py index e6abc78..47163b8 100644 --- a/plone/namedfile/tests/test_scaling.py +++ b/plone/namedfile/tests/test_scaling.py @@ -830,7 +830,7 @@ def testImgSrcSet(self): } tag = self.scaling.srcset("image", sizes="50vw") base = self.item.absolute_url() - expected = f"""""" + expected = f"""foo""" self.assertTrue(_ellipsis_match(expected, tag.strip())) def testImgSrcSetCustomSrc(self): @@ -850,7 +850,7 @@ def testImgSrcSetCustomSrc(self): } tag = self.scaling.srcset("image", sizes="50vw", scale_in_src="mini") base = self.item.absolute_url() - expected = f"""""" + expected = f"""foo""" self.assertTrue(_ellipsis_match(expected, tag.strip())) def testImgSrcSetInexistentScale(self): @@ -874,7 +874,7 @@ def testImgSrcSetInexistentScale(self): "image", sizes="50vw", scale_in_src="inexistent-scale-name" ) base = self.item.absolute_url() - expected = f"""""" + expected = f"""foo""" self.assertTrue(_ellipsis_match(expected, tag.strip())) def testImgSrcSetCustomTitle(self): @@ -894,7 +894,7 @@ def testImgSrcSetCustomTitle(self): } tag = self.scaling.srcset("image", sizes="50vw", title="My Custom Title") base = self.item.absolute_url() - expected = f"""""" + expected = f"""foo""" self.assertTrue(_ellipsis_match(expected, tag.strip())) def testImgSrcSetAdditionalAttributes(self): @@ -921,6 +921,7 @@ def testImgSrcSetAdditionalAttributes(self): loading="lazy", ) base = self.item.absolute_url() + expected = f"""This image shows nothing""" self.assertTrue(_ellipsis_match(expected, tag.strip()))