示例#1
0
    def test_transparent_thumbnailing(self):
        thumb_file = self.thumbnailer.get_thumbnail({"size": (100, 100)})
        thumb_file.seek(0)
        thumb = Image.open(thumb_file)
        self.assertFalse(utils.is_transparent(thumb), "%s shouldn't be transparent." % thumb_file.name)

        thumb_file = self.transparent_thumbnailer.get_thumbnail({"size": (100, 100)})
        thumb_file.seek(0)
        thumb = Image.open(thumb_file)
        self.assertTrue(utils.is_transparent(thumb), "%s should be transparent." % thumb_file.name)

        thumb_file = self.transparent_greyscale_thumbnailer.get_thumbnail({"size": (100, 100)})
        thumb_file.seek(0)
        thumb = Image.open(thumb_file)
        self.assertTrue(utils.is_transparent(thumb), "%s should be transparent." % thumb_file.name)
示例#2
0
    def generate_thumbnail(self, thumbnail_options, high_resolution=False):
        """
        Return an unsaved ``ThumbnailFile`` containing a thumbnail image.

        The thumbnail image is generated using the ``thumbnail_options``
        dictionary.
        """
        if high_resolution:
            orig_size = thumbnail_options["size"]  # remember original size
            thumbnail_options = thumbnail_options.copy()
            thumbnail_options["size"] = (orig_size[0] * 2, orig_size[1] * 2)
        image = self.generate_source_image(thumbnail_options)
        if image is None:
            raise exceptions.InvalidImageFormatError("The source file does not appear to be an image")

        thumbnail_image = engine.process_image(image, thumbnail_options, self.thumbnail_processors)
        quality = thumbnail_options.get("quality", self.thumbnail_quality)

        if high_resolution:
            thumbnail_options["size"] = orig_size  # restore original size

        filename = self.get_thumbnail_name(
            thumbnail_options, transparent=utils.is_transparent(thumbnail_image), high_resolution=high_resolution
        )

        img = engine.save_image(thumbnail_image, filename=filename, quality=quality)
        data = img.read()

        thumbnail = ThumbnailFile(
            filename, file=ContentFile(data), storage=self.thumbnail_storage, thumbnail_options=thumbnail_options
        )
        thumbnail.image = thumbnail_image
        thumbnail._committed = False

        return thumbnail
示例#3
0
def autocrop(im, autocrop=False, **kwargs):
    """
    Remove any unnecessary whitespace from the edges of the source image.

    This processor should be listed before :func:`scale_and_crop` so the
    whitespace is removed from the source image before it is resized.

    autocrop
        Activates the autocrop method for this image.

    """
    if autocrop:
        # If transparent, flatten.
        if utils.is_transparent(im) and False:
            no_alpha = Image.new('L', im.size, (255))
            no_alpha.paste(im, mask=im.split()[-1])
        else:
            no_alpha = im.convert('L')
        # Convert to black and white image.
        bw = no_alpha.convert('L')
        # bw = bw.filter(ImageFilter.MedianFilter)
        # White background.
        bg = Image.new('L', im.size, 255)
        bbox = ImageChops.difference(bw, bg).getbbox()
        if bbox:
            im = im.crop(bbox)
    return im
    def get_thumbnail(self, thumbnail_options, save=True):
        """
        Return a ``ThumbnailFile`` containing a thumbnail.

        It the file already exists, it will simply be returned.

        Otherwise a new thumbnail image is generated using the
        ``thumbnail_options`` dictionary. If the ``save`` argument is ``True``
        (default), the generated thumbnail will be saved too.
        """
        opaque_name = self.get_thumbnail_name(thumbnail_options,
                                              transparent=False)
        transparent_name = self.get_thumbnail_name(thumbnail_options,
                                                   transparent=True)
        if opaque_name == transparent_name:
            names = (opaque_name, )
        else:
            names = (opaque_name, transparent_name)
        for filename in names:
            if self.thumbnail_exists(filename):
                thumbnail = ThumbnailFile(name=filename,
                                          storage=self.thumbnail_storage)
                return thumbnail

        thumbnail = self.generate_thumbnail(thumbnail_options)
        if save:
            save_thumbnail(thumbnail, self.thumbnail_storage)
            # Ensure the right thumbnail name is used based on the transparency
            # of the image.
            filename = (utils.is_transparent(thumbnail.image)
                        and transparent_name or opaque_name)
            self.get_thumbnail_cache(filename, create=True, update=True)

        return thumbnail
示例#5
0
def autocrop(im, autocrop=False, **kwargs):
    """
    Remove any unnecessary whitespace from the edges of the source image.

    This processor should be listed before :func:`scale_and_crop` so the
    whitespace is removed from the source image before it is resized.

    autocrop
        Activates the autocrop method for this image.

    """
    if autocrop:
        # If transparent, flatten.
        if utils.is_transparent(im) and False:
            no_alpha = Image.new('L', im.size, (255))
            no_alpha.paste(im, mask=im.split()[-1])
        else:
            no_alpha = im.convert('L')
        # Convert to black and white image.
        bw = no_alpha.convert('L')
        # bw = bw.filter(ImageFilter.MedianFilter)
        # White background.
        bg = Image.new('L', im.size, 255)
        bbox = ImageChops.difference(bw, bg).getbbox()
        if bbox:
            im = im.crop(bbox)
    return im
示例#6
0
    def generate_thumbnail(self, thumbnail_options):
        """
        Return an unsaved ``ThumbnailFile`` containing a thumbnail image.

        The thumbnail image is generated using the ``thumbnail_options``
        dictionary.
        """
        image = self.generate_source_image(thumbnail_options)
        if image is None:
            raise exceptions.InvalidImageFormatError(
                "The source file does not appear to be an image")

        thumbnail_image = engine.process_image(image, thumbnail_options,
                                               self.thumbnail_processors)
        quality = thumbnail_options.get('quality', self.thumbnail_quality)

        filename = self.get_thumbnail_name(thumbnail_options,
                            transparent=utils.is_transparent(thumbnail_image))

        data = engine.save_image(thumbnail_image, filename=filename,
                                 quality=quality).read()

        thumbnail = ThumbnailFile(filename, ContentFile(data),
                                  storage=self.thumbnail_storage)
        thumbnail.image = thumbnail_image
        thumbnail._committed = False

        return thumbnail
    def generate_thumbnail(self, thumbnail_options):
        """
        Return an unsaved ``ThumbnailFile`` containing a thumbnail image.

        The thumbnail image is generated using the ``thumbnail_options``
        dictionary.
        """
        image = self.generate_source_image(thumbnail_options)
        if image is None:
            raise exceptions.InvalidImageFormatError(
                "The source file does not appear to be an image")

        thumbnail_image = engine.process_image(image, thumbnail_options,
                                               self.thumbnail_processors)
        quality = thumbnail_options.get('quality', self.thumbnail_quality)

        filename = self.get_thumbnail_name(
            thumbnail_options,
            transparent=utils.is_transparent(thumbnail_image))

        data = engine.save_image(thumbnail_image,
                                 filename=filename,
                                 quality=quality).read()

        thumbnail = ThumbnailFile(filename,
                                  ContentFile(data),
                                  storage=self.thumbnail_storage)
        thumbnail.image = thumbnail_image
        thumbnail._committed = False

        return thumbnail
示例#8
0
    def get_thumbnail(self, thumbnail_options, save=True):
        """
        Return a ``ThumbnailFile`` containing a thumbnail.

        It the file already exists, it will simply be returned.

        Otherwise a new thumbnail image is generated using the
        ``thumbnail_options`` dictionary. If the ``save`` argument is ``True``
        (default), the generated thumbnail will be saved too.
        """
        opaque_name = self.get_thumbnail_name(thumbnail_options,
                                              transparent=False)
        transparent_name = self.get_thumbnail_name(thumbnail_options,
                                                   transparent=True)
        if opaque_name == transparent_name:
            names = (opaque_name,)
        else:
            names = (opaque_name, transparent_name)
        for filename in names:
            if self.thumbnail_exists(filename):
                thumbnail = ThumbnailFile(name=filename,
                                          storage=self.thumbnail_storage)
                return thumbnail

        thumbnail = self.generate_thumbnail(thumbnail_options)
        if save:
            save_thumbnail(thumbnail, self.thumbnail_storage)
            # Ensure the right thumbnail name is used based on the transparency
            # of the image.
            filename = (utils.is_transparent(thumbnail.image) and
                        transparent_name or opaque_name)
            self.get_thumbnail_cache(filename, create=True, update=True)

        return thumbnail
示例#9
0
    def get_thumbnail(self, thumbnail_options, save=True, generate=None):
        """
        Return a ``ThumbnailFile`` containing a thumbnail.

        If a matching thumbnail already exists, it will simply be returned.

        By default (unless the ``Thumbnailer`` was instanciated with
        ``generate=False``), thumbnails that don't exist are generated.
        Otherwise ``None`` is returned.

        Force the generation behaviour by setting the ``generate`` param to
        either ``True`` or ``False`` as required.

        The new thumbnail image is generated using the ``thumbnail_options``
        dictionary. If the ``save`` argument is ``True`` (default), the
        generated thumbnail will be saved too.
        """
        opaque_name = self.get_thumbnail_name(thumbnail_options,
                                              transparent=False)
        transparent_name = self.get_thumbnail_name(thumbnail_options,
                                                   transparent=True)
        if opaque_name == transparent_name:
            names = (opaque_name,)
        else:
            names = (opaque_name, transparent_name)
        for filename in names:
            if self.thumbnail_exists(filename):
                return ThumbnailFile(
                    name=filename, storage=self.thumbnail_storage,
                    thumbnail_options=thumbnail_options)

        if generate is None:
            generate = self.generate
        if not generate:
            signals.thumbnail_missed.send(
                sender=self, options=thumbnail_options)
            return

        thumbnail = self.generate_thumbnail(thumbnail_options)
        if save:
            save_thumbnail(thumbnail, self.thumbnail_storage)
            # BEGIN: cache thumbnail modified time
            utils.invalidate_easy_cache(thumbnail.name)
            # END:
            signals.thumbnail_created.send(sender=thumbnail)
            # Ensure the right thumbnail name is used based on the transparency
            # of the image.
            filename = (utils.is_transparent(thumbnail.image) and
                        transparent_name or opaque_name)
            self.get_thumbnail_cache(filename, create=True, update=True)

            if self.thumbnail_high_resolution:
                thumbnail_2x = self.generate_thumbnail(thumbnail_options,
                                                       high_resolution=True)
                save_thumbnail(thumbnail_2x, self.thumbnail_storage)
                # BEGIN: cache thumbnail modified time
                utils.invalidate_easy_cache(thumbnail.name)
                # END:
        return thumbnail
示例#10
0
    def generate_thumbnail(self, thumbnail_options, high_resolution=False,
                           silent_template_exception=False):
        """
        Return an unsaved ``ThumbnailFile`` containing a thumbnail image.

        The thumbnail image is generated using the ``thumbnail_options``
        dictionary.
        """
        thumbnail_options = self.get_options(thumbnail_options)
        orig_size = thumbnail_options['size']  # remember original size
        # Size sanity check.
        min_dim, max_dim = 0, 0
        for dim in orig_size:
            try:
                dim = int(dim)
            except (TypeError, ValueError):
                continue
            min_dim, max_dim = min(min_dim, dim), max(max_dim, dim)
        if max_dim == 0 or min_dim < 0:
            raise exceptions.EasyThumbnailsError(
                "The source image is an invalid size (%sx%s)" % orig_size)

        if high_resolution:
            thumbnail_options['size'] = (orig_size[0] * 2, orig_size[1] * 2)
        image = engine.generate_source_image(
            self, thumbnail_options, self.source_generators,
            fail_silently=silent_template_exception)
        if image is None:
            raise exceptions.InvalidImageFormatError(
                "The source file does not appear to be an image")

        icc_profile = image.info.get("icc_profile")

        thumbnail_image = engine.process_image(image, thumbnail_options,
                                               self.thumbnail_processors)
        if high_resolution:
            thumbnail_options['size'] = orig_size  # restore original size

        filename = self.get_thumbnail_name(
            thumbnail_options,
            transparent=utils.is_transparent(thumbnail_image),
            high_resolution=high_resolution)
        quality = thumbnail_options['quality']
        subsampling = thumbnail_options['subsampling']

        img = engine.save_image(
            thumbnail_image, filename=filename, quality=quality,
            subsampling=subsampling, icc_profile=icc_profile)
        data = img.read()

        thumbnail = ThumbnailFile(
            filename, file=ContentFile(data), storage=self.thumbnail_storage,
            thumbnail_options=thumbnail_options)
        thumbnail.image = thumbnail_image
        thumbnail._committed = False

        return thumbnail
示例#11
0
    def generate_thumbnail(self, thumbnail_options, high_resolution=False,
                           silent_template_exception=False, keep_file_open=True):
        """
        Return an unsaved ``ThumbnailFile`` containing a thumbnail image.

        The thumbnail image is generated using the ``thumbnail_options``
        dictionary.
        """
        thumbnail_options = self.get_options(thumbnail_options)
        orig_size = thumbnail_options['size']  # remember original size
        # Size sanity check.
        min_dim, max_dim = 0, 0
        for dim in orig_size:
            try:
                dim = int(dim)
            except (TypeError, ValueError):
                continue
            min_dim, max_dim = min(min_dim, dim), max(max_dim, dim)
        if max_dim == 0 or min_dim < 0:
            raise exceptions.EasyThumbnailsError(
                "The source image is an invalid size (%sx%s)" % orig_size)

        if high_resolution:
            thumbnail_options['size'] = (orig_size[0] * 2, orig_size[1] * 2)
        image = engine.generate_source_image(
            self, thumbnail_options, self.source_generators,
            fail_silently=silent_template_exception,
            keep_file_open=keep_file_open)
        if image is None:
            raise exceptions.InvalidImageFormatError(
                "The source file does not appear to be an image")

        thumbnail_image = engine.process_image(image, thumbnail_options,
                                               self.thumbnail_processors)
        if high_resolution:
            thumbnail_options['size'] = orig_size  # restore original size

        filename = self.get_thumbnail_name(
            thumbnail_options,
            transparent=utils.is_transparent(thumbnail_image),
            high_resolution=high_resolution)
        quality = thumbnail_options['quality']
        subsampling = thumbnail_options['subsampling']

        img = engine.save_image(
            thumbnail_image, filename=filename, quality=quality,
            subsampling=subsampling)
        data = img.read()

        thumbnail = ThumbnailFile(
            filename, file=ContentFile(data), storage=self.thumbnail_storage,
            thumbnail_options=thumbnail_options)
        thumbnail.image = thumbnail_image
        thumbnail._committed = False

        return thumbnail
示例#12
0
    def test_transparent_thumbnailing(self):
        thumb_file = self.thumbnailer.get_thumbnail({'size': (100, 100)})
        thumb_file.seek(0)
        thumb = Image.open(thumb_file)
        self.assertFalse(utils.is_transparent(thumb),
                         "%s shouldn't be transparent." % thumb_file.name)

        thumb_file = self.transparent_thumbnailer.get_thumbnail(
            {'size': (100, 100)})
        thumb_file.seek(0)
        thumb = Image.open(thumb_file)
        self.assertTrue(utils.is_transparent(thumb),
                        "%s should be transparent." % thumb_file.name)

        thumb_file = self.transparent_greyscale_thumbnailer.get_thumbnail(
            {'size': (100, 100)})
        thumb_file.seek(0)
        thumb = Image.open(thumb_file)
        self.assertTrue(utils.is_transparent(thumb),
                        "%s should be transparent." % thumb_file.name)
示例#13
0
    def get_thumbnail(self, thumbnail_options, save=True, generate=None):
        """
        Return a ``ThumbnailFile`` containing a thumbnail.

        If a matching thumbnail already exists, it will simply be returned.

        By default (unless the ``Thumbnailer`` was instanciated with
        ``generate=False``), thumbnails that don't exist are generated.
        Otherwise ``None`` is returned.

        Force the generation behaviour by setting the ``generate`` param to
        either ``True`` or ``False`` as required.

        The new thumbnail image is generated using the ``thumbnail_options``
        dictionary. If the ``save`` argument is ``True`` (default), the
        generated thumbnail will be saved too.
        """
        opaque_name = self.get_thumbnail_name(thumbnail_options,
                                              transparent=False)
        transparent_name = self.get_thumbnail_name(thumbnail_options,
                                                   transparent=True)
        if opaque_name == transparent_name:
            names = (opaque_name, )
        else:
            names = (opaque_name, transparent_name)
        for filename in names:
            if self.thumbnail_exists(filename):
                return ThumbnailFile(name=filename,
                                     storage=self.thumbnail_storage,
                                     thumbnail_options=thumbnail_options)

        if generate is None:
            generate = self.generate
        if not generate:
            signals.thumbnail_missed.send(sender=self,
                                          options=thumbnail_options)
            return

        thumbnail = self.generate_thumbnail(thumbnail_options)
        if save:
            save_thumbnail(thumbnail, self.thumbnail_storage)
            signals.thumbnail_created.send(sender=thumbnail)
            # Ensure the right thumbnail name is used based on the transparency
            # of the image.
            filename = (utils.is_transparent(thumbnail.image)
                        and transparent_name or opaque_name)
            self.get_thumbnail_cache(filename, create=True, update=True)

            if self.thumbnail_high_resolution:
                thumbnail_2x = self.generate_thumbnail(thumbnail_options,
                                                       high_resolution=True)
                save_thumbnail(thumbnail_2x, self.thumbnail_storage)
        return thumbnail
示例#14
0
def colorspace(im, bw=False, replace_alpha=False, **kwargs):
    """
    Convert images to the correct color space.

    A passive option (i.e. always processed) of this method is that all images
    (unless grayscale) are converted to RGB colorspace.

    This processor should be listed before :func:`scale_and_crop` so palette is
    changed before the image is resized.

    bw
        Make the thumbnail grayscale (not really just black & white).

    replace_alpha
        Replace any transparency layer with a solid color. For example,
        ``replace_alpha='#fff'`` would replace the transparency layer with
        white.

    """
    if im.mode == 'I':
        # PIL (and pillow) have can't convert 16 bit grayscale images to lower
        # modes, so manually convert them to an 8 bit grayscale.
        im = im.point(list(_points_table()), 'L')

    is_transparent = utils.is_transparent(im)
    is_grayscale = im.mode in ('L', 'LA')
    new_mode = im.mode
    if is_grayscale or bw:
        new_mode = 'L'
    else:
        new_mode = 'RGB'

    if is_transparent:
        if replace_alpha:
            if im.mode != 'RGBA':
                im = im.convert('RGBA')
            base = Image.new('RGBA', im.size, replace_alpha)
            base.paste(im, mask=im)
            im = base
        else:
            new_mode = new_mode + 'A'

    if im.mode != new_mode:
        im = im.convert(new_mode)

    return im
示例#15
0
def colorspace(im, bw=False, replace_alpha=False, **kwargs):
    """
    Convert images to the correct color space.

    A passive option (i.e. always processed) of this method is that all images
    (unless grayscale) are converted to RGB colorspace.

    This processor should be listed before :func:`scale_and_crop` so palette is
    changed before the image is resized.

    bw
        Make the thumbnail grayscale (not really just black & white).

    replace_alpha
        Replace any transparency layer with a solid color. For example,
        ``replace_alpha='#fff'`` would replace the transparency layer with
        white.

    """
    if im.mode == 'I':
        # PIL (and pillow) have can't convert 16 bit grayscale images to lower
        # modes, so manually convert them to an 8 bit grayscale.
        im = im.point(list(_points_table()), 'L')

    is_transparent = utils.is_transparent(im)
    is_grayscale = im.mode in ('L', 'LA')
    new_mode = im.mode
    if is_grayscale or bw:
        new_mode = 'L'
    else:
        new_mode = 'RGB'

    if is_transparent:
        if replace_alpha:
            if im.mode != 'RGBA':
                im = im.convert('RGBA')
            base = Image.new('RGBA', im.size, replace_alpha)
            base.paste(im, mask=im)
            im = base
        else:
            new_mode = new_mode + 'A'

    if im.mode != new_mode:
        im = im.convert(new_mode)

    return im
示例#16
0
    def generate_thumbnail(self, thumbnail_options, high_resolution=False,
                           silent_template_exception=False):
        """
        Return an unsaved ``ThumbnailFile`` containing a thumbnail image.

        The thumbnail image is generated using the ``thumbnail_options``
        dictionary.
        """
        if high_resolution:
            orig_size = thumbnail_options['size']  # remember original size
            thumbnail_options = thumbnail_options.copy()
            thumbnail_options['size'] = (orig_size[0] * 2, orig_size[1] * 2)
        image = engine.generate_source_image(
            self, thumbnail_options, self.source_generators,
            fail_silently=silent_template_exception)
        if image is None:
            raise exceptions.InvalidImageFormatError(
                "The source file does not appear to be an image")

        thumbnail_image = engine.process_image(image, thumbnail_options,
                                               self.thumbnail_processors)
        quality = thumbnail_options.get('quality', self.thumbnail_quality)

        if high_resolution:
            thumbnail_options['size'] = orig_size  # restore original size

        filename = self.get_thumbnail_name(
            thumbnail_options,
            transparent=utils.is_transparent(thumbnail_image),
            high_resolution=high_resolution)

        img = engine.save_image(
            thumbnail_image, filename=filename, quality=quality)
        data = img.read()

        thumbnail = ThumbnailFile(
            filename, file=ContentFile(data), storage=self.thumbnail_storage,
            thumbnail_options=thumbnail_options)
        thumbnail.image = thumbnail_image
        thumbnail._committed = False

        return thumbnail
示例#17
0
def colorspace(im, bw=False, replace_alpha=False, **kwargs):
    """
    Convert images to the correct color space.

    A passive option (i.e. always processed) of this method is that all images
    (unless grayscale) are converted to RGB colorspace.

    This processor should be listed before :func:`scale_and_crop` so palette is
    changed before the image is resized.

    bw
        Make the thumbnail grayscale (not really just black & white).

    replace_alpha
        Replace any transparency layer with a solid color. For example,
        ``replace_alpha='#fff'`` would replace the transparency layer with
        white.

    """
    is_transparent = utils.is_transparent(im)
    if bw:
        if im.mode in ('L', 'LA'):
            return im
        if is_transparent:
            return im.convert('LA')
        else:
            return im.convert('L')

    if im.mode in ('L', 'RGB'):
        return im

    if is_transparent:
        if im.mode != 'RGBA':
            im = im.convert('RGBA')
        if not replace_alpha:
            return im
        base = Image.new('RGBA', im.size, replace_alpha)
        base.paste(im, mask=im)
        im = base

    return im.convert('RGB')
示例#18
0
def ensure_srgb(image):
    """
    Process an image file of unknown color profile.
    Transform to sRGB profile and return the result.
    """
    if not SRGB_PROFILE or image.mode in (u'L', u'LA', u'I'):
        return image

    with NamedTemporaryFile(suffix='.icc', delete=True) as icc_file:
        profile = image.info.get('icc_profile')
        if profile:
            icc_file.write(profile)
            icc_file.flush()
            try:
                output_mode = 'RGBA' if is_transparent(image) else 'RGB'
                image = ImageCms.profileToProfile(image, icc_file.name, SRGB_PROFILE, outputMode=output_mode)
            except PyCMSError:
                log.error("Unable to apply color profile!")
                pass

    return image
示例#19
0
    def generate_thumbnail(self,
                           thumbnail_options,
                           high_resolution=False,
                           silent_template_exception=False):
        """
        Return an unsaved ``ThumbnailFile`` containing a thumbnail image.

        The thumbnail image is generated using the ``thumbnail_options``
        dictionary.
        """
        thumbnail_options = self.get_options(thumbnail_options)
        orig_size = thumbnail_options['size']  # remember original size
        # Size sanity check.
        min_dim, max_dim = 0, 0
        for dim in orig_size:
            try:
                dim = int(dim)
            except (TypeError, ValueError):
                continue
            min_dim, max_dim = min(min_dim, dim), max(max_dim, dim)
        if max_dim == 0 or min_dim < 0:
            raise exceptions.EasyThumbnailsError(
                "The source image is an invalid size (%sx%s)" % orig_size)

        if high_resolution:
            thumbnail_options['size'] = (orig_size[0] * 2, orig_size[1] * 2)
        image = engine.generate_source_image(
            self,
            thumbnail_options,
            self.source_generators,
            fail_silently=silent_template_exception)
        if image is None:
            raise exceptions.InvalidImageFormatError(
                "The source file does not appear to be an image")

        thumbnail_image = None
        IS_GIF = False
        if isinstance(image, GifImageFile):
            IS_GIF = True
            import images2gif
            gif_image, gif_params = images2gif.readGif(image, False)
            frames = []
            for frame in gif_image:
                thumbnail_frame = engine.process_image(
                    frame, thumbnail_options, self.thumbnail_processors)
                frames.append(thumbnail_frame)
                if thumbnail_image is None:
                    thumbnail_image = frame
        else:
            thumbnail_image = engine.process_image(image, thumbnail_options,
                                                   self.thumbnail_processors)

        if high_resolution:
            thumbnail_options['size'] = orig_size  # restore original size

        filename = self.get_thumbnail_name(
            thumbnail_options,
            transparent=utils.is_transparent(thumbnail_image),
            high_resolution=high_resolution)
        quality = thumbnail_options['quality']
        subsampling = thumbnail_options['subsampling']

        if IS_GIF:
            try:
                # try save with speedup
                img = images2gif.writeGif(frames, **gif_params)
                saved = True
            except:
                # if not saved, simple save
                gif_params.update({
                    'subRectangles': False,
                })
                img = images2gif.writeGif(frames, **gif_params)
        else:
            img = engine.save_image(thumbnail_image,
                                    filename=filename,
                                    quality=quality,
                                    subsampling=subsampling)
        data = img.read()

        thumbnail = ThumbnailFile(filename,
                                  file=ContentFile(data),
                                  storage=self.thumbnail_storage,
                                  thumbnail_options=thumbnail_options)
        thumbnail.image = thumbnail_image
        thumbnail._committed = False

        return thumbnail
示例#20
0
    def generate_thumbnail(self, thumbnail_options, high_resolution=False,
                           silent_template_exception=False):
        """
        Return an unsaved ``ThumbnailFile`` containing a thumbnail image.

        The thumbnail image is generated using the ``thumbnail_options``
        dictionary.
        """
        thumbnail_options = self.get_options(thumbnail_options)
        orig_size = thumbnail_options['size']  # remember original size
        # Size sanity check.
        min_dim, max_dim = 0, 0
        for dim in orig_size:
            try:
                dim = int(dim)
            except (TypeError, ValueError):
                continue
            min_dim, max_dim = min(min_dim, dim), max(max_dim, dim)
        if max_dim == 0 or min_dim < 0:
            raise exceptions.EasyThumbnailsError(
                "The source image is an invalid size (%sx%s)" % orig_size)

        if high_resolution:
            thumbnail_options['size'] = (orig_size[0] * 2, orig_size[1] * 2)

        image = engine.generate_source_image(
            self, thumbnail_options, self.source_generators,
            fail_silently=silent_template_exception)
        if image is None:
            raise exceptions.InvalidImageFormatError(
                "The source file does not appear to be an image")

        self.open()
        source_image = Image.open(self.file)
        is_animated_gif = self.name.lower().endswith('.gif') \
            and source_image.format == 'GIF' \
            and source_image.info.get('duration') != None

        from io import BytesIO

        if is_animated_gif:
            frame_index = 0
            palette = source_image.getpalette()
            from easy_thumbnails.utils import images2gif
            images = images2gif.readGif(self.file, asNumpy=False)
            # convert the durations to 0.1 secs
            durations = [single_duration / 1000.0 for single_duration in images2gif.getDurationsOfGif(self.file)]
        else:
            images = [engine.generate_source_image(
            self, thumbnail_options, self.source_generators,
            fail_silently=silent_template_exception)]

        thumbnail_images = []

        if high_resolution:
            thumbnail_options['high_resolution'] = True

        for image in images:
            if image is None:
                raise exceptions.InvalidImageFormatError(
                    "The source file does not appear to be an image")
            thumb = engine.process_image(image, thumbnail_options, self.thumbnail_processors)
            thumbnail_images.append(thumb)

        thumbnail_image = thumbnail_images[0]

        quality = thumbnail_options.get('quality', self.thumbnail_quality)

        if high_resolution:
            thumbnail_options['size'] = orig_size  # restore original size
            del thumbnail_options['high_resolution']

        filename = self.get_thumbnail_name(
            thumbnail_options,
            transparent=utils.is_transparent(thumbnail_image),
            high_resolution=high_resolution)
        quality = thumbnail_options['quality']
        subsampling = thumbnail_options['subsampling']

        if is_animated_gif:
            from tempfile import NamedTemporaryFile
            try:
                output_temp_file = NamedTemporaryFile('rw', suffix='.gif')

                images2gif.writeGif(output_temp_file.name, thumbnail_images, duration=durations)
                output_temp_file = open(output_temp_file.name)
                data = output_temp_file.read()
            finally:
                output_temp_file.close()
                del output_temp_file
        else:
            img = engine.save_image(
                thumbnail_image, filename=filename, quality=quality)
            data = img.read()

        thumbnail = ThumbnailFile(
            filename, file=ContentFile(data), storage=self.thumbnail_storage,
            thumbnail_options=thumbnail_options)
        thumbnail._committed = False

        return thumbnail
示例#21
0
    def get_thumbnail(self, thumbnail_options, save=True, generate=None):
        """
        Return a ``ThumbnailFile`` containing a thumbnail.

        If a matching thumbnail already exists, it will simply be returned.

        By default (unless the ``Thumbnailer`` was instanciated with
        ``generate=False``), thumbnails that don't exist are generated.
        Otherwise ``None`` is returned.

        Force the generation behaviour by setting the ``generate`` param to
        either ``True`` or ``False`` as required.

        The new thumbnail image is generated using the ``thumbnail_options``
        dictionary. If the ``save`` argument is ``True`` (default), the
        generated thumbnail will be saved too.
        """
        opaque_name = self.get_thumbnail_name(thumbnail_options,
                                              transparent=False)
        transparent_name = self.get_thumbnail_name(thumbnail_options,
                                                   transparent=True)
        if opaque_name == transparent_name:
            names = (opaque_name,)
        else:
            names = (opaque_name, transparent_name)

        ####
        # If this thumbnail not exists, maybe errors.
        # TODO: check it
        try:
            obj = models.Thumbnail.objects.filter(Q(name=names[0]) | Q(name=names[1]))[0]
            name = obj.name
        except:
            name = ""

        return ThumbnailFile(name=name,
                     storage=self.thumbnail_storage,
                     thumbnail_options=thumbnail_options)

        ####        
        for filename in names:
            if self.thumbnail_exists(filename):
                return ThumbnailFile(name=filename,
                    storage=self.thumbnail_storage,
                    thumbnail_options=thumbnail_options)

        if generate is None:
            generate = self.generate
        if not generate:
            signals.thumbnail_missed.send(sender=self,
                options=thumbnail_options)
            return

        thumbnail = self.generate_thumbnail(thumbnail_options)
        if save:
            save_thumbnail(thumbnail, self.thumbnail_storage)
            signals.thumbnail_created.send(sender=thumbnail)
            # Ensure the right thumbnail name is used based on the transparency
            # of the image.
            filename = (utils.is_transparent(thumbnail.image) and
                        transparent_name or opaque_name)
            self.get_thumbnail_cache(filename, create=True, update=True)

        return thumbnail
示例#22
0
def iiif_image_api(request, identifier_param, region_param, size_param,
                   rotation_param, quality_param, format_param):
    """ Image repurposing endpoint for IIIF Image API 2.1 """
    ik_image, image = _get_image_or_404(identifier_param, load_image=True)

    is_transparent = et_utils.is_transparent(image)
    is_grayscale = image.mode in ('L', 'LA')

    # Map format names used for IIIF URL path extension to proper name
    format_mapping = {
        'jpg': 'jpeg',
        'tif': 'tiff',
    }

    try:
        # Parse region
        x, y, r_width, r_height = parse_region(region_param, image.width,
                                               image.height)

        # Parse size
        s_width, s_height = parse_size(size_param, r_width, r_height)

        # Parse rotation
        is_mirrored, rotation_degrees = \
            parse_rotation(rotation_param, s_width, s_height)

        # Parse quality
        quality = parse_quality(quality_param)

        # Parse format
        # TODO Add support for unsupported formats (see `parse_format`)
        image_format = os.path.splitext(ik_image.image.name)[1][1:].lower()
        output_format = parse_format(format_param, image_format)
        corrected_format = format_mapping.get(output_format, output_format)

        # Redirect to canonical URL if appropriate, per
        # http://iiif.io/api/image/2.1/#canonical-uri-syntax
        canonical_path = make_canonical_path(
            identifier_param,
            image.width,
            image.height,
            (x, y, r_width, r_height),  # Region
            (s_width, s_height),  # Size
            (is_mirrored, rotation_degrees),  # Rotation
            quality,
            output_format)
        if request.path != canonical_path:
            return HttpResponseRedirect(canonical_path)

        # Determine storage file name for item
        if iiif_storage:
            storage_path = build_iiif_file_storage_path(
                canonical_path, ik_image, iiif_storage)
        else:
            storage_path = None

        # Load pre-generated image from storage if one exists and is up-to-date
        # with the original image (per timestampt info embedded in the storage
        # path)
        # TODO The exists lookup is slow for S3 storage, cache metadata?
        # TODO Detect when original image would be unchanged & use it directly?
        if (storage_path and iiif_storage.exists(storage_path)):
            if is_remote_storage(iiif_storage, storage_path):
                return HttpResponseRedirect(iiif_storage.url(storage_path))
            else:
                return FileResponse(
                    iiif_storage.open(storage_path),
                    content_type='image/%s' % corrected_format,
                )

        ##################
        # Generate image #
        ##################

        # Apply region
        if x or y or r_width != image.width or r_height != image.height:
            box = (x, y, x + r_width, y + r_height)
            image = image.crop(box)

        # Apply size
        if s_width != r_width or s_height != r_height:
            size = (s_width, s_height)
            image = image.resize(size)

        # TODO Apply rotation

        # Apply quality
        # Much of this is cribbed from easythumbnails' `colorspace` processor
        # TODO Replace with glamkit-imagetools' sRGB colour space converter?
        if quality in ('default', 'color') and not is_grayscale:
            if is_transparent:
                new_mode = 'RGBA'
            else:
                new_mode = 'RGB'
        elif is_grayscale or quality == 'gray':
            if is_transparent:
                new_mode = 'LA'
            else:
                new_mode = 'L'
        if new_mode != image.mode:
            image = image.convert(new_mode)

        # Apply format and "save"
        result_image = BytesIO()
        image.save(result_image, format=corrected_format)

        # Save generated image to storage if possible
        if storage_path:
            iiif_storage.save(storage_path, result_image)

        if iiif_storage and is_remote_storage(iiif_storage, storage_path):
            return HttpResponseRedirect(iiif_storage.url(storage_path))
        else:
            result_image.seek(0)  # Reset image file in case it's just created
            return FileResponse(
                result_image.read(),
                content_type='image/%s' % corrected_format,
            )
    # Handle error conditions per iiif.io/api/image/2.1/#server-responses
    except ClientError, ex:
        return HttpResponseBadRequest(ex.message)  # 400 response
示例#23
0
    def generate_thumbnail(self, thumbnail_options, high_resolution=False,
                           silent_template_exception=False):
        """
        Return an unsaved ``ThumbnailFile`` containing a thumbnail image.

        The thumbnail image is generated using the ``thumbnail_options``
        dictionary.
        """
        thumbnail_options = self.get_options(thumbnail_options)
        orig_size = thumbnail_options['size']  # remember original size
        # Size sanity check.
        min_dim, max_dim = 0, 0
        for dim in orig_size:
            try:
                dim = int(dim)
            except (TypeError, ValueError):
                continue
            min_dim, max_dim = min(min_dim, dim), max(max_dim, dim)
        if max_dim == 0 or min_dim < 0:
            raise exceptions.EasyThumbnailsError(
                "The source image is an invalid size (%sx%s)" % orig_size)

        if high_resolution:
            thumbnail_options['size'] = (orig_size[0] * 2, orig_size[1] * 2)
        image = engine.generate_source_image(
            self, thumbnail_options, self.source_generators,
            fail_silently=silent_template_exception)
        if image is None:
            raise exceptions.InvalidImageFormatError(
                "The source file does not appear to be an image")

        thumbnail_image = None
        IS_GIF = False
        if isinstance(image, GifImageFile):
            IS_GIF = True
            import images2gif
            gif_image, gif_params = images2gif.readGif(image, False)
            frames = []
            for frame in gif_image:
                thumbnail_frame = engine.process_image(frame, thumbnail_options,
                                                       self.thumbnail_processors)
                frames.append(thumbnail_frame)
                if thumbnail_image is None:
                    thumbnail_image = frame
        else:
            thumbnail_image = engine.process_image(image, thumbnail_options,
                                               self.thumbnail_processors)

        if high_resolution:
            thumbnail_options['size'] = orig_size  # restore original size

        filename = self.get_thumbnail_name(
            thumbnail_options,
            transparent=utils.is_transparent(thumbnail_image),
            high_resolution=high_resolution)
        quality = thumbnail_options['quality']
        subsampling = thumbnail_options['subsampling']

        if IS_GIF:
            try:
                # try save with speedup
                img = images2gif.writeGif(frames, **gif_params)
                saved = True
            except:
                # if not saved, simple save
                gif_params.update({'subRectangles': False,})
                img = images2gif.writeGif(frames, **gif_params)
        else:
            img = engine.save_image(
                thumbnail_image, filename=filename, quality=quality,
                subsampling=subsampling)
        data = img.read()

        thumbnail = ThumbnailFile(
            filename, file=ContentFile(data), storage=self.thumbnail_storage,
            thumbnail_options=thumbnail_options)
        thumbnail.image = thumbnail_image
        thumbnail._committed = False

        return thumbnail