Commits

George Notaras committed 96fe56b

Added initial code (not tested - does not work)

Comments (0)

Files changed (5)

src/thumbnail_works/exceptions.py

+
+
+class ImageSizeError(Exception):
+    pass

src/thumbnail_works/fields.py

+# -*- coding: utf-8 -*-
+#
+#  This file is part of django-thumbnail-works.
+#
+#  django-thumbnail-works provides an enhanced ImageField that generates and
+#  manages thumbnails of the uploaded image.
+#
+#  Development Web Site:
+#    - http://www.codetrax.org/projects/django-thumbnail-works
+#  Public Source Code Repository:
+#    - https://source.codetrax.org/hgroot/django-thumbnail-works
+#
+#  Copyright 2010 George Notaras <gnot [at] g-loaded.eu>
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+
+import os
+
+from django.db.models.fields.files import ImageField, ImageFieldFile
+
+from thumbnail_works.exceptions import ImageSizeError
+from thumbnail_works.utils import get_width_height_from_string
+
+
+
+class Thumbnail:
+    
+    def __init__(self, name, size, source):
+        self.name = self._get_name(name)
+        self.width, self.height = get_width_height_from_string(size)
+        self.url = self._get_url(source.url)
+    
+    def _get_name(self, name):
+        return name.replace(' ', '_')
+    
+    def _get_url(self, source_url):
+        """
+        source: /media/images/photo.jpg
+        thumbnail: /media/images/photo.<thumbname>.jpg
+        """
+        root_dir = os.path.dirname(source_url)  # /media/images
+        filename = os.path.basename(source_url)
+        base_filename, ext = os.path.splitext(filename)
+        return os.path.join(root_dir, '%s.%s.%s' % (base_filename, self.name, ext))
+    
+        
+        
+class EnhancedImageFieldFile(ImageFieldFile):
+    
+    def __init__(self, *args, **kwargs):
+        super(EnhancedImageFieldFile, self).__init__(*args, **kwargs)
+        
+        # Set thumbnail objects as instance attributes
+        if self.field.thumbnails:
+            for thumbnail_name, thumbnail_size in self.field.thumbnails.items():
+                thumbnail_obj = Thumbnail(thumbnail_name, thumbnail_size, self)
+                setattr(self, thumbnail_name, thumbnail_obj)
+    
+    def save(self, name, content, save=True):
+        
+        # Before saving, resize the source image if a size has been set
+        width, height = get_width_height_from_string(self.field.resize_source)
+        
+        super(EnhancedImageFieldFile, self).save(name, content, save)
+        
+        name = self.field.generate_filename(self.instance, name)
+        self.name = self.storage.save(name, content)
+        setattr(self.instance, self.field.name, self.name)
+
+        # Update the filesize cache
+        self._size = len(content)
+        self._committed = True
+
+        # Save the object because it has changed, unless save is False
+        if save:
+            self.instance.save()
+        
+
+
+class EnhancedImageField(ImageField):
+    attr_class = EnhancedImageFieldFile
+    
+    def __init__(self, resize_source=None, thumbnails={}, **kwargs):
+        """
+        resize_source: image size in WIDTHxHEIGHT. If set, the uploaded image
+        will be resized to this size.
+        
+        Thumbnails format:
+        
+            <thumbnail_name> : <size>
+        
+        """
+        self.resize_source = resize_source
+        self.thumbnails = thumbnails
+        super(EnhancedImageField, self).__init__(**kwargs)
+

src/thumbnail_works/image_processors.py

+
+import StringIO
+from PIL import Image
+
+from django.core.files.base import ContentFile
+from django.core.files import File
+
+from thumbnail_works.utils import get_width_height_from_string
+from thumbnail_works import settings
+
+
+def resize(imagefile, size):
+    
+    # PIL_Image.open() accepts a file-like object, but it is needed
+    # to rewind it back to be able to get the data
+    imagefile.seek(0)
+    im = PIL_Image.open(imagefile)
+    
+    # Requested dimensions
+    width_req, height_req = get_width_height_from_string(size)
+    # Source image dimensions
+    width_source, height_source = im.size
+    
+    # Determine orientation
+    landscape_orientation = True
+    if width_source < height_source:
+        landscape_orientation = False
+    
+    # Convert to RGB if necessary
+    if im.mode not in ('L', 'RGB', 'RGBA'):
+        im = im.convert('RGB')
+    
+    """
+    The resize happens only if any of the following conditions is met:
+    
+        - the original width is greater than the requested width
+        - the original height is greater than the requested height
+    
+    """
+    
+    # Determine if resize is needed. (Also creates the temporary resized file)
+    if width_source > width_req or height_source > height_req:
+        # Do resize
+        # the thumbnail() method is used for resizing as it maintains the original
+        # image's aspect ratio (resize() does not).
+        im.thumbnail((width_req, height_req), Image.ANTIALIAS)
+        
+    
+    io = StringIO.StringIO()
+    
+    if settings.THUMBNAILS_FORMAT == 'JPEG':
+        im.save(io, 'JPEG', quality=settings.THUMBNAILS_QUALITY)
+    elif settings.THUMBNAILS_FORMAT == 'PNG':
+        im.save(io, 'PNG')
+    
+    #return ContentFile(io.getvalue())
+    return File(io)
+  

src/thumbnail_works/settings.py

 from django.conf import settings
 
 
-MY_APP_SETTING = getattr(settings, 'MY_APP_SETTING', '...')
+# JPEG, PNG
+THUMBNAILS_FORMAT = getattr(settings, 'DTW_THUMBNAILS_FORMAT', 'JPEG')
 
+# For JPEG format only
+THUMBNAILS_QUALITY = getattr(settings, 'DTW_THUMBNAILS_QUALITY', 85)
+
+
+# This is the name of the directory where the thumbnails will be stored
+THUMBNAILS_DIRNAME = getattr(settings, 'DTW_THUMBNAILS_DIRNAME', '')
+

src/thumbnail_works/utils.py

 #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
-#
+#
+
+from thumbnail_works.exceptions import ImageSizeError
+
+
+def get_width_height_from_string(self, size):
+    try:
+        bits = size.split('x', 1)
+    except AttributeError:
+        raise ImageSizeError('size must be a string of the form WIDTHxHEIGHT')
+    if len(bits) != 2:
+        raise ImageSizeError('size must be a string of the form WIDTHxHEIGHT')
+    try:
+        size_x = int(bits[0])
+        size_y = int(bits[1])
+    except ValueError:
+        raise ImageSizeError('size\'s WIDTH and HEIGHT must be integers')
+    return size_x, size_y
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.