def testQuality(self): img1 = scaleImage(CMYK, 84, 103)[0] img2 = scaleImage(CMYK, 84, 103, quality=50)[0] img3 = scaleImage(CMYK, 84, 103, quality=20)[0] self.assertNotEqual(img1, img2) self.assertNotEqual(img1, img3) self.failUnless(len(img1) > len(img2) > len(img3))
def icon(self, uid): try: portrait = getattr(self.mdata.portraits, uid) except AttributeError: default = getattr(self.context, 'defaultUser.png') self.request.response.setHeader('Content-type', default.content_type) return scaleImage(default._data, width=64)[0] self.request.response.setHeader('Content-type', portrait.content_type) return scaleImage(portrait.data, width=64)[0]
def create(self, fieldname, direction="thumbnail", height=None, width=None, **parameters): """ factory for image scales, see `IImageScaleStorage.scale` """ orig_value = self.context.data.get(fieldname) if orig_value is None: return if height is None and width is None: _, format = orig_value.contentType.split("/", 1) return None, format, (orig_value._width, orig_value._height) if hasattr(aq_base(orig_value), "open"): orig_data = orig_value.open() else: orig_data = getattr(aq_base(orig_value), "data", orig_value) if not orig_data: return try: result = scaleImage(orig_data, direction=direction, height=height, width=width, **parameters) # Disable Plone 5 implicit CSRF to allow scaling on GET if HAS_PLONE_PROTECT: alsoProvides(self.request, IDisableCSRFProtection) except (ConflictError, KeyboardInterrupt): raise except Exception: exception('could not scale "%r" of %r', orig_value, self.context.context.absolute_url()) return if result is not None: data, format, dimensions = result mimetype = "image/%s" % format.lower() value = orig_value.__class__(data, contentType=mimetype, filename=orig_value.filename) value.fieldname = fieldname return value, format, dimensions
def icon(self, uid): try: portrait = getattr(self.mdata.portraits, uid) except AttributeError: return None self.request.response.setHeader('Content-type', portrait.content_type) return scaleImage(portrait.data, width=32)[0]
def get_thumb(self, scale, key="image", direction="thumb"): """ Return data from plone scale or None""" #Make cache optional cachekey = (self.context.uid, scale, key) cached = thumb_cache.get(cachekey) if cached: return cached scales = get_image_scales() maxwidth, maxheight = scales[scale] blobs = IBlobs(self.context) if key in blobs: registry = get_current_registry() if blobs[key].mimetype in registry.settings[ 'supported_thumbnail_mimetypes']: with blobs[key].blob.open() as f: try: thumb_data, image_type, size = scaleImage( f, width=maxwidth, height=maxheight, direction=direction) except IOError: #FIXME: Logging? return thumb = Thumbnail(thumb_data, image_type=image_type, size=size) thumb_cache.put(cachekey, thumb) return thumb
def create(self, context, **parameters): value = self.field.get(context) data = getattr(aq_base(value), 'data', value) if isinstance(data, Pdata): data = str(data) if data: return scaleImage(data, **parameters)
def create(self, fieldname, direction='thumbnail', height=None, width=None, **parameters): """ factory for image scales, see `IImageScaleStorage.scale` """ orig_value = getattr(self.context, fieldname) if orig_value is None: return if height is None and width is None: _, format = orig_value.contentType.split('/', 1) return None, format, (orig_value._width, orig_value._height) if hasattr(aq_base(orig_value), 'open'): orig_data = orig_value.open() else: orig_data = getattr(aq_base(orig_value), 'data', orig_value) if not orig_data: return try: result = scaleImage(orig_data, direction=direction, height=height, width=width, **parameters) except (ConflictError, KeyboardInterrupt): raise except Exception: exception('could not scale "%r" of %r', orig_value, self.context.absolute_url()) return if result is not None: data, format, dimensions = result mimetype = 'image/%s' % format.lower() value = orig_value.__class__(data, contentType=mimetype, filename=orig_value.filename) value.fieldname = fieldname return value, format, dimensions
def get_thumb(self, scale, key = None, direction = "thumbnail"): """ Return data from plone scale or None""" #Make cache optional if key is None: key = getattr(self.context, 'blob_key', 'image') cachekey = (self.context.uid, scale, key, direction) cached = self.thumb_cache.get(cachekey) if cached: return cached if direction not in _ALLOWED_SCALE_DIRECTIONS: return scales = get_image_scales() maxwidth, maxheight = scales[scale] blobs = IBlobs(self.context) if key in blobs: registry = get_current_registry() if blobs[key].mimetype in registry.settings['supported_thumbnail_mimetypes']: with blobs[key].blob.open() as f: try: thumb_data, image_type, size = scaleImage( f, width = maxwidth, height = maxheight, direction = direction ) except IOError: #FIXME: Logging? return thumb = Thumbnail(thumb_data, image_type = image_type, size = size) self.thumb_cache.put(cachekey, thumb) return thumb
def create(self, fieldname, direction='thumbnail', height=None, width=None, **parameters): """ factory for image scales, see `IImageScaleStorage.scale` """ orig_value = self.context.data.get(fieldname) if orig_value is None: return if height is None and width is None: _, format = orig_value.contentType.split('/', 1) return None, format, (orig_value._width, orig_value._height) if hasattr(aq_base(orig_value), 'open'): orig_data = orig_value.open() else: orig_data = getattr(aq_base(orig_value), 'data', orig_value) if not orig_data: return try: result = scaleImage(orig_data, direction=direction, height=height, width=width, **parameters) except (ConflictError, KeyboardInterrupt): raise except Exception: exception('could not scale "%r" of %r', orig_value, self.context.context.absolute_url()) return if result is not None: data, format, dimensions = result mimetype = 'image/%s' % format.lower() value = orig_value.__class__(data, contentType=mimetype, filename=orig_value.filename) value.fieldname = fieldname return value, format, dimensions
def crop_factory(fieldname, direction='keep', **parameters): blob = Blob() result = blob.open('w') _, image_format, dimensions = scaleImage( data['data'], result=result, **parameters) result.close() return blob, image_format, dimensions
def create(self, fieldname, direction='thumbnail', height=None, width=None, **parameters): """ factory for image scales, see `IImageScaleStorage.scale` """ orig_value = getattr(self.context, fieldname) # BfS extension: if callable(orig_value): orig_value = orig_value() if orig_value is None: return if height is None and width is None: _, format = orig_value.contentType.split('/', 1) return None, format, (orig_value._width, orig_value._height) if hasattr(aq_base(orig_value), 'open'): orig_data = orig_value.open() else: orig_data = getattr(aq_base(orig_value), 'data', orig_value) if not orig_data: return # Handle cases where large image data is stored in FileChunks instead # of plain string if isinstance(orig_data, FileChunk): # Convert data to 8-bit string # (FileChunk does not provide read() access) orig_data = str(orig_data) # If quality wasn't in the parameters, try the site's default scaling # quality if it exists. if 'quality' not in parameters: quality = self.getQuality() if quality: parameters['quality'] = quality try: result = scaleImage(orig_data, direction=direction, height=height, width=width, **parameters) except (ConflictError, KeyboardInterrupt): raise except Exception: exception('could not scale "%r" of %r', orig_value, self.context.absolute_url()) return if result is not None: data, format, dimensions = result mimetype = 'image/%s' % format.lower() value = orig_value.__class__( data, contentType=mimetype, filename=orig_value.filename) value.fieldname = fieldname return value, format, dimensions
def image(self, subpath=None): """Return the image in a specific scale, either inline (default) or as attachment. :param subpath: [<image_scale>]/download] (optional). When 'download' is the last element in subpath, the image is served with a 'Content-Disposition: attachment' header. <image_scale> has to be one of the predefined image_scales - either from the defaults in this module or one set with a kotti.image_scales.<scale_name> in your app config ini file. :type subpath: str :result: complete response object :rtype: pyramid.response.Response """ if subpath is None: subpath = self.request.subpath width, height = (None, None) subpath = list(subpath) if (len(subpath) > 0) and (subpath[-1] == "download"): disposition = "attachment" subpath.pop() else: disposition = "inline" if len(subpath) == 1: scale = subpath[0] if scale in image_scales: # /path/to/image/scale/thumb width, height = image_scales[scale] if not (width and height): return self.request.uploaded_file_response( self.context.data, disposition) image, format, size = scaleImage(self.context.data.file.read(), width=width, height=height, direction="thumb") res = Response( headerlist=[ ('Content-Disposition', '{0};filename="{1}"'.format( disposition, self.context.filename.encode('ascii', 'ignore'))), ('Content-Length', str(len(image))), ('Content-Type', str(self.context.mimetype)), ], body=image, ) res.content_disposition = rfc6266.build_header( self.context.filename, disposition=disposition, filename_compat=unidecode(self.context.filename)) return res
def create_scale(self, data, direction, height, width, **parameters): return scaleImage( data, direction=direction, height=height, width=width, **parameters )
def testAlphaForcesPNG(self): # first image without alpha src = PIL.Image.new("RGBA", (256, 256), (255, 255, 255, 255)) for y in range(0, 256): for x in range(0, 256): src.putpixel((x, y), (x, y, 0, 255)) result = StringIO() src.save(result, "TIFF") self.assertEqual(scaleImage(result, 84, 103, "down")[1], "JPEG") # now with alpha src = PIL.Image.new("RGBA", (256, 256), (255, 255, 255, 128)) result = StringIO() for y in range(0, 256): for x in range(0, 256): src.putpixel((x, y), (x, y, 0, x)) src.save(result, "TIFF") self.assertEqual(scaleImage(result, 84, 103, "down")[1], "PNG")
def crop_factory(fieldname, direction='keep', **parameters): blob = Blob() result = blob.open('w') _, image_format, dimensions = scaleImage(data['data'], result=result, **parameters) result.close() return blob, image_format, dimensions
def create(self, fieldname, direction='thumbnail', height=None, width=None, **parameters): # import pdb # pdb.set_trace() """ factory for image scales, see `IImageScaleStorage.scale` """ orig_value = getattr(self.context, fieldname) if orig_value is None: return if height is None and width is None: _, format = orig_value.contentType.split('/', 1) return None, format, (orig_value._width, orig_value._height) if hasattr(aq_base(orig_value), 'open'): orig_data = orig_value.open() else: orig_data = getattr(aq_base(orig_value), 'data', orig_value) if not orig_data: return # Handle cases where large image data is stored in FileChunks instead # of plain string if isinstance(orig_data, FileChunk): # Convert data to 8-bit string # (FileChunk does not provide read() access) orig_data = str(orig_data) # If quality wasn't in the parameters, try the site's default scaling # quality if it exists. if 'quality' not in parameters: quality = self.getQuality() if quality: parameters['quality'] = quality try: result = scaleImage(orig_data, direction=direction, height=height, width=width, **parameters) except (ConflictError, KeyboardInterrupt): raise except Exception: exception('could not scale "%r" of %r', orig_value, self.context.absolute_url()) return if result is not None: data, format, dimensions = result mimetype = 'image/%s' % format.lower() value = orig_value.__class__(data, contentType=mimetype, filename=orig_value.filename) value.fieldname = fieldname return value, format, dimensions
def create(self, context, **parameters): value = self.field.get(context) data = getattr(aq_base(value), 'data', value) if isinstance(data, Pdata): data = str(data) if data: if 'quality' not in parameters and self.quality: parameters['quality'] = self.quality return scaleImage(data, **parameters)
def create(self, context, **parameters): wrapper = self.field.get(context) if wrapper: blob = Blob() result = blob.open('w') _, format, dimensions = scaleImage(wrapper.getBlob().open('r'), result=result, **parameters) result.close() return blob, format, dimensions
def image(self, subpath=None): """Return the image in a specific scale, either inline (default) or as attachment. :param subpath: [<image_scale>]/download] (optional). When 'download' is the last element in subpath, the image is served with a 'Content-Disposition: attachment' header. <image_scale> has to be one of the predefined image_scales - either from the defaults in this module or one set with a kotti.image_scales.<scale_name> in your app config ini file. :type subpath: str :result: complete response object :rtype: pyramid.response.Response """ if subpath is None: subpath = self.request.subpath width, height = (None, None) subpath = list(subpath) if (len(subpath) > 0) and (subpath[-1] == "download"): disposition = "attachment" subpath.pop() else: disposition = "inline" if len(subpath) == 1: scale = subpath[0] if scale in image_scales: # /path/to/image/scale/thumb width, height = image_scales[scale] if not (width and height): return UploadedFileResponse(self.context.data, self.request, disposition) image, format, size = scaleImage(self.context.data.file.read(), width=width, height=height, direction="thumb") res = Response( headerlist=[ ('Content-Disposition', '{0};filename="{1}"'.format( disposition, self.context.filename.encode('ascii', 'ignore'))), ('Content-Length', str(len(image))), ('Content-Type', str(self.context.mimetype)), ], body=image, ) return res
def crop_factory(fieldname, **parameters): result = scaleImage(image_file.read(), **parameters) if result is not None: data, format, dimensions = result mimetype = 'image/%s' % format.lower() value = field.__class__(data, contentType=mimetype, filename=field.filename) value.fieldname = fieldname return value, format, dimensions
def handle_save_action(self, action, data): if form.applyChanges(self.context, self.form_fields, data, self.adapters): zope.event.notify( zope.lifecycleevent.ObjectModifiedEvent(self.context)) zope.event.notify(ploneformbase.EditSavedEvent(self.context)) IStatusMessage(self.request).addStatusMessage(_("Changes saved."), type="info") else: zope.event.notify(ploneformbase.EditCancelledEvent(self.context)) IStatusMessage(self.request).addStatusMessage( _("No changes made."), type="info") self.settings = PageSliderSettings(self.context) slides = self.settings.slides index = data.get('index', -1) image = data.get('image') image_type = None if image != None: scale_width = getUtility(IRegistry)[ 'tx.slider.configlet.ISliderControlPanel.image_scale_width'] (image, image_type, image_size) = scaleImage(image, width=scale_width) else: if index != -1: image = slides[index].get('image') image_type = slides[index].get('image_type') # create new uuid on each save uuid = uuid4().hex value = { 'configuration': data.get('configuration'), 'heading': data.get('heading'), 'html': data.get('slide'), 'image': image, 'image_type': image_type, 'link_reference': data.get('link_reference'), 'url': data.get('url'), 'uuid': uuid } if index == -1: slides.append(value) index = len(slides) - 1 else: slides[index] = value self.settings.slides = slides url = getMultiAdapter((self.context, self.request), name='absolute_url')() + "/@@tx-slider-settings" self.request.response.redirect(url)
def testAutomaticGreyscale(self): src = PIL.Image.new("RGB", (256, 256), (255, 255, 255)) draw = PIL.ImageDraw.Draw(src) for i in range(0, 256): draw.line(((0, i), (256, i)), fill=(i, i, i)) result = StringIO() src.save(result, "JPEG") (imagedata, format, size) = scaleImage(result, 200, None, "down") image = PIL.Image.open(StringIO(imagedata)) self.assertEqual(max(image.size), 200) self.assertEqual(image.mode, 'L') self.assertEqual(image.format, 'JPEG')
def crop_factory(fieldname, **parameters): result = scaleImage(image_file.read(), **parameters) if result is not None: data, format, dimensions = result mimetype = 'image/%s' % format.lower() value = field.__class__( data, contentType=mimetype, filename=field.filename ) value.fieldname = fieldname return value, format, dimensions
def image(self): """return the image in a specific scale, either inline (default) or as attachment""" subpath = list(self.request.subpath) if (len(subpath) > 0) and (subpath[-1] == "download"): disposition = "attachment" subpath.pop() else: disposition = "inline" if len(subpath) == 1: scale = subpath[0] if scale in image_scales: # /path/to/image/scale/thumb width, height = image_scales[scale] else: # /path/to/image/scale/160x120 try: width, height = [int(v) for v in scale.split("x")] except ValueError: width, height = (None, None) elif len(subpath) == 2: # /path/to/image/scale/160/120 try: width, height = [int(v) for v in subpath] except ValueError: width, height = (None, None) else: # don't scale at all width, height = (None, None) if width and height: image, format, size = scaleImage(self.context.data, width=width, height=height, direction="thumb") else: image = self.context.data res = Response( headerlist=[ ( "Content-Disposition", '%s;filename="%s"' % (disposition, self.context.filename.encode("ascii", "ignore")), ), ("Content-Length", str(len(image))), ("Content-Type", str(self.context.mimetype)), ], app_iter=image, ) return res
def __call__(self, fieldname='image'): image_view = self.context.unrestrictedTraverse('@@imgview') image = getattr(image_view, 'img', None) url = self.context.absolute_url() if not image: logger.error("Error while recreating scale for %s" % url) raise AttributeError(fieldname) info = None if getattr(image, 'portal_type', '') == 'Image': field = image.getField(fieldname) if not field: raise AttributeError(fieldname) try: field.removeScales(image) field.createScales(image) logger.info("Succesfully scaled %s" % url) except Exception: logger.error("Error while recreating scale for %s" % url) else: # use plone.app.blob store scale for name, size in image.sizes.items(): image_data = image_view(name) if not image_data: continue width, height = size scale_result = scaleImage(image_data, width=width, height=height) if scale_result is not None: id = fieldname + "_" + name data, format_, dimensions = scale_result info = dict( data=data, id = id, content_type='image/{0}'.format(format_.lower()), filename='', ) fields = getattr(aq_base(self.context), blobScalesAttr, {}) scales = fields.setdefault(fieldname, {}) info['blob'] = Blob() blob = info['blob'].open('w') blob.write(info['data']) blob.close() del info['data'] scales[name] = info setattr(self.context, blobScalesAttr, fields) self.context._p_changed = True logger.info("Succesfully scaled %s " % url) return "Done"
async def __call__(self): registry = await get_registry() settings = registry.for_interface(IImagingSettings) scale_name = self.request.matchdict["scale"] allowed_sizes = settings["allowed_sizes"] if scale_name not in allowed_sizes: raise HTTPNotFound( content={"reason": f"{scale_name} is not supported"}) file = self.field.get(self.field.context or self.context) if file is None: raise HTTPNotFound( content={ "message": "File or custom filename required to download" }) adapter = get_multi_adapter((self.context, self.request, self.field), IFileManager) data = b"" async for chunk in adapter.iter_data(): data += chunk width, _, height = allowed_sizes[scale_name].partition(":") result, format_, size = scaleImage( data, int(width), int(height), quality=settings["quality"], direction="thumbnail", ) cors_renderer = app_settings["cors_renderer"](self.request) headers = await cors_renderer.get_headers() headers.update({ "CONTENT-DISPOSITION": 'attachment; filename="{}"'.format(file.filename) }) download_resp = Response( status=200, headers=headers, content_type=f"image/{format_}", content_length=len(result), ) await download_resp.prepare(self.request) await download_resp.write(result) await download_resp.write(eof=True) return download_resp
def crop_factory(fieldname, **parameters): # LMU patch: remove the scale parameter _parameters = { key: value for key, value in parameters.iteritems() if key != 'scale' } result = scaleImage(image_file.read(), **_parameters) if result is not None: data, format, dimensions = result mimetype = 'image/{0:s}'.format(format.lower()) field = self.get_image_field(fieldname) value = field.__class__(data, contentType=mimetype, filename=field.filename) value.fieldname = fieldname return value, format, dimensions
def create(self, fieldname, direction='thumbnail', height=None, width=None, **parameters): """ factory for image scales, see `IImageScaleStorage.scale` """ if not IPersistentCoverTile.providedBy(self.context): base_scales = queryMultiAdapter((self.context, self.request), name='images', default=None) return base_scales and base_scales.create( fieldname, direction, height, width, **parameters) orig_value = self.context.data.get(fieldname) if orig_value is None: return if height is None and width is None: _, format = orig_value.contentType.split('/', 1) return None, format, (orig_value._width, orig_value._height) if hasattr(aq_base(orig_value), 'open'): orig_data = orig_value.open() else: orig_data = getattr(aq_base(orig_value), 'data', orig_value) if not orig_data: return try: result = scaleImage(orig_data, direction=direction, height=height, width=width, **parameters) except (ConflictError, KeyboardInterrupt): raise except Exception: logging.exception( 'could not scale "%r" of %r', orig_value, self.context.context.absolute_url()) # FIXME: PEP 3101 return if result is not None: data, format, dimensions = result # FIXME: PEP 3101; how to avoid confusion among method and variable name? mimetype = 'image/%s' % format.lower() value = orig_value.__class__(data, contentType=mimetype, filename=orig_value.filename) value.fieldname = fieldname return value, format, dimensions
async def __call__(self): settings = self.request.container_settings.for_interface( IImagingSettings) scale_name = self.request.matchdict['scale'] allowed_sizes = settings['allowed_sizes'] if scale_name not in allowed_sizes: raise HTTPNotFound( content={'reason': f'{scale_name} is not supported'}) file = self.field.get(self.field.context or self.context) if file is None: raise HTTPNotFound( content={ 'message': 'File or custom filename required to download' }) adapter = get_multi_adapter((self.context, self.request, self.field), IFileManager) data = b'' async for chunk in adapter.iter_data(): data += chunk width, _, height = allowed_sizes[scale_name].partition(':') result, format_, size = scaleImage(data, int(width), int(height), quality=settings['quality'], direction='thumbnail') cors_renderer = app_settings['cors_renderer'](self.request) headers = await cors_renderer.get_headers() headers.update({ 'CONTENT-DISPOSITION': 'attachment; filename="{}"'.format(file.filename) }) download_resp = StreamResponse(headers=headers) download_resp.content_type = f'image/{format_}' if file.size: download_resp.content_length = len(result) await download_resp.prepare(self.request) await download_resp.write(result) await download_resp.drain() await download_resp.write_eof() return download_resp
def _get_scaled_img(self, item, image_field, size): request = getRequest() if ( ICatalogBrain.providedBy(item) or IContentListingObject.providedBy(item) ): obj = item.getObject() else: obj = item info = {} if hasattr(obj, image_field): scales = getMultiAdapter((obj, request), name='images') if size == 'lgip': stored_image = getattr(obj, image_field) scale = image_scale.scaleImage( stored_image.data, width=stored_image.getImageSize()[0], height=stored_image.getImageSize()[1], direction='keep', quality=10 ) if size == 'small': scale = scales.scale(image_field, width=300, height=300) if size == 'medium': scale = scales.scale(image_field, width=600, height=600) if size == 'large': scale = scales.scale(image_field, width=900, height=900) else: scale = scales.scale(image_field, width=1200, height=1200) if scale is not None: info['url'] = scale.url info['width'] = scale.width info['height'] = scale.height else: info['url'] = IMG info['width'] = '1px' info['height'] = '1px' else: info['url'] = IMG info['width'] = '1px' info['height'] = '1px' return info
def image(self, subpath=None): """Return the image in a specific scale, either inline (default) or as attachment.""" if subpath is None: subpath = self.request.subpath width, height = (None, None) subpath = list(subpath) if (len(subpath) > 0) and (subpath[-1] == "download"): disposition = "attachment" subpath.pop() else: disposition = "inline" if len(subpath) == 1: scale = subpath[0] if scale in image_scales: # /path/to/image/scale/thumb width, height = image_scales[scale] if width and height: image, format, size = scaleImage(self.context.data, width=width, height=height, direction="thumb") else: image = self.context.data res = Response( headerlist=[ ('Content-Disposition', '%s;filename="%s"' % (disposition, self.context.filename.encode('ascii', 'ignore'))), ('Content-Length', str(len(image))), ('Content-Type', str(self.context.mimetype)), ], body=image, ) return res
def testAutomaticPalette(self): # get a JPEG with more than 256 colors jpeg = PIL.Image.open(StringIO(PROFILE)) self.assertEqual(jpeg.mode, 'RGB') self.assertEqual(jpeg.format, 'JPEG') self.assertIsNone(jpeg.getcolors(maxcolors=256)) # convert to PNG dst = StringIO() jpeg.save(dst, "PNG") dst.seek(0) png = PIL.Image.open(dst) self.assertEqual(png.mode, 'RGB') self.assertEqual(png.format, 'PNG') self.assertIsNone(png.getcolors(maxcolors=256)) # scale it to a size where we get less than 256 colors (imagedata, format, size) = scaleImage(dst.getvalue(), 24, None, "down") image = PIL.Image.open(StringIO(imagedata)) # we should now have an image in palette mode self.assertEqual(image.mode, 'P') self.assertEqual(image.format, 'PNG')
def create(self, fieldname, direction='thumbnail', height=None, width=None, **parameters): """ factory for image scales, see `IImageScaleStorage.scale` """ if not IPersistentCoverTile.providedBy(self.context): base_scales = queryMultiAdapter((self.context, self.request), name='images', default=None) return base_scales and base_scales.create(fieldname, direction, height, width, **parameters) orig_value = self.context.data.get(fieldname) if orig_value is None: return if height is None and width is None: _, format = orig_value.contentType.split('/', 1) return None, format, (orig_value._width, orig_value._height) if hasattr(aq_base(orig_value), 'open'): orig_data = orig_value.open() else: orig_data = getattr(aq_base(orig_value), 'data', orig_value) if not orig_data: return try: result = scaleImage(orig_data, direction=direction, height=height, width=width, **parameters) except (ConflictError, KeyboardInterrupt): raise except Exception: logging.exception( 'could not scale "%r" of %r', orig_value, self.context.context.absolute_url()) # FIXME: PEP 3101 return if result is not None: data, format, dimensions = result # FIXME: PEP 3101; how to avoid confusion among method and variable name? mimetype = 'image/%s' % format.lower() value = orig_value.__class__(data, contentType=mimetype, filename=orig_value.filename) value.fieldname = fieldname return value, format, dimensions
def image(self, subpath=None): """Return the image in a specific scale, either inline (default) or as attachment.""" if subpath is None: subpath = self.request.subpath width, height = (None, None) subpath = list(subpath) if (len(subpath) > 0) and (subpath[-1] == "download"): disposition = "attachment" subpath.pop() else: disposition = "inline" if len(subpath) == 1: scale = subpath[0] if scale in image_scales: # /path/to/image/scale/thumb width, height = image_scales[scale] if width and height: image, format, size = scaleImage(self.context.data, width=width, height=height, direction="thumb") else: image = self.context.data res = Response( headerlist=[('Content-Disposition', '%s;filename="%s"' % ( disposition, self.context.filename.encode('ascii', 'ignore'))), ('Content-Length', str(len(image))), ('Content-Type', str(self.context.mimetype)), ], body=image, ) return res
def testAutomaticPalette(self): # get a JPEG with more than 256 colors jpeg = PIL.Image.open(StringIO(PROFILE)) self.assertEqual(jpeg.mode, 'RGB') self.assertEqual(jpeg.format, 'JPEG') self.assertIsNone(jpeg.getcolors(maxcolors=256)) # convert to PNG dst = StringIO() jpeg.save(dst, "PNG") dst.seek(0) png = PIL.Image.open(dst) self.assertEqual(png.mode, 'RGB') self.assertEqual(png.format, 'PNG') self.assertIsNone(png.getcolors(maxcolors=256)) # scale it to a size where we get less than 256 colors (imagedata, format, size) = scaleImage( dst.getvalue(), 24, None, "down" ) image = PIL.Image.open(StringIO(imagedata)) # we should now have an image in palette mode self.assertEqual(image.mode, 'P') self.assertEqual(image.format, 'PNG')
def get_thumb(self, scale, key = "image", direction = "thumb"): """ Return data from plone scale or None""" #Make cache optional cachekey = (self.context.uid, scale, key) cached = thumb_cache.get(cachekey) if cached: return cached scales = get_image_scales() maxwidth, maxheight = scales[scale] blobs = IBlobs(self.context) if key in blobs: registry = get_current_registry() if blobs[key].mimetype in registry.settings['supported_thumbnail_mimetypes']: with blobs[key].blob.open() as f: try: thumb_data, image_type, size = scaleImage(f, width = maxwidth, height = maxheight, direction = direction) except IOError: #FIXME: Logging? return thumb = Thumbnail(thumb_data, image_type = image_type, size = size) thumb_cache.put(cachekey, thumb) return thumb
def testRestrictHeightOnlyUpScaleZero(self): self.assertEqual(scaleImage(PNG, 0, 51, "up")[2], (42, 51))
def testNoStretchingUpScale(self): self.assertEqual(scaleImage(PNG, 200, 103, "up")[2], (84, 103))
def testSameSizeUpScale(self): self.assertEqual(scaleImage(PNG, 84, 103, "up")[2], (84, 103))
def testDoubleSizeUpScale(self): self.assertEqual(scaleImage(PNG, 168, 206, "up")[2], (168, 206))
def testHalfSizeUpScale(self): self.assertEqual(scaleImage(PNG, 42, 51, "up")[2], (42, 51))
def testRestrictWidthOnlyUpScaleZero(self): self.assertEqual(scaleImage(PNG, 42, 0, "up")[2], (42, 52))
def testRestrictWidthOnlyUpScale(self): self.assertEqual(scaleImage(PNG, 42, None, "up")[2], (42, 52))
def testKeepAspectRatioBBB(self): self.assertEqual(scaleImage(PNG, 80, 80, "keep")[2], (65, 80))
def testResultBuffer(self): img1 = scaleImage(PNG, 84, 103)[0] result = BytesIO() img2 = scaleImage(PNG, 84, 103, result=result)[0] self.assertEqual(result, img2) # the return value _is_ the buffer self.assertEqual(result.getvalue(), img1) # but with the same value
def testRestrictHeightOnlyUpScale(self): self.assertEqual(scaleImage(PNG, None, 51, "up")[2], (42, 51))
def testKeepAspectRatio(self): self.assertEqual(scaleImage(PNG, 80, 80, "keep")[2], (65, 80))
def testKeepAspectRatio(self): self.assertEqual(scaleImage(PNG, 80, 80, "thumbnail")[2], (65, 80))