##// END OF EJS Templates
Raise an error whena decompression bomb is launched to the thumbs generator
neko259 -
r1819:1bcad2dd default
parent child Browse files
Show More
@@ -1,215 +1,217 b''
1 1 # -*- encoding: utf-8 -*-
2 2 """
3 3 django-thumbs by Antonio Melé
4 4 http://django.es
5 5 """
6 6 from django.core.files.images import ImageFile
7 7 from django.db.models import ImageField
8 8 from django.db.models.fields.files import ImageFieldFile
9 9 from PIL import Image
10 10 from django.core.files.base import ContentFile
11 11 import io
12 12
13 13
14 14 def generate_thumb(img, thumb_size, format):
15 15 """
16 16 Generates a thumbnail image and returns a ContentFile object with the thumbnail
17 17
18 18 Parameters:
19 19 ===========
20 20 img File object
21 21
22 22 thumb_size desired thumbnail size, ie: (200,120)
23 23
24 24 format format of the original image ('jpeg','gif','png',...)
25 25 (this format will be used for the generated thumbnail, too)
26 26 """
27 27
28 Image.warnings.simplefilter('error', Image.DecompressionBombWarning)
29
28 30 img.seek(0) # see http://code.djangoproject.com/ticket/8222 for details
29 31 image = Image.open(img)
30 32
31 33 # get size
32 34 thumb_w, thumb_h = thumb_size
33 35 # If you want to generate a square thumbnail
34 36 if thumb_w == thumb_h:
35 37 # quad
36 38 xsize, ysize = image.size
37 39 # get minimum size
38 40 minsize = min(xsize, ysize)
39 41 # largest square possible in the image
40 42 xnewsize = (xsize - minsize) / 2
41 43 ynewsize = (ysize - minsize) / 2
42 44 # crop it
43 45 image2 = image.crop(
44 46 (xnewsize, ynewsize, xsize - xnewsize, ysize - ynewsize))
45 47 # load is necessary after crop
46 48 image2.load()
47 49 # thumbnail of the cropped image (with ANTIALIAS to make it look better)
48 50 image2.thumbnail(thumb_size, Image.ANTIALIAS)
49 51 else:
50 52 # not quad
51 53 image2 = image
52 54 image2.thumbnail(thumb_size, Image.ANTIALIAS)
53 55
54 56 output = io.BytesIO()
55 57 # PNG and GIF are the same, JPG is JPEG
56 58 if format.upper() == 'JPG':
57 59 format = 'JPEG'
58 60
59 61 image2.save(output, format)
60 62 return ContentFile(output.getvalue())
61 63
62 64
63 65 class ImageWithThumbsFieldFile(ImageFieldFile):
64 66 """
65 67 See ImageWithThumbsField for usage example
66 68 """
67 69
68 70 def __init__(self, *args, **kwargs):
69 71 super(ImageWithThumbsFieldFile, self).__init__(*args, **kwargs)
70 72 self.sizes = self.field.sizes
71 73
72 74 if self.sizes:
73 75 def get_size(self, size):
74 76 if not self:
75 77 return ''
76 78 else:
77 79 split = self.url.rsplit('.', 1)
78 80 thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
79 81 return thumb_url
80 82
81 83 for size in self.sizes:
82 84 (w, h) = size
83 85 setattr(self, 'url_%sx%s' % (w, h), get_size(self, size))
84 86
85 87 def save(self, name, content, save=True):
86 88 super(ImageWithThumbsFieldFile, self).save(name, content, save)
87 89
88 90 if self.sizes:
89 91 for size in self.sizes:
90 92 (w, h) = size
91 93 split = self.name.rsplit('.', 1)
92 94 thumb_name = '%s.%sx%s.%s' % (split[0], w, h, split[1])
93 95
94 96 # you can use another thumbnailing function if you like
95 97 thumb_content = generate_thumb(content, size, split[1])
96 98
97 99 thumb_name_ = self.storage.save(thumb_name, thumb_content)
98 100
99 101 if not thumb_name == thumb_name_:
100 102 raise ValueError(
101 103 'There is already a file named %s' % thumb_name)
102 104
103 105 def delete(self, save=True):
104 106 name = self.name
105 107 super(ImageWithThumbsFieldFile, self).delete(save)
106 108 if self.sizes:
107 109 for size in self.sizes:
108 110 (w, h) = size
109 111 split = name.rsplit('.', 1)
110 112 thumb_name = '%s.%sx%s.%s' % (split[0], w, h, split[1])
111 113 try:
112 114 self.storage.delete(thumb_name)
113 115 except:
114 116 pass
115 117
116 118
117 119 class ImageWithThumbsField(ImageField):
118 120 attr_class = ImageWithThumbsFieldFile
119 121 """
120 122 Usage example:
121 123 ==============
122 124 photo = ImageWithThumbsField(upload_to='images', sizes=((125,125),(300,200),)
123 125
124 126 To retrieve image URL, exactly the same way as with ImageField:
125 127 my_object.photo.url
126 128 To retrieve thumbnails URL's just add the size to it:
127 129 my_object.photo.url_125x125
128 130 my_object.photo.url_300x200
129 131
130 132 Note: The 'sizes' attribute is not required. If you don't provide it,
131 133 ImageWithThumbsField will act as a normal ImageField
132 134
133 135 How it works:
134 136 =============
135 137 For each size in the 'sizes' atribute of the field it generates a
136 138 thumbnail with that size and stores it following this format:
137 139
138 140 available_filename.[width]x[height].extension
139 141
140 142 Where 'available_filename' is the available filename returned by the storage
141 143 backend for saving the original file.
142 144
143 145 Following the usage example above: For storing a file called "photo.jpg" it saves:
144 146 photo.jpg (original file)
145 147 photo.125x125.jpg (first thumbnail)
146 148 photo.300x200.jpg (second thumbnail)
147 149
148 150 With the default storage backend if photo.jpg already exists it will use these filenames:
149 151 photo_.jpg
150 152 photo_.125x125.jpg
151 153 photo_.300x200.jpg
152 154
153 155 Note: django-thumbs assumes that if filename "any_filename.jpg" is available
154 156 filenames with this format "any_filename.[widht]x[height].jpg" will be available, too.
155 157
156 158 To do:
157 159 ======
158 160 Add method to regenerate thubmnails
159 161
160 162
161 163 """
162 164
163 165 preview_width_field = None
164 166 preview_height_field = None
165 167
166 168 def __init__(self, verbose_name=None, name=None, width_field=None,
167 169 height_field=None, sizes=None,
168 170 preview_width_field=None, preview_height_field=None,
169 171 **kwargs):
170 172 self.verbose_name = verbose_name
171 173 self.name = name
172 174 self.width_field = width_field
173 175 self.height_field = height_field
174 176 self.sizes = sizes
175 177 super(ImageField, self).__init__(**kwargs)
176 178
177 179 if sizes is not None and len(sizes) == 1:
178 180 self.preview_width_field = preview_width_field
179 181 self.preview_height_field = preview_height_field
180 182
181 183 def update_dimension_fields(self, instance, force=False, *args, **kwargs):
182 184 """
183 185 Update original image dimension fields and thumb dimension fields
184 186 (only if 1 thumb size is defined)
185 187 """
186 188
187 189 super(ImageWithThumbsField, self).update_dimension_fields(instance,
188 190 force, *args,
189 191 **kwargs)
190 192 thumb_width_field = self.preview_width_field
191 193 thumb_height_field = self.preview_height_field
192 194
193 195 if thumb_width_field is None or thumb_height_field is None \
194 196 or len(self.sizes) != 1:
195 197 return
196 198
197 199 original_width = getattr(instance, self.width_field)
198 200 original_height = getattr(instance, self.height_field)
199 201
200 202 if original_width > 0 and original_height > 0:
201 203 thumb_width, thumb_height = self.sizes[0]
202 204
203 205 w_scale = float(thumb_width) / original_width
204 206 h_scale = float(thumb_height) / original_height
205 207 scale_ratio = min(w_scale, h_scale)
206 208
207 209 if scale_ratio >= 1:
208 210 thumb_width_ratio = original_width
209 211 thumb_height_ratio = original_height
210 212 else:
211 213 thumb_width_ratio = int(original_width * scale_ratio)
212 214 thumb_height_ratio = int(original_height * scale_ratio)
213 215
214 216 setattr(instance, thumb_width_field, thumb_width_ratio)
215 setattr(instance, thumb_height_field, thumb_height_ratio) No newline at end of file
217 setattr(instance, thumb_height_field, thumb_height_ratio)
General Comments 0
You need to be logged in to leave comments. Login now