t('Autorotate'), 'help' => t('Autorotate image based on EXIF orientation and reset that tag.'), 'effect callback' => 'imagecache_autorotate_effect', 'dimensions callback' => 'imagecache_autorotate_dimensions', 'form callback' => 'imagecache_autorotate_form', 'summary theme' => 'imagecache_autorotate_summary', ); return $effects; } /** * Implements hook_theme(). * * Registers theme functions for the effect summaries. */ function imagecache_autorotate_theme() { return array( 'imagecache_autorotate_summary' => array( 'variables' => array('data' => NULL), ), ); } /** * Builds the auto-rotate form. * * This effect has no options, only some help text, so the form is displayed * anyway. */ function imagecache_autorotate_form() { $form = array(); $form['help'] = array( '#markup' => "

There are no user-configurable options for this process.

Certain cameras can embed orientation information into image files when they save them. This information is embedded in an EXIF tag and can be used to rotate images to their correct position for display. Not all cameras or images contain this information. This process is only useful for images that contain this information, whereas for other images it is harmless.

Although most modern browsers do support the orientation tag, the information may get lost or become incorrect by other operations. So, to support all browsers and prevent rotation errors, it is better to start each image style with this effect.

The expected/supported values are:
Tag: 0x0112 Orientation

Wikipedia: Exchangeable image file format

", ); return $form; } /** * Implements theme_hook() for the autorotate effect summary. * * param array $variables * An associative array containing: * - data: The current configuration for this image effect. * * @return string * The HTML for the summary of this image effect. * @ingroup themeable */ function theme_imagecache_autorotate_summary(/*array $variables*/) { return 'image based on its EXIF data.'; } /** * Autorotate image based on EXIF Orientation tag. */ function imagecache_autorotate_effect(stdClass $image /*, $data*/) { // Test to see if EXIF is supported by the current image type. if (in_array($image->info['mime_type'], array('image/jpeg', 'image/tiff'))) { // Hand over to toolkit. return image_toolkit_invoke('imagecache_autorotate', $image); } else if ($image->source === 'modules/image/sample.png' && user_access('administer image styles')) { if (!extension_loaded('exif')) { // Issue a warning if we are in the admin screen and the exif extension is // not enabled. drupal_set_message(t('The autorotate image effect requires the exif extension to be enabled.'), 'warning'); if ($image->toolkit === 'imagemagick') { drupal_set_message(t('Though imagemagick will work without the exif extension, subsequent effects may fail as the image dimensions cannot be updated.'), 'warning'); } } } return TRUE; } /** * GD toolkit specific implementation of this image effect. * * @param stdClass $image * * @return bool * true on success, false otherwise. */ function image_gd_imagecache_autorotate(stdClass $image) { if (!function_exists('exif_read_data')) { watchdog('imagecache_actions', 'Image %file could not be auto-rotated: !message', array('%file' => $image->source, '!message' => t('The exif_read_data() function is not available in this PHP installation. You probably have to enable the exif extension.'))); return FALSE; } // Read and check result. $exif = @exif_read_data(drupal_realpath($image->source)); if ($exif === FALSE && $image->extension === 'jpg') { watchdog('imagecache_actions', 'Image %file could not be auto-rotated: !message', array('%file' => $image->source, '!message' => t('The exif_read_data() function returned FALSE.'))); return FALSE; } if (isset($exif['Orientation'])) { // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html: // 1 = Horizontal (normal) // 2 = Mirror horizontal // 3 = Rotate 180 // 4 = Mirror vertical // 5 = Mirror horizontal and rotate 270 CW // 6 = Rotate 90 CW // 7 = Mirror horizontal and rotate 90 CW // 8 = Rotate 270 CW // @todo: Add horizontal and vertical flips etc. // imagecopy seems to be able to mirror, see conmments on // http://php.net/manual/en/function.imagecopy.php // @todo: Create sample set for tests. switch ($exif['Orientation']) { case 3: $degrees = 180; break; case 6: $degrees = 90; break; case 8: $degrees = 270; break; default: $degrees = 0; } if ($degrees != 0) { return image_rotate($image, $degrees); } } return TRUE; } /** * Imagemagick toolkit specific implementation of this image effect. * * @param stdClass $image * An image object. * * @return bool * true on success, false otherwise. * * @see http://www.imagemagick.org/script/command-line-options.php#auto-orient */ function image_imagemagick_imagecache_autorotate(stdClass $image) { // Use the exif extension, if enabled, to figure out the new dimensions. // Moreover (see [#2366163]): to prevent a bug in IM to incorrectly rotate the // image when it should not, we only pass the auto-orient argument when the // exif extension could detect the 'Orientation' tag. if (function_exists('exif_read_data')) { $exif = @exif_read_data(drupal_realpath($image->source)); if (isset($exif['Orientation'])) { switch ($exif['Orientation']) { case 1: // Normal orientation: no need to rotate or to change the dimensions. break; case 5: case 6: case 7: case 8: // 90 or 270 degrees rotation (+ optional mirror): swap dimensions. $image->ops[] = '-auto-orient'; $tmp = $image->info['width']; $image->info['width'] = $image->info['height']; $image->info['height'] = $tmp; break; default: // All other orientations: pass the arguments, but the dimensions // remain the same. $image->ops[] = '-auto-orient'; break; } } elseif ($exif === FALSE && $image->extension === 'jpg') { watchdog('imagecache_actions', 'Image %file could not be auto-rotated: !message', array('%file' => $image->source, '!message' => t('The exif_read_data() function returned FALSE.'))); } } else { // We do add the auto-orient argument to IM. IM will determine itself // whether to rotate or not. $image->ops[] = '-auto-orient'; // However we cannot keep track of the dimensions anymore. if ($image->info['width'] !== $image->info['height']) { $image->info['width'] = $image->info['height'] = NULL;; } } return TRUE; } /** * Image dimensions callback for this image effect. * * @param array $dimensions * An array with the dimensions (in pixels) to be modified. * param array $data * An associative array containing the effect data. */ function imagecache_autorotate_dimensions(array &$dimensions/*, array $data*/) { // We can only know the resulting dimensions if both dimensions are equal. // Otherwise we need to inspect the image itself, which is not passed in here. // (this callback was introduced to enhance performance by NOT accessing the // image file when rendering the width and height attributes of the html img // tag). if ($dimensions['width'] !== $dimensions['height']) { $dimensions['width'] = $dimensions['height'] = NULL; } }