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