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