├── LICENSE
├── MANIFEST.in
├── README.rst
├── dummyimage
├── __init__.py
├── fonts
│ └── DroidSans.ttf
├── forms.py
├── models.py
├── settings.py
├── templatetags
│ ├── __init__.py
│ └── dummyimage_tags.py
├── tests.py
├── urls.py
└── views.py
└── setup.py
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) Rolando Espinoza La fuente.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice,
8 | this list of conditions and the following disclaimer.
9 |
10 | 2. Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in the
12 | documentation and/or other materials provided with the distribution.
13 |
14 | 3. Neither the name of Rolando Espinoza La fuente nor the names of its
15 | contributors may be used to endorse or promote products derived from this
16 | software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include dummyimage/fonts *.ttf
2 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | Django DummyImage
2 | =================
3 |
4 | A simple app to generate dummy/filler images on the fly at whatever size you want.
5 |
6 | Inspired by http://dummyimage.com/ and http://github.com/xxx/fakeimage
7 |
8 | Installation
9 | ============
10 |
11 | Installation using ``pip``::
12 |
13 | $ pip install django-dummyimage
14 |
15 | Running tests::
16 |
17 | $ DJANGO_SETTINGS_MODULE=dummyimage.settings django-admin.py test dummyimage
18 |
19 | Running demo::
20 |
21 | $ DJANGO_SETTINGS_MODULE=dummyimage.settings django-admin.py runserver
22 | $ xdg-open "http://localhost:8000/500x150.png?text=hello+world"
23 |
24 |
25 | Setup & Settings
26 | ================
27 |
28 | Add ``dummyimage`` to your ``INSTALLED_APPS`` setting.
29 |
30 | Default settings::
31 |
32 | DUMMYIMAGE_MAX_DIMENSION = 1024
33 | DUMMYIMAGE_DEFAULT_BG = 'white'
34 | DUMMYIMAGE_DEFAULT_TEXT = 'grey'
35 | DUMMYIMAGE_DEFAULT_BORDER = 'grey'
36 |
37 |
38 | Template Tag
39 | ============
40 |
41 | Code::
42 |
43 |
44 |
45 | Output::
46 |
47 |
48 |
49 |
50 | Example::
51 |
52 | {% get_dummyimage_url 320 240 png as image %}
53 |
54 |
55 |
56 | Query Parameters
57 | ================
58 |
59 | Available parameters:
60 |
61 | - ``text=string`` text to be rendered in the middle of the image.
62 | - ``textcolor=color`` text color.
63 | - ``bgcolor=color`` background color.
64 | - ``bordercolor=color`` border color.
65 | - ``noborder=1`` disable border.
66 | - ``cross=1`` draw a cross in the through the image.
67 |
68 | .. note::
69 | Colors can be literal color names (e.g. ``white``, ``red``) or hexadecimal
70 | values starting with ``!``, for example: ``!333``, ``!AAA``, ``white``,
71 | ``blue``, ``!CBCBCB``.
72 |
--------------------------------------------------------------------------------
/dummyimage/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmax/django-dummyimage/e95c60a1a8f9f4c9ed1805dc219ed79ad83b877c/dummyimage/__init__.py
--------------------------------------------------------------------------------
/dummyimage/fonts/DroidSans.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmax/django-dummyimage/e95c60a1a8f9f4c9ed1805dc219ed79ad83b877c/dummyimage/fonts/DroidSans.ttf
--------------------------------------------------------------------------------
/dummyimage/forms.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 | from django import forms
4 | from django.conf import settings
5 |
6 |
7 | MAX_DIMENSION = getattr(settings, 'DUMMYIMAGE_MAX_DIMENSION', sys.maxint)
8 | DEFAULT_BG = getattr(settings, 'DUMMYIMAGE_DEFAULT_BG', 'white')
9 | DEFAULT_TEXT = getattr(settings, 'DUMMYIMAGE_DEFAULT_TEXT', 'grey')
10 | DEFAULT_BORDER = getattr(settings, 'DUMMYIMAGE_DEFAULT_BORDER', 'grey')
11 |
12 | DEFAULT_COLORS = {
13 | 'BG': DEFAULT_BG,
14 | 'TEXT': DEFAULT_TEXT,
15 | 'BORDER': DEFAULT_BORDER,
16 | }
17 |
18 | RE_HEX_FULL = re.compile('^[A-F0-9]{6}$', re.IGNORECASE)
19 | RE_HEX_SHORT = re.compile('^[A-F0-9]{3}$', re.IGNORECASE)
20 |
21 |
22 | def _get_color(value, type):
23 | """Returns valid color"""
24 | if value and value.startswith('!'):
25 | # normalize hex color
26 | if RE_HEX_FULL.match(value[1:]):
27 | color = '#%s' % ''.join(value[1:]).upper()
28 | elif RE_HEX_SHORT.match(value[1:]):
29 | # duplicate short values
30 | color = '#%s' % ''.join(c * 2 for c in value[1:]).upper()
31 | else:
32 | # invalid color
33 | color = DEFAULT_COLORS[type]
34 | return color
35 | else:
36 | # assume literal color. e.g. white, grey69, etc
37 | return value if value else DEFAULT_COLORS[type]
38 |
39 |
40 | class DummyImageForm(forms.Form):
41 | bgcolor = forms.CharField(initial=DEFAULT_BG, required=False)
42 | transparent = forms.BooleanField(initial=False, required=False)
43 |
44 | text = forms.CharField(required=False)
45 | textcolor = forms.CharField(initial=DEFAULT_TEXT, required=False)
46 |
47 | bordercolor = forms.CharField(initial=DEFAULT_BORDER, required=False)
48 | noborder = forms.BooleanField(initial=False, required=False)
49 | cross = forms.BooleanField(initial=False, required=False)
50 |
51 | width = forms.IntegerField(min_value=1, max_value=MAX_DIMENSION)
52 | height = forms.IntegerField(min_value=1, max_value=MAX_DIMENSION)
53 | rotate = forms.IntegerField(min_value=-359, max_value=359, initial=0,
54 | required=False)
55 |
56 | def clean_bgcolor(self):
57 | color = self.cleaned_data['bgcolor']
58 | color = _get_color(color, 'BG')
59 | return color
60 |
61 | def clean_textcolor(self):
62 | color = self.cleaned_data['textcolor']
63 | color = _get_color(color, 'TEXT')
64 | return color
65 |
66 | def clean_bordercolor(self):
67 | color = self.cleaned_data['bordercolor']
68 | color = _get_color(color, 'BORDER')
69 | return color
70 |
71 | def clean(self):
72 | cleaned_data = super(DummyImageForm, self).clean()
73 | text = cleaned_data.get('text')
74 | width = cleaned_data.get('width')
75 | height = cleaned_data.get('height')
76 |
77 | if not text and width and height:
78 | text = '%d x %d' % (width, height)
79 |
80 | return cleaned_data
81 |
--------------------------------------------------------------------------------
/dummyimage/models.py:
--------------------------------------------------------------------------------
1 | import os
2 | from PIL import Image
3 | from PIL import ImageDraw
4 | from PIL import ImageFont
5 |
6 | from dummyimage.forms import DummyImageForm
7 |
8 | # TODO: move to settings
9 | FONT_FILE = 'DroidSans.ttf'
10 | FONT_PATH = os.path.join(os.path.dirname(__file__), 'fonts', FONT_FILE)
11 |
12 |
13 | class DummyImage(object):
14 |
15 | class InvalidParams(Exception):
16 | def __init__(self, message, form):
17 | self.form = form
18 | super(DummyImage.InvalidParams, self).__init__(message)
19 |
20 | @classmethod
21 | def new(cls, width, height, **kwargs):
22 | data = kwargs.copy()
23 | data.update({
24 | 'width': width,
25 | 'height': height,
26 | })
27 | form = DummyImageForm(data=data)
28 |
29 | if not form.is_valid():
30 | raise cls.InvalidParams("Invalid image params", form)
31 |
32 | # custom text
33 | text = form.cleaned_data['text']
34 |
35 | # mode. Use RGBA if transparent
36 | mode = 'RGBA' if form.cleaned_data.get('transparent') else 'RGB'
37 | width = form.cleaned_data['width']
38 | height = form.cleaned_data['height']
39 | size = (width, height)
40 |
41 | # allow transparent color
42 | if 'RGBA' == mode:
43 | bgcolor = None
44 | else:
45 | bgcolor = form.cleaned_data['bgcolor']
46 |
47 | # color allows short hex format
48 | image = Image.new(mode, size, bgcolor)
49 | draw = ImageDraw.Draw(image)
50 |
51 | bordercolor = form.cleaned_data['bordercolor']
52 | # draw border
53 | if not form.cleaned_data.get('noborder'):
54 | draw.polygon([(0, 0), (width - 1, 0), (width - 1, height - 1),
55 | (0, height - 1)], outline=bordercolor)
56 |
57 | # draw cross
58 | if form.cleaned_data.get('cross'):
59 | draw.line([(0, 0), (width - 1, height - 1)], fill=bordercolor)
60 | draw.line([(0, height - 1), (width - 1, 0)], fill=bordercolor)
61 |
62 | # draw text centered
63 | if text:
64 | font = ImageFont.truetype(FONT_PATH, width / 10)
65 |
66 | center = (width / 2, height / 2)
67 | text_size = font.getsize(text)
68 | text_center = (center[0] - text_size[0] / 2,
69 | center[1] - text_size[1] / 2)
70 | draw.text(text_center, text, font=font,
71 | fill=form.cleaned_data['textcolor'])
72 |
73 | return image
74 |
--------------------------------------------------------------------------------
/dummyimage/settings.py:
--------------------------------------------------------------------------------
1 | DEBUG = True
2 | TEMPLATE_DEBUG = DEBUG
3 |
4 | DATABASES = {
5 | 'default': {
6 | 'ENGINE': 'django.db.backends.sqlite3',
7 | 'NAME': ':memory:',
8 | }
9 | }
10 |
11 | DUMMYIMAGE_MAX_DIMENSION = 1024
12 |
13 | ROOT_URLCONF = 'dummyimage.urls'
14 |
15 | INSTALLED_APPS = (
16 | 'dummyimage',
17 | )
18 |
19 |
--------------------------------------------------------------------------------
/dummyimage/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rmax/django-dummyimage/e95c60a1a8f9f4c9ed1805dc219ed79ad83b877c/dummyimage/templatetags/__init__.py
--------------------------------------------------------------------------------
/dummyimage/templatetags/dummyimage_tags.py:
--------------------------------------------------------------------------------
1 | from django.core.urlresolvers import reverse
2 | from django.template import Library
3 | from django.template import Node
4 | from django.template import TemplateSyntaxError
5 | from django.template import Variable
6 | from django.template import resolve_variable
7 | from django.utils.translation import ugettext as _
8 |
9 | register = Library()
10 |
11 | class DummyImageUrlNode(Node):
12 | def __init__(self, width, height, format, context_var):
13 | # TODO: support variable names instead of only integers
14 | self.width = width
15 | self.height = height
16 | self.format = format
17 | self.context_var = context_var
18 |
19 | def render(self, context):
20 | url = reverse('dummyimage.views.render_image',
21 | args=(self.width, self.height, self.format))
22 |
23 | if self.context_var:
24 | # push url to context
25 | context[self.context_var] = url
26 | return ''
27 | else:
28 | # render url
29 | return url
30 |
31 | def do_get_dummyimage_url(parser, token):
32 | """
33 | Returns dummy image URL
34 |
35 | Available formats: gif, jpg, png
36 | Filename format: {w}x{h}.{format} e.g. 100x100.jpg
37 |
38 | Usage::
39 | {% get_dummyimage_url [width] [height] [format] as [varname] %}
40 | {% get_dummyimage_url [width] [height] as [varname] %}
41 | {% get_dummyimage_url [filename] as [varname] %}
42 |
43 | {% get_dummyimage_url [width] [height] [format] %}
44 | {% get_dummyimage_url [width] [height] %}
45 | {% get_dummyimage_url [filename] %}
46 |
47 | Example::
48 |
49 |
50 |
51 | {% get_dummyimage_url 320 240 png as image_url %}
52 | {% get_dummyimage_url 320 240 as image_url %}
53 | {% get_dummyimage_url 320x240.gif as image_url %}
54 | """
55 | bits = token.contents.split()
56 | # optional arguments
57 | format = None
58 | varname = None
59 |
60 | # tag + 1-5 arguments
61 | if not 1 < len(bits) <= 6:
62 | raise TemplateSyntaxError(_("%s tag requires between one and five arguments") % bits[0])
63 |
64 | # tag + 5 arguments
65 | if len(bits) == 6:
66 | if not bits[4] == 'as':
67 | raise TemplateSyntaxError(_("if given, fourth argument to %s tag must be 'as'") % bits[0])
68 |
69 | width = bits[1]
70 | height = bits[2]
71 | format = bits[3]
72 | varname = bits[5]
73 |
74 | # tag + 4 arguments
75 | if len(bits) == 5:
76 | if not bits[3] == 'as':
77 | raise TemplateSyntaxError(_("if given, third argument to %s tag must be 'as'") % bits[0])
78 |
79 | width = bits[1]
80 | height = bits[2]
81 | varname = bits[4]
82 |
83 | # tag + 3 arguments
84 | if len(bits) == 4:
85 | if bits[2] == 'as':
86 | varname = bits[3]
87 | try:
88 | width, remain = bits[1].split('x')
89 | height, format = remain.split('.')
90 | except ValueError:
91 | raise TemplateSyntaxError(_("first argument to %s tag must be in format 'WidthxHeight.format'. e.g. 320x240.png") % bits[0])
92 | else:
93 | width = bits[1]
94 | height = bits[2]
95 | format = bits[3]
96 |
97 | # tag + 2 arguments
98 | if len(bits) == 3:
99 | width = bits[1]
100 | height = bits[2]
101 |
102 | # tag + 1 argument
103 | if len(bits) == 2:
104 | try:
105 | width, remain = bits[1].split('x')
106 | height, format = remain.split('.')
107 | except ValueError:
108 | raise TemplateSyntaxError(_("first argument to %s tag must be in format 'WidthxHeight.format'. e.g. 320x240.png") % bits[0])
109 |
110 | # validate width and height integers
111 | try:
112 | width = int(width)
113 | height = int(height)
114 | except ValueError:
115 | raise TemplateSyntaxError(_("width and height argument to %s tag must be integers") % bits[0])
116 |
117 | # validate format
118 | if not format:
119 | format = 'jpg' # default
120 | elif not format in ('jpg', 'gif', 'png'):
121 | raise TemplateSyntaxError(_("format argument must be either jpg, png or gif"))
122 |
123 | return DummyImageUrlNode(width, height, format, varname)
124 |
125 | register.tag('get_dummyimage_url', do_get_dummyimage_url)
126 |
127 |
128 |
--------------------------------------------------------------------------------
/dummyimage/tests.py:
--------------------------------------------------------------------------------
1 | from django.core.urlresolvers import reverse
2 | from django.template import Context
3 | from django.template import Template
4 |
5 | from django.test import Client
6 | from django.test import TestCase
7 |
8 | from dummyimage.forms import _get_color
9 |
10 |
11 | class TemplateTagTest(TestCase):
12 | def setUp(self):
13 | self.client = Client()
14 | self.get_url = lambda w, h, fmt: reverse(
15 | 'dummyimage.views.render_image',
16 | args=(w, h, fmt))
17 |
18 | def get_template(self, arguments):
19 | return '{%% load dummyimage_tags %%}{%% get_dummyimage_url %s %%}' % arguments
20 |
21 | def test_render_filename(self):
22 | url = self.get_url(100, 100, 'jpg')
23 | t = Template(self.get_template('100x100.jpg'))
24 | c = Context({})
25 | self.failUnlessEqual(url, t.render(c))
26 |
27 | def test_render_filename_to_context(self):
28 | url = self.get_url(100, 100, 'jpg')
29 | t = Template(self.get_template('100x100.jpg as myvar'))
30 | c = Context({})
31 | t.render(c)
32 | self.failUnlessEqual(url, c['myvar'])
33 |
34 | def test_render_width_and_height(self):
35 | url = self.get_url(100, 100, 'jpg')
36 | #@@@ default format jpg
37 | t = Template(self.get_template('100 100'))
38 | c = Context({})
39 | self.failUnlessEqual(url, t.render(c))
40 |
41 | def test_render_width_and_height_to_context(self):
42 | url = self.get_url(100, 100, 'jpg')
43 | #@@@ default format jpg
44 | t = Template(self.get_template('100 100 as myvar'))
45 | c = Context({})
46 | t.render(c)
47 | self.failUnlessEqual(url, c['myvar'])
48 |
49 | def test_render_width_height_format(self):
50 | url = self.get_url(100, 200, 'png')
51 | #@@@ default format jpg
52 | t = Template(self.get_template('100 200 png'))
53 | c = Context({})
54 | self.failUnlessEqual(url, t.render(c))
55 |
56 | def test_render_width_height_format_to_context(self):
57 | url = self.get_url(100, 200, 'png')
58 | #@@@ default format jpg
59 | t = Template(self.get_template('100 200 png as myvar'))
60 | c = Context({})
61 | t.render(c)
62 | self.failUnlessEqual(url, c['myvar'])
63 |
64 |
65 | class RenderViewTest(TestCase):
66 | def setUp(self):
67 | self.client = Client()
68 | self.get_url = lambda w, h, fmt: reverse(
69 | 'dummyimage.views.render_image',
70 | args=(w, h, fmt))
71 |
72 | def test_sizes_boundaries(self):
73 | # sizes
74 | response = self.client.get(self.get_url(0, 0, 'jpg'))
75 | self.failUnlessEqual(response.status_code, 404)
76 |
77 | response = self.client.get(self.get_url(1, 1, 'jpg'))
78 | self.failUnlessEqual(response.status_code, 200)
79 |
80 | response = self.client.get(self.get_url(1024, 1024, 'jpg'))
81 | self.failUnlessEqual(response.status_code, 200)
82 |
83 | response = self.client.get(self.get_url(1025, 1025, 'jpg'))
84 | self.failUnlessEqual(response.status_code, 404)
85 |
86 | def test_valid_formats(self):
87 | # formats
88 | response = self.client.get(self.get_url(1, 1, 'jpg'))
89 | self.failUnlessEqual(response.status_code, 200)
90 | self.failUnlessEqual(response['Content-Type'], 'image/jpeg')
91 |
92 | response = self.client.get(self.get_url(1, 1, 'gif'))
93 | self.failUnlessEqual(response.status_code, 200)
94 | self.failUnlessEqual(response['Content-Type'], 'image/gif')
95 |
96 | response = self.client.get(self.get_url(1, 1, 'png'))
97 | self.failUnlessEqual(response.status_code, 200)
98 | self.failUnlessEqual(response['Content-Type'], 'image/png')
99 |
100 | def test_invalid_formats(self):
101 | response = self.client.get(self.get_url(1, 1, 'avi'))
102 | self.failUnlessEqual(response.status_code, 404)
103 |
104 | response = self.client.get(self.get_url(1, 1, 'pic'))
105 | self.failUnlessEqual(response.status_code, 404)
106 |
107 | response = self.client.get(self.get_url(1, 1, 'bmp'))
108 | self.failUnlessEqual(response.status_code, 404)
109 |
110 |
111 | def test_rotation_param(self):
112 | ## Check Params
113 | url = self.get_url(1, 1, 'jpg')
114 |
115 | # rotation
116 | response = self.client.get(url, {'rotate': '-359'})
117 | self.failUnlessEqual(response.status_code, 200)
118 | response = self.client.get(url, {'rotate': '0'})
119 | self.failUnlessEqual(response.status_code, 200)
120 | response = self.client.get(url, {'rotate': '359'})
121 | self.failUnlessEqual(response.status_code, 200)
122 |
123 | def test_invalid_rotation_param(self):
124 | url = self.get_url(1, 1, 'jpg')
125 | # invalid
126 | response = self.client.get(url, {'rotate': 'asdf'})
127 | self.failUnlessEqual(response.status_code, 404)
128 |
129 | response = self.client.get(url, {'rotate': '-360'})
130 | self.failUnlessEqual(response.status_code, 404)
131 | response = self.client.get(url, {'rotate': '360'})
132 | self.failUnlessEqual(response.status_code, 404)
133 |
134 | def test_text_parameters(self):
135 | url = self.get_url(1, 1, 'jpg')
136 |
137 | response = self.client.get(url, {'text': 'asdf'})
138 | # TODO: verify actual font rendering
139 | self.assertEquals(response.status_code, 200)
140 |
141 |
142 | class GetColorTest(TestCase):
143 | def test_get_color_util(self):
144 | """
145 | Tests return of _get_color function
146 | """
147 | #@@@ defaults: bg -> white, text -> black, border -> grey
148 |
149 | def test_default_returns(self):
150 | # Test defaults
151 | self.failUnlessEqual('white', _get_color('', 'BG'))
152 | self.failUnlessEqual('grey', _get_color('', 'TEXT'))
153 | self.failUnlessEqual('grey', _get_color('', 'BORDER'))
154 |
155 | def test_default_values_if_invalid(self):
156 | self.failUnlessEqual('white', _get_color('!invalid', 'BG'))
157 | self.failUnlessEqual('grey', _get_color('!invalid', 'TEXT'))
158 | self.failUnlessEqual('grey', _get_color('!invalid', 'BORDER'))
159 |
160 | def test_hex_values(self):
161 | # Test hex values
162 | self.failUnlessEqual('#000000', _get_color('!000000', 'BG'))
163 | self.failUnlessEqual('#FFFFFF', _get_color('!ffffff', 'BG'))
164 |
165 | def test_short_hex_values(self):
166 | # Shorts
167 | self.failUnlessEqual('#001122', _get_color('!012', 'BG'))
168 | self.failUnlessEqual('#AABBCC', _get_color('!abc', 'BG'))
169 |
170 | def test_invalid_hex_values(self):
171 | # invalids
172 | self.failUnlessEqual('grey', _get_color('!xyz', 'TEXT'))
173 | self.failUnlessEqual('grey', _get_color('!1234567', 'BORDER'))
174 |
175 | def test_invalid_type(self):
176 | # Test exceptions
177 | # invalid type
178 | self.failUnlessRaises(KeyError, _get_color, '', 'TYPE')
179 | self.failUnlessRaises(KeyError, _get_color, '!invalid', 'TYPE')
180 |
181 |
--------------------------------------------------------------------------------
/dummyimage/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls.defaults import *
2 | from django.http import HttpResponseNotFound
3 |
4 | WIDTH_RE = r'(?P\d+)'
5 | HEIGHT_RE = r'(?P\d+)'
6 | FORMAT_RE = r'(?P[a-z]{3})'
7 | IMAGE_RE = r'^%sx%s\.%s$' % (WIDTH_RE, HEIGHT_RE, FORMAT_RE)
8 |
9 | urlpatterns = patterns('dummyimage.views',
10 | (IMAGE_RE, 'render_image'),
11 | )
12 |
13 |
14 | def view404(request):
15 | return HttpResponseNotFound()
16 |
17 |
18 | handler404 = view404
19 |
--------------------------------------------------------------------------------
/dummyimage/views.py:
--------------------------------------------------------------------------------
1 | from django.http import Http404
2 | from django.http import HttpResponse
3 |
4 | from .models import DummyImage
5 |
6 | AVAILABLE_FORMATS = ('jpeg', 'gif', 'png')
7 |
8 |
9 | def render_image(request, width, height, format):
10 | params = dict(request.GET.items())
11 | image_format = 'jpeg' if format == 'jpg' else format
12 | try:
13 | dummyimage = DummyImage.new(width, height, **params)
14 | except DummyImage.InvalidParams:
15 | raise Http404
16 |
17 | if image_format not in AVAILABLE_FORMATS:
18 | raise Http404
19 |
20 | # write image to response
21 | response = HttpResponse(mimetype='image/%s' % image_format)
22 | #TODO: catch exceptions
23 | dummyimage.save(response, image_format)
24 |
25 | return response
26 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from setuptools import setup, find_packages
3 |
4 | setup(
5 | name = "django-dummyimage",
6 | version = "0.1.1",
7 | description = "Dynamic Dummy Image Generator For Django!",
8 | author = "Rolando Espinoza La fuente",
9 | author_email = "darkrho@gmail.com",
10 | url = "https://github.com/darkrho/django-dummyimage",
11 | license = "BSD",
12 | packages = find_packages(),
13 | zip_safe=False, # because we're including media that Django needs
14 | include_package_data = True,
15 | install_requires = [
16 | 'django',
17 | 'pil',
18 | ],
19 | classifiers=[
20 | 'Programming Language :: Python',
21 | 'Framework :: Django',
22 | 'Development Status :: 4 - Beta',
23 | 'Intended Audience :: Developers',
24 | ],
25 | )
26 |
--------------------------------------------------------------------------------