diff --git a/addons/web/tests/test_ir_qweb.py b/addons/web/tests/test_ir_qweb.py index 06d3bd3e724..aa1586cb4e5 100644 --- a/addons/web/tests/test_ir_qweb.py +++ b/addons/web/tests/test_ir_qweb.py @@ -1,5 +1,8 @@ +import base64 from lxml import etree + from odoo.tests.common import TransactionCase +from odoo.tools.mimetypes import guess_mimetype class TestIrQweb(TransactionCase): def test_image_field(self): @@ -28,3 +31,44 @@ class TestIrQweb(TransactionCase): self.assertTrue(img.get("src").startswith("/web/image")) self.assertEqual(img.get("class"), "img img-fluid") self.assertEqual(img.get("alt"), "test image partner") + + def test_image_field_webp(self): + webp = "UklGRsCpAQBXRUJQVlA4WAoAAAAQAAAAGAQA/wMAQUxQSMywAAAdNANp22T779/0RUREkvqLOTPesG1T21jatpLTSbpXQzTMEw3zWMM81jCPnWG2fTM7vpndvpkd38y2758Y+6a/Ld/Mt3zzT/XwzCKlV0Ooo61UpZIsKLjKc98R" + webp_decoded = base64.b64decode(webp) + self.assertEqual(guess_mimetype(webp_decoded), "image/webp") + + view = self.env["ir.ui.view"].create({ + "key": "web.test_qweb", + "type": "qweb", + "arch": """ + + """ + }) + lang_record = self.env["res.lang"].create({ + "name": "test lang", + "flag_image": webp, + "code": "TEST" + }) + attachment = self.env["ir.attachment"].search([ + ("res_model", "=", "res.lang"), + ("res_id", '=', lang_record.id), + ("res_field", "=", "flag_image") + ]) + + jpeg_attach = self.env["ir.attachment"].create({ + "name": "webpcopy.jpg", + "res_model": "ir.attachment", + "res_id": attachment.id, + "datas": "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAF0lEQVR4nGJxKFrEwMDAxAAGgAAAAP//D+IBWx9K7TUAAAAASUVORK5CYII=" + }) + jpeg_datas = jpeg_attach.datas + + html = view.with_context(webp_as_jpg=False)._render_template(view.id, {"is_raw_image": True, "record": lang_record}) + tree = etree.fromstring(html) + img = tree.find("img") + self.assertEqual(img.get("src"), "data:image/webp;base64,%s" % webp) + + html = view.with_context(webp_as_jpg=True)._render_template(view.id, {"is_raw_image": True, "record": lang_record}) + tree = etree.fromstring(html) + img = tree.find("img") + self.assertEqual(img.get("src"), "data:image/png;base64,%s" % jpeg_datas.decode()) diff --git a/odoo/addons/base/models/ir_qweb_fields.py b/odoo/addons/base/models/ir_qweb_fields.py index a65e4dd6861..d0e3b9577f3 100644 --- a/odoo/addons/base/models/ir_qweb_fields.py +++ b/odoo/addons/base/models/ir_qweb_fields.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import base64 +import binascii from datetime import time import logging import re @@ -15,6 +16,7 @@ from odoo import api, fields, models, _, _lt, tools from odoo.tools import posix_to_ldml, float_utils, format_date, format_duration, pycompat from odoo.tools.mail import safe_attrs from odoo.tools.misc import get_lang, babel_locale_parse +from odoo.tools.mimetypes import guess_mimetype _logger = logging.getLogger(__name__) @@ -390,13 +392,21 @@ class ImageConverter(models.AbstractModel): @api.model def _get_src_data_b64(self, value, options): - try: # FIXME: maaaaaybe it could also take raw bytes? - image = Image.open(BytesIO(base64.b64decode(value))) + try: + img_b64 = base64.b64decode(value) + except binascii.Error: + raise ValueError("Invalid image content") from None + + if img_b64 and guess_mimetype(img_b64, '') == 'image/webp': + return self.env["ir.qweb"]._get_converted_image_data_uri(value) + + try: + image = Image.open(BytesIO(img_b64)) image.verify() except IOError: - raise ValueError("Non-image binary fields can not be converted to HTML") + raise ValueError("Non-image binary fields can not be converted to HTML") from None except: # image.verify() throws "suitable exceptions", I have no idea what they are - raise ValueError("Invalid image content") + raise ValueError("Invalid image content") from None return "data:%s;base64,%s" % (Image.MIME[image.format], value.decode('ascii'))