Added a CSS data URI generator

Signed-off-by: Jannis Leidel <jannis@leidel.info>
This commit is contained in:
Atamert Ölçgen
2010-02-17 23:15:59 +02:00
committed by Jannis Leidel
parent 24b67475ac
commit 316033916e
6 changed files with 67 additions and 0 deletions

View File

@@ -18,3 +18,5 @@ if COMPRESS_CSS_FILTERS is None:
if COMPRESS_JS_FILTERS is None:
COMPRESS_JS_FILTERS = []
COMPRESS_DATA_URI_MIN_SIZE = getattr(settings, 'COMPRESS_DATA_URI_MIN_SIZE', 1024)

View File

@@ -0,0 +1,46 @@
import os, re
from base64 import b64encode
from mimetypes import guess_type
from compressor.filters import FilterBase, FilterError
from compressor.conf import settings
class DataUriFilter(FilterBase):
"""Filter for embedding media as data: URIs.
Settings:
COMPRESS_DATA_URI_MIN_SIZE: Only files that are smaller than this
value will be embedded. Unit; bytes.
Don't use this class directly. Use a subclass.
"""
def input(self, filename=None, **kwargs):
if not filename or not filename.startswith(settings.MEDIA_ROOT):
return self.content
output = self.content
for url_pattern in self.url_patterns:
output = url_pattern.sub(self.data_uri_converter, output)
return output
def get_file_path(self, url):
return os.path.join(settings.MEDIA_ROOT, url[len(settings.MEDIA_URL):])
def data_uri_converter(self, matchobj):
url = matchobj.group(1).strip(' \'"')
if not url.startswith('data:'):
path = self.get_file_path(url)
if os.stat(path).st_size <= settings.COMPRESS_DATA_URI_MIN_SIZE:
data = b64encode(open(path, 'rb').read())
return 'url("data:%s;base64,%s")' % (guess_type(path)[0], data)
return 'url("%s")' % url
class CssDataUriFilter(DataUriFilter):
"""Filter for embedding media as data: URIs in CSS files.
See DataUriFilter.
"""
url_patterns = (
re.compile(r'url\(([^\)]+)\)'),
)

View File

@@ -137,6 +137,21 @@ class CssAbsolutizingTestCase(TestCase):
self.assertEqual(out, self.cssNode.hunks)
class CssDataUriTestCase(TestCase):
def setUp(self):
settings.COMPRESS = True
settings.COMPRESS_CSS_FILTERS = ['compressor.filters.datauri.CssDataUriFilter']
settings.MEDIA_URL = '/media/'
self.css = """
<link rel="stylesheet" href="/media/css/datauri.css" type="text/css" charset="utf-8">
"""
self.cssNode = CssCompressor(self.css)
def test_data_uris(self):
out = [u'.add { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLpZPrS5NhGIf9W7YvBYOkhlkoqCklWChv2WyKik7blnNris72bi6dus0DLZ0TDxW1odtopDs4D8MDZuLU0kXq61CijSIIasOvv94VTUfLiB74fXngup7nvrnvJABJ/5PfLnTTdcwOj4RsdYmo5glBWP6iOtzwvIKSWstI0Wgx80SBblpKtE9KQs/We7EaWoT/8wbWP61gMmCH0lMDvokT4j25TiQU/ITFkek9Ow6+7WH2gwsmahCPdwyw75uw9HEO2gUZSkfyI9zBPCJOoJ2SMmg46N61YO/rNoa39Xi41oFuXysMfh36/Fp0b7bAfWAH6RGi0HglWNCbzYgJaFjRv6zGuy+b9It96N3SQvNKiV9HvSaDfFEIxXItnPs23BzJQd6DDEVM0OKsoVwBG/1VMzpXVWhbkUM2K4oJBDYuGmbKIJ0qxsAbHfRLzbjcnUbFBIpx/qH3vQv9b3U03IQ/HfFkERTzfFj8w8jSpR7GBE123uFEYAzaDRIqX/2JAtJbDat/COkd7CNBva2cMvq0MGxp0PRSCPF8BXjWG3FgNHc9XPT71Ojy3sMFdfJRCeKxEsVtKwFHwALZfCUk3tIfNR8XiJwc1LmL4dg141JPKtj3WUdNFJqLGFVPC4OkR4BxajTWsChY64wmCnMxsWPCHcutKBxMVp5mxA1S+aMComToaqTRUQknLTH62kHOVEE+VQnjahscNCy0cMBWsSI0TCQcZc5ALkEYckL5A5noWSBhfm2AecMAjbcRWV0pUTh0HE64TNf0mczcnnQyu/MilaFJCae1nw2fbz1DnVOxyGTlKeZft/Ff8x1BRssfACjTwQAAAABJRU5ErkJggg=="); }\n.python { background-image: url("/media/img/python.png"); }\n.datauri { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0 vr4MkhoXe0rZigAAAABJRU5ErkJggg=="); }\n']
self.assertEqual(out, self.cssNode.hunks)
class CssMediaTestCase(TestCase):
def setUp(self):
self.css = """
@@ -170,6 +185,7 @@ def render(template_string, context_dict=None):
t = Template(template_string)
return t.render(c).strip()
class TemplatetagTestCase(TestCase):
def setUp(self):
settings.COMPRESS = True

View File

@@ -0,0 +1,3 @@
.add { background-image: url("../img/add.png"); }
.python { background-image: url("../img/python.png"); }
.datauri { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0 vr4MkhoXe0rZigAAAABJRU5ErkJggg=="); }

BIN
tests/media/img/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

BIN
tests/media/img/python.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB