Repository: wagtail/wagtail

Issue:
How do I request the original version of an uploaded image?
I'm expecting something along the lines of `{% image self.image original %}` but can't find it in the code.



Discussion:
We don't have anything like that at the moment. It's not even necessarily the case that files in original_images are accessible through a URL (in the wagtaildemo setup, they aren't). I'd argue that this is a good thing, as the original images may not be suitable for serving over the web - they may be oversized, and it's possible that in future we might support uploading images in non-web-compatible formats.

What do you need this for? One hacky workaround is `{% image self.image width-100000 %}` (since the image resize methods will never scale an image up).

(semi-related: #87)

Wes is implementing his designs for the new torchbox.com on wagtail, in which he's able to cut out and upload the exact images he needs at the exact sizes he'll use them at. Given wagtail/django's lack of restrictions on design in the browser, I expect this might be quite a regular occurrence. 

The `width-1000000` hack is perfectly fine for now, but a more explicit filter would obviously be better in the longer term.

This issue is now referenced in the docs: http://wagtail.readthedocs.org/en/latest/building_your_site/frontenddevelopers.html#images-tag


Resolving patch:
diff --git a/wagtail/wagtailimages/backends/base.py b/wagtail/wagtailimages/backends/base.py
--- a/wagtail/wagtailimages/backends/base.py
+++ b/wagtail/wagtailimages/backends/base.py
@@ -130,3 +130,8 @@ def resize_to_fill(self, image, size):
         """
         resized_image = self.resize_to_min(image, size)
         return self.crop_to_centre(resized_image, size)
+
+
+    def no_operation(self, image, param):
+        """Return the image unchanged"""
+        return image
diff --git a/wagtail/wagtailimages/models.py b/wagtail/wagtailimages/models.py
--- a/wagtail/wagtailimages/models.py
+++ b/wagtail/wagtailimages/models.py
@@ -1,5 +1,6 @@
 import StringIO
 import os.path
+import re
 
 from taggit.managers import TaggableManager
 
@@ -150,6 +151,7 @@ class Filter(models.Model):
         'width': 'resize_to_width',
         'height': 'resize_to_height',
         'fill': 'resize_to_fill',
+        'original': 'no_operation',
     }
 
     def __init__(self, *args, **kwargs):
@@ -157,22 +159,34 @@ def __init__(self, *args, **kwargs):
         self.method = None  # will be populated when needed, by parsing the spec string
 
     def _parse_spec_string(self):
-        # parse the spec string, which is formatted as (method)-(arg),
-        # and save the results to self.method_name and self.method_arg
-        try:
-            (method_name_simple, method_arg_string) = self.spec.split('-')
-            self.method_name = Filter.OPERATION_NAMES[method_name_simple]
-
-            if method_name_simple in ('max', 'min', 'fill'):
-                # method_arg_string is in the form 640x480
-                (width, height) = [int(i) for i in method_arg_string.split('x')]
-                self.method_arg = (width, height)
-            else:
-                # method_arg_string is a single number
-                self.method_arg = int(method_arg_string)
-
-        except (ValueError, KeyError):
-            raise ValueError("Invalid image filter spec: %r" % self.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'
+
+        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'):
         """


Test patch:
diff --git a/wagtail/wagtailimages/tests.py b/wagtail/wagtailimages/tests.py
--- a/wagtail/wagtailimages/tests.py
+++ b/wagtail/wagtailimages/tests.py
@@ -107,6 +107,13 @@ def test_resize_to_min(self):
         self.assertEqual(rendition.width, 160)
         self.assertEqual(rendition.height, 120)
 
+    def test_resize_to_original(self):
+        rendition = self.image.get_rendition('original')
+
+        # Check size
+        self.assertEqual(rendition.width, 640)
+        self.assertEqual(rendition.height, 480)
+
     def test_cache(self):
         # Get two renditions with the same filter
         first_rendition = self.image.get_rendition('width-400')
@@ -153,6 +160,13 @@ def test_resize_to_min(self):
         self.assertEqual(rendition.width, 160)
         self.assertEqual(rendition.height, 120)
 
+    def test_resize_to_original(self):
+        rendition = self.image.get_rendition('original')
+
+        # Check size
+        self.assertEqual(rendition.width, 640)
+        self.assertEqual(rendition.height, 480)
+
     def test_cache(self):
         # Get two renditions with the same filter
         first_rendition = self.image.get_rendition('width-400')