# -*- coding:Utf-8 -*- from PIL import Image __all__ = ( 'Pipeline', 'FilePipeline', 'ImagePipeline' ) class Pipeline(object): """ Basic Pipeline operations handler Each operation must have its `process_%` handler or will be ignored """ obj = None pipeline = [] def __new__(cls, obj, pipeline): instance = super(Pipeline, cls).__new__(cls) instance.obj = obj instance.pipeline = pipeline for line in instance.pipeline: for step, action in line.items(): func = u'process_{0}'.format(step) if hasattr(instance, func): instance.obj = getattr(instance, func)(action) return instance.obj class FilePipeline(Pipeline): """Pipeline that processes files""" def process_image(self, action): try: original = Image.open(self.obj) image = ImagePipeline(original, action) self.obj.open('w') image.save(self.obj) self.obj.close() return self.obj except: raise BufferError("cannot perform image operations") class ImagePipeline(Pipeline): """Pipeline that processes image files""" def process_ratio(self, ratio): """ Crop at a given ratio Ratio is [width, height], width and height must be int or float """ ratio = float(ratio[0]) / ratio[1] img_width, img_height = self.obj.size img_ratio = float(img_width) / img_height if img_ratio == ratio: return self.obj if img_ratio > ratio: height = img_height width = int(height * ratio) offset = int((img_width - width) / 2) geometry = (offset, 0, offset + width, height) else: width = img_width height = int(width / ratio) offset = int((img_height - height) / 2) geometry = (0, offset, width, offset + height) return self.obj.crop(geometry) def process_crop(self, geometry): """ Crop at a given geometry Geometry is offsets from [left, top, right, bottom] """ return self.obj.crop(geometry) def process_fit(self, geometry): """ Fit at a given geometry Geometry is [width, height], width and height are pixels and must be int """ return self.obj.thumbnail(geometry, Image.ANTIALIAS) def process_resize(self, data): """ Resize at a given geometry Data must be geometry or dict Geometry is [width, height], width and height are pixels and must be int Dict must contain: - geometry: [width, height] - constraint: string, one of "DONT_OVERSIZE" """ if isinstance(data, list): return self.obj.resize(data) if isinstance(data, dict): geometry = data.get('geometry') constraint = data.get('constraint', None) if constraint == "DONT_OVERSIZE" and (self.obj.size[0] < geometry[0] or self.obj.size[1] < geometry[1]): return self.process_ratio(geometry) else: return self.obj.resize(geometry)