thumbs.py
173 lines
| 5.6 KiB
| text/x-python
|
PythonLexer
/ boards / thumbs.py
neko259
|
r22 | # -*- 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 | ||||
neko259
|
r113 | |||
neko259
|
r22 | 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) | ||||
""" | ||||
neko259
|
r113 | |||
neko259
|
r22 | img.seek(0) # see http://code.djangoproject.com/ticket/8222 for details | ||
image = Image.open(img) | ||||
neko259
|
r113 | |||
neko259
|
r22 | # 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 | ||||
neko259
|
r113 | minsize = min(xsize, ysize) | ||
neko259
|
r22 | # largest square possible in the image | ||
neko259
|
r113 | xnewsize = (xsize - minsize) / 2 | ||
ynewsize = (ysize - minsize) / 2 | ||||
neko259
|
r22 | # crop it | ||
neko259
|
r113 | image2 = image.crop( | ||
(xnewsize, ynewsize, xsize - xnewsize, ysize - ynewsize)) | ||||
neko259
|
r22 | # 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) | ||||
neko259
|
r113 | |||
neko259
|
r22 | io = cStringIO.StringIO() | ||
# PNG and GIF are the same, JPG is JPEG | ||||
neko259
|
r113 | if format.upper() == 'JPG': | ||
neko259
|
r22 | format = 'JPEG' | ||
neko259
|
r113 | |||
neko259
|
r22 | image2.save(io, format) | ||
neko259
|
r113 | return ContentFile(io.getvalue()) | ||
neko259
|
r22 | |||
class ImageWithThumbsFieldFile(ImageFieldFile): | ||||
""" | ||||
See ImageWithThumbsField for usage example | ||||
""" | ||||
neko259
|
r113 | |||
neko259
|
r22 | def __init__(self, *args, **kwargs): | ||
super(ImageWithThumbsFieldFile, self).__init__(*args, **kwargs) | ||||
self.sizes = self.field.sizes | ||||
neko259
|
r113 | |||
neko259
|
r22 | if self.sizes: | ||
def get_size(self, size): | ||||
if not self: | ||||
return '' | ||||
else: | ||||
neko259
|
r113 | split = self.url.rsplit('.', 1) | ||
thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1]) | ||||
neko259
|
r22 | return thumb_url | ||
neko259
|
r113 | |||
neko259
|
r22 | for size in self.sizes: | ||
neko259
|
r113 | (w, h) = size | ||
setattr(self, 'url_%sx%s' % (w, h), get_size(self, size)) | ||||
neko259
|
r22 | def save(self, name, content, save=True): | ||
super(ImageWithThumbsFieldFile, self).save(name, content, save) | ||||
neko259
|
r113 | |||
neko259
|
r22 | if self.sizes: | ||
for size in self.sizes: | ||||
neko259
|
r113 | (w, h) = size | ||
split = self.name.rsplit('.', 1) | ||||
thumb_name = '%s.%sx%s.%s' % (split[0], w, h, split[1]) | ||||
neko259
|
r22 | # you can use another thumbnailing function if you like | ||
thumb_content = generate_thumb(content, size, split[1]) | ||||
neko259
|
r113 | |||
thumb_name_ = self.storage.save(thumb_name, thumb_content) | ||||
neko259
|
r22 | if not thumb_name == thumb_name_: | ||
neko259
|
r113 | raise ValueError( | ||
'There is already a file named %s' % thumb_name) | ||||
neko259
|
r22 | def delete(self, save=True): | ||
neko259
|
r113 | name = self.name | ||
neko259
|
r22 | super(ImageWithThumbsFieldFile, self).delete(save) | ||
if self.sizes: | ||||
for size in self.sizes: | ||||
neko259
|
r113 | (w, h) = size | ||
split = name.rsplit('.', 1) | ||||
thumb_name = '%s.%sx%s.%s' % (split[0], w, h, split[1]) | ||||
neko259
|
r22 | try: | ||
self.storage.delete(thumb_name) | ||||
except: | ||||
pass | ||||
neko259
|
r113 | |||
neko259
|
r22 | 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 | ||||
neko259
|
r114 | |||
neko259
|
r22 | |||
""" | ||||
neko259
|
r113 | |||
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 | ||||
neko259
|
r22 | self.sizes = sizes | ||
neko259
|
r114 | super(ImageField, self).__init__(**kwargs) | ||
from south.modelsinspector import add_introspection_rules | ||||
neko259
|
r137 | add_introspection_rules([], ["^boards\.thumbs\.ImageWithThumbsField"]) | ||