Source code for simpleimages.transforms

import six

from PIL import Image

from django.core.files.base import ContentFile
import django.core.files


[docs]class BasePILTransform(object): ''' Base transform object that provides helper methods to transform :py:class:`django.core.files.images.ImageFile` using :py:mod:`PIL`. Must subclass and override :py:meth:`~.BasePILTransform.transform_pil_image`. ''' IMAGE_QUALITY = 85
[docs] def __call__(self, original_django_file): ''' Returns the transformed version of :py:class:`PIL.Image.Image` Uses :py:meth:`~.BasePILTransform.transform_pil_image` to transform the :py:class:`PIL.Image.Image`. :param original_django_file: source file :type original_django_file: :py:class:`django.core.files.images.ImageFile` :return: transformed file :rtype: :py:class:`django.core.files.File` ''' if not isinstance(original_django_file, django.core.files.File): raise TypeError( 'image is a {0}, not a django File'.format(type(original_django_file)) ) if not original_django_file.file: raise ValueError( 'the django file has no file saved to it.' ) return self.pil_image_to_django_file( self.transform_pil_image( self.django_file_to_pil_image(original_django_file) ) )
[docs] def pil_image_to_django_file(self, pil_image): ''' Gets a PIL image ready to be able to be saved using :py:meth:`django.db.models.fields.files.FieldFile.save` It converts the mode first to ``RGB`` or ``L``, so that it can then save it as a ``JPEG``. It will save it as a progressive ``JPEG`` with a quality of :py:attr:`IMAGE_QUALITY`. :param pil_image: original image :type pil_image: :py:class:`PIL.Image.Image` :return: transformed image :rtype: :py:class:`django.core.files.base.ContentFile` ''' if pil_image.mode not in ('L', 'RGB'): pil_image = pil_image.convert("RGB") temp_io = six.BytesIO() pil_image.save( temp_io, "JPEG", quality=self.IMAGE_QUALITY, optimize=True, progressive=True, icc_profile=pil_image.info.get('icc_profile') ) temp_io.seek(0) django_file = ContentFile(temp_io.getvalue()) return django_file
[docs] def transform_pil_image(self, pil_image): ''' Returns the transformed version of the :py:class:`PIL.Image.Image` Do some logic on :py:class:`PIL.Image.Image`. Must subclass method to provide transformation logic. :param pil_image: original image :type pil_image: :py:class:`PIL.Image.Image` :return: transformed image :rtype: :py:class:`PIL.Image.Image` ''' raise NotImplementedError
[docs] def django_file_to_pil_image(self, django_file): ''' Converts a the file returned by :py:class:`django.db.models.fields.ImageField` to a PIL image. :param django_file: django file :type django_file: :py:class:`django.db.models.fields.files.FieldFile` :rtype: :py:class:`PIL.Image.Image` ''' django_file.open() pil_image = Image.open(django_file.file) return pil_image
[docs]class Scale(BasePILTransform): ''' Scales down an image to max height and/or width. If the original image is smaller than both/either specified dimensions than it will be left unchanged. '''
[docs] def __init__(self, width=None, height=None): ''' Initialize this class with a max height and/or width (in pixels). :param height: max height of scaled image :param width: max width of scaled image :type height: int or float :type width: int or float ''' self.dimensions = (width, height) if not any(self.dimensions): raise ValueError( 'Must be called with either `height` or `width`' )
[docs] def transform_pil_image(self, pil_image): ''' Uses :py:meth:`PIL.Image.Image.transform` to scale down the image. Based on `this stackoverflow discussions <http://stackoverflow.com/a/940368/907060>`_, uses :attr:`PIL.Image.ANTIALIAS` ''' max_width = min(self.dimensions[0] or float('inf'), pil_image.size[0]) max_height = min(self.dimensions[1] or float('inf'), pil_image.size[1]) max_dimensions = (max_width, max_height) pil_image.thumbnail(max_dimensions, Image.ANTIALIAS) return pil_image