You are here

imageapi_optimize.module in Image Optimize (or ImageAPI Optimize) 6

Image optimize functionalities

File

imageapi_optimize.module
View source
<?php

/**
 * @file
 * Image optimize functionalities
 */

/**
 * Implements hook_init().
 *
 * Abstract layer to all implemented method.
 */
function imageapi_optimize_init() {
  if (!($cache = cache_get('imageapi_optimize:methods'))) {
    $methods = _imageapi_optimize_get_methods();
  }
  else {
    $methods = $cache->data;
  }
  foreach ($methods as $method) {
    eval('function imageapi_optimize_image_' . $method . '(&$image) {
      $params = array_slice(func_get_args(), 1);
      return _imageapi_optimize_invoke("' . $method . '", $image, $params);
    }');
  }
}

/**
 * Implements hook_imageapi_toolkit().
 *
 * This hook only gets called to see is a module implements the imageapi hooks...
 */
function imageapi_optimize_imageapi_toolkit() {
}

/**
 * Settings form for the toolkit.
 */
function imageapi_optimize_settings_form() {
  $toolkits = array();
  foreach (imageapi_get_available_toolkits() as $toolkit => $info) {
    if ($toolkit != 'imageapi_optimize') {
      $toolkits[$toolkit] = $info['name'];
    }
  }
  $form['imageapi_optimize_basic'] = array(
    '#type' => 'fieldset',
    '#title' => t('Basic settings'),
  );
  $form['imageapi_optimize_basic']['imageapi_optimize_toolkit'] = array(
    '#type' => 'radios',
    '#title' => t('Base toolkit'),
    '#default_value' => variable_get('imageapi_optimize_toolkit', imageapi_default_toolkit()),
    '#options' => $toolkits,
    '#element_validate' => array(
      'imageapi_optimize_toolkit_element_validate',
    ),
  );
  $services = array(
    'internal' => 'Internal tools (configurable below)',
    'smushit' => 'Yahoo! Smush.It',
  );
  $form['imageapi_optimize_basic']['imageapi_optimize_service'] = array(
    '#type' => 'radios',
    '#title' => t('Choose which optimization service to use'),
    '#default_value' => variable_get('imageapi_optimize_service', 'internal'),
    '#options' => $services,
  );
  $form['imageapi_optimize_internal'] = array(
    '#type' => 'fieldset',
    '#title' => t('Paths to internal tools'),
    '#collapsible' => TRUE,
    '#collapsed' => variable_get('imageapi_optimize_service', 'internal') !== 'internal',
  );
  $form['imageapi_optimize_internal']['imageapi_optimize_advpng'] = array(
    '#type' => 'textfield',
    '#title' => t('Path to advpng'),
    '#default_value' => variable_get('imageapi_optimize_advpng', ''),
    '#element_validate' => array(
      'imageapi_optimize_validate_path',
    ),
    '#description' => t('Leave empty to skip this command. You can download it <a href="!link">here</a> (part of AdvanceCOMP).', array(
      '!link' => 'http://advancemame.sourceforge.net/comp-download.html',
    )),
  );
  $form['imageapi_optimize_internal']['imageapi_optimize_optipng'] = array(
    '#type' => 'textfield',
    '#title' => t('Path to optipng'),
    '#default_value' => variable_get('imageapi_optimize_optipng', ''),
    '#element_validate' => array(
      'imageapi_optimize_validate_path',
    ),
    '#description' => t('Leave empty to skip this command. You can download it <a href="!link">here</a>.', array(
      '!link' => 'http://optipng.sourceforge.net/',
    )),
  );
  $form['imageapi_optimize_internal']['imageapi_optimize_pngcrush'] = array(
    '#type' => 'textfield',
    '#title' => t('Path to pngcrush'),
    '#default_value' => variable_get('imageapi_optimize_pngcrush', ''),
    '#element_validate' => array(
      'imageapi_optimize_validate_path',
    ),
    '#description' => t('Leave empty to skip this command. You can download it <a href="!link">here</a>.', array(
      '!link' => 'http://pmt.sourceforge.net/pngcrush/',
    )),
  );
  $form['imageapi_optimize_internal']['imageapi_optimize_jpegtran'] = array(
    '#type' => 'textfield',
    '#title' => t('Path to jpegtran'),
    '#default_value' => variable_get('imageapi_optimize_jpegtran', '/usr/bin/jpegtran'),
    '#element_validate' => array(
      'imageapi_optimize_validate_path',
    ),
    '#description' => t('Leave empty to skip this command. This is a part of <a href="!link">libjpeg</a> and could probably on your system.', array(
      '!link' => 'http://ijg.org/',
    )),
  );
  $form['imageapi_optimize_internal']['imageapi_optimize_jfifremove'] = array(
    '#type' => 'textfield',
    '#title' => t('Path to jfifremove'),
    '#default_value' => variable_get('imageapi_optimize_jfifremove', ''),
    '#element_validate' => array(
      'imageapi_optimize_validate_path',
    ),
    '#description' => t('Leave empty to skip this command. You can download it <a href="!link">here</a>.', array(
      '!link' => 'http://lyncd.com/files/imgopt/jfifremove.c',
    )),
  );

  // Reloads methods because user may change toolkit
  $form['#submit'][] = '_imageapi_optimize_get_methods';
  return system_settings_form($form);
}
function imageapi_optimize_toolkit_element_validate($element) {
  if ($element['#value'] == 'imageapi_optimize') {
    form_set_error($element['#name'], t('You must select a different toolkit from ImageAPI Optimize itself.'));
  }
}
function imageapi_optimize_validate_path($element) {
  if ($errors = _imageapi_optimize_check_path($element['#value'])) {
    form_set_error($element['#parents'][0], implode('<br />', $errors));
    return FALSE;
  }
  return TRUE;
}

/**
 * All ImageAPI functions call their base functions
 */
function imageapi_optimize_image_open($image) {
  return _imageapi_optimize_invoke('open', $image) ? $image : FALSE;
}
function imageapi_optimize_image_close($image, $dst) {
  if (_imageapi_optimize_invoke('close', $image, array(
    $dst,
  ))) {
    return _imageapi_optimize_optimize($image, $dst);
  }
  return FALSE;
}

/**
 * Helper. Based on imageapi_invoke()
 */
function _imageapi_optimize_invoke($method, &$image, array $params = array()) {
  $function = variable_get('imageapi_optimize_toolkit', '') . '_image_' . $method;
  if (function_exists($function)) {
    array_unshift($params, $image);
    $params[0] =& $image;
    return call_user_func_array($function, $params);
  }
  return FALSE;
}

/**
 * Checks if a path exists and is executable
 *
 * Warning: it does not check if the command is harmful.
 * You should keep an eye on users with "administer imageapi" permission
 */
function _imageapi_optimize_check_path($path) {
  $errors = array();
  if (!empty($path)) {
    if (!is_file($path)) {
      $errors[] = t('The specified path %file does not exist.', array(
        '%file' => $path,
      ));
    }
    if (!$errors && !is_executable($path)) {
      $errors[] = t('The specified path %file is not executable.', array(
        '%file' => $path,
      ));
    }
    if ($errors && ($open_basedir = ini_get('open_basedir'))) {
      $errors[] = t('PHP\'s <a href="!open-basedir">open_basedir</a> security restriction is set to %open-basedir, which may be interfering with attempts to locate %file.', array(
        '%file' => $path,
        '%open-basedir' => $open_basedir,
        '!info-link' => url('http://php.net/features.safe-mode#ini.open-basedir'),
      ));
    }
  }
  return $errors;
}

/**
 * Optimizes image with external commands
 */
function _imageapi_optimize_optimize($image, $dst) {
  $optimize_service = '_imageapi_optimize_service_' . variable_get('imageapi_optimize_service', 'internal');
  $optimize_service($image, $dst);

  // If optimize service fails, there is no problem. Original image is saved.
  return TRUE;
}

/**
 * Uses internal tools to optimize
 */
function _imageapi_optimize_service_internal($image, $dst) {
  switch ($image->info['mime_type']) {
    case 'image/png':
      if ($cmd = variable_get('imageapi_optimize_optipng', '')) {
        exec("{$cmd} -o5 -quiet " . escapeshellarg($dst));
      }
      if ($cmd = variable_get('imageapi_optimize_pngcrush', '')) {
        $temp = tempnam(realpath(file_directory_temp()), 'file');
        exec("{$cmd} -rem alla -reduce -brute -q " . escapeshellarg($dst) . " " . escapeshellarg($temp) . " && mv " . escapeshellarg($temp) . " " . escapeshellarg($dst));
      }
      if ($cmd = variable_get('imageapi_optimize_advpng', '')) {
        exec("{$cmd} -z4q " . escapeshellarg($dst));
      }
      break;
    case 'image/jpeg':
      if ($cmd = variable_get('imageapi_optimize_jpegtran', '')) {
        _imageapi_optimize_exec("{$cmd} -copy none -optimize " . escapeshellarg($dst), $dst);
      }
      if ($cmd = variable_get('imageapi_optimize_jfifremove', '')) {
        _imageapi_optimize_exec("{$cmd} < " . escapeshellarg($dst), $dst);
      }
      break;
  }
  return TRUE;
}

/**
 * Use smushit.com to optimize
 */
function _imageapi_optimize_service_smushit($image, $dst) {
  if (!function_exists('json_decode')) {
    drupal_set_message(t('Required function, json_decode(), is not available.'), 'error');
    return FALSE;
  }
  $url = 'http://www.smushit.com/ysmush.it/ws.php';
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, array(
    'files' => '@' . $dst,
  ));
  $data = curl_exec($ch);
  curl_close($ch);
  $json = json_decode($data);

  // SmushIt return an error if it cannot optimize the image. Otherwise, it
  // returns an object, with 'dest' (temporary file) and 'percent' (savings)
  // among other properties.
  if (!$json->error) {
    $result = drupal_http_request($json->dest);
    if (!isset($result->error)) {
      file_save_data($result->data, $dst, FILE_EXISTS_REPLACE);
      return TRUE;
    }
  }
}

/**
 * Saves result of a command into file
 */
function _imageapi_optimize_exec($cmd, $dst) {
  ob_start();
  passthru($cmd);
  $output = ob_get_contents();
  ob_end_clean();
  file_put_contents($dst, $output);
}

/**
 * Gets all implemented methods by ImageAPI and contrib modules.
 * This function takes a dozens of miliseconds CPU times.
 */
function _imageapi_optimize_get_methods() {
  $methods = array();
  $prefix = variable_get('imageapi_optimize_toolkit', '') . '_image_';

  // Load ImageAPI methods that can be located in external files
  // managed by the ImageCache module.
  if (module_exists('imagecache')) {
    foreach (imagecache_action_definitions() as $definition) {
      if (isset($definition['file'])) {
        require_once './' . $definition['file'];
      }
    }
  }
  $funcs = get_defined_functions();
  foreach ($funcs['user'] as $func) {
    if (strpos($func, $prefix) === 0) {
      $method = substr($func, strlen($prefix));
      if (!in_array($method, array(
        'open',
        'close',
      ))) {
        $methods[] = $method;
      }
    }
  }
  cache_set('imageapi_optimize:methods', $methods);
  watchdog('imageapi', 'Refresh ImageAPI methods');
  return $methods;
}

Functions

Namesort descending Description
imageapi_optimize_imageapi_toolkit Implements hook_imageapi_toolkit().
imageapi_optimize_image_close
imageapi_optimize_image_open All ImageAPI functions call their base functions
imageapi_optimize_init Implements hook_init().
imageapi_optimize_settings_form Settings form for the toolkit.
imageapi_optimize_toolkit_element_validate
imageapi_optimize_validate_path
_imageapi_optimize_check_path Checks if a path exists and is executable
_imageapi_optimize_exec Saves result of a command into file
_imageapi_optimize_get_methods Gets all implemented methods by ImageAPI and contrib modules. This function takes a dozens of miliseconds CPU times.
_imageapi_optimize_invoke Helper. Based on imageapi_invoke()
_imageapi_optimize_optimize Optimizes image with external commands
_imageapi_optimize_service_internal Uses internal tools to optimize
_imageapi_optimize_service_smushit Use smushit.com to optimize