diff --git a/wagtail/wagtailimages/models.py b/wagtail/wagtailimages/models.py index c561cdcc5..99d732efb 100644 --- a/wagtail/wagtailimages/models.py +++ b/wagtail/wagtailimages/models.py @@ -1,5 +1,4 @@ import os.path -import re from six import BytesIO @@ -21,7 +20,7 @@ from unidecode import unidecode from wagtail.wagtailadmin.taggable import TagSearchable from wagtail.wagtailimages.backends import get_image_backend from wagtail.wagtailsearch import indexed -from .utils import validate_image_format +from wagtail.wagtailimages.utils import validate_image_format, parse_filter_spec @python_2_unicode_compatible @@ -144,49 +143,6 @@ class Filter(models.Model): """ spec = models.CharField(max_length=255, db_index=True) - OPERATION_NAMES = { - 'max': 'resize_to_max', - 'min': 'resize_to_min', - 'width': 'resize_to_width', - 'height': 'resize_to_height', - 'fill': 'resize_to_fill', - 'original': 'no_operation', - } - - def __init__(self, *args, **kwargs): - super(Filter, self).__init__(*args, **kwargs) - self.method = None # will be populated when needed, by parsing the spec string - - def _parse_spec_string(self): - # parse the spec string and save the results to - # self.method_name and self.method_arg. There are various possible - # formats to match against: - # 'original' - # 'width-200' - # 'max-320x200' - - if self.spec == 'original': - self.method_name = Filter.OPERATION_NAMES['original'] - self.method_arg = None - return - - match = re.match(r'(width|height)-(\d+)$', self.spec) - if match: - self.method_name = Filter.OPERATION_NAMES[match.group(1)] - self.method_arg = int(match.group(2)) - return - - match = re.match(r'(max|min|fill)-(\d+)x(\d+)$', self.spec) - if match: - self.method_name = Filter.OPERATION_NAMES[match.group(1)] - width = int(match.group(2)) - height = int(match.group(3)) - self.method_arg = (width, height) - return - - # Spec is not one of our recognised patterns - raise ValueError("Invalid image filter spec: %r" % self.spec) - def process_image(self, input_file, backend_name='default'): """ Given an input image file as a django.core.files.File object, @@ -195,17 +151,16 @@ class Filter(models.Model): """ backend = get_image_backend(backend_name) - if not self.method: - self._parse_spec_string() + method_name, method_arg = parse_filter_spec(self.spec) # If file is closed, open it input_file.open('rb') image = backend.open_image(input_file) file_format = image.format - method = getattr(backend, self.method_name) - - image = method(image, self.method_arg) + # Call method + method = getattr(backend, method_name) + image = method(image, method_arg) output = BytesIO() backend.save_image(image, output, file_format) diff --git a/wagtail/wagtailimages/utils.py b/wagtail/wagtailimages/utils.py index 2f0c0e676..00c5476cd 100644 --- a/wagtail/wagtailimages/utils.py +++ b/wagtail/wagtailimages/utils.py @@ -1,4 +1,5 @@ import os +import re from PIL import Image @@ -26,3 +27,38 @@ def validate_image_format(f): # Check that the internal format matches the extension if image.format.upper() != extension.upper(): raise ValidationError(_("Not a valid %s image. Please use a gif, jpeg or png file with the correct file extension.") % (extension.upper())) + + +# TODO: Cache results from this method in something like Python 3.2s LRU cache (available in Django 1.7 as django.utils.lru_cache) +def parse_filter_spec(filter_spec): + # parse the spec string and save the results to + # self.method_name and self.method_arg. There are various possible + # formats to match against: + # 'original' + # 'width-200' + # 'max-320x200' + + OPERATION_NAMES = { + 'max': 'resize_to_max', + 'min': 'resize_to_min', + 'width': 'resize_to_width', + 'height': 'resize_to_height', + 'fill': 'resize_to_fill', + 'original': 'no_operation', + } + + # original + if filter_spec == 'original': + return OPERATION_NAMES['original'], None + + # width/height + match = re.match(r'(width|height)-(\d+)$', filter_spec) + if match: + return OPERATION_NAMES[match.group(1)], int(match.group(2)) + + # max/min/fill + match = re.match(r'(max|min|fill)-(\d+)x(\d+)$', filter_spec) + if match: + width = int(match.group(2)) + height = int(match.group(3)) + return OPERATION_NAMES[match.group(1)], (width, height)