From 33fc99adb595f1f07bca1f9054ec3a9d78d8e4bd Mon Sep 17 00:00:00 2001 From: Raimon Esteve Date: Tue, 5 Jun 2018 11:06:20 +0200 Subject: [PATCH] Add image (default) and image_thumb fields and thumb method to crop/thumbnail image --- product.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/product.py b/product.py index 20474d6..bc08320 100644 --- a/product.py +++ b/product.py @@ -2,14 +2,19 @@ # copyright notices and license terms. from trytond.model import fields from trytond.pool import Pool, PoolMeta +from trytond.config import config as config_ +from PIL import Image +import io __all__ = ['Attachment', 'Template'] - +THUMB_QUALITY = config_.getint('product', 'thumb_quality', default=85) +THUMB_CROP = config_.get('product', 'thumb_crop', default='') +THUMB_WIDTH = config_.getint('product', 'thumb_width', default=300) +THUMB_HEIGHT = config_.getint('product', 'thumb_height', default=300) class Attachment: __metaclass__ = PoolMeta __name__ = 'ir.attachment' - product_image = fields.Boolean('Product Image') @@ -20,6 +25,81 @@ class Template: filter=[ ('product_image', '=', True), ]) + image = fields.Function(fields.Many2One('ir.attachment', 'Image'), + 'get_image') + image_thumb = fields.Function(fields.Binary('Image Thumb', + filename='image_thumb_filename'), 'get_image_thumb') + image_thumb_filename = fields.Function(fields.Char("File Name Image Thumb"), + 'get_image_thumb_filename') + + @classmethod + def get_image(cls, records, names): + res = {n: {r.id: None for r in records} for n in names} + for name in names: + for record in records: + if record.images: + res[name][record.id] = record.images[0].id + return res + + @staticmethod + def thumb(data, image_format, crop=THUMB_CROP, + width=THUMB_WIDTH, height=THUMB_HEIGHT, quality=THUMB_QUALITY): + size = (width, height) + try: + img = Image.open(io.BytesIO(data)) + except: + return + + if crop: + img_ratio = img.size[0] / float(img.size[1]) + ratio = size[0] / float(size[1]) + + # Scaled/cropped vertically or horizontally depending on the ratio + if ratio > img_ratio: + img = img.resize((size[0], size[0] * img.size[1] / img.size[0]), + Image.ANTIALIAS) + # Crop in the top, middle or bottom + if crop == 'top': + box = (0, 0, img.size[0], size[1]) + elif crop == 'bottom': + box = (0, img.size[1] - size[1], img.size[0], img.size[1]) + else : + box = (0, (img.size[1] - size[1]) / 2, img.size[0], + (img.size[1] + size[1]) / 2) + img = img.crop(box) + elif ratio < img_ratio: + img = img.resize((size[1] * img.size[0] / img.size[1], size[1]), + Image.ANTIALIAS) + # Crop in the top, middle or bottom + if crop == 'top': + box = (0, 0, size[0], img.size[1]) + elif crop == 'bottom': + box = (img.size[0] - size[0], 0, img.size[0], img.size[1]) + else : + box = ((img.size[0] - size[0]) / 2, 0, + (img.size[0] + size[0]) / 2, img.size[1]) + img = img.crop(box) + else: + img.thumbnail(size) + + imgByteArr = io.BytesIO() + img.save(imgByteArr, image_format, quality=quality) + return bytearray(imgByteArr.getvalue()) + + @classmethod + def get_image_thumb(cls, records, names): + res = {n: {r.id: None for r in records} for n in names} + for name in names: + for record in records: + if record.image and record.image.data: + image_format, = record.image.name.split('.')[-1:] + imgformat = (image_format.lower() if image_format.lower() + in {'png', 'jpeg'} else 'jpeg') + res[name][record.id] = cls.thumb(record.image.data, imgformat) + return res + + def get_image_thumb_filename(self, name): + return self.image.name if self.image else None @classmethod def delete(cls, templates):