Implement 'bgcolor' image operation.

Also fixes #3713.

Remove alpha channel if converting transparent image to JPEG
pull/4182/head
Karl Hobley 2017-10-06 12:28:56 +01:00 zatwierdzone przez Bertrand Bordage
rodzic c10e050076
commit 1e332b4b67
8 zmienionych plików z 99 dodań i 2 usunięć

Wyświetl plik

@ -253,6 +253,25 @@ For example, to make the tag always convert the image to a JPEG, use ``format-jp
You may also use ``format-png`` or ``format-gif``.
.. _image_background_color
Background color
----------------
The PNG and GIF image formats both support transparency, but if you want to
convert images to JPEG format, the transparency will need to be replaced with a
solid background color.
By default, Wagtail will set the background to white. But if a white background
doesn't fit your design, you can specify a color using the ``bgcolor`` filter.
This filter takes a single argument, which is a CSS 3 or 6 digit hex code
representing the color you would like to use:
.. code-block:: html+Django
{# Set the image backgrounds to black #}
{% image page.photo width-400 bgcolor-000 format-jpeg %}
.. _jpeg_image_quality:

Wyświetl plik

@ -30,7 +30,7 @@ install_requires = [
"beautifulsoup4>=4.5.1,<5.0",
"html5lib>=0.999,<1",
"Unidecode>=0.04.14,<1.0",
"Willow>=1.0,<1.1",
"Willow>=1.1,<1.2",
"requests>=2.11.1,<3.0",
]

Wyświetl plik

@ -2,6 +2,7 @@ import inspect
from wagtail.images.exceptions import InvalidFilterSpecError
from wagtail.images.rect import Rect
from wagtail.images.utils import parse_color_string
class Operation:
@ -236,3 +237,11 @@ class FormatOperation(Operation):
def run(self, willow, image, env):
env['output-format'] = self.format
class BackgroundColorOperation(Operation):
def construct(self, color_string):
self.color = parse_color_string(color_string)
def run(self, willow, image, env):
return willow.set_background_color_rgb(self.color)

Wyświetl plik

@ -395,6 +395,10 @@ class Filter:
else:
quality = 85
# If the image has an alpha channel, give it a white background
if willow.has_alpha():
willow = willow.set_background_color_rgb((255, 255, 255))
return willow.save_as_jpeg(output, quality=quality, progressive=True, optimize=True)
elif output_format == 'png':
return willow.save_as_png(output)

Wyświetl plik

@ -544,3 +544,44 @@ class TestJPEGQualityFilter(TestCase):
fil.run(image, f)
save.assert_called_with(f, 'JPEG', quality=40, optimize=True, progressive=True)
class TestBackgroundColorFilter(TestCase):
def test_original_has_alpha(self):
# Checks that the test image we're using has alpha
fil = Filter(spec='width-400')
image = Image.objects.create(
title="Test image",
file=get_test_image_file(),
)
out = fil.run(image, BytesIO())
self.assertTrue(out.has_alpha())
def test_3_digit_hex(self):
fil = Filter(spec='width-400|bgcolor-fff')
image = Image.objects.create(
title="Test image",
file=get_test_image_file(),
)
out = fil.run(image, BytesIO())
self.assertFalse(out.has_alpha())
def test_6_digit_hex(self):
fil = Filter(spec='width-400|bgcolor-ffffff')
image = Image.objects.create(
title="Test image",
file=get_test_image_file(),
)
out = fil.run(image, BytesIO())
self.assertFalse(out.has_alpha())
def test_invalid(self):
fil = Filter(spec='width-400|bgcolor-foo')
image = Image.objects.create(
title="Test image",
file=get_test_image_file(),
)
self.assertRaises(ValueError, fil.run, image, BytesIO())

Wyświetl plik

@ -10,7 +10,7 @@ Image = get_image_model()
def get_test_image_file(filename='test.png', colour='white', size=(640, 480)):
f = BytesIO()
image = PIL.Image.new('RGB', size, colour)
image = PIL.Image.new('RGBA', size, colour)
image.save(f, 'PNG')
return ImageFile(f, name=filename)

Wyświetl plik

@ -37,3 +37,26 @@ def get_fill_filter_spec_migrations(app_name, rendition_model_name):
Rendition.objects.using(db_alias).filter(filter_spec=filter_spec).update(filter=filter)
return (fill_filter_spec_forward, fill_filter_spec_reverse)
def parse_color_string(color_string):
"""
Parses a string a user typed into a tuple of 3 integers representing the
red, green and blue channels respectively.
May raise a ValueError if the string cannot be parsed.
The colour string must be a CSS 3 or 6 digit hex code without the '#' prefix.
"""
if len(color_string) == 3:
r = int(color_string[0], 16) * 17
g = int(color_string[1], 16) * 17
b = int(color_string[2], 16) * 17
elif len(color_string) == 6:
r = int(color_string[0:2], 16)
g = int(color_string[2:4], 16)
b = int(color_string[4:6], 16)
else:
ValueError('Color string must be either 3 or 6 hexadecimal digits long')
return r, g, b

Wyświetl plik

@ -86,6 +86,7 @@ def register_image_operations():
('height', image_operations.WidthHeightOperation),
('jpegquality', image_operations.JPEGQualityOperation),
('format', image_operations.FormatOperation),
('bgcolor', image_operations.BackgroundColorOperation),
]