? .cache ? .project ? .projectOptions ? files ? test.patch ? misc/Thumbs.db ? misc/farbtastic/Thumbs.db ? modules/gd ? modules/imagemagick ? sites/all/modules ? sites/default/settings.php Index: includes/file.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/file.inc,v retrieving revision 1.101 diff -u -r1.101 file.inc --- includes/file.inc 5 Jun 2007 12:13:20 -0000 1.101 +++ includes/file.inc 8 Jun 2007 05:46:22 -0000 @@ -707,6 +707,7 @@ // Check that it is smaller than the given dimensions. list($width, $height) = explode('x', $maximum_dimensions); if ($info['width'] > $width || $info['height'] > $height) { +image_rotate($file->filepath, $file->filepath, 45, 0xff00ff); // Try to resize the image to fit the dimensions. if (image_get_toolkit() && image_scale($file->filepath, $file->filepath, $width, $height)) { drupal_set_message(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', array('%dimensions' => $maximum_dimensions))); Index: includes/image.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/image.inc,v retrieving revision 1.21 diff -u -r1.21 image.inc --- includes/image.inc 12 May 2007 05:51:20 -0000 1.21 +++ includes/image.inc 8 Jun 2007 05:50:00 -0000 @@ -2,74 +2,51 @@ // $Id: image.inc,v 1.21 2007/05/12 05:51:20 dries Exp $ /** - * Return a list of available toolkits. - * - * @return An array of toolkit name => descriptive title. - */ -function image_get_available_toolkits() { - $toolkits = file_scan_directory('includes', 'image\..*\.inc$'); - - $output = array(); - foreach ($toolkits as $file => $toolkit) { - include_once "./$file"; - $function = str_replace('.', '_', $toolkit->name) .'_info'; - if (function_exists($function)) { - $info = $function(); - $output[$info['name']] = $info['title']; - } - } - $output['gd'] = t('Built-in GD2 toolkit'); - return $output; -} - -/** * Retrieve the name of the currently used toolkit. * * @return String containing the name of the toolkit. */ function image_get_toolkit() { - static $toolkit; - if (!$toolkit) { - $toolkit = variable_get('image_toolkit', 'gd'); - $toolkit_file = './includes/image.'. $toolkit .'.inc'; - if ($toolkit != 'gd' && file_exists($toolkit_file)) { - include_once $toolkit_file; - } - elseif (!image_gd_check_settings()) { - $toolkit = FALSE; - } + $toolkit = variable_get('image_toolkit', 'gd'); + + // Check existence of the image toolkit + if (isset($toolkit) && module_exists($toolkit)) { + // The toolkit is found, let's use it + return $toolkit; } - return $toolkit; + return FALSE; } /** * Invokes the given method using the currently selected toolkit. * - * @param $method A string containing the method to invoke. - * @param $params An optional array of parameters to pass to the toolkit method. - * - * @return Mixed values (typically Boolean for successful operation). + * @param $method + * A string containing the method to invoke. + * @param $source + * The file path to the source image. + * @param $destination + * The file path to the destination image. + * @param $params + * An optional array of parameters to pass to the toolkit method. + * Possible keys and values are: + * x The top left co-ordinate of the crop area (x axis value), + * y The top left co-ordinate of the crop area (y axis value). + * width The target width for resize. + * height The target height for resize. + * degrees The number of (clockwise) degrees to rotate the image. + * bg_color An hexidecimal integer specifying a color. E.g. 0x000000 + * for black, 0xff00ff for magenta, and 0xffffff for white. + * + * @return + * Mixed values (typically Boolean for successful operation). */ -function image_toolkit_invoke($method, $params = array()) { +function image_toolkit_invoke($method, $source, $destination, $params = array()) { if ($toolkit = image_get_toolkit()) { - $function = 'image_'. $toolkit .'_'. $method; - if (function_exists($function)) { - return call_user_func_array($function, $params); - } - else { - watchdog('php', 'The selected image handling toolkit %toolkit can not correctly process %function.', array('%toolkit' => $toolkit, '%function' => $function), WATCHDOG_ERROR); - return FALSE; - } - } - else { - if ($method == 'settings') { - return image_gd_settings(); - } + return module_invoke($toolkit, 'image_toolkit', $method, $source, $destination, $params); } } - /** * Get details about an image. * @@ -110,10 +87,14 @@ * * The resulting image always has the exact target dimensions. * - * @param $source The file path of the source image - * @param $destination The file path of the destination image - * @param $width The target width - * @param $height The target height + * @param $source + * The file path of the source image. + * @param $destination + * The file path of the destination image. + * @param $width + * The target width in pixels. + * @param $height + * The target height in pixels. * * @return TRUE or FALSE, based on success */ @@ -124,8 +105,10 @@ $x = round(($info['width'] * $scale - $width) / 2); $y = round(($info['height'] * $scale - $height) / 2); - if (image_toolkit_invoke('resize', array($source, $destination, $info['width'] * $scale, $info['height'] * $scale))) { - return image_toolkit_invoke('crop', array($destination, $destination, $x, $y, $width, $height)); + $params = array('width' => $info['width'] * $scale, 'height' => $info['height'] * $scale); + if (image_toolkit_invoke('resize', $source, $destination, $params)) { + $params = array('x' => $x, 'y' => $y, 'width' => $width, 'height' => $height); + return image_toolkit_invoke('crop', $destination, $destination, $params); } return FALSE; } @@ -136,10 +119,14 @@ * * The resulting image can be smaller for one or both target dimensions. * - * @param $source The file path of the source image - * @param $destination The file path of the destination image - * @param $width The target width - * @param $height The target height + * @param $source + * The file path of the source image + * @param $destination + * The file path of the destination image + * @param $width + * The target width + * @param $height + * The target height * * @return True or FALSE, based on success */ @@ -153,206 +140,67 @@ $aspect = $info['height'] / $info['width']; if ($aspect < $height / $width) { - $width = (int)min($width, $info['width']); - $height = (int)round($width * $aspect); + $params['width'] = (int)min($width, $info['width']); + $params['height'] = (int)round($width * $aspect); } else { - $height = (int)min($height, $info['height']); - $width = (int)round($height / $aspect); + $params['height'] = (int)min($height, $info['height']); + $params['width'] = (int)round($height / $aspect); } - return image_toolkit_invoke('resize', array($source, $destination, $width, $height)); + return image_toolkit_invoke('resize', $source, $destination, $params); } /** * Resize an image to the given dimensions (ignoring aspect ratio). * - * @param $source The filepath of the source image. - * @param $destination The file path of the destination image. - * @param $width The target width. - * @param $height The target height. + * @param $source + * The filepath of the source image. + * @param $destination + * The file path of the destination image. + * @param $width + * The target width in pixels. + * @param $height + * The target height in pixels. */ function image_resize($source, $destination, $width, $height) { - return image_toolkit_invoke('resize', array($source, $destination, $width, $height)); + return image_toolkit_invoke('resize', $source, $destination, array('width' => $width, 'height' => $height)); } /** * Rotate an image by the given number of degrees. * - * @param $source The filepath of the source image - * @param $destination The file path of the destination image - * @param $degrees The number of (clockwise) degrees to rotate the image + * @param $source + * The filepath of the source image + * @param $destination + * The file path of the destination image + * @param $degrees + * The number of (clockwise) degrees to rotate the image + * @param $bg_color + * An hexidecimal integer specifying the background color to use for the + * uncovered area of the image after the rotation. E.g. 0x000000 for black, + * 0xff00ff for magenta, and 0xffffff for white. */ -function image_rotate($source, $destination, $degrees) { - return image_toolkit_invoke('rotate', array($source, $destination, $degrees)); +function image_rotate($source, $destination, $degrees, $bg_color = 0x000000) { + return image_toolkit_invoke('rotate', $source, $destination, array('degrees' => $degrees, 'bg_color' => $bg_color)); } /** * Crop an image to the rectangle specified by the given rectangle. * - * @param $source The filepath of the source image - * @param $destination The file path of the destination image - * @param $x The top left co-ordinate of the crop area (x axis value) - * @param $y The top left co-ordinate of the crop area (y axis value) - * @param $width The target width - * @param $height The target height + * @param $source + * The filepath of the source image. + * @param $destination + * The file path of the destination image. + * @param $x + * The top left co-ordinate of the crop area (x axis value) + * @param $y + * The top left co-ordinate of the crop area (y axis value) + * @param $width + * The target width in pixels. + * @param $height + * The target height in pixels. */ function image_crop($source, $destination, $x, $y, $width, $height) { - return image_toolkit_invoke('crop', array($source, $destination, $x, $y, $width, $height)); -} - -/** - * GD2 toolkit functions - * With the minimal requirements of PHP 4.3 for Drupal, we use the built-in version of GD. - */ - -/** - * Retrieve settings for the GD2 toolkit. - */ -function image_gd_settings() { - if (image_gd_check_settings()) { - $form = array(); - $form['status'] = array('#value' => t('The built-in GD2 toolkit is installed and working properly.')); - - $form['image_jpeg_quality'] = array( - '#type' => 'textfield', - '#title' => t('JPEG quality'), - '#description' => t('Define the image quality for JPEG manipulations. Ranges from 0 to 100. Higher values mean better image quality but bigger files.'), - '#size' => 10, - '#maxlength' => 3, - '#default_value' => variable_get('image_jpeg_quality', 75), - '#field_suffix' => t('%'), - ); - - return $form; - } - else { - form_set_error('image_toolkit', t('The built-in GD image toolkit requires that the GD module for PHP be installed and configured properly. For more information see PHP\'s image documentation.', array('@url' => 'http://php.net/image'))); - return FALSE; - } -} - -/** - * Verify GD2 settings (that the right version is actually installed). - * - * @return boolean - */ -function image_gd_check_settings() { - if ($check = get_extension_funcs('gd')) { - if (in_array('imagegd2', $check)) { - // GD2 support is available. - return TRUE; - } - } - return FALSE; -} - -/** - * Scale an image to the specified size using GD. - */ -function image_gd_resize($source, $destination, $width, $height) { - if (!file_exists($source)) { - return FALSE; - } - - $info = image_get_info($source); - if (!$info) { - return FALSE; - } - - $im = image_gd_open($source, $info['extension']); - if (!$im) { - return FALSE; - } - - $res = imageCreateTrueColor($width, $height); - if ($info['extension'] == 'png') { - $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127); - imagealphablending($res, FALSE); - imagefilledrectangle($res, 0, 0, $width, $height, $transparency); - imagealphablending($res, TRUE); - imagesavealpha($res, TRUE); - } - imageCopyResampled($res, $im, 0, 0, 0, 0, $width, $height, $info['width'], $info['height']); - $result = image_gd_close($res, $destination, $info['extension']); - - imageDestroy($res); - imageDestroy($im); - - return $result; -} - -/** - * Rotate an image the given number of degrees. - */ -function image_gd_rotate($source, $destination, $degrees, $bg_color = 0) { - if (!function_exists('imageRotate')) { - return FALSE; - } - - $info = image_get_info($source); - if (!$info) { - return FALSE; - } - - $im = image_gd_open($source, $info['extension']); - if (!$im) { - return FALSE; - } - - $res = imageRotate($im, $degrees, $bg_color); - $result = image_gd_close($res, $destination, $info['extension']); - - return $result; -} - -/** - * Crop an image using the GD toolkit. - */ -function image_gd_crop($source, $destination, $x, $y, $width, $height) { - $info = image_get_info($source); - if (!$info) { - return FALSE; - } - - $im = image_gd_open($source, $info['extension']); - $res = imageCreateTrueColor($width, $height); - imageCopy($res, $im, 0, 0, $x, $y, $width, $height); - $result = image_gd_close($res, $destination, $info['extension']); - - imageDestroy($res); - imageDestroy($im); - - return $result; -} - -/** - * GD helper function to create an image resource from a file. - */ -function image_gd_open($file, $extension) { - $extension = str_replace('jpg', 'jpeg', $extension); - $open_func = 'imageCreateFrom'. $extension; - if (!function_exists($open_func)) { - return FALSE; - } - return $open_func($file); -} - -/** - * GD helper to write an image resource to a destination file. - */ -function image_gd_close($res, $destination, $extension) { - $extension = str_replace('jpg', 'jpeg', $extension); - $close_func = 'image'. $extension; - if (!function_exists($close_func)) { - return FALSE; - } - if ($extension == 'jpeg') { - return $close_func($res, $destination, variable_get('image_jpeg_quality', 75)); - } - else { - return $close_func($res, $destination); - } -} - - + return image_toolkit_invoke('crop', $source, $destination, array('x' => $x, 'y' => $y, 'width' => $width, 'height' => $height)); +} \ No newline at end of file Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.489 diff -u -r1.489 system.module --- modules/system/system.module 5 Jun 2007 09:15:02 -0000 1.489 +++ modules/system/system.module 8 Jun 2007 05:24:04 -0000 @@ -781,20 +781,37 @@ } function system_image_toolkit_settings() { - $toolkits_available = image_get_available_toolkits(); - if (count($toolkits_available) > 1) { - $form['image_toolkit'] = array( - '#type' => 'radios', - '#title' => t('Select an image processing toolkit'), - '#default_value' => variable_get('image_toolkit', image_get_toolkit()), - '#options' => $toolkits_available - ); + // @TODO: This is an in-elegant way to get the module's description from the + // .info file. There should be a function to do this. + $modules = module_rebuild_cache(); + $available_toolkits = array(); + foreach (module_implements('image_toolkit', TRUE) as $module) { + $available_toolkits[$module] = $modules[$module]->info['description']; + } + + if (count($available_toolkits) == 0) { + drupal_set_message('There is are no image toolkits available.'); } else { - $form['image_toolkit'] = array('#value' => '

'. t("No image toolkits found. Drupal will use PHP's built-in GD library for image handling.") .'

'); + $toolkit = image_get_toolkit(); + $form = array(); + + if (count($available_toolkits) > 1) { + // There is more than one toolkit, which one should Drupal use? + $form['image_toolkits'] = array( + '#type' => 'fieldset', + '#title' => t('Image processing toolkits'), + '#description' => t('For resizing, cropping and other image manipulations Drupal uses the %toolkit toolkit.', array('%toolkit' => $toolkit)), + ); + $form['image_toolkits']['image_toolkit'] = array( + '#type' => 'radios', + '#title' => t('Select an image processing toolkit'), + '#options' => $available_toolkits, + '#default_value' => $toolkit, + ); + } + return system_settings_form($form); } - $form['image_toolkit_settings'] = image_toolkit_invoke('settings'); - return system_settings_form($form); } function system_rss_feeds_settings() { --- modules/gd/gd.info +++ modules/gd/gd.info @@ -0,0 +1,5 @@ +; $Id: $ +name = GD2 +description = Built-in GD2 image processing support. +package = Core - optional +version = VERSION --- modules/gd/gd.install +++ modules/gd/gd.install @@ -0,0 +1,36 @@ + $info['GD Version'], + ); + + // Check PNG support + if (function_exists('imagecreatefrompng')) { + $requirements['gd']['severity'] = REQUIREMENT_OK; + } + else { + $requirements['gd']['severity'] = REQUIREMENT_ERROR; + $requirements['gd']['description'] = t('The GD library for PHP is enabled, but was compiled without PNG support. Please check the PHP image documentation for information on how to correct this.', array('@url' => 'http://www.php.net/manual/en/ref.image.php')); + } + } + else { + $requirements['gd'] = array( + 'value' => t('Not installed'), + 'severity' => REQUIREMENT_ERROR, + 'description' => t('The GD library for PHP is missing or outdated. Please check the PHP image documentation for information on how to correct this.', array('@url' => 'http://www.php.net/manual/en/ref.image.php')), + ); + } + $requirements['gd']['title'] = t('GD library'); + } + + return $requirements; +} + --- modules/gd/gd.module +++ modules/gd/gd.module @@ -0,0 +1,108 @@ + 'fieldset', + '#title' => t('GD2 preferences'), + ); + $form['image_toolkit_gd']['image_jpeg_quality'] = array( + '#type' => 'textfield', + '#title' => t('JPEG quality'), + '#description' => t('Define the image quality for JPEG manipulations. Ranges from 0 to 100. Higher values mean better image quality, but bigger files.'), + '#size' => 10, + '#maxlength' => 3, + '#default_value' => variable_get('image_jpeg_quality', 75), + '#field_suffix' => '%', + ); + $form['buttons']['#weight'] = 10; + } + return $form; +} + +function gd_image_toolkit($op, $source = NULL, $destination = NULL, $params = array()) { + if (!file_exists($source)) { + return FALSE; + } + + $info = image_get_info($source); + if (!$info) { + return FALSE; + } + + $im = _gd_open($source, $info['extension']); + if (!$im) { + return FALSE; + } + + switch ($op) { + case 'crop': + $res = imageCreateTrueColor($params['width'], $params['height']); + imageCopy($res, $im, 0, 0, $params['x'], $params['y'], $params['width'], $params['height']); + break; + + case 'resize': + $res = imageCreateTrueColor($params['width'], $params['height']); + if ($info['extension'] == 'png') { + $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127); + imagealphablending($res, FALSE); + imagefilledrectangle($res, 0, 0, $params['width'], $params['height'], $transparency); + imagealphablending($res, TRUE); + imagesavealpha($res, TRUE); + } + imageCopyResampled($res, $im, 0, 0, 0, 0, $params['width'], $params['height'], $info['width'], $info['height']); + break; + + case 'rotate': + $res = imageRotate($im, $params['degrees'], $params['bg_color']); + break; + } + $result = _gd_close($res, $destination, $info['extension']); + + imageDestroy($im); + imageDestroy($res); + + return $result; +} + +/** + * GD helper function to create an image resource from a file. + */ +function _gd_open($file, $extension) { + $extension = str_replace('jpg', 'jpeg', $extension); + $open_func = 'imageCreateFrom'. $extension; + if (!function_exists($open_func)) { + return FALSE; + } + return $open_func($file); +} + +/** + * GD helper to write an image resource to a destination file. + */ +function _gd_close($res, $destination, $extension) { + $extension = str_replace('jpg', 'jpeg', $extension); + $close_func = 'image'. $extension; + if (!function_exists($close_func)) { + return FALSE; + } + if ($extension == 'jpeg') { + return $close_func($res, $destination, variable_get('image_jpeg_quality', 75)); + } + else { + return $close_func($res, $destination); + } +} + --- modules/imagemagick/imagemagick.info +++ modules/imagemagick/imagemagick.info @@ -0,0 +1,5 @@ +; $Id: $ +name = ImageMagick +description = ImageMagick image toolkit. +package = Core - optional +version = VERSION --- modules/imagemagick/imagemagick.module +++ modules/imagemagick/imagemagick.module @@ -0,0 +1,149 @@ + 'fieldset', + '#title' => t('ImageMagick preferences'), + ); + $form['image_toolkit_imagemagick']['image_imagemagick_convert'] = array( + '#type' => 'textfield', + '#title' => t('Path to the "convert" binary'), + '#default_value' => variable_get('image_imagemagick_convert', '/usr/bin/convert'), + '#required' => TRUE, + ); + $form['buttons']['#weight'] = 10; + } +} + +function _imagemagick_validate(&$form, &$form_state) { + $path = realpath($form_state['values']['image_imagemagick_convert']); + + // Don't validate if we haven't been selected. + if ($form_state['values']['image_toolkit'] == 'imagemagick') { + if (_imagemagick_check_path($path)) { + _imagemagick_convert_exec($path, '-version', $output, $errors); + $form['image_toolkit_imagemagick']['image_imagemagick_version'] = array( + '#type' => 'item', + '#title' => t('Version'), + '#value' => '
'. check_plain($output) .'
', + ); + } + else { + if (ini_get('open_basedir')) { + form_set_error('image_imagemagick_convert', t("No file named %file could be found. PHP's open_basedir security resriction may be interfearing with the attempts to locate it.", array('%file' => $path, '@open-basedir' => url('http://us2.php.net/features.safe-mode') ))); + } + else { + form_set_error('image_imagemagick_convert', t('The specified ImageMagic path %file does not exist.', array('%file' => $path))); + } + } + } + return $form; +} + +function _imagemagick_check_path($path) { + if (is_file($path)) { + return TRUE; + } + return FALSE; +} + +function imagemagick_image_toolkit($op, $source = NULL, $destination = NULL, $params = array()) { + $convert_path = variable_get('image_imagemagick_convert', '/usr/bin/convert'); + if (!_imagemagick_check_path($convert_path)) { + drupal_set_message(t("ImageMagick could not be found. The admin will need to set the path on the image toolkit page.", array('@image-toolkit-settings' => url('admin/settings/image-toolkit'))), 'error'); + return FALSE; + } + + switch ($op) { + case 'crop': + $filter = ' -crop '. intval($params['width']) .'x'. intval($params['height']) .'+'. intval($params['x']) .'+'. intval($params['y']); + break; + + case 'resize': + $filter = ' -scale '. intval($params['width']) .'x'. intval($params['height']) .'! -filter QUADRATIC'; + break; + + case 'rotate': + $filter = ' -rotate '. intval($params['degrees']) .' -background #'. str_pad(dechex($params['bg_color']), 6, 0); + break; + + default: + return FALSE; + } + + $args = $filter .' '. escapeshellarg($source) .' '. escapeshellarg($destination); + if (0 != _imagemagick_convert_exec($convert_path, $args, $output, $errors)) { + return FALSE; + } + return file_exists($destination); +} + +/** + * Execute ImageMagick + * + * @param $convert_path + * Full path to the convert binary. + * @param $command_args + * String with parameters to pass to convert. This should be properly escaped + * with escapeshellarg(). + * @param $output + * Output string. + * @param $errors + * Error string. + * @return + * Exit code from the convert binary or FALSE on error. + */ +function _imagemagick_convert_exec($convert_path, $command_args, &$output, &$errors) { + if (strstr($_SERVER['SERVER_SOFTWARE'], 'Win32') || strstr($_SERVER['SERVER_SOFTWARE'], 'IIS')) { + // use window's start command to avoid the "black window" from showing up: + // http://us3.php.net/manual/en/function.exec.php#56599 + // use /D to run the command from PHP's current working directory so the + // file paths don't have to be absolute. + $convert_path = 'start "window title" /D'. getcwd() .' /b '. escapeshellarg($convert_path); + } + + $descriptors = array( + 0 => array('pipe', 'r'), // stdin + 1 => array('pipe', 'w'), // stdout + 2 => array('pipe', 'w') // stderr + ); + if ($h = proc_open($convert_path .' '. $command_args, $descriptors, $pipes)) { + $output = ''; + while (!feof($pipes[1])) { + $output .= fgets($pipes[1]); + } + + $errors = ''; + while (!feof($pipes[2])) { + $errors .= fgets($pipes[2]); + } + + drupal_set_message(t("ImageMagick command: %command", array('%command' => $convert_path .' '. $command_args))); + #drupal_set_message(t("ImageMagick output: %output", array('%output' => $output))); + if ($errors) { + drupal_set_message(t("ImageMagick reported an error: %error", array('%error' => $errors)), 'error'); + } + + fclose($pipes[0]); + fclose($pipes[1]); + fclose($pipes[2]); + return proc_close($h); + } + return FALSE; +} +