My first task is done. Here is it:
on file modules/exif/modules.inc change the line 245 to:
$this->setCallbacks('loadItemDetails|getSiteAdminViews|registerEventListeners');
then add this member function:
/**
* Event handler for GalleryEntity::save event.
* Rotate image based on exif data.
* @author Michel Moreira <michel@michelmoreira.com.br>
* @see GalleryEventListener::handleEvent
*/
function handleEvent($event) {
$entity = $event->getEntity();
/* See if is a data item */
if (!GalleryUtilities::isA($entity, 'GalleryDataItem')) {
return array(GalleryStatus::success(), '');
}
/* And if is one JPEG */
if ($entity->getMimeType() != 'image/jpeg' && $entity->getMimeType() != 'image/pjpeg') {
return array(GalleryStatus::success(), '');
}
/* Then gimme the path info */
list($ret, $path) = $entity->fetchPath();
if ($ret->isError()) {
return array($ret->wrap(__FILE__, __LINE__), null);
}
list ($ret, $exifData) = ExifHelper::getExifData($path, EXIF_DETAILED);
if ($ret->isError()) {
return array($ret->wrap(__FILE__, __LINE__), null);
}
GalleryCoreApi::relativeRequireOnce('modules/exif/lib/exifer/exif.inc');
$rawExifData = read_exif_data_raw($path, true);
$orientation = $rawExifData["IFD0"]["Orientation_Verbose"]["RawData"];
if ($orientation) {
list ($ret, $rotatekit) = GalleryCoreApi::getToolkitByOperation('image/jpeg', 'rotate');
list ($ret, $flipkit) = GalleryCoreApi::getToolkitByOperation('image/jpeg', 'flip');
$degree = 0;
$x = false;
$y = false;
switch ($orientation)
{
case 1: //No Rotation, No Flip
break;
case 2: //No Rotation, Flipped Horizontally
$x = true;
break;
case 3: //Rotated 180 degrees, No Flip
$degree = 180;
break;
case 4: //No Rotation, Flipped Vertically
$y = true;
break;
case 5: //Flipped Horizontally, Rotated 90 degrees counter clockwise
$x = true;
$degree = -90;
break;
case 6: //No Flip, Rotated 90 degrees clockwise
$degree = 90;
break;
case 7: //Flipped Horizontally, Rotated 90 degrees clockwise
$x = true;
$degree = 90;
break;
case 8: //No Flip, Rotated 90 degrees counter clockwise
$degree = 90;
break;
}
}
if (isset($flipkit)) {
$flipkit->performOperation($entity->getMimeType(), 'flip', $path, $path, array(0 => $x, 1=> $y));
}
if (isset($rotatekit)) {
$rotatekit->performOperation($entity->getMimeType(), 'rotate', $path, $path, array(0 => $degree));
}
return array(GalleryStatus::success(), '');
}
on file modules/core/gd/classes/GdToolkit.class
add this case test on performOperation function (line 395)
case 'flip':
/*
* PHP >= 3.0.6
* $parameters[0]: flip horizontally
* $parameters[1]: flip vertically
*/
$x = $parameters[0];
$y = $parameters[1];
if ($x || $y) {
list ($ret, $width, $height) = $this->_getImageDimensionsForResource($sourceRes);
if ($ret->isError()) {
return array($ret->wrap(__FILE__, __LINE__), null);
}
list($ret, $destRes) = $this->_getTrueColorImageRes($width, $height);
if ($ret->isError()) {
return array($ret->wrap(__FILE__, __LINE__), null);
}
if ($x) {
for ($x = 0; $x < $width; $x++) {
$gd->ImageCopy($destRes, $sourceRes, $width - 1 - $x, 0, $x, 0, 1, $height);
}
}
if ($y) {
for ($y = 0; $y < $height; $y++) {
ImageCopy($destRes, $sourceRes, 0, $height - 1 - $y, 0, $y, $width, 1);
}
}
}
break;
Needs add flip support for others image libraries. Needs also the option to enable/disable it on file upload or admin panel, or both.
:P
Posts: 8194
Just some quick, pedantic comments:
$orientation = $rawExifData["IFD0"]["Orientation_Verbose"]["RawData"];has the possibility to generate a PHP notice. I'd use isset() to check if the array key is defined before referencing it.
That can just be replaced with array($x, $y), which looks cleaner to me. Same with array(0 => $degree)
Anyway, cool beans
Posts: 2
Here the new version:
referencing a non-existing index also raises a notice.
/** * Event handler for GalleryEntity::save event. * Rotate image based on exif data. * @author Michel Moreira <michel@michelmoreira.com.br> * @see GalleryEventListener::handleEvent */ function handleEvent($event) { $entity = $event->getEntity(); /* See if is a data item */ if (!GalleryUtilities::isA($entity, 'GalleryDataItem')) { return array(GalleryStatus::success(), ''); } /* And if is one JPEG */ if ($entity->getMimeType() != 'image/jpeg' && $entity->getMimeType() != 'image/pjpeg') { return array(GalleryStatus::success(), ''); } /* Then gimme the path info */ list($ret, $path) = $entity->fetchPath(); if ($ret->isError()) { return array($ret->wrap(__FILE__, __LINE__), null); } list ($ret, $exifData) = ExifHelper::getExifData($path, EXIF_DETAILED); if ($ret->isError()) { return array($ret->wrap(__FILE__, __LINE__), null); } GalleryCoreApi::relativeRequireOnce('modules/exif/lib/exifer/exif.inc'); $rawExifData = read_exif_data_raw($path, true); if (isset($rawExifData) && array_key_exists("IFD0", $rawExifData) && array_key_exists("Orientation_Verbose", $rawExifData["IFD0"]) && array_key_exists("RawData", $rawExifData["IFD0"]["Orientation_Verbose"])) { $orientation = $rawExifData["IFD0"]["Orientation_Verbose"]["RawData"]; list ($ret, $rotatekit) = GalleryCoreApi::getToolkitByOperation('image/jpeg', 'rotate'); list ($ret, $flipkit) = GalleryCoreApi::getToolkitByOperation('image/jpeg', 'flip'); $degree = 0; $x = false; $y = false; switch ($orientation) { case 1: //No Rotation, No Flip break; case 2: //No Rotation, Flipped Horizontally $x = true; break; case 3: //Rotated 180 degrees, No Flip $degree = 180; break; case 4: //No Rotation, Flipped Vertically $y = true; break; case 5: //Flipped Horizontally, Rotated 90 degrees counter clockwise $x = true; $degree = -90; break; case 6: //No Flip, Rotated 90 degrees clockwise $degree = 90; break; case 7: //Flipped Horizontally, Rotated 90 degrees clockwise $x = true; $degree = 90; break; case 8: //No Flip, Rotated 90 degrees counter clockwise $degree = 90; break; } if (isset($flipkit)) { $flipkit->performOperation($entity->getMimeType(), 'flip', $path, $path, array($x, $y)); } if (isset($rotatekit)) { $rotatekit->performOperation($entity->getMimeType(), 'rotate', $path, $path, array($degree)); } } return array(GalleryStatus::success(), ''); }Posts: 313
Is this code getting into the EXIF module? IMHO it should really be part of the G2 distribution. One more thing that would be great to have is enabling/disabling this option when uploading files. Since some of my users have their images already rotated properly, and others don't.
Posts: 8601
BorgKing, yes it is posted here for code review prior to adding to cvs.
I agree this feature should be optional! For me a site admin option would suffice.. BorgKing, you'd prefer an option at upload time?
Posts: 7934
This looks like a fine start. There are some issues that we need to
figure out, though. It looks like this is modifying the raw original
image which is something that we seek to avoid whenever possible.
Instead of modifying the original, we should instead create a
preferred derivative with the correct toolkit command and remap any
derivatives as appropriate. Look at
modules/core/ItemEditRotateAndScale.inc for ane example. This will
let the user undo any potential damage caused by the toolkit. And
since we're not modfying the embedded exif data here, this will keep
the exif data in sync with the image so if you use the image as a
source for another addition it will be internally consistent and you
won't wind up rotating it twice.
see your indentation too (which is important).
so we should avoid paying the cost of bytecode compilation if we can avoid it.
(as well as ones for the other toolkits)
it's better to create a new API in ExifHelper specifically for this purpose
(and provide a unit test for that, too of course)
if (isset($rawExifData['IFD0']['Orientation_Verbose']['RawData'])) { $orientation = ...; }[/]
Posts: 313
mindless, yes I would prefer an option at upload time. Most of my users rotate the pictures before uploading them, but some do not, and for those it would be good to have this option. Somehow this just can't be an admin option only (just default) in Galleries with multiple users.
Posts: 8601
i think its ok for handleEvent to be in module.inc, just move the bulk of the work to a helper class.. the code in module.inc can check if the event is applicable (your first couple ifs, and you need to add one for newly created (see useralbum)..
on 2nd thought, shouldn't this be an ItemAddOption instead of event handler?
Posts: 7934
It actually probably does make more sense for this to be an ItemAddOption. It'll be easier to configure/control that way since all configuration can be done inline on the item-add page.
pain_elemental, take a look at modules/core/CreateThumbnailOption.inc for an example of how to create an ItemAddOption.
Posts: 8601
(or the ItemAddOption already in the exif module.. you can just add to that)
Posts: 39
I made a hack using jhead and jpegtran with "jhead - autorot <file>'.
I think this is the best way to go because it is lossless and could apply to the original without damage.
I have made a quick hack in modules/core/classes/helpers/GalleryItemHelper_medium.class in the addItemToAlbum() method.
What do you think, should there be a graphics toolkit or simply an option in the exif module for jhead path and per-file option?
I had a hard time with the path to jpegtran, but here is the hack:
$ret = $newItem->save(); if ($ret->isError()) { return array($ret->wrap(__FILE__, __LINE__), null); } /* hack autorotate jpeg */ if ($mimeType == 'image/jpeg' || $mimeType == 'image/jpg' || $mimeType == 'image/pjpeg') { list($ret, $filePath) = $newItem->fetchPath(); if (!$ret->isError()) { $cwd = getcwd(); chdir("/home/www/tourigny.ca/cgi-bin/"); $jhead_exe = "/home/www/tourigny.ca/cgi-bin/jhead-static"; $cmd = array($jhead_exe, "-autorot", $filePath); $platform = $gallery->getPlatform(); list($ret, $output) = $platform->exec(array($cmd)); chdir($cwd); if (!$ret) { echo "ERROR with command ";print_r($cmd);echo " for removing orientation<br>"; echo "output was: ";print_r($output); echo "<br>"; } } } $ret = GalleryCoreApi::addExistingItemToAlbum($newItem, $albumId, true);