root/verdjnlib/fields/photofield.py

Revision 20, (checked in by bryan, 8 months ago)

verdjnlib: Updated PhotoField to work with recent Django updates.

Line 
1 import os
2 import cStringIO
3 from PIL import Image as PILImage
4 from django.db import models
5 from django.dispatch import dispatcher
6
7 # Commented out -- No longer changing filenames
8 #from django.db.models import signals
9 #from django.conf import settings
10
11 # PhotoField is compatible with ImageField from either verdjnlib or Django
12 # One of the following two lines should be commented out
13 #from verdjnlib.fields import ImageField
14 from django.db.models import ImageField
15
16
17 FIT = 0
18 CROP = 1
19 DEFAULT_MODE = FIT
20 DEFAULT_WIDTH = 640
21 DEFAULT_HEIGHT = 480
22
23
24 class PhotoField(ImageField):
25
26     def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None,
27         width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT, mode=DEFAULT_MODE, quality=None, copy_fields=[],
28         **kwargs):
29         self.width_field, self.height_field = width_field, height_field
30         super(PhotoField, self).__init__(verbose_name, name, width_field, height_field, **kwargs)
31         self.width, self.height, self.mode, self.quality = width, height, mode, quality
32         self.copy_fields = copy_fields
33         # Commented out -- No longer changing filenames
34         #self.parent_pk = parent_pk or "UNDEFINED"
35
36     def get_internal_type(self):
37         return 'ImageField'
38
39     # Commented out -- No longer changing filenames
40     #def _update_parent_pk(self, instance=None):
41     #    #print "_update_parent_pk invoked"
42     #    self.parent_pk = instance._get_pk_val()
43
44     def contribute_to_class(self, cls, name):
45         super(PhotoField, self).contribute_to_class(cls, name)
46         # Add get_FIELD_content_type method
47         setattr(cls, 'get_%s_content_type' % self.name, lambda instance: "image/jpeg")
48         # Commented out -- No longer changing filenames
49         #dispatcher.connect(self._update_parent_pk, signals.post_save, sender=cls)
50
51     def get_content_type(self):
52         return "image/jpeg"
53
54     def save_file(self, new_data, new_object, original_object, change, rel, save=True):
55         field_names = self.get_manipulator_field_names('')
56         upload_field_name = field_names[0]
57         # Commented out -- No longer changing filenames
58         #filename = '%s-%s.jpg' % (self.parent_pk, self.get_attname())
59
60         # If there is no DeleteCheckbox or the DeleteCheckbox was not checked
61         if len(field_names) < 3 or not new_data.get(field_names[2], False):
62             if new_data.get(upload_field_name, False):
63                 if rel:
64                     # Commented out -- No longer changing filenames
65                     #new_data[upload_field_name][0]["filename"] = filename
66                     new_data[upload_field_name][0]["content"] = PhotoField.resize(new_data[upload_field_name][0]["content"], self.width, self.height, self.mode, self.quality)
67                 else:
68                     # Commented out -- No longer changing filenames
69                     #new_data[upload_field_name]["filename"] = filename
70                     new_data[upload_field_name]["content"] = PhotoField.resize(new_data[upload_field_name]["content"], self.width, self.height, self.mode, self.quality)
71                 # Commented out -- No longer changing filenames
72                 ## If file exists, delete it
73                 #filepath = os.path.join(settings.MEDIA_ROOT, self.upload_to, filename)
74                 #if os.path.exists(filepath):
75                 #    os.remove(filepath)
76         super(PhotoField, self).save_file(new_data, new_object, original_object, change, rel, save)
77
78     def resize(cls, data, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT, mode=DEFAULT_MODE, quality=None):
79         """Resize an image given as the string data to fit the bounding box
80            (width, height). Mode may either be FIT, in which case the image is
81            shrunk to fit completely within the bounding box (the new image may
82            be smaller than the bounding box), or CROP, in which case the image
83            is cropped to the aspect ratio of the bounding box, and then resized
84            to fit (the new image will be the same size as the bounding box)."""
85         data_file = cStringIO.StringIO(data)
86         pil_obj = PILImage.open(data_file)
87         if pil_obj.mode != 'RGB':
88             pil_obj = pil_obj.convert('RGB')
89
90         if mode == FIT:
91             # Reduce the image's size by using the PIL's thumbnail method, which
92             # preserves the aspect ratio of the original image, but does not
93             # guarantee that the image will be (scale_width x scale_height) in
94             # size. (It will extend to at least one of those dimensions, however.)
95             pil_obj.thumbnail((int(width), int(height)), PILImage.ANTIALIAS)
96
97         elif mode == CROP:
98             x, y = pil_obj.size
99
100             # If the main image is larger in both dimensions than the desired
101             # thumbnail (this is the normal situation,) crop the image to the
102             # aspect ratio of the thumbnail, centred on the main image, and
103             # reduce the cropped image to thumbnail-size.
104             if x >= width and y >= height:
105                 aspect = float(width) / float(height)
106                 crop_width = min(int(aspect*y), x)
107                 crop_height = min(int(float(x)/aspect), y)
108                 crop_point_x = (x-crop_width)/2
109                 crop_point_y = (y-crop_height)/2
110                 pil_obj = pil_obj.crop((crop_point_x, crop_point_y, crop_point_x+crop_width, crop_point_y+crop_height))
111                 pil_obj.thumbnail((width, height), PILImage.ANTIALIAS)
112
113             # Otherwise, if either the width or height of the main image is
114             # less than the thumbnail size, crop the image to the largest
115             # rectangle which fits inside of the thumbnail size. (This will
116             # be the image's complete extent in at least one dimension.) In
117             # this case, we do not reduce the size of the cropped image.
118             else:
119                 crop_width = min(width, x)
120                 crop_height = min(height, y)
121                 crop_point_x = (x-crop_width)/2
122                 crop_point_y = (y-crop_height)/2
123                 pil_obj = pil_obj.crop((crop_point_x, crop_point_y, crop_point_x+crop_width, crop_point_y+crop_height))
124
125         out_file = cStringIO.StringIO()
126         if quality:
127             pil_obj.save(out_file, 'JPEG', quality=quality)
128         else:
129             pil_obj.save(out_file, 'JPEG')
130         out_file.reset()
131         return out_file.read()
132
133     resize=classmethod(resize)
134
Note: See TracBrowser for help on using the browser.