def inline_images(self, css_src): """Here we will "inline" any images under a certain size threshold into the CSS in the form of "data:" URIs. IE 8 can't handle URLs longer than 32KB, so any image whose data URI is larger than that is skipped. """ KB = 1024.0 MAX_FILE_SIZE = 24 * KB # Largest size we consider for inlining MAX_DATA_URI_SIZE = 32 * KB # IE8's maximum URL size # We only want to replace asset references that show up inside of # `url()` rules (this avoids weird constructs like IE-specific filters # for transparent PNG support). base_pattern = get_static_pattern( self.settings.get('static_url_prefix')) pattern = r"""(url\(["']?)%s(["']?\))""" % base_pattern # Track duplicate images so that we can warn about them seen_assets = defaultdict(int) def replacer(match): before, url_prefix, rel_path, after = match.groups() path = make_absolute_static_path(self.settings['static_dir'], rel_path) assert os.path.isfile(path), (path, str(self)) if os.stat(path).st_size > MAX_FILE_SIZE: logging.debug('Not inlining %s (%.2fKB)', path, os.stat(path).st_size / KB) return match.group(0) else: encoded = base64.b64encode(open(path).read()) mime_type, _ = mimetypes.guess_type(path) if not mime_type and path.endswith('.otf'): mime_type = 'application/octet-stream' if not mime_type and path.endswith('.ttf'): mime_type = 'font/ttf' if not mime_type and path.endswith('.eot'): mime_type = 'application/vnd.ms-fontobject' if not mime_type and path.endswith('.woff'): mime_type = 'application/x-font-woff' if not mime_type and path.endswith('.json'): mime_type = 'application/json' if not mime_type and path.endswith('.svg'): mime_type = 'image/svg+xml' data_uri = 'data:%s;base64,%s' % (mime_type, encoded) if len(data_uri) >= MAX_DATA_URI_SIZE: logging.debug('Not inlining %s (%.2fKB encoded)', path, len(data_uri) / KB) return match.group(0) seen_assets['%s%s' % (url_prefix, rel_path)] += 1 return ''.join([before, data_uri, after]) result = re.sub(pattern, replacer, css_src) for url, count in seen_assets.iteritems(): if count > 1: logging.warn('inline asset duplicated %dx: %s', count, url) return result
def sub_static_version(src, manifest, replacement_prefix, static_dir, static_url_prefix): """Adjusts any static URLs in the given source to point to a different location. Static URLs are determined based on the the 'static_url_prefix' setting. They will be updated to point to the given replacement_prefix, which can be a string or a list of strings (in which case the actual replacement prefix will be chosen by sharding each asset's base name). """ def replacer(match): prefix, rel_path = match.groups() path = make_relative_static_path(static_dir, rel_path) if path in manifest.assets: versioned_path = manifest.assets[path]['versioned_path'] if isinstance(replacement_prefix, (list, tuple)): prefix = get_shard_from_list(replacement_prefix, versioned_path) else: prefix = replacement_prefix replacement_link = prefix.rstrip( '/') + '/' + versioned_path.lstrip('/') logging.info('replacing %s -> %s', path, replacement_link) return replacement_link logging.warn('Missing path %s in manifest, using %s', path, match.group(0)) return match.group(0) pattern = get_static_pattern(static_url_prefix) return re.sub(pattern, replacer, src)
def sub_static_version(src, manifest, replacement_prefix, static_dir, static_url_prefix): """Adjusts any static URLs in the given source to point to a different location. Static URLs are determined based on the the 'static_url_prefix' setting. They will be updated to point to the given replacement_prefix, which can be a string or a list of strings (in which case the actual replacement prefix will be chosen by sharding each asset's base name). """ def replacer(match): prefix, rel_path = match.groups() path = make_relative_static_path(static_dir, rel_path) if path in manifest.assets: versioned_path = manifest.assets[path]['versioned_path'] if isinstance(replacement_prefix, (list, tuple)): prefix = get_shard_from_list(replacement_prefix, versioned_path) else: prefix = replacement_prefix replacement_link = prefix.rstrip('/') + '/' + versioned_path.lstrip('/') logging.info('replacing %s -> %s', path, replacement_link) return replacement_link logging.warn('Missing path %s in manifest, using %s', path, match.group(0)) return match.group(0) pattern = get_static_pattern(static_url_prefix) return re.sub(pattern, replacer, src)
def inline_images(self, css_src): """Here we will "inline" any images under a certain size threshold into the CSS in the form of "data:" URIs. IE 8 can't handle URLs longer than 32KB, so any image whose data URI is larger than that is skipped. """ KB = 1024.0 MAX_FILE_SIZE = 24 * KB # Largest size we consider for inlining MAX_DATA_URI_SIZE = 32 * KB # IE8's maximum URL size # We only want to replace asset references that show up inside of # `url()` rules (this avoids weird constructs like IE-specific filters # for transparent PNG support). base_pattern = get_static_pattern(self.settings.get('static_url_prefix')) pattern = r"""(url\(["']?)%s(["']?\))""" % base_pattern # Track duplicate images so that we can warn about them seen_assets = defaultdict(int) def replacer(match): before, url_prefix, rel_path, after = match.groups() path = make_absolute_static_path(self.settings['static_dir'], rel_path) assert os.path.isfile(path), (path, str(self)) if os.stat(path).st_size > MAX_FILE_SIZE: logging.debug('Not inlining %s (%.2fKB)', path, os.stat(path).st_size / KB) return match.group(0) else: encoded = base64.b64encode(open(path).read()) mime_type, _ = mimetypes.guess_type(path) if not mime_type and path.endswith('.otf'): mime_type = 'application/octet-stream' if not mime_type and path.endswith('.ttf'): mime_type = 'font/ttf' if not mime_type and path.endswith('.eot'): mime_type = 'application/vnd.ms-fontobject' if not mime_type and path.endswith('.woff'): mime_type = 'application/x-font-woff' if not mime_type and path.endswith('.json'): mime_type = 'application/json' if not mime_type and path.endswith('.svg'): mime_type = 'image/svg+xml' data_uri = 'data:%s;base64,%s' % (mime_type, encoded) if len(data_uri) >= MAX_DATA_URI_SIZE: logging.debug('Not inlining %s (%.2fKB encoded)', path, len(data_uri) / KB) return match.group(0) seen_assets['%s%s' % (url_prefix, rel_path)] += 1 return ''.join([before, data_uri, after]) result = re.sub(pattern, replacer, css_src) for url, count in seen_assets.iteritems(): if count > 1: logging.warn('inline asset duplicated %dx: %s', count, url) return result
def static_finder(s, static_url_prefix): pattern = get_static_pattern(static_url_prefix) return re.compile(pattern).finditer(s)