Vishesh Garg 2025-04-23 19:58:32 -04:00 zatwierdzone przez GitHub
commit db130175be
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
3 zmienionych plików z 163 dodań i 2 usunięć

Wyświetl plik

@ -83,6 +83,12 @@ Or resize an image and retrieve the resized image object (rendition) for more be
<div class="wrapper" style="background-image: url({{ background.url }});"></div>
```
When working with SVG images, you can use the `preserve_svg` parameter to prevent operations that would require rasterizing the SVG. When preserve_svg is set to True and the image is an SVG, operations that would require rasterization (like format conversion) will be automatically filtered out, ensuring SVGs remain as vector graphics. This is especially useful in loops processing both raster images and SVGs.
```html+jinja
{{ image(page.svg_image, "width-400|format-webp", preserve_svg=True) }}
```
See [](image_tag) for more information
### `srcset_image()`
@ -108,6 +114,12 @@ Or resize an image and retrieve the renditions for more bespoke use:
<div class="wrapper" style="background-image: image-set(url({{ bg.renditions[0].url }}) 1x, url({{ bg.renditions[1].url }}) 2x);"></div>
```
When working with SVG images, you can use the `preserve_svg` parameter to prevent operations that would require rasterizing the SVG.
```html+jinja
{{ srcset_image(page.svg_image, "width-400|format-webp", preserve_svg=True) }}
```
### `picture()`
Resize or convert an image, rendering a `<picture>` tag including multiple `source` formats with `srcset` for multiple sizes, and a fallback `<img>` tag.
@ -152,6 +164,12 @@ Or resize an image and retrieve the renditions for more bespoke use:
<div class="wrapper" style="background-image: image-set(url({{ bg.formats['avif'][0].url }}) 1x type('image/avif'), url({{ bg.formats['avif'][1].url }}) 2x type('image/avif'), url({{ bg.formats['jpeg'][0].url }}) 1x type('image/jpeg'), url({{ bg.formats['jpeg'][1].url }}) 2x type('image/jpeg'));"></div>
```
For SVG images, you can use the preserve_svg parameter to ensure they remain as vector graphics:
```html+jinja
{{ picture(page.header_image, "format-{avif,webp,jpeg}|width-{400,800}", sizes="80vw") }}
```
### `|richtext`
Transform Wagtail's internal HTML representation, expanding internal references to pages and images.

Wyświetl plik

@ -4,6 +4,7 @@ from jinja2.ext import Extension
from .models import Filter, Picture, ResponsiveImage
from .shortcuts import get_rendition_or_not_found, get_renditions_or_not_found
from .templatetags.wagtailimages_tags import image_url
from .utils import to_svg_safe_spec
def image(image, filterspec, **attrs):
@ -16,6 +17,9 @@ def image(image, filterspec, **attrs):
"(given filter: {})".format(filterspec)
)
if "preserve_svg" in filterspec.split("|") and image.is_svg():
filterspec = to_svg_safe_spec(filterspec)
rendition = get_rendition_or_not_found(image, filterspec)
if attrs:
@ -24,7 +28,7 @@ def image(image, filterspec, **attrs):
return rendition
def srcset_image(image, filterspec, **attrs):
def srcset_image(image, filterspec, preserve_svg=False, **attrs):
if not image:
return ""
@ -34,13 +38,17 @@ def srcset_image(image, filterspec, **attrs):
"(given filter: {})".format(filterspec)
)
# SVG support for jinja2
if preserve_svg and image.is_svg():
filterspec = to_svg_safe_spec(filterspec)
specs = Filter.expand_spec(filterspec)
renditions = get_renditions_or_not_found(image, specs)
return ResponsiveImage(renditions, attrs)
def picture(image, filterspec, **attrs):
def picture(image, filterspec, preserve_svg=False, **attrs):
if not image:
return ""
@ -50,6 +58,10 @@ def picture(image, filterspec, **attrs):
"(given filter: {})".format(filterspec)
)
# SVG support for jinja2
if preserve_svg and image.is_svg:
filterspec = to_svg_safe_spec(filterspec)
specs = Filter.expand_spec(filterspec)
renditions = get_renditions_or_not_found(image, specs)

Wyświetl plik

@ -0,0 +1,131 @@
from django.test import TestCase
from wagtail.images.exceptions import InvalidFilterSpecError
from wagtail.images.models import Image
from wagtail.images.tests.utils import (
get_test_image_file,
get_test_image_file_svg,
)
from wagtail.test.utils import WagtailTestUtils
class TestJinja2SVGSupport(WagtailTestUtils, TestCase):
"""Test SVG support in Jinja2 templates with preserve_svg parameter."""
def setUp(self):
# Create a real test engine
from django.template.loader import engines
self.engine = engines["jinja2"]
# Create a raster image
self.raster_image = Image.objects.create(
title="Test raster image",
file=get_test_image_file(),
)
# Create an SVG image
self.svg_image = Image.objects.create(
title="Test SVG image",
file=get_test_image_file_svg(),
)
# Patch the is_svg method to simulate SVG detection
self.original_is_svg = Image.is_svg
def tearDown(self):
# Restore the original is_svg method
Image.is_svg = self.original_is_svg
def render(self, string, context=None):
if context is None:
context = {}
template = self.engine.from_string(string)
return template.render(context)
def test_image_with_raster_image(self):
"""Test that raster images work normally without preserve_svg."""
html = self.render(
'{{ image(img, "width-200|format-webp") }}', {"img": self.raster_image}
)
self.assertIn('width="200"', html)
self.assertIn(".webp", html) # Format conversion applied
def test_image_with_svg_without_preserve(self):
"""Test that without preserve_svg, SVGs get all operations (which would fail in production)."""
with self.assertRaises(AttributeError):
self.render(
'{{ image(img, "width-200|format-webp") }}', {"img": self.svg_image}
)
def test_image_with_svg_with_preserve(self):
"""Test that with preserve_svg=True, SVGs only get safe operations."""
html = self.render(
'{{ image(img, "width-200|format-webp", preserve_svg=True) }}',
{"img": self.svg_image},
)
# Check the SVG is preserved
self.assertIn(".svg", html)
self.assertNotIn(".webp", html)
def test_srcset_image_with_svg_preserve(self):
"""Test that preserve_svg works with srcset_image function."""
html = self.render(
'{{ srcset_image(img, "width-{200,400}|format-webp", sizes="100vw", preserve_svg=True) }}',
{"img": self.svg_image},
)
# Should preserve SVG format
self.assertIn(".svg", html)
self.assertNotIn(".webp", html)
def test_picture_with_svg_preserve(self):
"""Test that preserve_svg works with picture function."""
html = self.render(
'{{ picture(img, "format-{avif,webp,jpeg}|width-400", preserve_svg=True) }}',
{"img": self.svg_image},
)
# Should preserve SVG format
self.assertIn(".svg", html)
self.assertNotIn(".webp", html)
self.assertNotIn(".avif", html)
self.assertNotIn(".jpeg", html)
def test_preserve_svg_with_multiple_operations(self):
"""Test preserve_svg with multiple operations, some safe, some unsafe for SVGs."""
html = self.render(
'{{ image(img, "width-300|height-200|format-webp|fill-100x100|jpegquality-80", preserve_svg=True) }}',
{"img": self.svg_image},
)
# Should preserve SVG format
self.assertIn(".svg", html)
self.assertNotIn(".webp", html)
self.assertNotIn("jpegquality-80", html)
def test_invalid_filter_spec_error(self):
"""Test that invalid filter specs still raise appropriate errors."""
with self.assertRaises(InvalidFilterSpecError):
self.render(
'{{ image(img, "invalidfilter", preserve_svg=True) }}',
{"img": self.svg_image},
)
def test_preserve_svg_with_custom_attributes(self):
"""Test preserve_svg works with custom HTML attributes."""
html = self.render(
'{{ image(img, "width-200|format-webp", class="my-image", alt="Custom alt", preserve_svg=True) }}',
{"img": self.svg_image},
)
# Check custom attributes are present
self.assertIn('class="my-image"', html)
self.assertIn('alt="Custom alt"', html)
# SVG should be preserved
self.assertNotIn(".webp", html)
self.assertIn(".svg", html)