From 74647b5a7bf449d79e9b8b2d53b665c0fafac7fa Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Wed, 7 Jan 2026 15:43:18 +0700 Subject: [PATCH] feat: implement server-side image compression for sign image fields. --- __init__.py | 1 + __manifest__.py | 0 __pycache__/__init__.cpython-312.pyc | Bin 224 -> 206 bytes controllers/__init__.py | 2 + controllers/main.py | 68 ++++++++++++++++++ data/sign_item_type_data.xml | 0 models/__init__.py | 0 models/__pycache__/__init__.cpython-312.pyc | Bin 239 -> 221 bytes .../sign_item_type.cpython-312.pyc | Bin 664 -> 646 bytes models/sign_item_type.py | 0 static/src/js/sign_image_upload.js | 0 static/src/xml/sign_items_image.xml | 0 12 files changed, 71 insertions(+) mode change 100644 => 100755 __init__.py mode change 100644 => 100755 __manifest__.py create mode 100644 controllers/__init__.py create mode 100644 controllers/main.py mode change 100644 => 100755 data/sign_item_type_data.xml mode change 100644 => 100755 models/__init__.py mode change 100644 => 100755 models/sign_item_type.py mode change 100644 => 100755 static/src/js/sign_image_upload.js mode change 100644 => 100755 static/src/xml/sign_items_image.xml diff --git a/__init__.py b/__init__.py old mode 100644 new mode 100755 index cde864b..c3d410e --- a/__init__.py +++ b/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- from . import models +from . import controllers diff --git a/__manifest__.py b/__manifest__.py old mode 100644 new mode 100755 diff --git a/__pycache__/__init__.cpython-312.pyc b/__pycache__/__init__.cpython-312.pyc index bc933a5bb3ba7eb4928b5c15ed124db102905b1d..eda52dd2e6cb689360b8aa53bc767bbdb94d8c4a 100644 GIT binary patch delta 37 rcmaFBc#e_#G%qg~0}$-Zb(+W>#OJA>k)NBYUtF4zT9i^bu{Ie1$!rVQ delta 55 zcmX@d_<)i7G%qg~0}$9)^I diff --git a/controllers/__init__.py b/controllers/__init__.py new file mode 100644 index 0000000..757b12a --- /dev/null +++ b/controllers/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import main diff --git a/controllers/main.py b/controllers/main.py new file mode 100644 index 0000000..716abcf --- /dev/null +++ b/controllers/main.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +import base64 +import io +import logging +from PIL import Image + +from odoo import http +from odoo.http import request +from odoo.tools import image_process +from odoo.addons.sign.controllers.main import Sign + +_logger = logging.getLogger(__name__) + +class SignController(Sign): + + @http.route(['/sign/sign//'], type='json', auth='public') + def sign_document(self, request_id, token, signature=None, items=None, **kwargs): + if items: + # Filter items that look like images (base64) + # We can't easily know the type of item just from the ID without a lookup + # But we can try to decode and compress if it looks like a large base64 string + + # Optimization: Fetch all item types at once to identify image field types + item_ids = [int(k) for k in items.keys()] + sign_items = request.env['sign.item'].sudo().browse(item_ids) + image_items_ids = set(sign_items.filtered(lambda i: i.type_id.item_type == 'image').ids) + + for key, value in items.items(): + if int(key) in image_items_ids and value and isinstance(value, str): + if value.startswith('/sign/image/'): + # Skip URLs (handled by sign_sequence_field or already processed) + continue + + try: + # Value comes as "data:image/png;base64,..." usually + # But Odoo sometimes strips header or not. + # sign_image_upload.js sends the full data URL. + + header = None + image_data = value + if ',' in value: + header, image_data = value.split(',', 1) + + # Compress + # Decode + image_bytes = base64.b64decode(image_data) + + # Process image: limit size to max 1920x1920 and reasonable quality + # image_process returns the processed image binary + processed_image = image_process(image_bytes, size=(1920, 1920), quality=80, output_format='JPEG') + + # Re-encode + new_base64 = base64.b64encode(processed_image).decode('utf-8') + + # Re-attach header if it was there, but ensure it matches new format (JPEG) + # Actually, keeping original header type might be misleading if we converted to JPEG. + # But for Odoo chatter/display, usually data:image/jpeg;base64 is safer if we converted. + + new_value = f"data:image/jpeg;base64,{new_base64}" + items[key] = new_value + + _logger.info(f"Compressed image for item {key}: {len(value)} -> {len(new_value)} chars") + + except Exception as e: + _logger.warning(f"Failed to compress image for item {key}: {e}") + # Leave original value if compression fails + + return super().sign_document(request_id, token, signature=signature, items=items, **kwargs) diff --git a/data/sign_item_type_data.xml b/data/sign_item_type_data.xml old mode 100644 new mode 100755 diff --git a/models/__init__.py b/models/__init__.py old mode 100644 new mode 100755 diff --git a/models/__pycache__/__init__.cpython-312.pyc b/models/__pycache__/__init__.cpython-312.pyc index fc2c61767873f7191eb2428e96803af41954bec3..f05683c5793a8f9b445a5a2185bb73e29e5b1fa3 100644 GIT binary patch delta 37 rcmaFQc$bm;G%qg~0}$-Zb(+W>#TTHTk)NBYUtF4zT9i^bu{8?-&dLlF delta 55 zcmcc1_@0sbG%qg~0}wb~v!BQvrI4hbk)NBYUtF4zT9i_$SDBccpX896Sdf^fmz=Mc JnKH3B3jp7w5|jV{ diff --git a/models/__pycache__/sign_item_type.cpython-312.pyc b/models/__pycache__/sign_item_type.cpython-312.pyc index a25abc5b089e0c9a27ece9886b4043a2083c166a..eba9e0e7640c3f441f9b8d389230e597a072cc01 100644 GIT binary patch delta 39 tcmbQi+Q!O#nwOW00SNZyI&I{xX5B{oW>m<~&&bbB)h{m1NG(dK)T>NP&QEg4O)N;v(@W0R L%S@R(h4B*r;SCb- diff --git a/models/sign_item_type.py b/models/sign_item_type.py old mode 100644 new mode 100755 diff --git a/static/src/js/sign_image_upload.js b/static/src/js/sign_image_upload.js old mode 100644 new mode 100755 diff --git a/static/src/xml/sign_items_image.xml b/static/src/xml/sign_items_image.xml old mode 100644 new mode 100755