View source
<?php
define('CSS_EMIMAGE_IE_DATAURI_LIMIT', 32768);
define('CSS_EMIMAGE_DUPLICATE_EMBED_LIMIT', 10240);
define('CSS_EMIMAGE_INLINE_DATAURI_LIMIT', 4096);
function css_emimage_help($path, $arg) {
switch ($path) {
case 'admin/help#css_emimage':
$output = '<p>' . t('Replaces image URLs in aggregated CSS files with embedded images when <em>CSS optimization</em> has been enabled in the <a href="@performance">Performance settings</a>.', array(
'@performance' => url('admin/settings/performance'),
)) . '</p>';
return $output;
}
}
function css_emimage_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'system_performance_settings') {
$form['bandwidth_optimizations']['css_emimage'] = array(
'#type' => 'fieldset',
'#title' => t('CSS Embedded Images'),
'css_emimage_force_inline' => array(
'#type' => 'checkbox',
'#title' => t('Always inline embedded images'),
'#description' => t('By default CSS Embedded Images places image data exceeding !limit in a CSS file separate from the rest of the site styles. This allows for parallel rendering of site styles while the relatively large image data is in transit, providing an improved user experience (especially for visitors with slow connections). Enabling this option will force images to always be embedded inline, similar to the 6.x-1.x behavior; however, this is not recommended.', array(
'!limit' => format_size(variable_get('css_emimage_inline_datauri_limit', CSS_EMIMAGE_INLINE_DATAURI_LIMIT)),
)),
'#default_value' => variable_get('css_emimage_force_inline', 0),
),
'css_emimage_ielimit' => array(
'#type' => 'checkbox',
'#title' => t('Only embed images less than 32KB'),
'#description' => t('Internet Explorer does not support embedded images larger than 32KB. If you are not concerned about IE support you can ignore this limitation; otherwise, it is best to leave this checked.'),
'#default_value' => variable_get('css_emimage_ielimit', 1),
),
);
}
}
function css_emimage_advagg_filenames_alter(&$filenames) {
global $base_path;
$output = array();
foreach ($filenames as $values) {
$filetype = $values['filetype'];
$files = $values['files'];
$counter = $values['counter'];
$bundle_md5 = $values['bundle_md5'];
if ($filetype != 'css') {
$output[] = $values;
continue;
}
$cached_data_key = 'css_emimage_' . $bundle_md5;
$cached_data = advagg_cached_bundle_get($cached_data_key, 'css_emimage_filenames_alter');
if (!empty($cached_data)) {
$good = TRUE;
foreach ($cached_data as $info) {
$saved = cache_get($info['bundle_md5'], 'cache_css_emimage_advagg');
if (empty($saved->data)) {
$good = FALSE;
}
}
if ($good) {
$output = array_merge($output, $cached_data);
continue;
}
}
$cached_data = array();
$has_image = FALSE;
foreach ($files as $filename) {
$filename_md5 = md5($filename);
$data = advagg_get_file_data($filename_md5);
if (!empty($data['css_emimage'])) {
$has_image = TRUE;
break;
}
if (!isset($data['css_emimage'])) {
if (!advagg_file_exists($filename)) {
continue;
}
$css = file_get_contents($filename);
$pattern = '/(background(?:-image)?|list-style(?:-image)?):[^{};)]*?((?:none|url\\([\'"]?(.+?)[\'"]?\\)))([^{};]*)/i';
if (preg_match_all($pattern, $css, $matches) > 0) {
$images = $matches[3];
foreach ($matches[3] as $imagename) {
if (empty($imagename)) {
continue;
}
$imagename = preg_replace('/^' . preg_quote($base_path, '/') . '/i', '', $imagename);
if (file_exists($filename) == FALSE) {
continue;
}
$has_image = TRUE;
break;
}
}
if (!$has_image) {
$lock_name = 'advagg_set_file_data_' . $filename_md5;
if (!lock_acquire($lock_name, 10)) {
lock_wait($lock_name);
}
$data = advagg_get_file_data($filename_md5);
$data['css_emimage'] = 0;
advagg_set_file_data($filename_md5, $data);
lock_release($lock_name);
}
}
if ($has_image) {
break;
}
}
if ($has_image) {
$saved = cache_get($bundle_md5, 'cache_css_emimage_advagg');
if (!empty($saved->data)) {
if ($saved->data != 'none') {
$output[] = $values;
$cached_data = array(
$values,
);
cache_set($cached_data_key, $cached_data, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY);
continue;
}
else {
cache_clear_all($bundle_md5, 'cache_advagg');
}
}
$values['bundle_md5'] = md5($bundle_md5 . 'base');
$saved = cache_get($values['bundle_md5'], 'cache_css_emimage_advagg');
if (empty($saved->data)) {
cache_set($values['bundle_md5'], 'base', 'cache_css_emimage_advagg');
}
$output[] = $values;
$cached_data[] = $values;
$values['bundle_md5'] = md5($bundle_md5 . 'emimage');
$saved = cache_get($values['bundle_md5'], 'cache_css_emimage_advagg');
if (empty($saved->data)) {
cache_set($values['bundle_md5'], 'emimage', 'cache_css_emimage_advagg');
}
$output[] = $values;
$cached_data[] = $values;
$values['bundle_md5'] = md5($bundle_md5 . 'orig');
$saved = cache_get($values['bundle_md5'], 'cache_css_emimage_advagg');
if (empty($saved->data)) {
cache_set($values['bundle_md5'], 'orig', 'cache_css_emimage_advagg');
}
$output[] = $values;
$cached_data[] = $values;
cache_set($cached_data_key, $cached_data, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY);
}
else {
$saved = cache_get($values['bundle_md5'], 'cache_css_emimage_advagg');
if (empty($saved->data)) {
cache_set($values['bundle_md5'], 'none', 'cache_css_emimage_advagg');
}
$output[] = $values;
$data = array(
$values,
);
cache_set($cached_data_key, $data, 'cache_advagg_bundle_reuse', CACHE_TEMPORARY);
$key = md5($bundle_md5 . 'base');
$saved = cache_get($key, 'cache_css_emimage_advagg');
if (!empty($saved->data)) {
cache_clear_all($key, 'cache_advagg');
}
$key = md5($bundle_md5 . 'emimage');
$saved = cache_get($key, 'cache_css_emimage_advagg');
if (!empty($saved->data)) {
cache_clear_all($key, 'cache_advagg');
}
$key = md5($bundle_md5 . 'orig');
$saved = cache_get($key, 'cache_css_emimage_advagg');
if (!empty($saved->data)) {
cache_clear_all($key, 'cache_advagg');
}
}
}
$filenames = $output;
}
function css_emimage_advagg_css_extra_alter(&$values) {
list($filename, $bundle_md5, $prefix, $suffix) = $values;
if (!empty($prefix) || !empty($suffix)) {
return;
}
$type = cache_get($bundle_md5, 'cache_css_emimage_advagg');
if (!empty($type->data)) {
$type = $type->data;
}
if ($type == 'base') {
$prefix = "<!--[if gte IE 8]><!-->";
$suffix = "<!--<![endif]-->";
}
elseif ($type == 'emimage') {
$prefix = "<!--[if gte IE 8]><!-->";
$suffix = "<!--<![endif]-->";
}
elseif ($type == 'orig') {
$prefix = "<!--[if lt IE 8]>";
$suffix = "<![endif]-->";
}
$values = array(
$filename,
$bundle_md5,
$prefix,
$suffix,
);
}
function css_emimage_advagg_css_alter($data, $files, $bundle_md5) {
$type = cache_get($bundle_md5, 'cache_css_emimage_advagg');
if (!empty($type->data)) {
$type = $type->data;
}
if (empty($type)) {
return;
}
if ($type == 'none' || $type == 'orig') {
return;
}
_css_emimage_text_processor($data, $bundle_md5, $type);
if (empty($data)) {
$data = 'html {display:block;}';
}
}
function css_emimage_advagg_files_table($row, $checksum) {
global $base_path;
if ($row['filetype'] != 'css') {
return;
}
$images = array();
$css = advagg_build_css_bundle(array(
$row['filename'],
));
$lock_name = 'advagg_set_file_data_' . $row['filename_md5'];
if (!lock_acquire($lock_name)) {
lock_wait($lock_name);
}
$data = advagg_get_file_data($row['filename_md5']);
$pattern = '/(background(?:-image)?|list-style(?:-image)?):[^{};)]*?((?:none|url\\([\'"]?(.+?)[\'"]?\\)))([^{};]*)/i';
if (preg_match_all($pattern, $css, $matches) > 0) {
$images = $matches[3];
}
if (empty($images)) {
if (isset($data['css_emimage']) && $data['css_emimage'] == 0) {
lock_release($lock_name);
return;
}
$data['css_emimage'] = 0;
advagg_set_file_data($row['filename_md5'], $data);
lock_release($lock_name);
return;
}
$out = array();
$rebuild = FALSE;
$save = FALSE;
foreach ($images as $filename) {
if (empty($filename)) {
lock_release($lock_name);
continue;
}
$filename = preg_replace('/^' . preg_quote($base_path, '/') . '/i', '', $filename);
if (file_exists($filename) == FALSE) {
lock_release($lock_name);
continue;
}
$checksum = advagg_checksum($filename);
if (!empty($data['css_emimage'][$filename])) {
if ($data['css_emimage'][$filename] != $checksum) {
$rebuild = TRUE;
$save = TRUE;
}
}
else {
$save = TRUE;
}
$data['css_emimage'] = array();
$data['css_emimage'][$filename] = $checksum;
}
if ($save) {
advagg_set_file_data($row['filename_md5'], $data);
}
lock_release($lock_name);
return $rebuild;
}
function css_emimage_advagg_master_reset() {
cache_clear_all('*', 'cache_css_emimage_advagg', TRUE);
}
function css_emimage_theme_registry_alter(&$theme_registry) {
if (isset($theme_registry['page']) && !module_exists('advagg')) {
if (($key = array_search('css_emimage_preprocess_page', $theme_registry['page']['preprocess functions'])) !== FALSE) {
unset($theme_registry['page']['preprocess functions'][$key]);
}
$theme_registry['page']['preprocess functions'][] = 'css_emimage_preprocess_page';
if (($key = array_search('css_gzip_preprocess_page', $theme_registry['page']['preprocess functions'])) !== FALSE) {
unset($theme_registry['page']['preprocess functions'][$key]);
$theme_registry['page']['preprocess functions'][] = 'css_gzip_preprocess_page';
}
}
}
function css_emimage_preprocess_page(&$variables) {
if (!empty($variables['styles']) && variable_get('preprocess_css', 0)) {
$variables['styles'] = _css_emimage_process($variables['styles']);
}
}
function _css_emimage_process($styles) {
global $base_url;
$path_to_files_directory = base_path() . file_directory_path();
$pattern = '/<link(.*?)href=".*?' . preg_quote($path_to_files_directory, '/') . '(.*?)(\\?[^"]*)?"(.*?)\\/>/';
if (preg_match_all($pattern, $styles, $matches) > 0) {
foreach ($matches[2] as $i => $aggregated_file_name) {
$file_path = file_directory_path();
$aggregated_file_path = $file_path . $aggregated_file_name;
$emimage_file_name = str_replace('.css', '.emimage.css', $aggregated_file_name);
$emimage_file_path = $file_path . $emimage_file_name;
$orig_file_name = str_replace('.css', '.orig.css', $aggregated_file_name);
$orig_file_path = $file_path . $orig_file_name;
if (!file_exists($emimage_file_path) || filemtime($aggregated_file_path) > filemtime($emimage_file_path)) {
$contents = $orig_contents = file_get_contents($aggregated_file_path);
file_save_data($orig_contents, $orig_file_path, FILE_EXISTS_REPLACE);
_css_emimage_text_processor($orig_contents, $orig_file_path, 'base');
file_save_data($orig_contents, $aggregated_file_path, FILE_EXISTS_REPLACE);
_css_emimage_text_processor($contents, $orig_file_path, 'emimage');
file_save_data($contents, $emimage_file_path, FILE_EXISTS_REPLACE);
}
if (file_exists($emimage_file_path) && filesize($emimage_file_path)) {
$styles = str_replace($matches[0][$i], "<!--[if gte IE 8]><!-->\n" . (filesize($aggregated_file_path) ? $matches[0][$i] . "\n" : '') . str_replace($aggregated_file_name, $emimage_file_name, $matches[0][$i]) . "\n<!--<![endif]-->\n" . "<!--[if lt IE 8]>\n" . str_replace($aggregated_file_name, $orig_file_name, $matches[0][$i]) . "\n<![endif]-->", $styles);
}
}
}
return $styles;
}
function _css_emimage_text_processor(&$data, $key, $type) {
static $values = array();
if ($type != 'base' && $type != 'emimage') {
return;
}
if (empty($values[$key][$type])) {
_css_emimage_collect_static(array(
array(),
array(),
));
$contents = $data;
$datauri_css = '';
$pattern = '/([^{}]+){([^{}]*?(background(?:-image)?|list-style(?:-image)?):[^{};)]*?(?:none|url\\([\'"]?.+?[\'"]?\\))[^{}]*)}/i';
$contents = preg_replace_callback($pattern, '_css_emimage_replace', $contents);
if (!is_null($contents)) {
list($declarations, $file_stats) = _css_emimage_collect_static();
$datauri_total_length = 0;
foreach ($file_stats as $fs) {
if (count($fs['indices']) > 1 && $fs['total_length'] > variable_get('css_emimage_duplicate_embed_limit', CSS_EMIMAGE_DUPLICATE_EMBED_LIMIT)) {
foreach ($fs['indices'] as $fsi) {
$declarations[$fsi]['base64'] = '';
}
}
else {
$datauri_total_length += $fs['total_length'];
}
}
list($ext_contents, $ext_data) = _css_emimage_build_external($contents, $declarations);
if (variable_get('css_emimage_force_inline', 0) || $datauri_total_length && $datauri_total_length <= variable_get('css_emimage_inline_datauri_limit', CSS_EMIMAGE_INLINE_DATAURI_LIMIT)) {
$inline = _css_emimage_build_inline($contents, $declarations);
if (strlen($inline) < strlen($ext_contents) + strlen($ext_data)) {
$datauri_css = $inline;
}
else {
$datauri_css = "{$ext_contents}\n{$ext_data}";
}
$contents = '';
}
else {
$contents = $ext_contents;
$datauri_css = $ext_data;
}
}
else {
$error_code = preg_last_error();
$error_messages = array(
PREG_NO_ERROR => 'NO_ERROR',
PREG_INTERNAL_ERROR => 'INTERNAL_ERROR',
PREG_BACKTRACK_LIMIT_ERROR => 'BACKTRACK_LIMIT_ERROR',
PREG_RECURSION_LIMIT_ERROR => 'RECURSION_LIMIT_ERROR',
PREG_BAD_UTF8_ERROR => 'BAD_UTF8_ERROR',
PREG_BAD_UTF8_OFFSET_ERROR => 'BAD_UTF8_OFFSET_ERROR',
);
watchdog('css_emimage', 'Error while trying to embed images in your CSS, falling back to unmodified CSS. PCRE error was: !error.', array(
'!error' => array_key_exists($error_code, $error_messages) ? $error_messages[$error_code] : $error_code,
), WATCHDOG_ERROR);
return;
}
$values[$key]['base'] = $contents;
$values[$key]['emimage'] = $datauri_css;
}
$data = $values[$key][$type];
unset($values[$key][$type]);
}
function _css_emimage_replace($matches) {
list($declaration, $selector, $properties) = $matches;
$pattern = '/(background(?:-image)?|list-style(?:-image)?):[^{};)]*?((?:none|url\\([\'"]?(.+?)[\'"]?\\)))([^{};]*)/i';
preg_match_all($pattern, $properties, $matches);
foreach ($matches[1] as $i => $property) {
$url = $matches[2][$i];
$file = $matches[3][$i];
$important = stripos($matches[4][$i], '!important') !== FALSE;
if ($file && strpos($file, base_path()) === 0 && ($image = image_get_info($file = substr($file, strlen(base_path()))))) {
$ielimit = variable_get('css_emimage_ielimit', 1);
if ($ielimit && $image['file_size'] * 1.3333 >= CSS_EMIMAGE_IE_DATAURI_LIMIT) {
$image = NULL;
}
$token = _css_emimage_collect(array(
$selector,
$property,
$url,
$file,
$important,
$image,
));
$declaration = str_replace($url, $token, $declaration);
}
else {
_css_emimage_collect(array(
$selector,
$property,
$url,
$file,
$important,
NULL,
));
}
}
return $declaration;
}
function _css_emimage_collect($info) {
list($declarations, $file_stats) = _css_emimage_collect_static();
if (!$declarations) {
$declarations = array();
}
if (!$file_stats) {
$file_stats = array();
}
if (is_array($info)) {
list($selector, $property, $url, $file, $important, $image) = $info;
$selector = trim($selector);
if ($property == 'background' || $property == 'list-style') {
$property .= '-image';
}
$current = array_pop($declarations);
switch (TRUE) {
case $current && ($current['property'] != $property || $current['file'] != $file || $current['important'] != $important):
array_push($declarations, $current);
case !$current:
$pos = count($declarations);
$current = array(
'pos' => $pos,
'token' => "[css_emimage:{$pos}]",
'selectors' => array(
$selector,
),
'property' => $property,
'url' => $url,
'file' => $file,
'important' => $important,
'base64' => $image ? base64_encode(file_get_contents($file)) : '',
'mime_type' => $image ? $image['mime_type'] : '',
);
if ($current['base64']) {
if (!isset($file_stats[$file])) {
$file_stats[$file] = array(
'indices' => array(
$pos,
),
'total_length' => strlen($current['base64']),
);
}
else {
$file_stats[$file]['indices'][] = $pos;
$file_stats[$file]['total_length'] += strlen($current['base64']);
}
}
break;
default:
$current['selectors'][] = $selector;
break;
}
array_push($declarations, $current);
_css_emimage_collect_static(array(
$declarations,
$file_stats,
));
return $current['token'];
}
return FALSE;
}
function _css_emimage_collect_static($data = NULL) {
static $store = array();
if (!is_null($data)) {
$store = $data;
}
return $store;
}
function _css_emimage_build_inline($css, $declarations) {
foreach ($declarations as $data) {
if ($data['base64']) {
$css = str_replace($data['token'], 'url(data:' . $data['mime_type'] . ';base64,' . $data['base64'] . ')', $css);
}
else {
$css = str_replace($data['token'], $data['url'], $css);
}
}
return $css;
}
function _css_emimage_build_external($css, $declarations) {
$datauri_css = '';
foreach ($declarations as $data) {
if ($data['base64']) {
$css = str_replace($data['token'], 'none', $css);
$datauri_css .= implode(',', $data['selectors']) . '{' . $data['property'] . ':url(data:' . $data['mime_type'] . ';base64,' . $data['base64'] . ')' . ($data['important'] ? ' !important' : '') . ";}\n";
}
else {
if ($datauri_css) {
$css = str_replace($data['token'], 'none', $css);
$datauri_css .= implode(',', $data['selectors']) . '{' . $data['property'] . ':' . $data['url'] . ($data['important'] ? ' !important' : '') . ";}\n";
}
else {
$css = str_replace($data['token'], $data['url'], $css);
}
}
}
return array(
$css,
$datauri_css,
);
}