You are here

css_emimage.module in CSS Embedded Images 6

Same filename and directory in other branches
  1. 6.2 css_emimage.module
  2. 7 css_emimage.module

File

css_emimage.module
View source
<?php

/**
 * Implementation of hook_help().
 */
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;
  }
}

/**
 * Implementation of hook_form_alter().
 */
function css_emimage_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == 'system_performance_settings') {
    $form['bandwidth_optimizations']['preprocess_css']['#description'] .= t(' Once the CSS files have been aggregated, image URLs will be replaced with embedded images.');
    $form['bandwidth_optimizations']['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),
      '#weight' => $form['bandwidth_optimizations']['preprocess_css']['#weight'] + 0.1,
    );
  }
}

/**
 * Implementation of hook_theme_registry_alter().
 * 
 * Make css_emimage's page preprocess function run after everything else.
 * If the css_gzip module is installed, move it's preprocess function after ours.
 */
function css_emimage_theme_registry_alter(&$theme_registry) {
  if (isset($theme_registry['page'])) {

    // Move our preprocess function after everything else.
    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';

    // Move css_gzip's preprocess function after ours.
    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';
    }
  }
}

/**
 * Implementation of hook_preprocess_hook().
 * 
 * Replace URLs with data URIs in aggregated CSS files if CSS optimization is turned on.
 */
function css_emimage_preprocess_page(&$variables) {
  if (!empty($variables['styles']) && variable_get('preprocess_css', 0)) {
    $variables['styles'] = _css_emimage_process($variables['styles']);
  }
}

/**
 * Helper function to replace URLs with data URIs.
 */
function _css_emimage_process($styles) {
  $path_to_files_directory = base_path() . file_directory_path();
  $pattern = '/href=".*?' . preg_quote($path_to_files_directory, '/') . '(.*?)(\\?|")/';
  if (preg_match_all($pattern, $styles, $matches) > 0) {
    foreach ($matches[1] as $aggregated_file_name) {
      $datauri_file_name = str_replace('.css', '.emimage.css', $aggregated_file_name);
      $datauri_file_path = file_directory_path() . $datauri_file_name;

      // Save the processed CSS file if it doesn't exist yet.
      if (!file_exists($datauri_file_path)) {
        $contents = file_get_contents(file_directory_path() . $aggregated_file_name);
        $datauri_contents = preg_replace_callback('/[^}]+{[^{}]+(url\\([\'"]?' . preg_quote(base_path(), '/') . '([^\'")]+)[\'"]?\\))[^{}]*}/i', '_css_emimage_replace', $contents);

        // Check if an error occurred parsing the CSS.
        if (is_null($datauri_contents)) {
          $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);
          $datauri_contents = $contents;
        }

        // Save the contents to the new CSS file.
        file_save_data($datauri_contents, $datauri_file_path, FILE_EXISTS_REPLACE);
      }

      // Replace the aggregated file with the processed CSS file.
      $styles = str_replace($aggregated_file_name, $datauri_file_name, $styles);
    }
  }
  return $styles;
}

/**
 * preg_replace_callback() callback to replace URLs with data URIs.
 */
function _css_emimage_replace($matches) {
  $replacement = $matches[0];
  if ($image = image_get_info($matches[2])) {
    $ielimit = variable_get('css_emimage_ielimit', 1);

    // only embed images less than 32KB, thanks IE
    if (!$ielimit || $ielimit && $image['file_size'] * 1.3333 < 32768) {
      $replacement = str_replace($matches[1], 'url(data:' . $image['mime_type'] . ';base64,' . base64_encode(file_get_contents($matches[2])) . ')', $replacement);

      // Overrides for IE6 and IE7
      $replacement .= "\n * html " . $matches[0] . "\n *+html " . $matches[0] . "\n";
    }
  }
  return $replacement;
}

Functions

Namesort descending Description
css_emimage_form_alter Implementation of hook_form_alter().
css_emimage_help Implementation of hook_help().
css_emimage_preprocess_page Implementation of hook_preprocess_hook().
css_emimage_theme_registry_alter Implementation of hook_theme_registry_alter().
_css_emimage_process Helper function to replace URLs with data URIs.
_css_emimage_replace preg_replace_callback() callback to replace URLs with data URIs.