Commit | Line | Data |
---|---|---|
626048e1 AD |
1 | # -*- encoding: utf-8 -*- |
2 | """ | |
3 | django-thumbs by Antonio Melé | |
4 | Modified by Arthur Darcet to support on demand resizing | |
5 | """ | |
6 | from django.db.models import ImageField | |
7 | from django.db.models.fields.files import ImageFieldFile | |
8 | from PIL import Image | |
9 | from django.core.files.base import ContentFile | |
10 | import cStringIO, re | |
11 | ||
12 | ||
13 | def generate_thumb(img, thumb_size, format): | |
14 | img.seek(0) # see http://code.djangoproject.com/ticket/8222 for details | |
15 | image = Image.open(img) | |
16 | if image.mode not in ('L', 'RGB'): | |
17 | image = image.convert('RGB') | |
18 | thumb_w, thumb_h = thumb_size | |
19 | if thumb_w == thumb_h and False: | |
20 | xsize, ysize = image.size | |
21 | minsize = min(xsize,ysize) | |
22 | xnewsize = (xsize-minsize)/2 | |
23 | ynewsize = (ysize-minsize)/2 | |
24 | image2 = image.crop((xnewsize, ynewsize, xsize-xnewsize, ysize-ynewsize)) | |
25 | image2.load() | |
26 | image2.thumbnail(thumb_size, Image.ANTIALIAS) | |
27 | else: | |
28 | image2 = image | |
29 | image2.thumbnail(thumb_size, Image.ANTIALIAS) | |
30 | io = cStringIO.StringIO() | |
31 | if format.upper()=='JPG': | |
32 | format = 'JPEG' | |
33 | image2.save(io, format) | |
34 | return ContentFile(io.getvalue()) | |
35 | ||
36 | ||
37 | class ImageWithThumbsFieldFile(ImageFieldFile): | |
38 | @staticmethod | |
39 | def _get_for_size(s, (w,h)): | |
40 | split = s.rsplit('.',1) | |
41 | return u'{}.{}x{}.{}'.format(split[0],w,h,split[1]) | |
42 | ||
43 | def __getattr__(self, attr): | |
44 | s = attr.split('url_') | |
45 | if len(s) != 2 or 'x' not in s[1]: | |
46 | raise AttributeError | |
47 | if not self.name: | |
48 | raise AttributeError('The picture has no file associated with it.') | |
49 | size = map(int, s[1].split('x',1)) | |
50 | name = ImageWithThumbsFieldFile._get_for_size(self.name, size) | |
51 | url = ImageWithThumbsFieldFile._get_for_size(self.url, size) | |
52 | if not self.storage.exists(name): | |
53 | thumb_content = generate_thumb(self, size, name.rsplit('.',1)[1]) | |
54 | self.storage.save(name, thumb_content) | |
55 | return url | |
56 | ||
57 | def delete(self, save=True): | |
58 | directory, name = self.name.rsplit('/', 1) | |
59 | _, files = self.storage.listdir(directory) | |
60 | reg = re.compile(r'{}.[0-9]+x[0-9]+.{}'.format(*name.rsplit('.',1))) | |
61 | for f in files: | |
62 | if reg.match(f): | |
63 | self.storage.delete(u'{}/{}'.format(directory, f)) | |
64 | super(ImageWithThumbsFieldFile, self).delete(save) | |
65 | ||
66 | ||
67 | class ImageWithThumbsField(ImageField): | |
68 | attr_class = ImageWithThumbsFieldFile | |
69 | """ | |
70 | Usage example: | |
71 | ============== | |
72 | photo = ImageWithThumbsField(upload_to='images', sizes=((125,125),(300,200),) | |
73 | ||
74 | To retrieve image URL, exactly the same way as with ImageField: | |
75 | my_object.photo.url | |
76 | To retrieve thumbnails URL's just add the size to it: | |
77 | my_object.photo.url_125x125 | |
78 | my_object.photo.url_300x200 | |
79 | ||
80 | Note: The 'sizes' attribute is not required. If you don't provide it, | |
81 | ImageWithThumbsField will act as a normal ImageField | |
82 | ||
83 | How it works: | |
84 | ============= | |
85 | For each size in the 'sizes' atribute of the field it generates a | |
86 | thumbnail with that size and stores it following this format: | |
87 | ||
88 | available_filename.[width]x[height].extension | |
89 | ||
90 | Where 'available_filename' is the available filename returned by the storage | |
91 | backend for saving the original file. | |
92 | ||
93 | Following the usage example above: For storing a file called "photo.jpg" it saves: | |
94 | photo.jpg (original file) | |
95 | photo.125x125.jpg (first thumbnail) | |
96 | photo.300x200.jpg (second thumbnail) | |
97 | ||
98 | With the default storage backend if photo.jpg already exists it will use these filenames: | |
99 | photo_.jpg | |
100 | photo_.125x125.jpg | |
101 | photo_.300x200.jpg | |
102 | ||
103 | Note: django-thumbs assumes that if filename "any_filename.jpg" is available | |
104 | filenames with this format "any_filename.[widht]x[height].jpg" will be available, too. | |
105 | ||
106 | To do: | |
107 | ====== | |
108 | Add method to regenerate thubmnails | |
109 | ||
110 | """ | |
111 | def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs): | |
112 | self.verbose_name=verbose_name | |
113 | self.name=name | |
114 | self.width_field=width_field | |
115 | self.height_field=height_field | |
116 | super(ImageField, self).__init__(**kwargs) |