##// END OF EJS Templates
fix issue #3
chedge -
r92:e0b54648 default
parent child Browse files
Show More
@@ -1,164 +1,160 b''
1 # -*- encoding: utf-8 -*-
1 # -*- encoding: utf-8 -*-
2 """
2 """
3 django-thumbs by Antonio Melé
3 django-thumbs by Antonio Melé
4 http://django.es
4 http://django.es
5 """
5 """
6 from django.db.models import ImageField
6 from django.db.models import ImageField
7 from django.db.models.fields.files import ImageFieldFile
7 from django.db.models.fields.files import ImageFieldFile
8 from PIL import Image
8 from PIL import Image
9 from django.core.files.base import ContentFile
9 from django.core.files.base import ContentFile
10 import cStringIO
10 import cStringIO
11
11
12 def generate_thumb(img, thumb_size, format):
12 def generate_thumb(img, thumb_size, format):
13 """
13 """
14 Generates a thumbnail image and returns a ContentFile object with the thumbnail
14 Generates a thumbnail image and returns a ContentFile object with the thumbnail
15
15
16 Parameters:
16 Parameters:
17 ===========
17 ===========
18 img File object
18 img File object
19
19
20 thumb_size desired thumbnail size, ie: (200,120)
20 thumb_size desired thumbnail size, ie: (200,120)
21
21
22 format format of the original image ('jpeg','gif','png',...)
22 format format of the original image ('jpeg','gif','png',...)
23 (this format will be used for the generated thumbnail, too)
23 (this format will be used for the generated thumbnail, too)
24 """
24 """
25
25
26 img.seek(0) # see http://code.djangoproject.com/ticket/8222 for details
26 img.seek(0) # see http://code.djangoproject.com/ticket/8222 for details
27 image = Image.open(img)
27 image = Image.open(img)
28
28
29 # Convert to RGB if necessary
30 if image.mode not in ('L', 'RGB'):
31 image = image.convert('RGB')
32
33 # get size
29 # get size
34 thumb_w, thumb_h = thumb_size
30 thumb_w, thumb_h = thumb_size
35 # If you want to generate a square thumbnail
31 # If you want to generate a square thumbnail
36 if thumb_w == thumb_h:
32 if thumb_w == thumb_h:
37 # quad
33 # quad
38 xsize, ysize = image.size
34 xsize, ysize = image.size
39 # get minimum size
35 # get minimum size
40 minsize = min(xsize,ysize)
36 minsize = min(xsize,ysize)
41 # largest square possible in the image
37 # largest square possible in the image
42 xnewsize = (xsize-minsize)/2
38 xnewsize = (xsize-minsize)/2
43 ynewsize = (ysize-minsize)/2
39 ynewsize = (ysize-minsize)/2
44 # crop it
40 # crop it
45 image2 = image.crop((xnewsize, ynewsize, xsize-xnewsize, ysize-ynewsize))
41 image2 = image.crop((xnewsize, ynewsize, xsize-xnewsize, ysize-ynewsize))
46 # load is necessary after crop
42 # load is necessary after crop
47 image2.load()
43 image2.load()
48 # thumbnail of the cropped image (with ANTIALIAS to make it look better)
44 # thumbnail of the cropped image (with ANTIALIAS to make it look better)
49 image2.thumbnail(thumb_size, Image.ANTIALIAS)
45 image2.thumbnail(thumb_size, Image.ANTIALIAS)
50 else:
46 else:
51 # not quad
47 # not quad
52 image2 = image
48 image2 = image
53 image2.thumbnail(thumb_size, Image.ANTIALIAS)
49 image2.thumbnail(thumb_size, Image.ANTIALIAS)
54
50
55 io = cStringIO.StringIO()
51 io = cStringIO.StringIO()
56 # PNG and GIF are the same, JPG is JPEG
52 # PNG and GIF are the same, JPG is JPEG
57 if format.upper()=='JPG':
53 if format.upper()=='JPG':
58 format = 'JPEG'
54 format = 'JPEG'
59
55
60 image2.save(io, format)
56 image2.save(io, format)
61 return ContentFile(io.getvalue())
57 return ContentFile(io.getvalue())
62
58
63 class ImageWithThumbsFieldFile(ImageFieldFile):
59 class ImageWithThumbsFieldFile(ImageFieldFile):
64 """
60 """
65 See ImageWithThumbsField for usage example
61 See ImageWithThumbsField for usage example
66 """
62 """
67 def __init__(self, *args, **kwargs):
63 def __init__(self, *args, **kwargs):
68 super(ImageWithThumbsFieldFile, self).__init__(*args, **kwargs)
64 super(ImageWithThumbsFieldFile, self).__init__(*args, **kwargs)
69 self.sizes = self.field.sizes
65 self.sizes = self.field.sizes
70
66
71 if self.sizes:
67 if self.sizes:
72 def get_size(self, size):
68 def get_size(self, size):
73 if not self:
69 if not self:
74 return ''
70 return ''
75 else:
71 else:
76 split = self.url.rsplit('.',1)
72 split = self.url.rsplit('.',1)
77 thumb_url = '%s.%sx%s.%s' % (split[0],w,h,split[1])
73 thumb_url = '%s.%sx%s.%s' % (split[0],w,h,split[1])
78 return thumb_url
74 return thumb_url
79
75
80 for size in self.sizes:
76 for size in self.sizes:
81 (w,h) = size
77 (w,h) = size
82 setattr(self, 'url_%sx%s' % (w,h), get_size(self, size))
78 setattr(self, 'url_%sx%s' % (w,h), get_size(self, size))
83
79
84 def save(self, name, content, save=True):
80 def save(self, name, content, save=True):
85 super(ImageWithThumbsFieldFile, self).save(name, content, save)
81 super(ImageWithThumbsFieldFile, self).save(name, content, save)
86
82
87 if self.sizes:
83 if self.sizes:
88 for size in self.sizes:
84 for size in self.sizes:
89 (w,h) = size
85 (w,h) = size
90 split = self.name.rsplit('.',1)
86 split = self.name.rsplit('.',1)
91 thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
87 thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
92
88
93 # you can use another thumbnailing function if you like
89 # you can use another thumbnailing function if you like
94 thumb_content = generate_thumb(content, size, split[1])
90 thumb_content = generate_thumb(content, size, split[1])
95
91
96 thumb_name_ = self.storage.save(thumb_name, thumb_content)
92 thumb_name_ = self.storage.save(thumb_name, thumb_content)
97
93
98 if not thumb_name == thumb_name_:
94 if not thumb_name == thumb_name_:
99 raise ValueError('There is already a file named %s' % thumb_name)
95 raise ValueError('There is already a file named %s' % thumb_name)
100
96
101 def delete(self, save=True):
97 def delete(self, save=True):
102 name=self.name
98 name=self.name
103 super(ImageWithThumbsFieldFile, self).delete(save)
99 super(ImageWithThumbsFieldFile, self).delete(save)
104 if self.sizes:
100 if self.sizes:
105 for size in self.sizes:
101 for size in self.sizes:
106 (w,h) = size
102 (w,h) = size
107 split = name.rsplit('.',1)
103 split = name.rsplit('.',1)
108 thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
104 thumb_name = '%s.%sx%s.%s' % (split[0],w,h,split[1])
109 try:
105 try:
110 self.storage.delete(thumb_name)
106 self.storage.delete(thumb_name)
111 except:
107 except:
112 pass
108 pass
113
109
114 class ImageWithThumbsField(ImageField):
110 class ImageWithThumbsField(ImageField):
115 attr_class = ImageWithThumbsFieldFile
111 attr_class = ImageWithThumbsFieldFile
116 """
112 """
117 Usage example:
113 Usage example:
118 ==============
114 ==============
119 photo = ImageWithThumbsField(upload_to='images', sizes=((125,125),(300,200),)
115 photo = ImageWithThumbsField(upload_to='images', sizes=((125,125),(300,200),)
120
116
121 To retrieve image URL, exactly the same way as with ImageField:
117 To retrieve image URL, exactly the same way as with ImageField:
122 my_object.photo.url
118 my_object.photo.url
123 To retrieve thumbnails URL's just add the size to it:
119 To retrieve thumbnails URL's just add the size to it:
124 my_object.photo.url_125x125
120 my_object.photo.url_125x125
125 my_object.photo.url_300x200
121 my_object.photo.url_300x200
126
122
127 Note: The 'sizes' attribute is not required. If you don't provide it,
123 Note: The 'sizes' attribute is not required. If you don't provide it,
128 ImageWithThumbsField will act as a normal ImageField
124 ImageWithThumbsField will act as a normal ImageField
129
125
130 How it works:
126 How it works:
131 =============
127 =============
132 For each size in the 'sizes' atribute of the field it generates a
128 For each size in the 'sizes' atribute of the field it generates a
133 thumbnail with that size and stores it following this format:
129 thumbnail with that size and stores it following this format:
134
130
135 available_filename.[width]x[height].extension
131 available_filename.[width]x[height].extension
136
132
137 Where 'available_filename' is the available filename returned by the storage
133 Where 'available_filename' is the available filename returned by the storage
138 backend for saving the original file.
134 backend for saving the original file.
139
135
140 Following the usage example above: For storing a file called "photo.jpg" it saves:
136 Following the usage example above: For storing a file called "photo.jpg" it saves:
141 photo.jpg (original file)
137 photo.jpg (original file)
142 photo.125x125.jpg (first thumbnail)
138 photo.125x125.jpg (first thumbnail)
143 photo.300x200.jpg (second thumbnail)
139 photo.300x200.jpg (second thumbnail)
144
140
145 With the default storage backend if photo.jpg already exists it will use these filenames:
141 With the default storage backend if photo.jpg already exists it will use these filenames:
146 photo_.jpg
142 photo_.jpg
147 photo_.125x125.jpg
143 photo_.125x125.jpg
148 photo_.300x200.jpg
144 photo_.300x200.jpg
149
145
150 Note: django-thumbs assumes that if filename "any_filename.jpg" is available
146 Note: django-thumbs assumes that if filename "any_filename.jpg" is available
151 filenames with this format "any_filename.[widht]x[height].jpg" will be available, too.
147 filenames with this format "any_filename.[widht]x[height].jpg" will be available, too.
152
148
153 To do:
149 To do:
154 ======
150 ======
155 Add method to regenerate thubmnails
151 Add method to regenerate thubmnails
156
152
157 """
153 """
158 def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, sizes=None, **kwargs):
154 def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, sizes=None, **kwargs):
159 self.verbose_name=verbose_name
155 self.verbose_name=verbose_name
160 self.name=name
156 self.name=name
161 self.width_field=width_field
157 self.width_field=width_field
162 self.height_field=height_field
158 self.height_field=height_field
163 self.sizes = sizes
159 self.sizes = sizes
164 super(ImageField, self).__init__(**kwargs) No newline at end of file
160 super(ImageField, self).__init__(**kwargs)
General Comments 0
You need to be logged in to leave comments. Login now