Merge branch 'release/1.0.2'

* release/1.0.2:
  Fixing some reST syntax.
  Bumping version numbers.
  Adding a changelog.
  Basic tests for extension_to_format and format_to_extension. References #45.
  Fix in `format_to_extension` mapping
  On-demand loading of PIL plugins
  Fix conversion of PNG "palette" or "P" mode images to JPEG. "P" mode images need to be converted to 'RGB' if target image format is not PNG or GIF.
  Back to dev.
This commit is contained in:
Bryan Veloso 2011-11-03 14:19:20 +09:00
commit 3fd2450e8d
7 changed files with 154 additions and 22 deletions

28
docs/changelog.rst Normal file
View file

@ -0,0 +1,28 @@
Changelog
=========
v1.0.2
------
- Added this changelog.
- Enhanced extension detection, format detection, and conversion between the
two. This eliminates the reliance on an image being loaded into memory
beforehand in order to detect said image's extension.
- Fixed a regression from the 0.4.x series in which ImageKit was unable to
convert a PNG file in ``P`` or "palette" mode to JPEG.
v1.0.1
------
- Minor fixes related to the rendering of ``README.rst`` as a reStructured
text file.
- Fixed the included admin template not being found when ImageKit was and
the packaging of the included admin templates.
v1.0
----
- Initial release of the *new* field-based ImageKit API.

View file

@ -48,9 +48,9 @@ copyright = u'2011, Justin Driscoll, Bryan Veloso, Greg Newman, Chris Drackett &
# built documents.
#
# The short X.Y version.
version = '1.0.1'
version = '1.0.2'
# The full version, including alpha/beta/rc tags.
release = '1.0.1'
release = '1.0.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View file

@ -22,6 +22,7 @@ Digging Deeper
.. toctree::
apireference
changelog
Indices and tables

View file

@ -1,4 +1,4 @@
__title__ = 'django-imagekit'
__author__ = 'Justin Driscoll, Bryan Veloso, Greg Newman, Chris Drackett, Matthew Tretter, Eric Eldredge'
__version__ = (1, 0, 1, 'final', 0)
__version__ = (1, 0, 2, 'final', 0)
__license__ = 'BSD'

View file

@ -10,7 +10,9 @@ from django.db.models.signals import post_save, post_delete
from django.utils.encoding import force_unicode, smart_str
from imagekit.lib import Image, ImageFile
from imagekit.utils import img_to_fobj, get_spec_files, open_image
from imagekit.utils import img_to_fobj, get_spec_files, open_image, \
format_to_extension, extension_to_format, UnknownFormatError, \
UnknownExtensionError
from imagekit.processors import ProcessorPipeline
# Modify image file buffer size.
@ -95,24 +97,26 @@ class ImageSpec(_ImageSpecMixin):
dispatch_uid='%s.delete' % uid)
def get_registered_extensions():
Image.preinit()
return Image.EXTENSION
def _get_suggested_extension(name, format):
if format:
# Try to look up an extension by the format.
extensions = [k for k, v in get_registered_extensions().iteritems() \
if v == format.upper()]
else:
extensions = []
original_extension = os.path.splitext(name)[1]
if not extensions or original_extension.lower() in extensions:
# If the original extension matches the format, use it.
try:
suggested_extension = format_to_extension(format)
except UnknownFormatError:
extension = original_extension
else:
extension = extensions[0]
if suggested_extension.lower() == original_extension.lower():
extension = original_extension
else:
try:
original_format = extension_to_format(original_extension)
except UnknownExtensionError:
extension = suggested_extension
else:
# If the formats match, give precedence to the original extension.
if format.lower() == original_format.lower():
extension = original_extension
else:
extension = suggested_extension
return extension
@ -129,7 +133,10 @@ class _ImageSpecFileMixin(object):
# The extension is explicit, so assume they want the matching format.
extension = os.path.splitext(filename)[1].lower()
# Try to guess the format from the extension.
format = get_registered_extensions().get(extension)
try:
format = extension_to_format(extension)
except UnknownExtensionError:
pass
format = format or img.format or original_format or 'JPEG'
if format != 'JPEG':

View file

@ -10,10 +10,11 @@ def img_to_fobj(img, format, **kwargs):
tmp = tempfile.TemporaryFile()
# Preserve transparency if the image is in Pallette (P) mode.
if img.mode == 'P':
transparency_formats = ('PNG', 'GIF', )
if img.mode == 'P' and format in transparency_formats:
kwargs['transparency'] = len(img.split()[-1].getcolors())
else:
img.convert('RGB')
img = img.convert('RGB')
img.save(tmp, format, **kwargs)
tmp.seek(0)
@ -49,3 +50,82 @@ def _wrap_copy(f):
pass
return img
return copy
class UnknownExtensionError(Exception):
pass
class UnknownFormatError(Exception):
pass
_pil_init = 0
def _preinit_pil():
"""Loads the standard PIL file format drivers. Returns True if ``preinit()``
was called (and there's a potential that more drivers were loaded) or False
if there is no possibility that new drivers were loaded.
"""
global _pil_init
if _pil_init < 1:
Image.preinit()
_pil_init = 1
return True
return False
def _init_pil():
"""Loads all PIL file format drivers. Returns True if ``init()`` was called
(and there's a potential that more drivers were loaded) or False if there is
no possibility that new drivers were loaded.
"""
global _pil_init
_preinit_pil()
if _pil_init < 2:
Image.init()
_pil_init = 2
return True
return False
def _extension_to_format(extension):
return Image.EXTENSION.get(extension.lower())
def _format_to_extension(format):
for k, v in Image.EXTENSION.iteritems():
if v == format.upper():
return k
return None
def extension_to_format(extension):
"""Returns the format that corresponds to the provided extension.
"""
format = _extension_to_format(extension)
if not format and _preinit_pil():
format = _extension_to_format(extension)
if not format and _init_pil():
format = _extension_to_format(extension)
if not format:
raise UnknownExtensionError(extension)
return format
def format_to_extension(format):
"""Returns the first extension that matches the provided format.
"""
extension = _format_to_extension(format)
if not extension and _preinit_pil():
extension = _format_to_extension(format)
if not extension and _init_pil():
extension = _format_to_extension(format)
if not extension:
raise UnknownFormatError(format)
return extension

View file

@ -5,6 +5,7 @@ from django.core.files.base import ContentFile
from django.db import models
from django.test import TestCase
from imagekit import utils
from imagekit.lib import Image
from imagekit.models import ImageSpec
from imagekit.processors import Adjust
@ -18,7 +19,6 @@ class Photo(models.Model):
class IKTest(TestCase):
""" Base TestCase class. """
def generate_image(self):
tmp = tempfile.TemporaryFile()
Image.new('RGB', (800, 600)).save(tmp, 'JPEG')
@ -53,3 +53,19 @@ class IKTest(TestCase):
def test_thumbnail_source_file(self):
self.assertEqual(
self.photo.thumbnail.source_file, self.photo.original_image)
class IKUtilsTest(TestCase):
def test_extension_to_format(self):
self.assertEqual(utils.extension_to_format('.jpeg'), 'JPEG')
self.assertEqual(utils.extension_to_format('.rgba'), 'SGI')
with self.assertRaises(utils.UnknownExtensionError):
utils.extension_to_format('.txt')
def test_format_to_extension_no_init(self):
self.assertEqual(utils.format_to_extension('PNG'), '.png')
self.assertEqual(utils.format_to_extension('ICO'), '.ico')
with self.assertRaises(utils.UnknownFormatError):
utils.format_to_extension('TXT')