diff --git a/modules/company/company.py b/modules/company/company.py index d792ae204b..813d915fc7 100644 --- a/modules/company/company.py +++ b/modules/company/company.py @@ -12,6 +12,7 @@ except ImportError: PIL = None from trytond.config import config +from trytond.i18n import gettext from trytond.model import ( MatchMixin, ModelSQL, ModelView, Unique, fields, sequence_ordered) from trytond.model.exceptions import SQLConstraintError @@ -22,6 +23,8 @@ from trytond.tools import timezone as tz from trytond.transaction import Transaction from trytond.wizard import Button, StateTransition, StateView, Wizard +from .exceptions import LogoValidationError + SIZE_MAX = config.getint('company', 'logo_size_max', default=2048) TIMEZONES = [(z, z) for z in tz.available_timezones()] TIMEZONES += [(None, '')] @@ -118,9 +121,13 @@ class Company(ModelSQL, ModelView): @classmethod def _logo_convert(cls, image, **_params): if not PIL: - return + return image data = io.BytesIO() - img = PIL.Image.open(io.BytesIO(image)) + try: + img = PIL.Image.open(io.BytesIO(image)) + except PIL.UnidentifiedImageError as e: + raise LogoValidationError(gettext( + 'company.msg_company_logo_error'), str(e)) from e img.thumbnail((SIZE_MAX, SIZE_MAX)) if img.mode != 'RGBA': img = img.convert('RGBA') diff --git a/modules/company/exceptions.py b/modules/company/exceptions.py new file mode 100644 index 0000000000..ec34e85140 --- /dev/null +++ b/modules/company/exceptions.py @@ -0,0 +1,8 @@ +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. + +from trytond.model.exceptions import ValidationError + + +class LogoValidationError(ValidationError): + pass diff --git a/modules/company/message.xml b/modules/company/message.xml index 2ab52108d6..f69d76290c 100644 --- a/modules/company/message.xml +++ b/modules/company/message.xml @@ -6,5 +6,8 @@ this repository contains the full copyright notices and license terms. --> The size of cached company logo must be unique. + + Can not open or identify the image set for the company logo. + diff --git a/modules/product_image/exceptions.py b/modules/product_image/exceptions.py new file mode 100644 index 0000000000..777fd04854 --- /dev/null +++ b/modules/product_image/exceptions.py @@ -0,0 +1,8 @@ +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. + +from trytond.model.exceptions import ValidationError + + +class ImageValidationError(ValidationError): + pass diff --git a/modules/product_image/message.xml b/modules/product_image/message.xml index e7c3e0f169..cff3e4d9c2 100644 --- a/modules/product_image/message.xml +++ b/modules/product_image/message.xml @@ -6,5 +6,8 @@ this repository contains the full copyright notices and license terms. --> The size of cached image must be unique. + + Can not open or identify the image set for the product. + diff --git a/modules/product_image/product.py b/modules/product_image/product.py index 21b3fbf83e..f1a7d699dc 100644 --- a/modules/product_image/product.py +++ b/modules/product_image/product.py @@ -4,9 +4,11 @@ import io import math from urllib.parse import quote, urlencode, urljoin +import PIL import PIL.Image from trytond.config import config +from trytond.i18n import gettext from trytond.model import ( MatchMixin, ModelSQL, ModelView, Unique, fields, sequence_ordered) from trytond.pool import PoolMeta @@ -16,6 +18,8 @@ from trytond.transaction import Transaction from trytond.url import http_host from trytond.wsgi import Base64Converter +from .exceptions import ImageValidationError + if config.getboolean('product', 'image_filestore', default=False): file_id = 'image_id' store_prefix = config.get('product', 'image_prefix', default=None) @@ -162,7 +166,11 @@ class ImageMixin(_ImageMixin): @classmethod def convert(cls, image, **_params): data = io.BytesIO() - img = PIL.Image.open(io.BytesIO(image)) + try: + img = PIL.Image.open(io.BytesIO(image)) + except PIL.UnidentifiedImageError as e: + raise ImageValidationError(gettext( + 'product_image.msg_product_image_error'), str(e)) from e img.thumbnail((SIZE_MAX, SIZE_MAX)) if img.mode != 'RGB': img = img.convert('RGBA') diff --git a/trytond/trytond/ir/avatar.py b/trytond/trytond/ir/avatar.py index dc8b6b141d..5576d14a74 100644 --- a/trytond/trytond/ir/avatar.py +++ b/trytond/trytond/ir/avatar.py @@ -14,7 +14,9 @@ except ImportError: PIL = None from trytond.config import config +from trytond.i18n import gettext from trytond.model import ModelSQL, Unique, fields +from trytond.model.exceptions import ValidationError from trytond.pool import Pool from trytond.transaction import Transaction from trytond.wsgi import Base64Converter @@ -31,6 +33,10 @@ URL_BASE = config.get('web', 'avatar_base', default='') FONT = os.path.join(os.path.dirname(__file__), 'fonts', 'karla.ttf') +class AvatarValidationError(ValidationError): + pass + + class ImageMixin: __slots__ = () image = fields.Binary( @@ -118,7 +124,11 @@ class Avatar(ImageMixin, ResourceMixin, ModelSQL): if not PIL or not image: return image data = io.BytesIO() - img = Image.open(io.BytesIO(image)) + try: + img = Image.open(io.BytesIO(image)) + except PIL.UnidentifiedImageError as e: + raise AvatarValidationError(gettext( + 'ir.msg_avatar_image_error'), str(e)) from e width, height = img.size size = min(width, height) img = img.crop(( diff --git a/trytond/trytond/ir/exceptions.py b/trytond/trytond/ir/exceptions.py index 5db5ed512c..266f0c0513 100644 --- a/trytond/trytond/ir/exceptions.py +++ b/trytond/trytond/ir/exceptions.py @@ -1,6 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. +from .avatar import AvatarValidationError from .chat import InvalidEMailError as ChatInvalidEmailError from .lang import DateError as LanguageDateError from .lang import DeleteDefaultError as LanguageDeleteDefaultError @@ -14,6 +15,7 @@ from .translation import OverriddenError as TranslationOverriddenError from .trigger import ConditionError as TriggerConditionError __all__ = [ + AvatarValidationError, ChatInvalidEmailError, DeactivateDependencyError, LanguageDateError, diff --git a/trytond/trytond/ir/message.xml b/trytond/trytond/ir/message.xml index 13228c20d7..0ebb88ed16 100644 --- a/trytond/trytond/ir/message.xml +++ b/trytond/trytond/ir/message.xml @@ -418,6 +418,9 @@ this repository contains the full copyright notices and license terms. --> The size of an avatar must be unique. + + Can not open or identify the image set as avatar. + Avatar