Commit dd9d59ee by Timothée Peignier

add datauri variant support

parent e5c94fc0
import base64
import os
import re
import subprocess
......@@ -7,10 +8,30 @@ from compress.conf import settings
from compress.storage import storage
from compress.utils import to_class
MAX_IMAGE_SIZE = 32700
EMBEDDABLE = r'[\A\/]embed\/'
URL_DETECTOR = r'url\([\'"]?([^\s)]+\.[a-z]+)[\'"]?\)'
URL_REPLACER = r'url\(__EMBED__(.+?)(\?\d+)?\)'
MIME_TYPES = {
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.tif': 'image/tiff',
'.tiff': 'image/tiff',
'.ttf': 'font/truetype',
'.otf': 'font/opentype',
'.woff': 'font/woff'
}
EMBED_EXTS = MIME_TYPES.keys()
FONT_EXTS = ['.ttf', '.otf', '.woff']
class Compressor(object):
asset_contents = {}
def __init__(self, verbose=False):
self.verbose = verbose
......@@ -29,14 +50,19 @@ class Compressor(object):
js = getattr(compressor(verbose=self.verbose), 'compress_js')(js)
return js
def compress_css(self, paths):
def compress_css(self, paths, variant=None):
"""Concatenate and compress CSS files"""
css = self.concatenate_and_rewrite(paths)
css = self.concatenate_and_rewrite(paths, variant)
for compressor in self.css_compressors:
css = getattr(compressor(verbose=self.verbose), 'compress_css')(css)
if not variant:
return css
elif variant == "datauri":
return self.with_data_uri(css)
else:
raise CompressorError("\"%s\" is not a valid variant" % variant)
def concatenate_and_rewrite(self, paths):
def concatenate_and_rewrite(self, paths, variant=None):
"""Concatenate together files and rewrite urls"""
stylesheets = []
for path in paths:
......@@ -44,10 +70,7 @@ class Compressor(object):
asset_path = match.group(1)
if asset_path.startswith("http") or asset_path.startswith("//"):
return "url(%s)" % asset_path
asset_url = urlparse.urljoin(
settings.COMPRESS_URL,
self.construct_asset_path(asset_path, path)[1:]
)
asset_url = self.construct_asset_path(asset_path, path, variant)
return "url(%s)" % asset_url
content = self.read_file(path)
content = re.sub(URL_DETECTOR, reconstruct, content)
......@@ -59,12 +82,45 @@ class Compressor(object):
content = '\n'.join([self.read_file(path) for path in paths])
return "(function() { %s }).call(this);" % content
def construct_asset_path(self, asset_path, css_path):
def construct_asset_path(self, asset_path, css_path, variant=None):
public_path = self.absolute_path(asset_path, css_path)
if os.path.isabs(asset_path):
return asset_path
else:
return self.relative_path(public_path)
if self.embeddable(public_path, variant):
return "__EMBED__%s" % public_path
if not os.path.isabs(asset_path):
asset_path = self.relative_path(public_path)
return urlparse.urljoin(settings.COMPRESS_URL, asset_path[1:])
def embeddable(self, path, variant):
name, ext = os.path.splitext(path)
font = ext in FONT_EXTS
if not variant:
return False
if not re.match(path, EMBEDDABLE) and not os.path.exists(path):
return False
if not ext in EMBED_EXTS:
return False
if not (font or len(self.encoded_content(path)) < MAX_IMAGE_SIZE):
return False
return True
def with_data_uri(self, css):
def datauri(match):
path = match.group(1)
mime_type = self.mime_type(path)
data = self.encoded_content(path)
return "url(\"data:%s;charset=utf-8;base64,%s\")" % (mime_type, data)
return re.sub(URL_REPLACER, datauri, css)
def encoded_content(self, path):
if path in self.__class__.asset_contents:
return self.__class__.asset_contents[path]
data = self.read_file(path)
self.__class__.asset_contents[path] = base64.b64encode(data)
return self.__class__.asset_contents[path]
def mime_type(self, path):
name, ext = os.path.splitext(path)
return MIME_TYPES[ext]
def absolute_path(self, asset_path, css_path):
if os.path.isabs(asset_path):
......
......@@ -37,12 +37,14 @@ class Packager(object):
self.compressor.relative_path(filename)[1:])
def pack_stylesheets(self, package):
return self.pack(package, self.compressor.compress_css, css_compressed)
variant = package.get('variant', None)
return self.pack(package, self.compressor.compress_css, css_compressed,
variant=variant)
def compile(self, paths):
return self.compiler.compile(paths)
def pack(self, package, compress, signal):
def pack(self, package, compress, signal, **kwargs):
if settings.COMPRESS_AUTO or self.force:
need_update, version = self.versioning.need_update(
package['output'], package['paths'])
......@@ -54,7 +56,7 @@ class Packager(object):
print "Version: %s" % version
print "Saving: %s" % self.compressor.relative_path(output_filename)
paths = self.compile(package['paths'])
content = compress(paths)
content = compress(paths, **kwargs)
self.save_file(output_filename, content)
signal.send(sender=self, package=package, version=version)
else:
......@@ -93,6 +95,8 @@ class Packager(object):
packages[name]['context'] = config[name]['extra_context']
if 'template_name' in config[name]:
packages[name]['template'] = config[name]['template_name']
if 'variant' in config[name]:
packages[name]['variant'] = config[name]['variant']
return packages
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment