diff --git a/news/179.bugfix b/news/179.bugfix new file mode 100644 index 0000000..31d6e75 --- /dev/null +++ b/news/179.bugfix @@ -0,0 +1 @@ +set sizes attribute on picture-tags [MrTango] diff --git a/news/180.feature b/news/180.feature new file mode 100644 index 0000000..b4138f5 --- /dev/null +++ b/news/180.feature @@ -0,0 +1,2 @@ +allow to set lazy to false, to suppress the loading="lazy" attribute +[MrTango] diff --git a/plone/namedfile/picture.py b/plone/namedfile/picture.py index d9e4241..299eea1 100644 --- a/plone/namedfile/picture.py +++ b/plone/namedfile/picture.py @@ -47,11 +47,19 @@ def get_scale_width(self, scale): return scale_info[0] def create_picture_tag( - self, sourceset, attributes, uid=None, fieldname=None, resolve_urls=False + self, + sourceset, + attributes, + uid=None, + fieldname=None, + resolve_urls=False, + lazy=True, ): """Converts the img tag to a picture tag with picture_variant definition""" width = None height = None + target_width = None + sizes = "" src = attributes.get("src") if not uid and not src: raise TypeError("Either uid or attributes['src'] need to be given.") @@ -68,6 +76,7 @@ def create_picture_tag( for i, source in enumerate(sourceset): target_scale = source["scale"] media = source.get("media") + sizes = source.get("sizes") additional_scales = source.get("additionalScales", None) if additional_scales is None: @@ -78,6 +87,8 @@ def create_picture_tag( source_srcset = [] for scale in source_scales: scale_width = self.get_scale_width(scale) + if scale == target_scale: + target_width = scale_width if not scale_width: logger.warning("No width found for scale %s.", scale) continue @@ -86,13 +97,13 @@ def create_picture_tag( scale_obj = scale_view.scale(fieldname, scale, pre=True) scale_url = scale_obj.url else: - # obj = self.resolve_uid_url(src) - # scale_view = obj.unrestrictedTraverse("@@images", None) - # scale_obj = scale_view.scale(fieldname, scale, pre=True) - # scale_url = scale_obj.url scale_url = self.update_src_scale(src=src, scale=scale) source_srcset.append(f"{scale_url} {scale_width}w") - source_tag = soup.new_tag("source", srcset=",\n".join(source_srcset)) + if not sizes: + sizes = f"(min-width: 576px) {target_width}px, 98vw" + source_tag = soup.new_tag( + "source", srcset=",\n".join(source_srcset), sizes=sizes + ) if media: source_tag["media"] = media picture_tag.append(source_tag) @@ -104,19 +115,14 @@ def create_picture_tag( width = scale_obj.width height = scale_obj.height else: - # obj = self.resolve_uid_url(src) - # scale_view = obj.unrestrictedTraverse("@@images", None) - # scale_obj = scale_view.scale(fieldname, target_scale, pre=True) - # scale_url = scale_obj.url - # width = scale_obj.width - # height = scale_obj.height scale_url = self.update_src_scale(src=src, scale=target_scale) img_tag = soup.new_tag("img", src=scale_url) for k, attr in attributes.items(): if k in ["src", "srcset"]: continue img_tag.attrs[k] = attr - img_tag["loading"] = "lazy" + if lazy: + img_tag["loading"] = "lazy" if width: img_tag["width"] = width if height: diff --git a/plone/namedfile/scaling.py b/plone/namedfile/scaling.py index f79fc6a..7294a03 100644 --- a/plone/namedfile/scaling.py +++ b/plone/namedfile/scaling.py @@ -691,6 +691,7 @@ def picture( alt=None, css_class=None, title=_marker, + lazy=True, **kwargs, ): img2picturetag = Img2PictureTag() @@ -737,6 +738,7 @@ def picture( resolve_urls=True, uid=scale.context.UID(), fieldname=fieldname, + lazy=lazy, ).prettify() diff --git a/plone/namedfile/tests/test_scaling.py b/plone/namedfile/tests/test_scaling.py index aa34934..653678f 100644 --- a/plone/namedfile/tests/test_scaling.py +++ b/plone/namedfile/tests/test_scaling.py @@ -88,7 +88,8 @@ def patch_Img2PictureTag_picture_variants(): "sourceset": [ { "scale": "preview", - "additionalScales": ["preview", "large", "larger"], + "additionalScales": ["large", "larger"], + "sizes": "(min-width: 576px) 350px, (min-width: 768px) 600px, 98vw", } ], }, @@ -538,15 +539,83 @@ def testGetPictureTagByName(self, mock_uuid_to_object): mock_uuid_to_object.return_value = self.item tag = self.scaling.picture("image", picture_variant="medium") expected = """ - +http://nohost/item/@@images/image-1200-....png 1200w".../> """ self.assertTrue(_ellipsis_match(expected, tag.strip())) + # The exact placement of the img tag attributes can differ, especially + # with different beautifulsoup versions. + # So check here that all attributes are present. + self.assertIn('height="200"', tag) + self.assertIn('loading="lazy"', tag) + self.assertIn('title="foo"', tag) + self.assertIn('width="200"', tag) + self.assertIn('sizes="(min-width: 576px) 600px, 98vw"', tag) + + @patch.object( + plone.namedfile.scaling, + "get_picture_variants", + new=patch_Img2PictureTag_picture_variants, + spec=True, + ) + @patch.object( + plone.namedfile.picture, + "get_allowed_scales", + new=patch_Img2PictureTag_allowed_scales, + spec=True, + ) + @patch.object(plone.namedfile.picture, "uuidToObject", spec=True) + def testGetPictureTagByNameNotLazy(self, mock_uuid_to_object): + ImageScaling._sizes = patch_Img2PictureTag_allowed_scales() + mock_uuid_to_object.return_value = self.item + tag = self.scaling.picture("image", picture_variant="medium", lazy=False) + print(tag) + expected = """ + + +""" + self.assertTrue(_ellipsis_match(expected, tag.strip())) + # The exact placement of the img tag attributes can differ, especially + # with different beautifulsoup versions. + # So check here that all attributes are present. + self.assertIn('height="200"', tag) + self.assertNotIn('loading="lazy"', tag) + self.assertIn('title="foo"', tag) + self.assertIn('width="200"', tag) + self.assertIn('sizes="(min-width: 576px) 600px, 98vw"', tag) + @patch.object( + plone.namedfile.scaling, + "get_picture_variants", + new=patch_Img2PictureTag_picture_variants, + spec=True, + ) + @patch.object( + plone.namedfile.picture, + "get_allowed_scales", + new=patch_Img2PictureTag_allowed_scales, + spec=True, + ) + @patch.object(plone.namedfile.picture, "uuidToObject", spec=True) + def testGetPictureTagCustomSizes(self, mock_uuid_to_object): + ImageScaling._sizes = patch_Img2PictureTag_allowed_scales() + mock_uuid_to_object.return_value = self.item + tag = self.scaling.picture("image", picture_variant="small") + expected = """ + + +""" + self.assertTrue(_ellipsis_match(expected, tag.strip())) # The exact placement of the img tag attributes can differ, especially # with different beautifulsoup versions. # So check here that all attributes are present. @@ -554,6 +623,9 @@ def testGetPictureTagByName(self, mock_uuid_to_object): self.assertIn('loading="lazy"', tag) self.assertIn('title="foo"', tag) self.assertIn('width="200"', tag) + self.assertIn( + 'sizes="(min-width: 576px) 350px, (min-width: 768px) 600px, 98vw"', tag + ) @patch.object( plone.namedfile.scaling, @@ -579,11 +651,11 @@ def testGetPictureTagWithAltAndTitle(self, mock_uuid_to_object): ) base = self.item.absolute_url() expected = f""" - +{base}/@@images/image-1200-....png 1200w".../> """ self.assertTrue(_ellipsis_match(expected, tag.strip()))