forked from nsi-iff/nsi_site
-
Notifications
You must be signed in to change notification settings - Fork 0
/
thumbs.py
181 lines (150 loc) · 5.97 KB
/
thumbs.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# -*- encoding: utf-8 -*-
"""
django-thumbs by Antonio Melé
http://django.es
"""
from django.db.models import ImageField
from django.db.models.fields.files import ImageFieldFile
from PIL import Image
from django.core.files.base import ContentFile
import cStringIO
from south.modelsinspector import add_introspection_rules
def generate_thumb(img, thumb_size, format):
"""
Generates a thumbnail image and returns a ContentFile object with the thumbnail
Parameters:
===========
img File object
thumb_size desired thumbnail size, ie: (200,120)
format format of the original image ('jpeg','gif','png',...)
(this format will be used for the generated thumbnail, too)
"""
img.seek(0) # see http://code.djangoproject.com/ticket/8222 for details
image = Image.open(img)
# Convert to RGB if necessary
if image.mode not in ('L', 'RGB', 'RGBA'):
image = image.convert('RGB')
# get size
thumb_w, thumb_h = thumb_size
# If you want to generate a square thumbnail
if thumb_w == thumb_h:
# quad
xsize, ysize = image.size
# get minimum size
minsize = min(xsize,ysize)
# largest square possible in the image
xnewsize = (xsize-minsize)/2
ynewsize = (ysize-minsize)/2
# crop it
image2 = image.crop((xnewsize, ynewsize, xsize-xnewsize, ysize-ynewsize))
# load is necessary after crop
image2.load()
# thumbnail of the cropped image (with ANTIALIAS to make it look better)
image2.thumbnail(thumb_size, Image.ANTIALIAS)
else:
# not quad
image2 = image
image2.thumbnail(thumb_size, Image.ANTIALIAS)
io = cStringIO.StringIO()
# PNG and GIF are the same, JPG is JPEG
if format.upper()=='JPG':
format = 'JPEG'
image2.save(io, format)
return ContentFile(io.getvalue())
class ImageWithThumbsFieldFile(ImageFieldFile):
"""
See ImageWithThumbsField for usage example
"""
def __init__(self, *args, **kwargs):
super(ImageWithThumbsFieldFile, self).__init__(*args, **kwargs)
if self.field.sizes:
def get_size(self, size):
if not self:
return ''
else:
split = self.url.rsplit('.',1)
thumb_url = '%s.%sx%s.%s' % (split[0],w,h,split[1])
return thumb_url
for size in self.field.sizes:
(w,h) = size
setattr(self, 'url_%sx%s' % (w,h), get_size(self, size))
def save(self, name, content, save=True):
super(ImageWithThumbsFieldFile, self).save(name, content, save)
if self.field.sizes:
for size in self.field.sizes:
(w,h) = size
split = self.name.rsplit('.',1)
thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
# you can use another thumbnailing function if you like
thumb_content = generate_thumb(content, size, split[1])
thumb_name_ = self.storage.save(thumb_name, thumb_content)
if not thumb_name == thumb_name_:
raise ValueError('There is already a file named %s' % thumb_name)
def delete(self, save=True):
name=self.name
super(ImageWithThumbsFieldFile, self).delete(save)
if self.field.sizes:
for size in self.field.sizes:
(w,h) = size
split = name.rsplit('.',1)
thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
try:
self.storage.delete(thumb_name)
except:
pass
class ImageWithThumbsField(ImageField):
attr_class = ImageWithThumbsFieldFile
"""
Usage example:
==============
photo = ImageWithThumbsField(upload_to='images', sizes=((125,125),(300,200),)
To retrieve image URL, exactly the same way as with ImageField:
my_object.photo.url
To retrieve thumbnails URL's just add the size to it:
my_object.photo.url_125x125
my_object.photo.url_300x200
Note: The 'sizes' attribute is not required. If you don't provide it,
ImageWithThumbsField will act as a normal ImageField
How it works:
=============
For each size in the 'sizes' atribute of the field it generates a
thumbnail with that size and stores it following this format:
available_filename.[width]x[height].extension
Where 'available_filename' is the available filename returned by the storage
backend for saving the original file.
Following the usage example above: For storing a file called "photo.jpg" it saves:
photo.jpg (original file)
photo.125x125.jpg (first thumbnail)
photo.300x200.jpg (second thumbnail)
With the default storage backend if photo.jpg already exists it will use these filenames:
photo_.jpg
photo_.125x125.jpg
photo_.300x200.jpg
Note: django-thumbs assumes that if filename "any_filename.jpg" is available
filenames with this format "any_filename.[widht]x[height].jpg" will be available, too.
To do:
======
Add method to regenerate thubmnails
"""
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, sizes=None, **kwargs):
self.verbose_name=verbose_name
self.name=name
self.width_field=width_field
self.height_field=height_field
self.sizes = sizes
super(ImageField, self).__init__(**kwargs)
add_introspection_rules([
(
(ImageWithThumbsField, ),
[],
{
"verbose_name": ["verbose_name", {"default": None}],
"name": ["name", {"default": None}],
"width_field": ["width_field", {"default": None}],
"height_field": ["height_field", {"default": None}],
"sizes": ["sizes", {"default": None}],
},
),
],
["^thumbs.ImageWithThumbsField",]
)