-
Notifications
You must be signed in to change notification settings - Fork 318
/
engine.py
150 lines (132 loc) · 5.07 KB
/
engine.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import os
from io import BytesIO, StringIO
from django.utils.module_loading import import_string
from PIL import Image
from easy_thumbnails.conf import settings
from easy_thumbnails.options import ThumbnailOptions
class NoSourceGenerator(Exception):
"""
Exception that is raised if no source generator can process the source
file.
"""
def __str__(self):
return "Tried {0} source generators with no success".format(
len(self.args))
def process_image(source, processor_options, processors=None):
"""
Process a source PIL image through a series of image processors, returning
the (potentially) altered image.
"""
processor_options = ThumbnailOptions(processor_options)
if processors is None:
processors = [
import_string(name)
for name in settings.THUMBNAIL_PROCESSORS]
image = source
for processor in processors:
image = processor(image, **processor_options)
return image
def save_pil_image(image, destination=None, filename=None, **options):
"""
Save a PIL image.
"""
if destination is None:
destination = BytesIO()
filename = filename or ''
# Ensure plugins are fully loaded so that Image.EXTENSION is populated.
Image.init()
format = Image.EXTENSION.get(os.path.splitext(filename)[1].lower(), 'JPEG')
if format in settings.THUMBNAIL_IMAGE_SAVE_OPTIONS:
for key, value in settings.THUMBNAIL_IMAGE_SAVE_OPTIONS[format].items():
options.setdefault(key, value)
saved = False
if format == 'JPEG':
if image.mode.endswith('A'):
# From PIL 4.2, saving an image with a transparency layer raises an
# IOError, so explicitly remove it.
image = image.convert(image.mode[:-1])
if settings.THUMBNAIL_PROGRESSIVE and (
max(image.size) >= settings.THUMBNAIL_PROGRESSIVE):
options['progressive'] = True
try:
if options.pop('keep_icc_profile', False):
options['icc_profile'] = image.info.get('icc_profile')
image.save(destination, format=format, optimize=1, **options)
saved = True
except IOError:
# Try again, without optimization (PIL can't optimize an image
# larger than ImageFile.MAXBLOCK, which is 64k by default). This
# shouldn't be triggered very often these days, as recent versions
# of pillow avoid the MAXBLOCK limitation.
pass
else:
if format != 'WEBP' and 'quality' in options:
options.pop('quality')
if not saved:
image.save(destination, format=format, **options)
if hasattr(destination, 'seek'):
destination.seek(0)
return destination
def generate_source_image(source_file, processor_options, generators=None,
fail_silently=True):
"""
Processes a source ``File`` through a series of source generators, stopping
once a generator returns an image.
The return value is this image instance or ``None`` if no generators
return an image.
If the source file cannot be opened, it will be set to ``None`` and still
passed to the generators.
"""
processor_options = ThumbnailOptions(processor_options)
# Keep record of whether the source file was originally closed. Not all
# file-like objects provide this attribute, so just fall back to False.
was_closed = getattr(source_file, 'closed', False)
if generators is None:
generators = [
import_string(name)
for name in settings.THUMBNAIL_SOURCE_GENERATORS]
exceptions = []
try:
for generator in generators:
source = source_file
# First try to open the file.
try:
source.open()
except Exception:
# If that failed, maybe the file-like object doesn't support
# reopening so just try seeking back to the start of the file.
try:
source.seek(0)
except Exception:
source = None
try:
image = generator(source, **processor_options)
except Exception as e:
if not fail_silently:
if len(generators) == 1:
raise
exceptions.append(e)
image = None
if image:
return image
finally:
# Attempt to close the file if it was closed originally (but fail
# silently).
if was_closed:
try:
source_file.close()
except Exception:
pass
if exceptions and not fail_silently:
raise NoSourceGenerator(*exceptions)
def save_svg_image(image, destination=None, filename=None, **options):
"""
Save a SVG image.
"""
from easy_thumbnails.VIL import Image
if destination is None:
destination = StringIO()
image.save(destination, format='SVG', **options)
if hasattr(destination, 'seek'):
destination.seek(0)
return destination