kopia lustrzana https://github.com/wagtail/wagtail
Merge af0deb3da4
into 74b5933ae7
commit
db130175be
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
Ładowanie…
Reference in New Issue