Skip to content

Svg improvements, also more verbose images-test page #133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jun 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/133.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- feature: "scale" SVGs by setting the correct height and width for the given scale in its metadata.
16 changes: 7 additions & 9 deletions plone/namedfile/scaling.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions plone/namedfile/test.pt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
i18n:name=""
i18n:translate=""
>picture tags</a>,
<a href="#srcset"
i18n="name"
i18n:translate=""
>img tag with srcset</a>,
<a href="#stored"
i18n:name=""
i18n:translate=""
Expand Down Expand Up @@ -198,6 +202,26 @@
</p>
</section>

<section class="section"
id="srcset"
>
<h2 i18n:translate="">img with srcset attributes</h2>
<p i18n:translate="">
srcset allows the browser to select the correct image, depending on the space the image has on a page.
</p>
<p i18n:translate="msg_images_test_srcset">
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.
</p>
<p>
<img tal:replace="structure python:images.srcset('image', sizes='(min-width: 1400px) 550px, 90vw')" />
</p>
<p>
<code>
<img tal:replace="python:images.srcset('image', sizes='(min-width: 1400px) 550px, 90vw')" />
</code>
</p>
</section>

<section class="section"
id="stored"
>
Expand Down
6 changes: 5 additions & 1 deletion plone/namedfile/tests/test_scaling_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion plone/namedfile/utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down