Overview

Packages

  • Ansel
  • None

Classes

  • Ansel
  • Ansel_Ajax_Application
  • Ansel_Ajax_Imple_EditCaption
  • Ansel_Ajax_Imple_EditFaces
  • Ansel_Ajax_Imple_EditGalleryFaces
  • Ansel_Ajax_Imple_Embed
  • Ansel_Ajax_Imple_GallerySlugCheck
  • Ansel_Ajax_Imple_ImageSaveGeotag
  • Ansel_Ajax_Imple_LocationAutoCompleter
  • Ansel_Ajax_Imple_MapLayerSelect
  • Ansel_Ajax_Imple_TagActions
  • Ansel_Ajax_Imple_ToggleGalleryActions
  • Ansel_Ajax_Imple_ToggleOtherGalleries
  • Ansel_Ajax_Imple_UploadNotification
  • Ansel_Api
  • Ansel_Exception
  • Ansel_Faces
  • Ansel_Faces_Base
  • Ansel_Faces_Facedetect
  • Ansel_Faces_User
  • Ansel_Factory_Faces
  • Ansel_Factory_Storage
  • Ansel_Factory_Styles
  • Ansel_Form_Ecard
  • Ansel_Form_Image
  • Ansel_Form_ImageDate
  • Ansel_Form_Upload
  • Ansel_Gallery
  • Ansel_Gallery_Decorator_Date
  • Ansel_GalleryMode_Base
  • Ansel_GalleryMode_Date
  • Ansel_GalleryMode_Normal
  • Ansel_Image
  • Ansel_ImageGenerator
  • Ansel_ImageGenerator_Mini
  • Ansel_ImageGenerator_PolaroidThumb
  • Ansel_ImageGenerator_PolaroidThumbStack
  • Ansel_ImageGenerator_RoundedThumb
  • Ansel_ImageGenerator_RoundedThumbStack
  • Ansel_ImageGenerator_Screen
  • Ansel_ImageGenerator_ShadowThumb
  • Ansel_ImageGenerator_ShadowThumbStack
  • Ansel_ImageGenerator_SquareThumb
  • Ansel_ImageGenerator_Thumb
  • Ansel_LoginTasks_SystemTask_Upgrade
  • Ansel_Report
  • Ansel_Report_letter
  • Ansel_Report_mail
  • Ansel_Report_tickets
  • Ansel_Search
  • Ansel_Search_exif
  • Ansel_Search_Tag
  • Ansel_Storage
  • Ansel_Style
  • Ansel_Tagger
  • Ansel_Test
  • Ansel_Tile_DateGallery
  • Ansel_Tile_Gallery
  • Ansel_Tile_Image
  • Ansel_View_Ansel
  • Ansel_View_Base
  • Ansel_View_EmbeddedRenderer_GalleryLink
  • Ansel_View_EmbeddedRenderer_Mini
  • Ansel_View_EmbeddedRenderer_Slideshow
  • Ansel_View_Gallery
  • Ansel_View_GalleryProperties
  • Ansel_View_GalleryRenderer_Base
  • Ansel_View_GalleryRenderer_Gallery
  • Ansel_View_GalleryRenderer_GalleryLightbox
  • Ansel_View_Image
  • Ansel_View_List
  • Ansel_View_Results
  • Ansel_View_Slideshow
  • Ansel_View_Upload
  • Ansel_Widget
  • Ansel_Widget_Actions
  • Ansel_Widget_Base
  • Ansel_Widget_GalleryFaces
  • Ansel_Widget_Geotag
  • Ansel_Widget_ImageFaces
  • Ansel_Widget_Links
  • Ansel_Widget_OtherGalleries
  • Ansel_Widget_OwnerFaces
  • Ansel_Widget_SimilarPhotos
  • Ansel_Widget_Tags
  • Ansel_XPPublisher
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * Class to describe a single Ansel image.
   4:  *
   5:  * Copyright 2001-2012 Horde LLC (http://www.horde.org/)
   6:  *
   7:  * See the enclosed file COPYING for license information (GPL). If you
   8:  * did not receive this file, see http://www.horde.org/licenses/gpl.
   9:  *
  10:  * @author Chuck Hagenbuch <chuck@horde.org>
  11:  * @author Michael J. Rubinsky <mrubinsk@horde.org>
  12:  * @package Ansel
  13:  */
  14: class Ansel_Image Implements Iterator
  15: {
  16:     /**
  17:      * The gallery id of this image's parent gallery
  18:      *
  19:      * @var integer
  20:      */
  21:     public $gallery;
  22: 
  23:     /**
  24:      * Image Id
  25:      *
  26:      * @var integer
  27:      */
  28:     public $id = null;
  29: 
  30:     /**
  31:      * The filename for this image
  32:      *
  33:      * @var string
  34:      */
  35:     public $filename = 'Untitled';
  36: 
  37:     /**
  38:      * Image caption
  39:      *
  40:      * @var string
  41:      */
  42:     public $caption = '';
  43: 
  44:     /**
  45:      * The image's mime type
  46:      *
  47:      * @var string
  48:      */
  49:     public $type = 'image/jpeg';
  50: 
  51:     /**
  52:      * Timestamp of uploaded datetime
  53:      *
  54:      * @var integer
  55:      */
  56:     public $uploaded;
  57: 
  58:     /**
  59:      * Sort count for this image
  60:      *
  61:      * @var integer
  62:      */
  63:     public $sort;
  64: 
  65:     /**
  66:      * The number of comments for this image, if available.
  67:      *
  68:      * @var integer
  69:      */
  70:     public $commentCount;
  71: 
  72:     /**
  73:      * Number of faces in this image
  74:      * @var integer
  75:      */
  76:     public $facesCount;
  77: 
  78:     /**
  79:      * Latitude
  80:      *
  81:      * @var string
  82:      */
  83:     public $lat;
  84: 
  85:     /**
  86:      * Longitude
  87:      *
  88:      * @var string
  89:      */
  90:     public $lng;
  91: 
  92:     /**
  93:      * Textual location
  94:      *
  95:      * @var string
  96:      */
  97:     public $location;
  98: 
  99:     /**
 100:      * Timestamp for when image was geotagged
 101:      *
 102:      * @var integer
 103:      */
 104:     public $geotag_timestamp;
 105: 
 106:     /**
 107:      * Timestamp of original date.
 108:      *
 109:      * @var integer
 110:      */
 111:     public $originalDate;
 112: 
 113:     /**
 114:      * Horde_Image object for this image.
 115:      *
 116:      * @var Horde_Image_Base
 117:      */
 118:     protected $_image;
 119: 
 120:     /**
 121:      * Dirty flag
 122:      *
 123:      * @var boolean
 124:      */
 125:     protected $_dirty;
 126: 
 127:     /**
 128:      * Flags for loaded views
 129:      *
 130:      * @var array
 131:      */
 132:     protected $_loaded = array();
 133: 
 134:     /**
 135:      * Binary image data for loaded views
 136:      *
 137:      * @var array
 138:      */
 139:     protected $_data = array();
 140:     /**
 141:      * Holds an array of tags for this image
 142:      *
 143:      * @var array
 144:      */
 145:     protected $_tags = array();
 146: 
 147:     /**
 148:      * Cache the raw EXIF data locally
 149:      *
 150:      * @var array
 151:      */
 152:     protected $_exif = array();
 153: 
 154:     /**
 155:      * Const'r
 156:      *
 157:      * @param array $image
 158:      *
 159:      * @return Ansel_Image
 160:      */
 161:     public function __construct(array $image = array())
 162:     {
 163:         if ($image) {
 164:             $this->filename = $image['image_filename'];
 165: 
 166:             if  (!empty($image['gallery_id'])) {
 167:                 $this->gallery = $image['gallery_id'];
 168:             }
 169:             if (!empty($image['image_caption'])) {
 170:                 $this->caption = $image['image_caption'];
 171:             }
 172:             if (isset($image['image_sort'])) {
 173:                 $this->sort = $image['image_sort'];
 174:             }
 175:             if (!empty($image['image_id'])) {
 176:                 $this->id = $image['image_id'];
 177:             }
 178:             if (!empty($image['data'])) {
 179:                 $this->_data['full'] = $image['data'];
 180:             }
 181:             if (!empty($image['image_uploaded_date'])) {
 182:                 $this->uploaded = $image['image_uploaded_date'];
 183:             } else {
 184:                 $this->uploaded = time();
 185:             }
 186:             if (!empty($image['image_type'])) {
 187:                 $this->type = $image['image_type'];
 188:             }
 189:             if (!empty($image['tags'])) {
 190:                 $this->_tags = $image['tags'];
 191:             }
 192:             if (!empty($image['image_faces'])) {
 193:                 $this->facesCount = $image['image_faces'];
 194:             }
 195: 
 196:             $this->location = !empty($image['image_location']) ? $image['image_location'] : '';
 197: 
 198:             // The following may have to be rewritten by EXIF.
 199:             // EXIF requires both an image id and a stream, so we can't
 200:             // get EXIF data before we save the image to the VFS.
 201:             if (!empty($image['image_original_date'])) {
 202:                 $this->originalDate = $image['image_original_date'];
 203:             } else {
 204:                 $this->originalDate = $this->uploaded;
 205:             }
 206:             $this->lat = !empty($image['image_latitude']) ? $image['image_latitude'] : '';
 207:             $this->lng = !empty($image['image_longitude']) ? $image['image_longitude'] : '';
 208:             $this->geotag_timestamp = !empty($image['image_geotag_date']) ? $image['image_geotag_date'] : '0';
 209:         }
 210: 
 211:         $this->_image = Ansel::getImageObject();
 212:         $this->_image->reset();
 213:         $this->id = !empty($image['image_id']) ? $image['image_id'] : null;
 214:     }
 215: 
 216:     /**
 217:      * Obtain a reference to the underlying Horde_Image
 218:      *
 219:      * @return Horde_Image_Base
 220:      */
 221:     public function &getHordeImage()
 222:     {
 223:         return $this->_image;
 224:     }
 225: 
 226:     /**
 227:      * Return the vfs path for this image.
 228:      *
 229:      * @param string $view        The view we want.
 230:      * @param Ansel_Style $style  A gallery style.
 231:      *
 232:      * @return string  The vfs path for this image.
 233:      */
 234:     public function getVFSPath($view = 'full', Ansel_Style $style = null)
 235:     {
 236:         return $this->getVFSPathFromHash($this->getViewHash($view, $style));
 237:     }
 238: 
 239:     /**
 240:      * Generate a path on the VFS given a known style hash.
 241:      *
 242:      * @param string $hash  The sytle hash
 243:      *
 244:      * @return string the VFS path to the directory for the provided hash
 245:      */
 246:     public function getVFSPathFromHash($hash)
 247:     {
 248:          return '.horde/ansel/'
 249:                 . substr(str_pad($this->id, 2, 0, STR_PAD_LEFT), -2)
 250:                 . '/' . $hash;
 251:     }
 252: 
 253:     /**
 254:      * Returns the file name of this image as used in the VFS backend.
 255:      *
 256:      * @param string $view  The image view (full, screen, thumb, mini).
 257:      *
 258:      * @return string  This image's VFS file name.
 259:      */
 260:     public function getVFSName($view)
 261:     {
 262:         $vfsname = $this->id;
 263: 
 264:         if ($view == 'full' && $this->type) {
 265:             $type = strpos($this->type, '/') === false ?
 266:                 'image/' . $this->type :
 267:                 $this->type;
 268:             if ($ext = Horde_Mime_Magic::mimeToExt($type)) {
 269:                 $vfsname .= '.' . $ext;
 270:             }
 271:         } elseif (($GLOBALS['conf']['image']['type'] == 'jpeg') || $view == 'screen') {
 272:             $vfsname .= '.jpg';
 273:         } else {
 274:             $vfsname .= '.png';
 275:         }
 276: 
 277:         return $vfsname;
 278:     }
 279: 
 280:     /**
 281:      * Loads the given view into memory.
 282:      *
 283:      * @param string $view        Which view to load.
 284:      * @param Ansel_Style $style  The gallery style.
 285:      *
 286:      * @throws Ansel_Exception
 287:      */
 288:     public function load($view = 'full', Ansel_Style $style = null)
 289:     {
 290:         // If this is a new image that hasn't been saved yet, we will
 291:         // already have the full data loaded. If we auto-rotate the image
 292:         // then there is no need to save it just to load it again.
 293:         if ($view == 'full' && !empty($this->_data['full'])) {
 294:             $this->_image->loadString($this->_data['full']);
 295:             $this->_loaded['full'] = true;
 296:             return;
 297:         } elseif ($view == 'full') {
 298:             try {
 299:                 $data = $GLOBALS['injector']
 300:                     ->getInstance('Horde_Core_Factory_Vfs')
 301:                     ->create('images')
 302:                     ->read($this->getVFSPath('full'), $this->getVFSName('full'));
 303:             } catch (Horde_Vfs_Exception $e) {
 304:                 Horde::logMessage($e, 'ERR');
 305:                 throw new Ansel_Exception($e);
 306:             }
 307:             $viewHash = 'full';
 308:         } else {
 309:             $viewHash = $this->getViewHash($view, $style);
 310: 
 311:             // If we've already loaded the data, just return now.
 312:             if (!empty($this->_loaded[$viewHash])) {
 313:                 return;
 314:             }
 315:             $this->createView($view, $style);
 316: 
 317:             // If createView() had to resize the full image, we've already
 318:             // loaded the data, so return now.
 319:             if (!empty($this->_loaded[$viewHash])) {
 320:                 return;
 321:             }
 322: 
 323:             // Get the VFS info.
 324:             $vfspath = $this->getVFSPath($view, $style);
 325: 
 326:             // Read in the requested view.
 327:             try {
 328:                 $data = $GLOBALS['injector']
 329:                     ->getInstance('Horde_Core_Factory_Vfs')
 330:                     ->create('images')
 331:                     ->read($vfspath, $this->getVFSName($view));
 332:             } catch (Horde_Vfs_Exception $e) {
 333:                 Horde::logMessage($e, 'ERR');
 334:                 throw new Ansel_Exception($e);
 335:             }
 336:         }
 337: 
 338:         /* We've definitely successfully loaded the image now. */
 339:         $this->_loaded[$viewHash] = true;
 340:         $this->_data[$viewHash] = $data;
 341:         $this->_image->loadString($data);
 342:     }
 343: 
 344:     /**
 345:      * Check if an image view exists and returns the vfs name complete with
 346:      * the hash directory name prepended if appropriate.
 347:      *
 348:      * @param integer $id         Image id to check
 349:      * @param string $view        Which view to check for
 350:      * @param Ansel_Style $style  Style object
 351:      *
 352:      * @return mixed  False if image does not exists | string vfs name
 353:      */
 354:     static public function viewExists($id, $view, Ansel_Style $style)
 355:     {
 356:         // We cannot check empty styles since we cannot get the hash
 357:         if (empty($style)) {
 358:             return false;
 359:         }
 360: 
 361:         // Get the VFS path.
 362:         $view = $style->getHash($view);
 363: 
 364:         // Can't call the various vfs methods here, since this method needs
 365:         // to be called statically.
 366:         $vfspath = '.horde/ansel/' . substr(str_pad($id, 2, 0, STR_PAD_LEFT), -2) . '/' . $view;
 367: 
 368:         // Get VFS name
 369:         $vfsname = $id . '.';
 370:         if ($GLOBALS['conf']['image']['type'] == 'jpeg' || $view == 'screen') {
 371:             $vfsname .= 'jpg';
 372:         } else {
 373:             $vfsname .= 'png';
 374:         }
 375: 
 376:         if ($GLOBALS['injector']->getInstance('Horde_Core_Factory_Vfs')->create('images')->exists($vfspath, $vfsname)) {
 377:             return $view . '/' . $vfsname;
 378:         } else {
 379:             return false;
 380:         }
 381:     }
 382: 
 383:     /**
 384:      * Creates and caches the given view.
 385:      *
 386:      * @param string $view         Which view to create.
 387:      * @param Ansel_Style  $style  A style object
 388:      *
 389:      * @throws Ansel_Exception
 390:      */
 391:     public function createView($view, Ansel_Style $style = null)
 392:     {
 393:         // Default to the gallery's style
 394:         if (empty($style)) {
 395:             $style = $GLOBALS['injector']
 396:                 ->getInstance('Ansel_Storage')
 397:                 ->getGallery($this->gallery)
 398:                 ->getStyle();
 399:         }
 400: 
 401:         // Get the VFS info.
 402:         $vfspath = $this->getVFSPath($view, $style);
 403:         if ($GLOBALS['injector']->getInstance('Horde_Core_Factory_Vfs')
 404:             ->create('images')
 405:             ->exists($vfspath, $this->getVFSName($view))) {
 406:             return;
 407:         }
 408:         try {
 409:             $data = $GLOBALS['injector']
 410:                 ->getInstance('Horde_Core_Factory_Vfs')
 411:                 ->create('images')
 412:                 ->read($this->getVFSPath('full'), $this->getVFSName('full'));
 413:         } catch (Horde_Vfs_Exception $e) {
 414:             Horde::logMessage($e, 'ERR');
 415:             throw new Ansel_Exception($e);
 416:         }
 417: 
 418:         // Force screen images to ALWAYS be jpegs for performance/size
 419:         if ($view == 'screen' && $GLOBALS['conf']['image']['type'] != 'jpeg') {
 420:             $originalType = $this->_image->setType('jpeg');
 421:         } else {
 422:             $originalType = false;
 423:         }
 424: 
 425:         $vHash = $this->getViewHash($view, $style);
 426:         $this->_image->loadString($data);
 427:         if ($view == 'thumb') {
 428:             $viewType = $style->thumbstyle;
 429:         } else {
 430:             // Screen, Mini
 431:             $viewType = ucfirst($view);
 432:         }
 433: 
 434:         try {
 435:             $iview = Ansel_ImageGenerator::factory(
 436:                 $viewType, array('image' => $this, 'style' => $style));
 437:         } catch (Ansel_Exception $e) {
 438:             // It could be we don't support the requested effect, try
 439:             // ansel_default before giving up.
 440:             if ($view == 'thumb' && $viewType != 'Thumb') {
 441:                 $iview = Ansel_ImageGenerator::factory(
 442:                     'Thumb',
 443:                      array(
 444:                          'image' => $this,
 445:                          'style' => Ansel::getStyleDefinition('ansel_default')));
 446:             } else {
 447:                 // If it wasn't a thumb, then something else must be wrong
 448:                 throw $e;
 449:             }
 450:         }
 451: 
 452:         // generate the view
 453:         $iview->create();
 454: 
 455:         // Cache the data from the new ImageGenerator
 456:         try {
 457:             $this->_data[$vHash] = $this->_image->raw();
 458:         } catch (Horde_Image_Exception $e) {
 459:             throw new Ansel_Exception($e);
 460:         }
 461: 
 462:         // ...and put it in Horde_Image obejct, then save
 463:         $this->_image->loadString($this->_data[$vHash]);
 464:         $this->_loaded[$vHash] = true;
 465:         $GLOBALS['injector']->getInstance('Horde_Core_Factory_Vfs')
 466:             ->create('images')
 467:             ->writeData($vfspath, $this->getVFSName($vHash), $this->_data[$vHash], true);
 468: 
 469:         // Autowatermark the screen view
 470:         if ($view == 'screen' &&
 471:             $GLOBALS['prefs']->getValue('watermark_auto') &&
 472:             $GLOBALS['prefs']->getValue('watermark_text') != '') {
 473: 
 474:             $this->watermark('screen');
 475:             $GLOBALS['injector']->getInstance('Horde_Core_Factory_Vfs')
 476:                 ->create('images')
 477:                 ->writeData($vfspath, $this->getVFSName($view), $this->_image->_data);
 478:         }
 479: 
 480:         // Revert any type change
 481:         if ($originalType) {
 482:             $this->_image->setType($originalType);
 483:         }
 484:     }
 485: 
 486:     /**
 487:      * Writes the current data to vfs, used when creating a new image
 488:      *
 489:      * @return boolean
 490:      * @throws Ansel_Exception
 491:      */
 492:     protected function _writeData()
 493:     {
 494:         $this->_dirty = false;
 495: 
 496:         try {
 497:             $GLOBALS['injector']->getInstance('Horde_Core_Factory_Vfs')
 498:                 ->create('images')
 499:                 ->writeData(
 500:                     $this->getVFSPath('full'),
 501:                     $this->getVFSName('full'),
 502:                     $this->_data['full'],
 503:                     true);
 504:         } catch (Horde_Vfs_Exception $e) {
 505:             throw new Ansel_Exception($e);
 506:         }
 507: 
 508:         return true;
 509:     }
 510: 
 511:     /**
 512:      * Change the image data. Deletes old cache and writes the new
 513:      * data to the VFS. Used when updating an image
 514:      *
 515:      * @param string $data  The new data for this image.
 516:      * @param string $view  If specified, the $data represents only this
 517:      *                      particular view. Cache will not be deleted.
 518:      *
 519:      * @throws Ansel_Exception
 520:      */
 521:     public function updateData($data, $view = 'full')
 522:     {
 523:         /* Delete old cached data if we are replacing the full image */
 524:         if ($view == 'full') {
 525:             $this->deleteCache();
 526:         }
 527: 
 528:         try {
 529:             $GLOBALS['injector']->getInstance('Horde_Core_Factory_Vfs')
 530:                 ->create('images')->writeData(
 531:                     $this->getVFSPath($view),
 532:                     $this->getVFSName($view),
 533:                     $data,
 534:                     true);
 535:         } catch (Horde_Vfs_Exception $e) {
 536:             throw new Ansel_Exception($e);
 537:         }
 538:     }
 539: 
 540:     /**
 541:      * Update the image's geotag data. Saves to backend storage as well, so no
 542:      * need to call self::save()
 543:      *
 544:      * @param string $lat       Latitude
 545:      * @param string $lng       Longitude
 546:      * @param string $location  Textual location
 547:      *
 548:      */
 549:     public function geotag($lat, $lng, $location = '')
 550:     {
 551:         $this->lat = $lat;
 552:         $this->lng = $lng;
 553:         $this->location = $location;
 554:         $this->geotag_timestamp = time();
 555:         $this->save();
 556:     }
 557: 
 558:     /**
 559:      * Save image details to storage.
 560:      *
 561:      * @return integer image id
 562:      * @throws Ansel_Exception
 563:      */
 564:     public function save()
 565:     {
 566:         // Existing image, just save and exit
 567:         if ($this->id) {
 568:             return $GLOBALS['injector']
 569:                 ->getInstance('Ansel_Storage')
 570:                 ->saveImage($this);
 571:         }
 572: 
 573:         // New image, need to save the image files
 574:         $GLOBALS['injector']->getInstance('Ansel_Storage')->saveImage($this);
 575: 
 576:         // The EXIF functions require a stream, need to save before we read
 577:         $this->_writeData();
 578: 
 579:         // Get the EXIF data if we are not a gallery key image.
 580:         if ($this->gallery > 0) {
 581:             $needUpdate = $this->getEXIF();
 582:         }
 583: 
 584:         // Create tags from exif data if desired
 585:         $fields = @unserialize($GLOBALS['prefs']->getValue('exif_tags'));
 586:         if ($fields) {
 587:             $this->_exifToTags($fields);
 588:         }
 589: 
 590:         // Save the tags
 591:         if (count($this->_tags)) {
 592:             try {
 593:                 $this->setTags($this->_tags);
 594:             } catch (Exception $e) {
 595:                 // Since we got this far, the image has been added, so
 596:                 // just log the tag failure.
 597:                 Horde::logMessage($e, 'ERR');
 598:             }
 599:         }
 600: 
 601:         // Save again if EXIF changed any values
 602:         if (!empty($needUpdate)) {
 603:             $GLOBALS['injector']->getInstance('Ansel_Storage')->saveImage($this);
 604:         }
 605: 
 606:         return $this->id;
 607:     }
 608: 
 609:     /**
 610:      * Replace this image's image data.
 611:      *
 612:      * @param array $imageData  An array of image data, the same keys as Const'r
 613:      *
 614:      * @return void
 615:      * @throws Ansel_Exception
 616:      */
 617:     public function replace(array $imageData)
 618:     {
 619:         // Reset the data array and remove all cached images
 620:         $this->_data = array();
 621:         $this->reset();
 622: 
 623:         // Remove attributes
 624:         $GLOBALS['injector']
 625:             ->getInstance('Ansel_Storage')
 626:             ->clearImageAttributes($this->id);
 627: 
 628:         // Load the new image data
 629:         $this->getEXIF();
 630:         $this->updateData($imageData);
 631:     }
 632: 
 633:     /**
 634:      * Adds specified EXIF fields to this image's tags.
 635:      * Called during image upload/creation.
 636:      *
 637:      * @param array $fields  An array of EXIF fields to import as a tag.
 638:      *
 639:      * @return void
 640:      */
 641:     protected function _exifToTags(array $fields = array())
 642:     {
 643:         $tags = array();
 644:         foreach ($fields as $field) {
 645:             if (!empty($this->_exif[$field])) {
 646:                 if (substr($field, 0, 8) == 'DateTime') {
 647:                     $d = new Horde_Date(strtotime($this->_exif[$field]));
 648:                     $tags[] = $d->format("Y-m-d");
 649:                 } elseif ($field == 'Keywords') {
 650:                     $tags = array_merge($tags, explode(',', $this->_exif[$field]));
 651:                 } else {
 652:                     $tags[] = $this->_exif[$field];
 653:                 }
 654:             }
 655:         }
 656: 
 657:         $this->_tags = array_merge($this->_tags, $tags);
 658:     }
 659: 
 660:     /**
 661:      * Reads the EXIF data from the image, caches in the object and writes to
 662:      * storage. Also populates any local properties that come from the EXIF
 663:      * data.
 664:      *
 665:      * @param boolean $replacing  Set to true if we are replacing the exif data.
 666:      *
 667:      * @return boolean  True if any local properties were modified, False if not.
 668:      * @throws Ansel_Exception
 669:      */
 670:     public function getEXIF($replacing = false)
 671:     {
 672:         /* Clear the local copy */
 673:         $this->_exif = array();
 674: 
 675:         /* Get the data */
 676:         try {
 677:             $imageFile = $GLOBALS['injector']
 678:                 ->getInstance('Horde_Core_Factory_Vfs')
 679:                 ->create('images')
 680:                 ->readFile(
 681:                     $this->getVFSPath('full'),
 682:                     $this->getVFSName('full'));
 683:         } catch (Horde_Vfs_Exception $e) {
 684:             throw new Ansel_Exception($e);
 685:         }
 686:         $exif = Horde_Image_Exif::factory(
 687:             $GLOBALS['conf']['exif']['driver'],
 688:             !empty($GLOBALS['conf']['exif']['params']) ?
 689:                 $GLOBALS['conf']['exif']['params'] :
 690:                 array());
 691: 
 692:         try {
 693:             $exif_fields = $exif->getData($imageFile);
 694:         } catch (Horde_Image_Exception $e) {
 695:             // Log the error, but it's not the end of the world, so just ignore
 696:             Horde::logMessage($e, 'ERR');
 697:             $exif_fields = array();
 698:             return false;
 699:         }
 700: 
 701:         // Flag to determine if we need to resave the image data.
 702:         $needUpdate = false;
 703: 
 704:         // Populate any local properties that come from EXIF
 705:         if (!empty($exif_fields['GPSLatitude'])) {
 706:             $this->lat = $exif_fields['GPSLatitude'];
 707:             $this->lng = $exif_fields['GPSLongitude'];
 708:             $this->geotag_timestamp = time();
 709:             $needUpdate = true;
 710:         }
 711: 
 712:         if (!empty($exif_fields['DateTimeOriginal'])) {
 713:             $this->originalDate = $exif_fields['DateTimeOriginal'];
 714:             $needUpdate = true;
 715:         }
 716: 
 717:         // Overwrite any existing value for caption with exif data
 718:         $exif_title = $GLOBALS['prefs']->getValue('exif_title');
 719:         if (!empty($exif_fields[$exif_title])) {
 720:             $this->caption = $exif_fields[$exif_title];
 721:             $needUpdate = true;
 722:         }
 723: 
 724:         // Attempt to autorotate based on Orientation field
 725:         $this->_autoRotate($exif_fields['Orientation']);
 726: 
 727:         // Save attributes.
 728:         if ($replacing) {
 729:             $GLOBALS['injector']
 730:                 ->getInstance('Ansel_Storage')
 731:                 ->clearImageAttributes($this->id);
 732:         }
 733: 
 734:         foreach ($exif_fields as $name => $value) {
 735:             if (!empty($value)) {
 736:                 $GLOBALS['injector']
 737:                     ->getInstance('Ansel_Storage')
 738:                     ->saveImageAttribute($this->id, $name, $value);
 739:                 $this->_exif[$name] = Horde_Image_Exif::getHumanReadable($name, $value);
 740:             }
 741:         }
 742: 
 743:         return $needUpdate;
 744:     }
 745: 
 746:     /**
 747:      * Autorotate based on EXIF orientation field. Updates the data in memory
 748:      * only.
 749:      */
 750:     protected function _autoRotate($orientation)
 751:     {
 752:         if (!empty($orientation) && $orientation != 1) {
 753:             switch ($orientation) {
 754:             case 2:
 755:                 $this->mirror();
 756:                 break;
 757: 
 758:             case 3:
 759:                 $this->rotate('full', 180);
 760:                 break;
 761: 
 762:             case 4:
 763:                 $this->mirror();
 764:                 $this->rotate('full', 180);
 765:                 break;
 766: 
 767:             case 5:
 768:                 $this->flip();
 769:                 $this->rotate('full', 90);
 770:                 break;
 771: 
 772:             case 6:
 773:                 $this->rotate('full', 90);
 774:                 break;
 775: 
 776:             case 7:
 777:                 $this->mirror();
 778:                 $this->rotate('full', 90);
 779:                 break;
 780: 
 781:             case 8:
 782:                 $this->rotate('full', 270);
 783:                 break;
 784:             }
 785: 
 786:             if ($this->_dirty) {
 787:                 $this->_data['full'] = $this->raw();
 788:                 $this->_writeData();
 789:             }
 790:         }
 791:     }
 792: 
 793:     /**
 794:      * Reset the image, removing all loaded views.
 795:      *
 796:      */
 797:     public function reset()
 798:     {
 799:         $this->_image->reset();
 800:         $this->_loaded = array();
 801:     }
 802: 
 803:     /**
 804:      * Deletes the specified cache file.
 805:      *
 806:      * If none is specified, deletes all of the cache files.
 807:      *
 808:      * @param string $view  Which cache file to delete.
 809:      */
 810:     public function deleteCache($view = 'all')
 811:     {
 812:         // Delete cached screen image.
 813:         if ($view == 'all' || $view == 'screen') {
 814:             try {
 815:                 $GLOBALS['injector']
 816:                     ->getInstance('Horde_Core_Factory_Vfs')
 817:                     ->create('images')
 818:                     ->deleteFile(
 819:                         $this->getVFSPath('screen'),
 820:                         $this->getVFSName('screen'));
 821:             } catch (Horde_Vfs_Exception $e   ) {}
 822:         }
 823: 
 824:         // Delete cached mini image.
 825:         if ($view == 'all' || $view == 'mini') {
 826:             try {
 827:                 $GLOBALS['injector']
 828:                     ->getInstance('Horde_Core_Factory_Vfs')
 829:                     ->create('images')
 830:                     ->deleteFile(
 831:                         $this->getVFSPath('mini'),
 832:                         $this->getVFSName('mini'));
 833:             } catch (Horde_Vfs_Exception $e) {}
 834:         }
 835:         if ($view == 'all' || $view == 'thumb') {
 836:             $hashes = $GLOBALS['injector']
 837:                 ->getInstance('Ansel_Storage')
 838:                 ->getHashes();
 839:             foreach ($hashes as $hash) {
 840:                 try {
 841:                     $GLOBALS['injector']->getInstance('Horde_Core_Factory_Vfs')
 842:                         ->create('images')
 843:                         ->deleteFile(
 844:                             $this->getVFSPathFromHash($hash),
 845:                             $this->getVFSName('thumb'));
 846:                 } catch (Horde_Vfs_Exception $e) {}
 847:             }
 848:         }
 849:     }
 850: 
 851:     /**
 852:      * Returns the raw data for the given view.
 853:      *
 854:      * @param string $view  Which view to return.
 855:      *
 856:      * @return string  The raw binary image data
 857:      */
 858:     public function raw($view = 'full')
 859:     {
 860:         if ($this->_dirty) {
 861:             $data = $this->_image->raw( );
 862:             $this->reset();
 863:             return $data;
 864:         } else {
 865:             $this->load($view);
 866:             return $this->_data[$view];
 867:         }
 868:     }
 869: 
 870:     /**
 871:      * Sends the correct HTTP headers to the browser to download this image.
 872:      *
 873:      * @param string $view  The view to download.
 874:      */
 875:     public function downloadHeaders($view = 'full')
 876:     {
 877:         global $browser, $conf;
 878: 
 879:         $filename = $this->filename;
 880:         if ($view != 'full') {
 881:             if ($ext = Horde_Mime_Magic::mimeToExt('image/' . $conf['image']['type'])) {
 882:                 $filename .= '.' . $ext;
 883:             }
 884:         }
 885: 
 886:         $browser->downloadHeaders($filename);
 887:     }
 888: 
 889:     /**
 890:      * Display the requested view.
 891:      *
 892:      * @param string $view        Which view to display.
 893:      * @param Ansel_Style $style  Force use of this gallery style.
 894:      *
 895:      * @throws Horde_Exception_PermissionDenied, Ansel_Exception
 896:      */
 897:     public function display($view = 'full', Ansel_Style $style = null)
 898:     {
 899:         if ($view == 'full' && !$this->_dirty) {
 900:             // Check full photo permissions
 901:             $gallery = $GLOBALS['injector']
 902:                 ->getInstance('Ansel_Storage')
 903:                 ->getGallery($this->gallery);
 904:             if (!$gallery->canDownload()) {
 905:                 throw Horde_Exception_PermissionDenied(
 906:                     sprintf(_("Access denied downloading photos from \"%s\"."), $gallery->get('name')));
 907:             }
 908: 
 909:             try {
 910:                 $data = $GLOBALS['injector']
 911:                     ->getInstance('Horde_Core_Factory_Vfs')
 912:                     ->create('images')->read(
 913:                         $this->getVFSPath('full'),
 914:                         $this->getVFSName('full'));
 915:             } catch (Horde_Vfs_Exception $e) {
 916:                 throw new Ansel_Exception($e);
 917:             }
 918:             echo $data;
 919:         } else {
 920:             $this->load($view, $style);
 921:             $this->_image->display();
 922:         }
 923:     }
 924: 
 925:     /**
 926:      * Wraps the given view into a file.
 927:      *
 928:      * @param string $view  Which view to wrap up.
 929:      *
 930:      * @throws Ansel_Exception
 931:      */
 932:     public function toFile($view = 'full')
 933:     {
 934:         try {
 935:             $this->load($view);
 936:             return $this->_image->toFile(
 937:                 $this->_dirty ?
 938:                     false :
 939:                     $this->_data[$view]);
 940:         } catch (Horde_Exception $e) {
 941:             Horde::logMessage($e, 'ERR');
 942:             throw new Ansel_Exception($e);
 943:         }
 944:     }
 945: 
 946:     /**
 947:      * Returns the dimensions of the given view.
 948:      *
 949:      * @param string $view  The view (full, screen etc..) to get dimensions for
 950:      *
 951:      * @return array  A hash of 'width and 'height' dimensions.
 952:      * @throws Ansel_Exception
 953:      */
 954:     public function getDimensions($view = 'full')
 955:     {
 956:         try {
 957:             $this->load($view);
 958:             return $this->_image->getDimensions();
 959:         } catch (Horde_Exception $e) {
 960:             Horde::logMessage($e, 'INFO');
 961:             throw new Ansel_Exception($e);
 962:         }
 963:     }
 964: 
 965:     /**
 966:      * Rotates the image.
 967:      *
 968:      * @param string $view    The view (size) to work with.
 969:      * @param integer $angle  What angle to rotate the image by.
 970:      */
 971:     public function rotate($view = 'full', $angle)
 972:     {
 973:         $this->load($view);
 974:         $this->_dirty = true;
 975:         $this->_image->rotate($angle);
 976:     }
 977: 
 978:     /**
 979:      * Crop this image to desired dimensions. Crops the currently loaded
 980:      * view present in the Horde_Image object.
 981:      *
 982:      * @see Horde_Image_Base::crop for explanation of parameters
 983:      *
 984:      * @param integer $x1
 985:      * @param integer $y1
 986:      * @param integer $x2
 987:      * @param integer $y2
 988:      *
 989:      * @throws Ansel_Exception
 990:      */
 991:     public function crop($x1, $y1, $x2, $y2)
 992:     {
 993:         $this->_dirty = true;
 994:         try {
 995:             $this->_image->crop($x1, $y1, $x2, $y2);
 996:         } catch (Horde_Image_Exception $e) {
 997:             throw new Ansel_Exception($e);
 998:         }
 999:     }
1000: 
1001:     /**
1002:      * Resize the current image.
1003:      *
1004:      * @param integer $width        The new width.
1005:      * @param integer $height       The new height.
1006:      * @param boolean $ratio        Maintain original aspect ratio.
1007:      * @param boolean $keepProfile  Keep the image meta data.
1008:      *
1009:      * @throws Ansel_Exception
1010:      */
1011:     public function resize($width, $height, $ratio = true, $keepProfile = false)
1012:     {
1013:         try {
1014:             $this->_image->resize($width, $height, $ratio, $keepProfile);
1015:         } catch (Horde_Image_Exception $e) {
1016:             throw new Ansel_Exception($e);
1017:         }
1018:     }
1019: 
1020:     /**
1021:      * Converts the image to grayscale.
1022:      *
1023:      * @param string $view The view (screen, full, etc...) to work with.
1024:      *
1025:      * @throws Ansel_Exception
1026:      */
1027:     public function grayscale($view = 'full')
1028:     {
1029:         $this->load($view);
1030:         $this->_dirty = true;
1031:         try {
1032:             $this->_image->grayscale();
1033:         } catch (Horde_Image_Exception $e) {
1034:             throw new Ansel_Exception($e);
1035:         }
1036:     }
1037: 
1038:     /**
1039:      * Watermarks the image.
1040:      *
1041:      * @param string $view       The view (size) to work with.
1042:      * @param string $watermark  String to use as the watermark.
1043:      * @param string $halign     Horizontal alignment (Left, Right, Center)
1044:      * @param string $valign     Vertical alignment (Top, Center, Bottom)
1045:      * @param string $font       The font to use (not all image drivers will
1046:      *                           support this).
1047:      *
1048:      * @throws Ansel_Exception
1049:      */
1050:     public function watermark($view = 'full', $watermark = null, $halign = null,
1051:             $valign = null, $font = null)
1052:     {
1053:         if (empty($watermark)) {
1054:             $watermark = $GLOBALS['prefs']->getValue('watermark_text');
1055:         }
1056:         if (empty($halign)) {
1057:             $halign = $GLOBALS['prefs']->getValue('watermark_horizontal');
1058:         }
1059:         if (empty($valign)) {
1060:             $valign = $GLOBALS['prefs']->getValue('watermark_vertical');
1061:         }
1062:         if (empty($font)) {
1063:             $font = $GLOBALS['prefs']->getValue('watermark_font');
1064:         }
1065:         if (empty($watermark)) {
1066:             $identity = $GLOBALS['injector']
1067:                 ->getInstance('Horde_Core_Factory_Identity')
1068:                 ->create();
1069:             $name = $identity->getValue('fullname');
1070:             if (empty($name)) {
1071:                 $name = $GLOBALS['registry']->getAuth();
1072:             }
1073:             $watermark = sprintf(_("(c) %s %s"), date('Y'), $name);
1074:         }
1075: 
1076:         $this->load($view);
1077:         $this->_dirty = true;
1078:         $params = array(
1079:             'text' => $watermark,
1080:             'halign' => $halign,
1081:             'valign' => $valign,
1082:             'fontsize' => $font);
1083:         if (!empty($GLOBALS['conf']['image']['font'])) {
1084:             $params['font'] = $GLOBALS['conf']['image']['font'];
1085:         }
1086: 
1087:         try {
1088:             $this->_image->addEffect('TextWatermark', $params);
1089:         } catch (Horde_Image_Exception $e) {
1090:             throw new Ansel_Exception($e);
1091:         }
1092:     }
1093: 
1094:     /**
1095:      * Flips the image.
1096:      *
1097:      * @param string $view The view to work with.
1098:      *
1099:      * @throws Ansel_Exception
1100:      */
1101:     public function flip($view = 'full')
1102:     {
1103:         $this->load($view);
1104:         $this->_dirty = true;
1105: 
1106:         try {
1107:             $this->_image->flip();
1108:         } catch (Horde_Image_Exception $e) {
1109:             throw new Ansel_Exception($e);
1110:         }
1111:     }
1112: 
1113:     /**
1114:      * Mirrors the image.
1115:      *
1116:      * @param string $view The view (size) to work with.
1117:      *
1118:      * @throws Ansel_Exception
1119:      */
1120:     public function mirror($view = 'full')
1121:     {
1122:         $this->load($view);
1123:         $this->_dirty = true;
1124:         try {
1125:             $this->_image->mirror();
1126:         } catch (Horde_Image_Exception $e) {
1127:             throw new Ansel_Exception($e);
1128:         }
1129:     }
1130: 
1131:     /**
1132:      * Add an effect to the effect stack
1133:      *
1134:      * @param string $type    The effect to add.
1135:      * @param array  $params  The effect parameters.
1136:      *
1137:      * @throws Ansel_Exception
1138:      */
1139:     public function addEffect($type, $params = array())
1140:     {
1141:         try {
1142:             $this->_image->addEffect($type, $params);
1143:         } catch (Horde_Image_Exception $e) {
1144:             Horde::logMessage($e, 'ERR');
1145:             throw new Ansel_Exception($e);
1146:         }
1147:     }
1148: 
1149:     /**
1150:      * Apply any pending effects to the underlaying Horde_Image
1151:      *
1152:      * @throws Ansel_Exception
1153:      */
1154:     public function applyEffects()
1155:     {
1156:         try {
1157:             $this->_image->applyEffects();
1158:         } catch (Horde_Image_Exception $e) {
1159:             throw new Ansel_Exception($e);
1160:         }
1161:     }
1162: 
1163:     /**
1164:      * Returns this image's tags.
1165:      *
1166:      * @see Ansel_Tags::readTags()
1167:      *
1168:      * @return array  An array of tags
1169:      * @throws Horde_Exception_PermissionDenied, Ansel_Exception
1170:      */
1171:     public function getTags()
1172:     {
1173:         if (count($this->_tags)) {
1174:             return $this->_tags;
1175:         }
1176:         $gallery = $GLOBALS['injector']->getInstance('Ansel_Storage')
1177:             ->getGallery($this->gallery);
1178:         if ($gallery->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::READ)) {
1179:             return $GLOBALS['injector']->getInstance('Ansel_Tagger')
1180:                 ->getTags($this->id, 'image');
1181:         } else {
1182:             throw new Horde_Exception_PermissionDenied(
1183:                 _("Access denied viewing this photo."));
1184:         }
1185:     }
1186: 
1187:     /**
1188:      * Either add or replace this image's tags.
1189:      *
1190:      * @param array $tags       An array of tag names
1191:      * @param boolean $replace  Replace all tags with those provided.
1192:      *
1193:      * @throws Horde_Exception_PermissionDenied
1194:      */
1195:     public function setTags(array $tags, $replace = true)
1196:     {
1197:         $gallery = $GLOBALS['injector']
1198:             ->getInstance('Ansel_Storage')
1199:             ->getGallery(abs($this->gallery));
1200:         if ($gallery->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::EDIT)) {
1201:             $this->_tags = array();
1202: 
1203:             if ($replace) {
1204:                 $GLOBALS['injector']
1205:                     ->getInstance('Ansel_Tagger')
1206:                     ->replaceTags(
1207:                         (string)$this->id,
1208:                         $tags,
1209:                         $gallery->get('owner'),
1210:                         'image');
1211:             } else {
1212:                 $GLOBALS['injector']
1213:                     ->getInstance('Ansel_Tagger')
1214:                     ->tag(
1215:                         (string)$this->id,
1216:                         $tags,
1217:                         $gallery->get('owner'),
1218:                         'image');
1219:             }
1220:         } else {
1221:             throw new Horde_Exception_PermissionDenied(_("Access denied adding tags to this photo."));
1222:         }
1223:     }
1224: 
1225:     /**
1226:      * Remove a single tag from this image's tag collection
1227:      *
1228:      * @param string $tag  The tag name to remove.
1229:      */
1230:     public function removeTag($tag)
1231:     {
1232:         $gallery = $GLOBALS['injector']
1233:             ->getInstance('Ansel_Storage')
1234:             ->getGallery(abs($this->gallery));
1235:         if ($gallery->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::EDIT)) {
1236:             $GLOBALS['injector']
1237:                 ->getInstance('Ansel_Tagger')
1238:                 ->untag(
1239:                     (string)$this->id,
1240:                     $tag);
1241:         }
1242:     }
1243: 
1244:     /**
1245:      * Get the Ansel_View_Image_Thumb object
1246:      *
1247:      * @param Ansel_Gallery $parent  The parent Ansel_Gallery object.
1248:      * @param Ansel_Style   $style   A gallery definition to use.
1249:      * @param boolean       $mini    Force the use of a mini thumbnail?
1250:      * @param array         $params  Any additional parameters the Ansel_Tile
1251:      *                               object may need.
1252:      *
1253:      * @return string  HTML for this image's view tile.
1254:      */
1255:     public function getTile(Ansel_Gallery $parent = null,
1256:                             Ansel_Style $style = null,
1257:                             $mini = false,
1258:                             array $params = array())
1259:     {
1260:         if (!is_null($parent) && is_null($style)) {
1261:             $style = $parent->getStyle();
1262:         }
1263: 
1264:         return Ansel_Tile_Image::getTile($this, $style, $mini, $params);
1265:     }
1266: 
1267:     /**
1268:      * Get the image type for the requested view.
1269:      *
1270:      * @return string  The requested view's mime type
1271:      */
1272:     public function getType($view = 'full')
1273:     {
1274:         if ($view == 'full') {
1275:             return $this->type;
1276:         } elseif ($view == 'screen') {
1277:             return 'image/jpg';
1278:         } else {
1279:             return 'image/' . $GLOBALS['conf']['image']['type'];
1280:         }
1281:     }
1282: 
1283:     /**
1284:      * Return a hash key for the given view and style.
1285:      *
1286:      * @param string $view        The view (thumb, prettythumb etc...)
1287:      * @param Ansel_Style $style  The style.
1288:      *
1289:      * @return string  A md5 hash suitable for use as a key.
1290:      */
1291:     public function getViewHash($view, Ansel_Style $style = null)
1292:     {
1293:         // These views do not care about style...just return the $view value.
1294:         if ($view == 'screen' || $view == 'mini' || $view == 'full') {
1295:             return $view;
1296:         }
1297: 
1298:         if (is_null($style)) {
1299:             $gallery = $GLOBALS['injector']->getInstance('Ansel_Storage')
1300:                 ->getGallery(abs($this->gallery));
1301:             $style = $gallery->getStyle();
1302:         }
1303: 
1304:         return $style->getHash($view);
1305:     }
1306: 
1307:     /**
1308:      * Get the image attributes from the backend.
1309:      *
1310:      * @param boolean $format     Format the EXIF data. If false, the raw data
1311:      *                            is returned.
1312:      *
1313:      * @return array  The EXIF data.
1314:      */
1315:     public function getAttributes($format = false)
1316:     {
1317:         $attributes = $GLOBALS['injector']->getInstance('Ansel_Storage')
1318:             ->getImageAttributes($this->id);
1319:         $exif = Horde_Image_Exif::factory(
1320:             $GLOBALS['conf']['exif']['driver'],
1321:             !empty($GLOBALS['conf']['exif']['params']) ?
1322:                 $GLOBALS['conf']['exif']['params'] :
1323:                 array());
1324:         $fields = Horde_Image_Exif::getFields($exif);
1325:         $output = array();
1326: 
1327:         foreach ($fields as $field => $data) {
1328:             if (!isset($attributes[$field])) {
1329:                 continue;
1330:             }
1331:             $value = Horde_Image_Exif::getHumanReadable(
1332:                 $field,
1333:                 Horde_String::convertCharset($attributes[$field], $GLOBALS['conf']['sql']['charset'], 'UTF-8'));
1334:             if (!$format) {
1335:                 $output[$field] = $value;
1336:             } else {
1337:                 $description = isset($data['description']) ? $data['description'] : $field;
1338:                 $output[] = '<td><strong>' . $description . '</strong></td><td>' . htmlspecialchars($value) . '</td>';
1339:             }
1340:         }
1341: 
1342:         return $output;
1343:     }
1344: 
1345:     /**
1346:      * Indicates if this image represents a multipage image.
1347:      *
1348:      * @return boolean
1349:      * @throws Ansel_Exception
1350:      */
1351:     public function isMultiPage()
1352:     {
1353:         $this->load();
1354:         try {
1355:             return $this->_image->getImagePageCount() > 1;
1356:         } catch (Horde_Image_Exception $e) {
1357:             throw new Ansel_Exception($e);
1358:         }
1359:     }
1360: 
1361:     /**
1362:      * Get the number of pages that a multipage image contains.
1363:      *
1364:      * @return integer  The number of pages.
1365:      * @throws Ansel_Exception
1366:      */
1367:     public function getImagePageCount()
1368:     {
1369:         if (empty($this->_loaded['full'])) {
1370:             $this->load();
1371:         }
1372: 
1373:         try {
1374:             return $this->_image->getImagePageCount();
1375:         } catch (Horde_Image_Exception $e) {
1376:             throw new Ansel_Exception($e);
1377:         }
1378:     }
1379: 
1380:     /**
1381:      * Reset the iterator to the first image in the set.
1382:      *
1383:      * @return void
1384:      * @throws Ansel_Exception
1385:      */
1386:     public function rewind()
1387:     {
1388:         if (empty($this->_loaded['full'])) {
1389:             $this->load();
1390:         }
1391:         try {
1392:             $this->_image->rewind();
1393:         } catch (Horde_Image_Exception $e) {
1394:             throw new Ansel_Exception($e);
1395:         }
1396:     }
1397: 
1398:     /**
1399:      * Return the current image from the internal iterator.
1400:      *
1401:      * @return Ansel_Image
1402:      */
1403:     public function current()
1404:     {
1405:         if (empty($this->_loaded['full'])) {
1406:             $this->load();
1407:         }
1408:         try {
1409:             return $this->_buildImageObject($this->_image->current());
1410:         } catch (Horde_Image_Exception $e) {
1411:             throw new Ansel_Exception($e);
1412:         }
1413:     }
1414: 
1415:     /**
1416:      * Get the index of the internal iterator.
1417:      *
1418:      * @return integer
1419:      * @throws Ansel_Exception
1420:      */
1421:     public function key()
1422:     {
1423:         if (empty($this->_loaded['full'])) {
1424:             $this->load();
1425:         }
1426:         try {
1427:             return $this->_image->key();
1428:         } catch (Horde_Image_Exception $e) {
1429:             throw new Ansel_Exception($e);
1430:         }
1431:     }
1432: 
1433:     /**
1434:      * Advance the iterator
1435:      *
1436:      * @return mixed Ansel_Image or false if not valid()
1437:      */
1438:     public function next()
1439:     {
1440:         if (empty($this->_loaded['full'])) {
1441:             $this->load();
1442:         }
1443:         if ($next = $this->_image->next()) {
1444:             return $this->_buildImageObject($next);
1445:         }
1446: 
1447:         return false;
1448:     }
1449: 
1450:     /**
1451:      * Deterimines if the current iterator item is valid.
1452:      *
1453:      * @return boolean
1454:      * @throws Ansel_Exception
1455:      */
1456:     public function valid()
1457:     {
1458:         if (empty($this->_loaded['full'])) {
1459:             $this->load();
1460:         }
1461:         try {
1462:             return $this->_image->valid();
1463:         } catch (Horde_Image_Exception $e) {
1464:             throw new Ansel_Exception($e);
1465:         }
1466:     }
1467: 
1468:     /**
1469:      * Build an Ansel_Image from a given Horde_Image.
1470:      * Used to wrap iterating the Horde_Image
1471:      *
1472:      * @param Horde_Image_Base $image  The Horde_Image
1473:      *
1474:      * @return Ansel_Image
1475:      */
1476:     protected function _buildImageObject(Horde_Image_Base $image)
1477:     {
1478:         $params = array(
1479:                 'image_filename' => $this->filename,
1480:                 'data' => $image->raw(),
1481:         );
1482:         $newImage = new Ansel_Image($params);
1483: 
1484:         return $newImage;
1485:     }
1486: }
1487: 
API documentation generated by ApiGen