thumbs.py
172 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
|
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
|
r113 | super(ImageField, self).__init__(**kwargs) | |
from south.modelsinspector import add_introspection_rules | |||
add_introspection_rules([], ["^boards\.thumbs\.ImageWithThumbsField"]) |