From 811646011275be4947939960c855effdc5612eff Mon Sep 17 00:00:00 2001 From: Tamas Mako <1412056+tomako@users.noreply.github.com> Date: Fri, 12 Jul 2024 19:10:52 +0200 Subject: [PATCH] Avoid Pillow round-trip in desiger images (#6441) Co-authored-by: Adrian Moennich --- indico/modules/designer/pdf.py | 2 +- indico/modules/designer/placeholders.py | 15 +++++++-------- indico/modules/designer/placeholders_test.py | 4 ++-- indico/modules/events/registration/util.py | 8 ++++++-- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/indico/modules/designer/pdf.py b/indico/modules/designer/pdf.py index 40799a3b6b4..081d29acd97 100644 --- a/indico/modules/designer/pdf.py +++ b/indico/modules/designer/pdf.py @@ -134,7 +134,7 @@ def _draw_item(self, canvas, item, tpl_data, content, margin_x, margin_y): item_height = (item['height'] / PIXELS_CM * cm) if item.get('height') is not None else None item_preserve_aspect_ratio = item.get('preserve_aspect_ratio', True) - if isinstance(content, Image.Image): + if isinstance(content, BytesIO): canvas.drawImage(ImageReader(content), margin_x + item_x, self.height - margin_y - item_height - item_y, item_width, item_height, mask='auto', preserveAspectRatio=item_preserve_aspect_ratio) else: diff --git a/indico/modules/designer/placeholders.py b/indico/modules/designer/placeholders.py index 8d4648a1274..f5f4cbdb5bb 100644 --- a/indico/modules/designer/placeholders.py +++ b/indico/modules/designer/placeholders.py @@ -8,7 +8,6 @@ from io import BytesIO from babel.numbers import format_currency -from PIL import Image from indico.modules.designer.models.images import DesignerImageFile from indico.modules.events.registration.util import generate_ticket_qr_code @@ -105,8 +104,7 @@ class EventLogoPlaceholder(DesignerPlaceholder): def render(cls, event): if not event.has_logo: return '' - buf = BytesIO(event.logo) - return Image.open(buf) + return BytesIO(event.logo) class EventDatesPlaceholder(DesignerPlaceholder): @@ -370,8 +368,8 @@ class RegistrationPicturePlaceholder(RegistrationPDPlaceholder): @classmethod def render(cls, registration): if picture_data := registration.get_personal_data_picture(): - buf = picture_data.open() - return Image.open(buf) + with picture_data.open() as fd: + return BytesIO(fd.read()) return None @@ -406,8 +404,8 @@ class FixedImagePlaceholder(DesignerPlaceholder): @classmethod def render(cls, item): - buf = DesignerImageFile.get(item['image_id']).open() - return Image.open(buf) + with DesignerImageFile.get(item['image_id']).open() as fd: + return BytesIO(fd.read()) class RegistrationFormFieldPlaceholder(DesignerPlaceholder): @@ -452,7 +450,8 @@ def _render(self, data): def _render_image(self, data): if data.storage_file_id is not None: - return Image.open(data.open()) + with data.open() as fd: + return BytesIO(fd.read()) def _render_accommodation(self, friendly_data): if friendly_data['is_no_accommodation']: diff --git a/indico/modules/designer/placeholders_test.py b/indico/modules/designer/placeholders_test.py index b35989302ef..eb4100baad5 100644 --- a/indico/modules/designer/placeholders_test.py +++ b/indico/modules/designer/placeholders_test.py @@ -7,11 +7,11 @@ import itertools from datetime import datetime +from io import BytesIO from operator import attrgetter from pathlib import Path import pytest -from PIL import Image from indico.modules.designer import placeholders from indico.modules.events.models.persons import EventPerson, EventPersonLink @@ -124,4 +124,4 @@ def test_render_image_placeholders(dummy_event, dummy_reg, dummy_designer_image_ for ph in image_placeholders: kwargs = _get_render_kwargs(ph, dummy_event, dummy_person, dummy_reg, dummy_item) res = ph.render(**kwargs) - assert isinstance(res, Image.Image) or res is None + assert isinstance(res, BytesIO) or res is None diff --git a/indico/modules/events/registration/util.py b/indico/modules/events/registration/util.py index 8d30e9bf343..90412a8e0fe 100644 --- a/indico/modules/events/registration/util.py +++ b/indico/modules/events/registration/util.py @@ -736,9 +736,10 @@ def _base64_encode_uuid(uid): def generate_ticket_qr_code(person): - """Generate a Pillow `Image` with a QR Code encoding a check-in ticket. + """Generate an image with a QR code encoding a check-in ticket. :param registration: corresponding `Registration` object + :return: A `BytesIO` containing the image data """ qr = QRCode( version=None, @@ -750,7 +751,10 @@ def generate_ticket_qr_code(person): qr_data = json.dumps(data, separators=(',', ':')) if not isinstance(data, str) else data qr.add_data(qr_data) qr.make(fit=True) - return qr.make_image()._img + buf = BytesIO() + qr.make_image().save(buf) + buf.seek(0) + return buf def get_event_regforms(event, user, with_registrations=False, only_in_acl=False):