1. Gregor Müllegger
  2. django-imagequery

Commits

David Danier  committed 814870c

format -> formats, operation -> operations, skipping the 's' was a bad idea

  • Participants
  • Parent commits 03571ea
  • Branches default

Comments (0)

Files changed (8)

File imagequery/__init__.py

View file
 from imagequery.query import RawImageQuery, NewImageQuery, ImageQuery
-from imagequery.format import Format
-from imagequery.operation import Operation, CommandOperation
+from imagequery.formats import Format
+from imagequery.operations import Operation, CommandOperation
 

File imagequery/format.py

-class FormatDoesNotExist(Exception):
-    pass
-
-_formats = {}
-
-def register(name, format):
-    _formats[name] = format
-
-def get(name):
-    try:
-        return _formats[name]
-    except KeyError:
-        raise FormatDoesNotExist()
-
-
-class Format(object):
-    """
-    A Format represents a fixed image manipulation
-    
-    Format's allow you to define an image manipulation based on ImageQuery. You
-    can write your own Format's just by extending the Format class and implement
-    the execute() method.
-    
-    Example:
-    from imagequery import format
-    
-    class MyShinyNewFormat(format.Format):
-        def execute(self, imagequery):
-            # it's always good to privide a query name, so you can easily
-            # empty the cache for the format
-            # (the name will be created as a path in storage)
-            return imagequery.operation1().operation2().query_name('shiny')
-    
-    After defining your format you can register it, this is mainly useful when
-    using the format inside the templates:
-    format.register('shiny', MyShinyNewFormat)
-    Inside the template (outputs the url):
-    {% load imagequery_tags %}{% image_format "shiny" obj.image %}
-    
-    Note:
-    When using Format's yourself (without the templatetags) you should be aware
-    that you have to pass it an existing ImageQuery. This is needed to simplify
-    the storage handling, as Format's don't need to care about storage.
-    
-    Format's mainly provide some methods to be used in your code, like returning
-    the URL/path of the generated image.
-    """
-    
-    # we don't allow passing filenames here, as this would need us to
-    # repeat big parts of the storage-logic
-    def __init__(self, imagequery):
-        self._query = imagequery
-    
-    def execute(self, query):
-        ''' needs to be filled by derivates '''
-        return query
-    
-    def _execute(self):
-        try:
-            return self._executed
-        except AttributeError:
-            self._executed = self.execute(self._query)
-            return self._executed
-    
-    def name(self):
-        """ like Imagequery: return the name of the associated file """
-        return self._execute().name()
-    
-    def path(self):
-        """ like Imagequery: return the full path of the associated file """
-        return self._execute().path()
-    
-    def url(self):
-        """ like Imagequery: return the URL of the associated file """
-        try:
-            return self._execute().url()
-        except:
-            return None # TODO: Do this right.
-    
-    def height(self):
-        return self._execute().height()
-    
-    def width(self):
-        return self._execute().width()
-

File imagequery/formats.py

View file
+class FormatDoesNotExist(Exception):
+    pass
+
+_formats = {}
+
+def register(name, format):
+    _formats[name] = format
+
+def get(name):
+    try:
+        return _formats[name]
+    except KeyError:
+        raise FormatDoesNotExist()
+
+
+class Format(object):
+    """
+    A Format represents a fixed image manipulation
+    
+    Format's allow you to define an image manipulation based on ImageQuery. You
+    can write your own Format's just by extending the Format class and implement
+    the execute() method.
+    
+    Example:
+    from imagequery import formats
+    
+    class MyShinyNewFormat(formats.Format):
+        def execute(self, imagequery):
+            # it's always good to privide a query name, so you can easily
+            # empty the cache for the format
+            # (the name will be created as a path in storage)
+            return imagequery.operation1().operation2().query_name('shiny')
+    
+    After defining your format you can register it, this is mainly useful when
+    using the format inside the templates:
+    formats.register('shiny', MyShinyNewFormat)
+    Inside the template (outputs the url):
+    {% load imagequery_tags %}{% image_format "shiny" obj.image %}
+    
+    Note:
+    When using Format's yourself (without the templatetags) you should be aware
+    that you have to pass it an existing ImageQuery. This is needed to simplify
+    the storage handling, as Format's don't need to care about storage.
+    
+    Format's mainly provide some methods to be used in your code, like returning
+    the URL/path of the generated image.
+    """
+    
+    # we don't allow passing filenames here, as this would need us to
+    # repeat big parts of the storage-logic
+    def __init__(self, imagequery):
+        self._query = imagequery
+    
+    def execute(self, query):
+        ''' needs to be filled by derivates '''
+        return query
+    
+    def _execute(self):
+        try:
+            return self._executed
+        except AttributeError:
+            self._executed = self.execute(self._query)
+            return self._executed
+    
+    def name(self):
+        """ like Imagequery: return the name of the associated file """
+        return self._execute().name()
+    
+    def path(self):
+        """ like Imagequery: return the full path of the associated file """
+        return self._execute().path()
+    
+    def url(self):
+        """ like Imagequery: return the URL of the associated file """
+        try:
+            return self._execute().url()
+        except:
+            return None # TODO: Do this right.
+    
+    def height(self):
+        return self._execute().height()
+    
+    def width(self):
+        return self._execute().width()
+

File imagequery/operation.py

-import os
-import Image
-from imagequery.utils import get_image_object, get_font_object, get_coords
-
-
-class Operation(object):
-    """
-    Image Operation, like scaling
-    """
-    
-    args = ()
-    args_defaults = {}
-    attrs = {}
-
-    def __init__(self, *args, **kwargs):
-        allowed_args = list(self.args)
-        allowed_args.reverse()
-        for key in self.args_defaults:
-            setattr(self, key, self.args_defaults[key])
-        for value in args:
-            assert allowed_args, 'too many arguments, only accepting %s arguments' % len(self.args)
-            key = allowed_args.pop()
-            setattr(self, key, value)
-        for key in kwargs:
-            assert key in allowed_args, '%s is not an accepted keyword argument' % key
-            setattr(self, key, kwargs[key])
-
-    def __unicode__(self):
-        content = [self.__class__.__name__]
-        args = '-'.join([str(getattr(self, key)) for key in self.args])
-        if args:
-            content.append(args)
-        return '_'.join(content)
-
-    def execute(self, image, query):
-        return image
-
-
-class DummyOperation(Operation):
-    pass
-
-
-class CommandOperation(Operation):
-    def file_operation(self, image, query, command):
-        import tempfile, subprocess
-        suffix = '.%s' % os.path.basename(query.source).split('.', -1)[1]
-        whfile, wfile = tempfile.mkstemp(suffix)
-        image.save(wfile)
-        rhfile, rfile = tempfile.mkstemp(suffix)
-        proc = subprocess.Popen(command % {'infile': wfile, 'outfile': rfile}, shell=True)
-        proc.wait()
-        image = Image.open(rfile)
-        return image
-
-
-class Enhance(Operation):
-    args = ('enhancer', 'factor')
-
-    def execute(self, image, query):
-        enhancer = self.enhancer(image)
-        return enhancer.enhance(self.factor)
-
-
-class Resize(Operation):
-    args = ('x', 'y', 'filter')
-    args_defaults = {
-        'x': None,
-        'y': None,
-        'filter': Image.ANTIALIAS,
-    }
-
-    def execute(self, image, query):
-        if self.x is None and self.y is None:
-            self.x, self.y = image.size
-        elif self.x is None:
-            orig_x, orig_y = image.size
-            ratio = float(self.y) / float(orig_y)
-            self.x = int(orig_x * ratio)
-        elif self.y is None:
-            orig_x, orig_y = image.size
-            ratio = float(self.x) / float(orig_x)
-            self.y = int(orig_y * ratio)
-        return image.resize((self.x, self.y), self.filter)
-
-
-class Scale(Operation):
-    args = ('x', 'y', 'filter')
-    args_defaults = {
-        'filter': Image.ANTIALIAS,
-    }
-
-    def execute(self, image, query):
-        image = image.copy()
-        image.thumbnail((self.x, self.y), self.filter)
-        return image
-
-
-class Invert(Operation):
-    args = ('keep_alpha',)
-    def execute(self, image, query):
-        import ImageChops
-        if self.keep_alpha:
-            image = image.convert('RGBA')
-            channels = list(image.split())
-            for i in xrange(0, 3):
-                channels[i] = ImageChops.invert(channels[i])
-            return Image.merge('RGBA', channels)
-        else:
-            return ImageChops.invert(image)
-
-
-class Grayscale(Operation):
-    def execute(self, image, query):
-        import ImageOps
-        return ImageOps.grayscale(image)
-
-
-class Flip(Operation):
-    def execute(self, image, query):
-        import ImageOps
-        return ImageOps.flip(image)
-
-
-class Mirror(Operation):
-    def execute(self, image, query):
-        import ImageOps
-        return ImageOps.mirror(image)
-
-
-class Blur(Operation):
-    args = ('amount',)
-    def execute(self, image, query):
-        import ImageFilter
-        for i in xrange(0, self.amount):
-            image = image.filter(ImageFilter.BLUR)
-        return image
-
-
-class Filter(Operation):
-    args = ('filter',)
-    def execute(self, image, query):
-        return image.filter(self.filter)
-
-
-class Crop(Operation):
-    args = ('x', 'y', 'w', 'h')
-    def execute(self, image, query):
-        box = (
-            self.x,
-            self.y,
-            self.x + self.w,
-            self.y + self.h,
-        )
-        return image.crop(box)
-
-
-class Fit(Operation):
-    args = ('x', 'y', 'centering', 'method')
-    args_defaults = {
-        'method': Image.ANTIALIAS,
-        'centering': (0.5, 0.5),
-    }
-    def execute(self, image, query):
-        import ImageOps
-        return ImageOps.fit(image, (self.x, self.y), self.method, centering=self.centering)
-
-
-class Blank(Operation):
-    args = ('x','y','color','mode')
-    args_defaults = {
-        'x': None,
-        'y': None,
-        'color': None,
-        'mode': 'RGBA',
-    }
-    def execute(self, image, query):
-        x, y = self.x, self.y
-        if x is None:
-            x = image.size[0]
-        if y is None:
-            y = image.size[1]
-        if self.color:
-            return Image.new(self.mode, (x, y), self.color)
-        else:
-            return Image.new(self.mode, (x, y))
-
-
-class Paste(Operation):
-    args = ('image','x','y','storage')
-    def execute(self, image, query):
-        athor = get_image_object(self.image, self.storage)
-        x2, y2 = athor.size
-        x1 = get_coords(image.size[0], athor.size[0], self.x)
-        y1 = get_coords(image.size[1], athor.size[1], self.y)
-        box = (
-            x1,
-            y1,
-            x1 + x2,
-            y1 + y2,
-        )
-        # Note that if you paste an "RGBA" image, the alpha band is ignored.
-        # You can work around this by using the same image as both source image and mask.
-        image = image.copy()
-        if athor.mode == 'RGBA':
-            if image.mode == 'RGBA':
-                import ImageChops
-                channels = image.split()
-                alpha = channels[3]
-                image = Image.merge('RGB', channels[0:3])
-                athor_channels = athor.split()
-                athor_alpha = athor_channels[3]
-                athor = Image.merge('RGB', athor_channels[0:3])
-                image.paste(athor, box, mask=athor_alpha)
-                # merge alpha
-                athor_image_alpha = Image.new('L', image.size, color=0)
-                athor_image_alpha.paste(athor_alpha, box)
-                new_alpha = ImageChops.add(alpha, athor_image_alpha)
-                image = Image.merge('RGBA', image.split() + (new_alpha,))
-            else:
-                image.paste(athor, box, mask=athor)
-        else:
-            image.paste(athor, box)
-        return image
-
-
-class Background(Operation):
-    args = ('image','x','y','storage')
-    def execute(self, image, query):
-        background = Image.new('RGBA', image.size, color=(0,0,0,0))
-        athor = get_image_object(self.image, self.storage)
-        x2,y2 = image.size
-        x1 = get_coords(image.size[0], athor.size[0], self.x)
-        y1 = get_coords(image.size[1], athor.size[1], self.y)
-        box = (
-            x1,
-            y1,
-            x1 + x2,
-            y1 + y2,
-        )
-        background.paste(athor, box, mask=athor)
-        background.paste(image, None, mask=image)
-        return background
-
-
-class Convert(Operation):
-    args = ('mode', 'matrix')
-    args_defaults = {
-        'matrix': None,
-    }
-    def execute(self, image, query):
-        if self.matrix:
-            return image.convert(self.mode, self.matrix)
-        else:
-            return image.convert(self.mode)
-
-
-class GetChannel(Operation):
-    args = ('channel',)
-    channel_map = {
-        'red': 0,
-        'green': 1,
-        'blue': 2,
-        'alpha': 3,
-    }
-    def execute(self, image, query):
-        image = image.convert('RGBA')
-        alpha = image.split()[self.channel_map[self.channel]]
-        return Image.merge('RGBA', (alpha, alpha, alpha, alpha))
-
-
-class ApplyAlpha(GetChannel):
-    args = ('alphamap',)
-    def execute(self, image, query):
-        # TODO: Use putalpha(band)?
-        image = image.convert('RGBA')
-        alphamap = get_image_object(self.alphamap).convert('RGBA')
-        data = image.split()[self.channel_map['red']:self.channel_map['alpha']]
-        alpha = alphamap.split()[self.channel_map['alpha']]
-        alpha = alpha.resize(image.size, Image.ANTIALIAS)
-        return Image.merge('RGBA', data + (alpha,))
-
-
-class Blend(Operation):
-    args = ('image','alpha','storage')
-    channel_map = {
-        'alpha': 0.5,
-    }
-    def execute(self, image, query):
-        athor = get_image_object(self.image, self.storage)
-        return Image.blend(image, athor, self.alpha)
-
-
-class Text(Operation):
-    args = ('text','x','y','font','size','fill')
-    args_defaults = {
-        'size': None,
-        'fill': None,
-    }
-    def execute(self, image, query):
-        import ImageDraw
-        from imagequery import ImageQuery # late import to avoid circular import
-        image = image.copy()
-        font = get_font_object(self.font, self.size)
-        size, offset = ImageQuery.img_textbox(self.text, self.font, self.size)
-        x = get_coords(image.size[0], size[0], self.x) + offset[0]
-        y = get_coords(image.size[1], size[1], self.y) + offset[1]
-        draw = ImageDraw.Draw(image)
-        text = self.text
-        # HACK
-        if Image.VERSION == '1.1.5' and isinstance(text, unicode):
-            text = text.encode('utf-8')
-        draw.text((x, y), text, font=font, fill=self.fill)
-        return image
-
-
-# TODO: enhance text operations
-
-class TextImage(Operation):
-    args = ('text', 'font', 'size', 'mode')
-    args_defaults = {
-        'size': None,
-        'fill': None,
-    }
-    def execute(self, image, query):
-        font = get_font_object(self.font, self.size)
-        font.getmask(self.text)
-
-
-class FontDefaults(Operation):
-    args = ('font', 'size', 'fill')
-
-    @property
-    def attrs(self):
-        return {
-            'font': self.font,
-            'size': self.size,
-            'fill': self.fill,
-        }
-
-
-class Composite(Operation):
-    args = ('image','mask','storage')
-    def execute(self, image, query):
-        athor = get_image_object(self.image, self.storage)
-        mask = get_image_object(self.mask, self.storage)
-        return Image.composite(image, athor, mask)
-
-
-class Offset(Operation):
-    args = ('x','y')
-    def execute(self, image, query):
-        import ImageChops
-        return ImageChops.offset(image, self.x, self.y)
-
-
-class Padding(Operation):
-    args = ('left','top','right','bottom','color')
-    def execute(self, image, query):
-        left, top, right, bottom = self.left, self.top, self.right, self.bottom
-        color = self.color
-        if top is None:
-            top = left
-        if right is None:
-            right = left
-        if bottom is None:
-            bottom = top
-        if color is None:
-            color = (0,0,0,0)
-        new_width = left + right + image.size[0]
-        new_height = top + bottom + image.size[1]
-        new = Image.new('RGBA', (new_width, new_height), color=color)
-        new.paste(image, (left, top))
-        return new
-
-
-class Opacity(Operation):
-    args = ('opacity',)
-    def execute(self, image, query):
-        opacity = int(self.opacity * 255)
-        background = Image.new('RGBA', image.size, color=(0,0,0,0))
-        mask = Image.new('RGBA', image.size, color=(0,0,0,opacity))
-        box = (0,0) + image.size
-        background.paste(image, box, mask)
-        return background
-
-
-class Clip(Operation):
-    args = ('start','end',)
-    args_defaults = {
-        'start': None,
-        'end': None,
-    }
-    def execute(self, image, query):
-        start = self.start
-        if start is None:
-            start = (0, 0)
-        end = self.end
-        if end is None:
-            end = image.size
-        new = image.crop(self.start + self.end)
-        new.load() # crop is a lazy operation, see docs
-        return new
-

File imagequery/operations.py

View file
+import os
+import Image
+from imagequery.utils import get_image_object, get_font_object, get_coords
+
+
+class Operation(object):
+    """
+    Image Operation, like scaling
+    """
+    
+    args = ()
+    args_defaults = {}
+    attrs = {}
+
+    def __init__(self, *args, **kwargs):
+        allowed_args = list(self.args)
+        allowed_args.reverse()
+        for key in self.args_defaults:
+            setattr(self, key, self.args_defaults[key])
+        for value in args:
+            assert allowed_args, 'too many arguments, only accepting %s arguments' % len(self.args)
+            key = allowed_args.pop()
+            setattr(self, key, value)
+        for key in kwargs:
+            assert key in allowed_args, '%s is not an accepted keyword argument' % key
+            setattr(self, key, kwargs[key])
+
+    def __unicode__(self):
+        content = [self.__class__.__name__]
+        args = '-'.join([str(getattr(self, key)) for key in self.args])
+        if args:
+            content.append(args)
+        return '_'.join(content)
+
+    def execute(self, image, query):
+        return image
+
+
+class DummyOperation(Operation):
+    pass
+
+
+class CommandOperation(Operation):
+    def file_operation(self, image, query, command):
+        import tempfile, subprocess
+        suffix = '.%s' % os.path.basename(query.source).split('.', -1)[1]
+        whfile, wfile = tempfile.mkstemp(suffix)
+        image.save(wfile)
+        rhfile, rfile = tempfile.mkstemp(suffix)
+        proc = subprocess.Popen(command % {'infile': wfile, 'outfile': rfile}, shell=True)
+        proc.wait()
+        image = Image.open(rfile)
+        return image
+
+
+class Enhance(Operation):
+    args = ('enhancer', 'factor')
+
+    def execute(self, image, query):
+        enhancer = self.enhancer(image)
+        return enhancer.enhance(self.factor)
+
+
+class Resize(Operation):
+    args = ('x', 'y', 'filter')
+    args_defaults = {
+        'x': None,
+        'y': None,
+        'filter': Image.ANTIALIAS,
+    }
+
+    def execute(self, image, query):
+        if self.x is None and self.y is None:
+            self.x, self.y = image.size
+        elif self.x is None:
+            orig_x, orig_y = image.size
+            ratio = float(self.y) / float(orig_y)
+            self.x = int(orig_x * ratio)
+        elif self.y is None:
+            orig_x, orig_y = image.size
+            ratio = float(self.x) / float(orig_x)
+            self.y = int(orig_y * ratio)
+        return image.resize((self.x, self.y), self.filter)
+
+
+class Scale(Operation):
+    args = ('x', 'y', 'filter')
+    args_defaults = {
+        'filter': Image.ANTIALIAS,
+    }
+
+    def execute(self, image, query):
+        image = image.copy()
+        image.thumbnail((self.x, self.y), self.filter)
+        return image
+
+
+class Invert(Operation):
+    args = ('keep_alpha',)
+    def execute(self, image, query):
+        import ImageChops
+        if self.keep_alpha:
+            image = image.convert('RGBA')
+            channels = list(image.split())
+            for i in xrange(0, 3):
+                channels[i] = ImageChops.invert(channels[i])
+            return Image.merge('RGBA', channels)
+        else:
+            return ImageChops.invert(image)
+
+
+class Grayscale(Operation):
+    def execute(self, image, query):
+        import ImageOps
+        return ImageOps.grayscale(image)
+
+
+class Flip(Operation):
+    def execute(self, image, query):
+        import ImageOps
+        return ImageOps.flip(image)
+
+
+class Mirror(Operation):
+    def execute(self, image, query):
+        import ImageOps
+        return ImageOps.mirror(image)
+
+
+class Blur(Operation):
+    args = ('amount',)
+    def execute(self, image, query):
+        import ImageFilter
+        for i in xrange(0, self.amount):
+            image = image.filter(ImageFilter.BLUR)
+        return image
+
+
+class Filter(Operation):
+    args = ('filter',)
+    def execute(self, image, query):
+        return image.filter(self.filter)
+
+
+class Crop(Operation):
+    args = ('x', 'y', 'w', 'h')
+    def execute(self, image, query):
+        box = (
+            self.x,
+            self.y,
+            self.x + self.w,
+            self.y + self.h,
+        )
+        return image.crop(box)
+
+
+class Fit(Operation):
+    args = ('x', 'y', 'centering', 'method')
+    args_defaults = {
+        'method': Image.ANTIALIAS,
+        'centering': (0.5, 0.5),
+    }
+    def execute(self, image, query):
+        import ImageOps
+        return ImageOps.fit(image, (self.x, self.y), self.method, centering=self.centering)
+
+
+class Blank(Operation):
+    args = ('x','y','color','mode')
+    args_defaults = {
+        'x': None,
+        'y': None,
+        'color': None,
+        'mode': 'RGBA',
+    }
+    def execute(self, image, query):
+        x, y = self.x, self.y
+        if x is None:
+            x = image.size[0]
+        if y is None:
+            y = image.size[1]
+        if self.color:
+            return Image.new(self.mode, (x, y), self.color)
+        else:
+            return Image.new(self.mode, (x, y))
+
+
+class Paste(Operation):
+    args = ('image','x','y','storage')
+    def execute(self, image, query):
+        athor = get_image_object(self.image, self.storage)
+        x2, y2 = athor.size
+        x1 = get_coords(image.size[0], athor.size[0], self.x)
+        y1 = get_coords(image.size[1], athor.size[1], self.y)
+        box = (
+            x1,
+            y1,
+            x1 + x2,
+            y1 + y2,
+        )
+        # Note that if you paste an "RGBA" image, the alpha band is ignored.
+        # You can work around this by using the same image as both source image and mask.
+        image = image.copy()
+        if athor.mode == 'RGBA':
+            if image.mode == 'RGBA':
+                import ImageChops
+                channels = image.split()
+                alpha = channels[3]
+                image = Image.merge('RGB', channels[0:3])
+                athor_channels = athor.split()
+                athor_alpha = athor_channels[3]
+                athor = Image.merge('RGB', athor_channels[0:3])
+                image.paste(athor, box, mask=athor_alpha)
+                # merge alpha
+                athor_image_alpha = Image.new('L', image.size, color=0)
+                athor_image_alpha.paste(athor_alpha, box)
+                new_alpha = ImageChops.add(alpha, athor_image_alpha)
+                image = Image.merge('RGBA', image.split() + (new_alpha,))
+            else:
+                image.paste(athor, box, mask=athor)
+        else:
+            image.paste(athor, box)
+        return image
+
+
+class Background(Operation):
+    args = ('image','x','y','storage')
+    def execute(self, image, query):
+        background = Image.new('RGBA', image.size, color=(0,0,0,0))
+        athor = get_image_object(self.image, self.storage)
+        x2,y2 = image.size
+        x1 = get_coords(image.size[0], athor.size[0], self.x)
+        y1 = get_coords(image.size[1], athor.size[1], self.y)
+        box = (
+            x1,
+            y1,
+            x1 + x2,
+            y1 + y2,
+        )
+        background.paste(athor, box, mask=athor)
+        background.paste(image, None, mask=image)
+        return background
+
+
+class Convert(Operation):
+    args = ('mode', 'matrix')
+    args_defaults = {
+        'matrix': None,
+    }
+    def execute(self, image, query):
+        if self.matrix:
+            return image.convert(self.mode, self.matrix)
+        else:
+            return image.convert(self.mode)
+
+
+class GetChannel(Operation):
+    args = ('channel',)
+    channel_map = {
+        'red': 0,
+        'green': 1,
+        'blue': 2,
+        'alpha': 3,
+    }
+    def execute(self, image, query):
+        image = image.convert('RGBA')
+        alpha = image.split()[self.channel_map[self.channel]]
+        return Image.merge('RGBA', (alpha, alpha, alpha, alpha))
+
+
+class ApplyAlpha(GetChannel):
+    args = ('alphamap',)
+    def execute(self, image, query):
+        # TODO: Use putalpha(band)?
+        image = image.convert('RGBA')
+        alphamap = get_image_object(self.alphamap).convert('RGBA')
+        data = image.split()[self.channel_map['red']:self.channel_map['alpha']]
+        alpha = alphamap.split()[self.channel_map['alpha']]
+        alpha = alpha.resize(image.size, Image.ANTIALIAS)
+        return Image.merge('RGBA', data + (alpha,))
+
+
+class Blend(Operation):
+    args = ('image','alpha','storage')
+    channel_map = {
+        'alpha': 0.5,
+    }
+    def execute(self, image, query):
+        athor = get_image_object(self.image, self.storage)
+        return Image.blend(image, athor, self.alpha)
+
+
+class Text(Operation):
+    args = ('text','x','y','font','size','fill')
+    args_defaults = {
+        'size': None,
+        'fill': None,
+    }
+    def execute(self, image, query):
+        import ImageDraw
+        from imagequery import ImageQuery # late import to avoid circular import
+        image = image.copy()
+        font = get_font_object(self.font, self.size)
+        size, offset = ImageQuery.img_textbox(self.text, self.font, self.size)
+        x = get_coords(image.size[0], size[0], self.x) + offset[0]
+        y = get_coords(image.size[1], size[1], self.y) + offset[1]
+        draw = ImageDraw.Draw(image)
+        text = self.text
+        # HACK
+        if Image.VERSION == '1.1.5' and isinstance(text, unicode):
+            text = text.encode('utf-8')
+        draw.text((x, y), text, font=font, fill=self.fill)
+        return image
+
+
+# TODO: enhance text operations
+
+class TextImage(Operation):
+    args = ('text', 'font', 'size', 'mode')
+    args_defaults = {
+        'size': None,
+        'fill': None,
+    }
+    def execute(self, image, query):
+        font = get_font_object(self.font, self.size)
+        font.getmask(self.text)
+
+
+class FontDefaults(Operation):
+    args = ('font', 'size', 'fill')
+
+    @property
+    def attrs(self):
+        return {
+            'font': self.font,
+            'size': self.size,
+            'fill': self.fill,
+        }
+
+
+class Composite(Operation):
+    args = ('image','mask','storage')
+    def execute(self, image, query):
+        athor = get_image_object(self.image, self.storage)
+        mask = get_image_object(self.mask, self.storage)
+        return Image.composite(image, athor, mask)
+
+
+class Offset(Operation):
+    args = ('x','y')
+    def execute(self, image, query):
+        import ImageChops
+        return ImageChops.offset(image, self.x, self.y)
+
+
+class Padding(Operation):
+    args = ('left','top','right','bottom','color')
+    def execute(self, image, query):
+        left, top, right, bottom = self.left, self.top, self.right, self.bottom
+        color = self.color
+        if top is None:
+            top = left
+        if right is None:
+            right = left
+        if bottom is None:
+            bottom = top
+        if color is None:
+            color = (0,0,0,0)
+        new_width = left + right + image.size[0]
+        new_height = top + bottom + image.size[1]
+        new = Image.new('RGBA', (new_width, new_height), color=color)
+        new.paste(image, (left, top))
+        return new
+
+
+class Opacity(Operation):
+    args = ('opacity',)
+    def execute(self, image, query):
+        opacity = int(self.opacity * 255)
+        background = Image.new('RGBA', image.size, color=(0,0,0,0))
+        mask = Image.new('RGBA', image.size, color=(0,0,0,opacity))
+        box = (0,0) + image.size
+        background.paste(image, box, mask)
+        return background
+
+
+class Clip(Operation):
+    args = ('start','end',)
+    args_defaults = {
+        'start': None,
+        'end': None,
+    }
+    def execute(self, image, query):
+        start = self.start
+        if start is None:
+            start = (0, 0)
+        end = self.end
+        if end is None:
+            end = image.size
+        new = image.crop(self.start + self.end)
+        new.load() # crop is a lazy operation, see docs
+        return new
+

File imagequery/query.py

View file
 from django.utils.encoding import smart_str
 from django.core.files.base import File, ContentFile
 from django.db.models.fields.files import FieldFile
-from imagequery import operation
+from imagequery import operations
 from imagequery.settings import CACHE_DIR, DEFAULT_OPTIONS, default_storage,\
     default_cache_storage
 from imagequery.utils import get_image_object, get_font_object, get_coords
         return q
 
     def blank(self,x=None,y=None,color=None):
-        return self.append(operation.Blank(x,y,color))
+        return self.append(operations.Blank(x,y,color))
 
     def paste(self, image, x=0, y=0, storage=None):
         '''
         '''
         if storage is None:
             storage = self.storage
-        return self.append(operation.Paste(image,x,y,storage))
+        return self.append(operations.Paste(image,x,y,storage))
 
     def background(self, image, x=0, y=0, storage=None):
         '''
         '''
         if storage is None:
             storage = self.storage
-        return self.append(operation.Background(image,x,y,storage))
+        return self.append(operations.Background(image,x,y,storage))
 
     def blend(self, image, alpha=0.5, storage=None):
         if storage is None:
             storage = self.storage
-        return self.append(operation.Blend(image,alpha,storage))
+        return self.append(operations.Blend(image,alpha,storage))
 
     def resize(self, x=None, y=None, filter=Image.ANTIALIAS):
-        return self.append(operation.Resize(x,y,filter))
+        return self.append(operations.Resize(x,y,filter))
 
     def scale(self, x, y, filter=Image.ANTIALIAS):
-        return self.append(operation.Scale(x,y,filter))
+        return self.append(operations.Scale(x,y,filter))
 
     def crop(self, x, y, w, h):
-        return self.append(operation.Crop(x,y,w,h))
+        return self.append(operations.Crop(x,y,w,h))
 
     def fit(self, x, y, centering=(0.5,0.5), method=Image.ANTIALIAS):
-        return self.append(operation.Fit(x,y,centering,method))
+        return self.append(operations.Fit(x,y,centering,method))
 
     def enhance(self, enhancer, factor):
-        return self.append(operation.Enhance(enhancer, factor))
+        return self.append(operations.Enhance(enhancer, factor))
 
     def sharpness(self, amount=2.0):
         '''
 
     def blur(self, amount=1):
         #return self.sharpness(1-(amount-1))
-        return self.append(operation.Blur(amount))
+        return self.append(operations.Blur(amount))
 
     def filter(self, image_filter):
-        return self.append(operation.Filter(image_filter))
+        return self.append(operations.Filter(image_filter))
     
     def truecolor(self):
-        return self.append(operation.Convert('RGBA'))
+        return self.append(operations.Convert('RGBA'))
 
     def invert(self, keep_alpha=True):
-        return self.append(operation.Invert(keep_alpha))
+        return self.append(operations.Invert(keep_alpha))
 
     def flip(self):
-        return self.append(operation.Flip())
+        return self.append(operations.Flip())
 
     def mirror(self):
-        return self.append(operation.Mirror())
+        return self.append(operations.Mirror())
 
     def grayscale(self):
-        return self.append(operation.Grayscale())
+        return self.append(operations.Grayscale())
 
     def alpha(self):
-        return self.append(operation.GetChannel('alpha'))
+        return self.append(operations.GetChannel('alpha'))
 
     def applyalpha(self, alphamap):
-        return self.append(operation.ApplyAlpha(alphamap))
+        return self.append(operations.ApplyAlpha(alphamap))
 
     def composite(self, image, mask, storage=None):
         if storage is None:
             storage = self.storage
-        return self.append(operation.Composite(image, mask, storage))
+        return self.append(operations.Composite(image, mask, storage))
 
     def offset(self, x, y):
-        return self.append(operation.Offset(x, y))
+        return self.append(operations.Offset(x, y))
 
     def padding(self, left, top=None, right=None, bottom=None, color=None):
-        return self.append(operation.Padding(left, top, right, bottom, color))
+        return self.append(operations.Padding(left, top, right, bottom, color))
 
     def opacity(self, opacity):
-        return self.append(operation.Opacity(opacity))
+        return self.append(operations.Opacity(opacity))
 
     def clip(self, start=None, end=None):
-        return self.append(operation.Clip(start, end))
+        return self.append(operations.Clip(start, end))
 
     def shadow(self, color):
         #mask = self.alpha().invert()
     # text operations
 
     def text(self, text, x, y, font, size=None, fill=None):
-        return self.append(operation.Text(text, x, y, font, size, fill))
+        return self.append(operations.Text(text, x, y, font, size, fill))
 
     @staticmethod
     def textbox(text, font, size=None):

File imagequery/templatetags/imagequery_tags.py

View file
 from django import template
-from imagequery import ImageQuery, format
+from imagequery import ImageQuery, formats
 from imagequery.utils import get_imagequery
 from django.db.models.fields.files import ImageFieldFile
 from django.utils.encoding import smart_unicode
         except template.VariableDoesNotExist:
             return ''
         try:
-            format_cls = format.get(formatname)
+            format_cls = formats.get(formatname)
         except format.FormatDoesNotExist:
             return ''
-        result = format_cls(get_imagequery(image))
+        format = format_cls(get_imagequery(image))
         if self.name:
-            context[self.name] = result
+            context[self.name] = format
             return ''
         else:
-            return result.url()
+            return format.url()
 
 
 @register.tag
     """
     Allows you to use predefined Format's for changing your images according to
     predefined sets of operations. Format's must be registered for using them
-    here (using imagequery.format.register("name", MyFormat).
+    here (using imagequery.formats.register("name", MyFormat).
     
     You can get the resulting Format instance as a context variable.
     

File imagequery/tests/__init__.py

View file
 from django.core.files.storage import FileSystemStorage
 from django.db import models
 from imagequery.query import ImageQuery, RawImageQuery, NewImageQuery
-from imagequery import format
+from imagequery import formats
 
 
 class ImageModel(models.Model):
         return self.name
 
 
-class TestFormat(format.Format):
+class TestFormat(formats.Format):
     def execute(self, qs):
         return qs.grayscale().query_name('test_format')
 
         self.tmpstorage = FileSystemStorage(location=self.tmpstorage_dir)
         self.tmpstorage_save_dir = tempfile.mkdtemp()
         self.tmpstorage_save = FileSystemStorage(location=self.tmpstorage_save_dir)
-        self.registered_formats = format._formats
-        format._formats = {}
-        format.register('test', TestFormat)
+        self.registered_formats = formats._formats
+        formats._formats = {}
+        formats.register('test', TestFormat)
 
     def tearDown(self):
         shutil.rmtree(self.tmp_dir)
-        format._formats = self.registered_formats
+        formats._formats = self.registered_formats
     
     def sample(self, path):
         return os.path.join(self.sample_dir, path)