Overview

Packages

  • Image
  • None

Classes

  • Horde_Image
  • Horde_Image_Base
  • Horde_Image_Effect
  • Horde_Image_Effect_Border
  • Horde_Image_Effect_Gd_DropShadow
  • Horde_Image_Effect_Gd_RoundCorners
  • Horde_Image_Effect_Gd_TextWatermark
  • Horde_Image_Effect_Gd_Unsharpmask
  • Horde_Image_Effect_Im_Border
  • Horde_Image_Effect_Im_CenterCrop
  • Horde_Image_Effect_Im_Composite
  • Horde_Image_Effect_Im_DropShadow
  • Horde_Image_Effect_Im_LiquidResize
  • Horde_Image_Effect_Im_PhotoStack
  • Horde_Image_Effect_Im_PolaroidImage
  • Horde_Image_Effect_Im_RoundCorners
  • Horde_Image_Effect_Im_TextWatermark
  • Horde_Image_Effect_Im_Unsharpmask
  • Horde_Image_Effect_Imagick_Border
  • Horde_Image_Effect_Imagick_CenterCrop
  • Horde_Image_Effect_Imagick_Composite
  • Horde_Image_Effect_Imagick_DropShadow
  • Horde_Image_Effect_Imagick_LiquidResize
  • Horde_Image_Effect_Imagick_PhotoStack
  • Horde_Image_Effect_Imagick_PolaroidImage
  • Horde_Image_Effect_Imagick_RoundCorners
  • Horde_Image_Effect_Imagick_SmartCrop
  • Horde_Image_Effect_Imagick_TextWatermark
  • Horde_Image_Effect_Imagick_Unsharpmask
  • Horde_Image_Exception
  • Horde_Image_Exif
  • Horde_Image_Exif_Base
  • Horde_Image_Exif_Bundled
  • Horde_Image_Exif_Exiftool
  • Horde_Image_Exif_Parser_Base
  • Horde_Image_Exif_Php
  • Horde_Image_Gd
  • Horde_Image_Im
  • Horde_Image_Imagick
  • Horde_Image_Png
  • Horde_Image_Svg
  • Horde_Image_Swf
  • Horde_Image_Translation
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Imagick driver for the Horde_Image API
  4:  *
  5:  * Copyright 2007-2012 Horde LLC (http://www.horde.org/)
  6:  *
  7:  * See the enclosed file COPYING for license information (LGPL). If you
  8:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  9:  *
 10:  * @author  Michael J. Rubinsky <mrubinsk@horde.org>
 11:  * @package Image
 12:  */
 13: class Horde_Image_Imagick extends Horde_Image_Base
 14: {
 15:     /**
 16:      * The underlaying Imagick object
 17:      *
 18:      * @var Imagick
 19:      */
 20:     protected $_imagick;
 21: 
 22:     /**
 23:      * Flag for iterator, since calling nextImage on Imagick would result in a
 24:      * fatal error if there are no more images.
 25:      *
 26:      * @var boolean
 27:      */
 28:     private $_noMoreImages = false;
 29: 
 30:     /**
 31:      * Capabilites of this driver.
 32:      *
 33:      * @var array
 34:      */
 35:     protected $_capabilities = array('resize',
 36:                                      'crop',
 37:                                      'rotate',
 38:                                      'grayscale',
 39:                                      'flip',
 40:                                      'mirror',
 41:                                      'sepia',
 42:                                      'canvas',
 43:                                      'multipage',
 44:                                      'pdf');
 45:     /**
 46:      * Const'r
 47:      *
 48:      * @see Horde_Image_Base::_construct
 49:      */
 50:     public function __construct($params, $context = array())
 51:     {
 52:         if (!Horde_Util::loadExtension('imagick')) {
 53:             throw new Horde_Image_Exception('Required PECL Imagick extension not found.');
 54:         }
 55:         parent::__construct($params, $context);
 56:         ini_set('imagick.locale_fix', 1);
 57:         $this->_imagick = new Imagick();
 58:         if (!empty($params['filename'])) {
 59:             $this->loadFile($params['filename']);
 60:         } elseif(!empty($params['data'])) {
 61:             $this->loadString($params['data']);
 62:         } else {
 63:             $this->_width = max(array($this->_width, 1));
 64:             $this->_height = max(array($this->_height, 1));
 65:             try {
 66:                 $this->_imagick->newImage($this->_width, $this->_height, $this->_background);
 67:             } catch (ImagickException $e) {
 68:                 throw new Horde_Image_Exception($e);
 69:             }
 70:         }
 71: 
 72:         try {
 73:             $this->_imagick->setImageFormat($this->_type);
 74:         } catch (ImagickException $e) {
 75:             throw new Horde_Image_Exception($e);
 76:         }
 77:     }
 78: 
 79:     /**
 80:      * Load image data from a string.
 81:      *
 82:      * @param string $id
 83:      * @param string $image_data
 84:      *
 85:      * @return void
 86:      */
 87:     public function loadString($image_data)
 88:     {
 89:         parent::loadString($image_data);
 90:         $this->_imagick->clear();
 91:         try {
 92:             $this->_imagick->readImageBlob($this->_data);
 93:             $this->_imagick->setImageFormat($this->_type);
 94:             $this->_imagick->setIteratorIndex(0);
 95:         } catch (ImagickException $e) {
 96:             throw new Horde_Image_Exception($e);
 97:         }
 98:         unset($this->_data);
 99:     }
100: 
101:     /**
102:      * Load the image data from a file.
103:      *
104:      * @param string $filename  The full path and filename to the file to load
105:      *                          the image data from. The filename will also be
106:      *                          used for the image id.
107:      *
108:      * @return mixed
109:      */
110:     public function loadFile($filename)
111:     {
112:         // parent function loads image data into $this->_data
113:         parent::loadFile($filename);
114:         $this->_imagick->clear();
115:         try {
116:             $this->_imagick->readImageBlob($this->_data);
117:             $this->_imagick->setImageFormat($this->_type);
118:             $this->_imagick->setIteratorIndex(0);
119:         } catch (ImagickException $e) {
120:             throw new Horde_Image_Exception($e);
121:         }
122:         unset($this->_data);
123:     }
124: 
125:     /**
126:      * Set the image type
127:      *
128:      * @see Horde_Image_Base::setType()
129:      */
130:     public function setType($type)
131:     {
132:         $old = parent::setType($type);
133:         try {
134:             $this->_imagick->setImageFormat($this->_type);
135:         } catch (ImagickException $e) {
136:             // Don't care about an empty wand here.
137:         }
138: 
139:         return $old;
140:     }
141: 
142:     /*
143:      * Return the raw image data.
144:      *
145:      * @param boolean $convert  Ignored for imagick driver.
146:      *
147:      * @return string  The raw image data.
148:      */
149:     public function raw($convert = false)
150:     {
151:         try {
152:             return $this->_imagick->getImageBlob();
153:         } catch (ImagickException $e) {
154:             throw Horde_Image_Exception($e);
155:         }
156:     }
157: 
158:     public function reset()
159:     {
160:         parent::reset();
161:         $this->_imagick->clear();
162:         $this->_noMoreImages = false;
163:     }
164: 
165:     /**
166:      * Resize current image.
167:      *
168:      * @see Horde_Image_im::resize()
169:      *
170:      * @return void
171:      */
172:     public function resize($width, $height, $ratio = true, $keepProfile = false)
173:     {
174:         try {
175:             if ($keepProfile) {
176:                 $this->_imagick->resizeImage($width, $height, $ratio);
177:             } else {
178:                 $this->_imagick->thumbnailImage($width, $height, $ratio);
179:             }
180:         } catch (ImagickException $e) {
181:             throw new Horde_Image_Exception($e);
182:         }
183:         $this->clearGeometry();
184:     }
185: 
186:     /**
187:      * *ALWAYS* use getDimensions() to get image geometry...instance
188:      * variables only cache geometry until it changes, then they go
189:      * to zero.
190:      *
191:      * @return array of geometry information.
192:      */
193:     public function getDimensions()
194:     {
195:         if ($this->_height == 0 && $this->_width == 0) {
196:             try {
197:                 $size = $this->_imagick->getImageGeometry();
198:             } catch (ImagickException $e) {
199:                 return array('width' => 0, 'height' => 0);
200:                 //throw new Horde_Image_Exception($e);
201:             }
202: 
203:             $this->_height = $size['height'];
204:             $this->_width = $size['width'];
205:         }
206: 
207:         return array('width' => $this->_width,
208:                      'height' => $this->_height);
209: 
210:     }
211: 
212:     /**
213:      * Crop the current image.
214:      *
215:      * @param integer $x1  x for the top left corner
216:      * @param integer $y1  y for the top left corner
217:      * @param integer $x2  x for the bottom right corner of the cropped image.
218:      * @param integer $y2  y for the bottom right corner of the cropped image.
219:      */
220:     public function crop($x1, $y1, $x2, $y2)
221:     {
222:         try {
223:             $result = $this->_imagick->cropImage($x2 - $x1, $y2 - $y1, $x1, $y1);
224:             $this->_imagick->setImagePage(0, 0, 0, 0);
225:         } catch (ImagickException $e) {
226:             throw new Horde_Image_Exception($e);
227:         }
228:         $this->clearGeometry();
229:     }
230: 
231:     /**
232:      * Rotate the current image.
233:      *
234:      * @param integer $angle       The angle to rotate the image by,
235:      *                             in the clockwise direction.
236:      * @param integer $background  The background color to fill any triangles.
237:      */
238:     public function rotate($angle, $background = 'white')
239:     {
240:         try {
241:             $this->_imagick->rotateImage($background, $angle);
242:         } catch (ImagickException $e) {
243:             throw new Horde_Image_Exception($e);
244:         }
245:         $this->clearGeometry();
246:     }
247: 
248:     /**
249:      * Flip the current image.
250:      */
251:     public function flip()
252:     {
253:         try {
254:             $this->_imagick->flipImage();
255:         } catch (ImagickException $e) {
256:             throw new Horde_Image_Exception($e);
257:         }
258:     }
259: 
260:     /**
261:      * Mirror the current image.
262:      */
263:     public function mirror()
264:     {
265:         try {
266:             $this->_imagick->flopImage();
267:         } catch (ImagickException $e) {
268:             throw new Horde_Image_Exception($e);
269:         }
270:     }
271: 
272:     /**
273:      * Convert the current image to grayscale.
274:      */
275:     public function grayscale()
276:     {
277:         try {
278:             $this->_imagick->setImageColorSpace(Imagick::COLORSPACE_GRAY);
279:         } catch (ImageException $e) {
280:             throw new Horde_Image_Exception($e);
281:         }
282:     }
283: 
284:     /**
285:      * Sepia filter.
286:      *
287:      * @param integer $threshold  Extent of sepia effect.
288:      */
289:     public function sepia($threshold =  85)
290:     {
291:         try {
292:             $this->_imagick->sepiaToneImage($threshold);
293:         } catch (ImagickException $e) {
294:             throw new Horde_Image_Exception($e);
295:         }
296:     }
297: 
298:     /**
299:      * Draws a text string on the image in a specified location, with
300:      * the specified style information.
301:      *
302:      * @TODO: Need to differentiate between the stroke (border) and the fill color,
303:      *        but this is a BC break, since we were just not providing a border.
304:      *
305:      * @param string  $text       The text to draw.
306:      * @param integer $x          The left x coordinate of the start of the text string.
307:      * @param integer $y          The top y coordinate of the start of the text string.
308:      * @param string  $font       The font identifier you want to use for the text.
309:      * @param string  $color      The color that you want the text displayed in.
310:      * @param integer $direction  An integer that specifies the orientation of the text.
311:      * @param string  $fontsize   Size of the font (small, medium, large, giant)
312:      */
313:     public function text($string, $x, $y, $font = '', $color = 'black', $direction = 0, $fontsize = 'small')
314:     {
315:         $fontsize = Horde_Image::getFontSize($fontsize);
316:         $pixel = new ImagickPixel($color);
317:         $draw = new ImagickDraw();
318:         $draw->setFillColor($pixel);
319:         if (!empty($font)) {
320:             $draw->setFont($font);
321:         }
322:         $draw->setFontSize($fontsize);
323:         $draw->setGravity(Imagick::GRAVITY_NORTHWEST);
324:         try {
325:             $res = $this->_imagick->annotateImage($draw, $x, $y, $direction, $string);
326:         } catch (ImagickException $e) {
327:             throw new Horde_Image_Exception($e);
328:         }
329:         $draw->destroy();
330:     }
331: 
332:     /**
333:      * Draw a circle.
334:      *
335:      * @param integer $x     The x coordinate of the centre.
336:      * @param integer $y     The y coordinate of the centre.
337:      * @param integer $r     The radius of the circle.
338:      * @param string $color  The line color of the circle.
339:      * @param string $fill   The color to fill the circle.
340:      */
341:     public function circle($x, $y, $r, $color, $fill = 'none')
342:     {
343:         $draw = new ImagickDraw();
344:         $draw->setFillColor(new ImagickPixel($fill));
345:         $draw->setStrokeColor(new ImagickPixel($color));
346:         $draw->circle($x, $y, $r + $x, $y);
347:         try {
348:             $res = $this->_imagick->drawImage($draw);
349:         } catch (ImagickException $e) {
350:             throw new Horde_Image_Exception($e);
351:         }
352:         $draw->destroy();
353:     }
354: 
355:     /**
356:      * Draw a polygon based on a set of vertices.
357:      *
358:      * @param array $vertices  An array of x and y labeled arrays
359:      *                         (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
360:      * @param string $color    The color you want to draw the polygon with.
361:      * @param string $fill     The color to fill the polygon.
362:      */
363:     public function polygon($verts, $color, $fill = 'none')
364:     {
365:         $draw = new ImagickDraw();
366:         $draw->setFillColor(new ImagickPixel($fill));
367:         $draw->setStrokeColor(new ImagickPixel($color));
368:         $draw->polygon($verts);
369:         try {
370:             $res = $this->_imagick->drawImage($draw);
371:         } catch (ImagickException $e) {
372:             throw new Horde_Image_Exception($e);
373:         }
374:         $draw->destroy();
375:     }
376: 
377:     /**
378:      * Draw a rectangle.
379:      *
380:      * @param integer $x       The left x-coordinate of the rectangle.
381:      * @param integer $y       The top y-coordinate of the rectangle.
382:      * @param integer $width   The width of the rectangle.
383:      * @param integer $height  The height of the rectangle.
384:      * @param string $color    The line color of the rectangle.
385:      * @param string $fill     The color to fill the rectangle.
386:      */
387:     public function rectangle($x, $y, $width, $height, $color, $fill = 'none')
388:     {
389:         $draw = new ImagickDraw();
390:         $draw->setStrokeColor(new ImagickPixel($color));
391:         $draw->setFillColor(new ImagickPixel($fill));
392:         $draw->rectangle($x, $y, $x + $width, $y + $height);
393:         try {
394:             $res = $this->_imagick->drawImage($draw);
395:         } catch (ImagickException $e) {
396:             throw new Horde_Image_Exception($e);
397:         }
398:         $draw->destroy();
399:     }
400: 
401:     /**
402:      * Draw a rounded rectangle.
403:      *
404:      * @param integer $x       The left x-coordinate of the rectangle.
405:      * @param integer $y       The top y-coordinate of the rectangle.
406:      * @param integer $width   The width of the rectangle.
407:      * @param integer $height  The height of the rectangle.
408:      * @param integer $round   The width of the corner rounding.
409:      * @param string  $color   The line color of the rectangle.
410:      * @param string  $fill    The color to fill the rounded rectangle with.
411:      */
412:     public function roundedRectangle($x, $y, $width, $height, $round, $color, $fill)
413:     {
414:         $draw = new ImagickDraw();
415:         $draw->setStrokeColor(new ImagickPixel($color));
416:         $draw->setFillColor(new ImagickPixel($fill));
417:         $draw->roundRectangle($x, $y, $x + $width, $y + $height, $round, $round);
418:         try {
419:             $res = $this->_imagick->drawImage($draw);
420:         } catch (ImagickException $e) {
421:             throw new Horde_Image_Exception($e);
422:         }
423:         $draw->destroy();
424:     }
425: 
426:     /**
427:      * Draw a line.
428:      *
429:      * @param integer $x0     The x coordinate of the start.
430:      * @param integer $y0     The y coordinate of the start.
431:      * @param integer $x1     The x coordinate of the end.
432:      * @param integer $y1     The y coordinate of the end.
433:      * @param string $color   The line color.
434:      * @param string $width   The width of the line.
435:      */
436:     public function line($x0, $y0, $x1, $y1, $color = 'black', $width = 1)
437:     {
438:         $draw = new ImagickDraw();
439:         $draw->setStrokeColor(new ImagickPixel($color));
440:         $draw->setStrokeWidth($width);
441:         $draw->line($x0, $y0, $x1, $y1);
442:         try {
443:             $res = $this->_imagick->drawImage($draw);
444:         } catch (ImagickException $e) {
445:             throw Horde_Image_Exception($e);
446:         }
447:         $draw->destroy();
448:     }
449: 
450:     /**
451:      * Draw a dashed line.
452:      *
453:      * @param integer $x0           The x co-ordinate of the start.
454:      * @param integer $y0           The y co-ordinate of the start.
455:      * @param integer $x1           The x co-ordinate of the end.
456:      * @param integer $y1           The y co-ordinate of the end.
457:      * @param string $color         The line color.
458:      * @param string $width         The width of the line.
459:      * @param integer $dash_length  The length of a dash on the dashed line
460:      * @param integer $dash_space   The length of a space in the dashed line
461:      */
462:     public function dashedLine($x0, $y0, $x1, $y1, $color = 'black', $width = 1, $dash_length = 2, $dash_space = 2)
463:     {
464:         $draw = new ImagickDraw();
465:         $draw->setStrokeColor(new ImagickPixel($color));
466:         $draw->setStrokeWidth($width);
467:         $draw->setStrokeDashArray(array($dash_length, $dash_space));
468:         $draw->line($x0, $y0, $x1, $y1);
469:         try {
470:             $res = $this->_imagick->drawImage($draw);
471:         } catch (ImageException $e) {
472:             throw new Horde_Image_Exception($e);
473:         }
474:         $draw->destroy();
475:     }
476: 
477:     /**
478:      * Draw a polyline (a non-closed, non-filled polygon) based on a
479:      * set of vertices.
480:      *
481:      * @param array $vertices  An array of x and y labeled arrays
482:      *                         (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
483:      * @param string $color    The color you want to draw the line with.
484:      * @param string $width    The width of the line.
485:      */
486:     public function polyline($verts, $color, $width = 1)
487:     {
488:         $draw = new ImagickDraw();
489:         $draw->setStrokeColor(new ImagickPixel($color));
490:         $draw->setStrokeWidth($width);
491:         $draw->setFillColor(new ImagickPixel('none'));
492:         $draw->polyline($verts);
493:         try {
494:             $res = $this->_imagick->drawImage($draw);
495:         } catch (ImagickException $e) {
496:             throw new Horde_Image_Exception($e);
497:         }
498:         $draw->destroy();
499:     }
500: 
501:     /**
502:      * Draw an arc.
503:      *
504:      * @TODO
505:      *
506:      * @param integer $x      The x coordinate of the centre.
507:      * @param integer $y      The y coordinate of the centre.
508:      * @param integer $r      The radius of the arc.
509:      * @param integer $start  The start angle of the arc.
510:      * @param integer $end    The end angle of the arc.
511:      * @param string  $color  The line color of the arc.
512:      * @param string  $fill   The fill color of the arc (defaults to none).
513:      */
514:     public function arc($x, $y, $r, $start, $end, $color = 'black', $fill = 'none')
515:     {
516:         throw new Horde_Image_Exception('Not Yet Implemented.');
517:     }
518: 
519:     public function applyEffects()
520:     {
521:         // noop for this driver.
522:     }
523: 
524:     public function __get($property)
525:     {
526:         switch ($property) {
527:         case "imagick":
528:             return $this->_imagick;
529:         }
530:     }
531: 
532:     /**
533:      * Utility function to wrap Imagick::borderImage. Use when you don't want
534:      * to replace all pixels in the clipping area with the border color i.e.
535:      * you want to "frame" the existing image. Preserves transparency etc...
536:      *
537:      * @param Imagick &$image  The Imagick object to border.
538:      * @param integer $width
539:      * @param integer $height
540:      *
541:      * @return void
542:      */
543:     static public function frameImage(&$image, $color, $width, $height)
544:     {
545:         // Need to jump through these hoops in order to preserve any
546:         // transparency.
547:         try {
548:             $border = $image->clone();
549:             $border->borderImage(new ImagickPixel($color), $width, $height);
550:             $border->compositeImage($image, Imagick::COMPOSITE_COPY, $width, $height);
551:             $image->clear();
552:             $image->addImage($border);
553:         } catch (ImagickException $e) {
554:             throw new Horde_Image_Exception($e);
555:         }
556:         $border->destroy();
557:     }
558: 
559:     /**
560:      * Reset the imagick iterator to the first image in the set.
561:      *
562:      * @return void
563:      */
564:     public function rewind()
565:     {
566:         $this->_logDebug('Horde_Image_Imagick#rewind');
567:         $this->_imagick->setFirstIterator();
568:         $this->_noMoreImages = false;
569:     }
570: 
571:     /**
572:      * Return the current image from the internal iterator.
573:      *
574:      * @return Horde_Image_Imagick
575:      */
576:     public function current()
577:     {
578:         $this->_logDebug('Horde_Image_Imagick#current');
579:         $params = array('data' => $this->raw());
580:         $image = new Horde_Image_Imagick($params, $this->_context);
581: 
582:         return $image;
583:     }
584: 
585:     /**
586:      * Get the index of the internal iterator.
587:      *
588:      * @return integer
589:      */
590:     public function key()
591:     {
592:         $this->_logDebug('Horde_Image_Imagick#key: ' . $this->_imagick->getIteratorIndex());
593:         return $this->_imagick->getIteratorIndex();
594:     }
595: 
596:     /**
597:      * Advance the iterator
598:      *
599:      * @return Horde_Image_Imagick
600:      */
601:     public function next()
602:     {
603:         if ($this->_imagick->hasNextImage()) {
604:             $this->_imagick->nextImage();
605:             return $this->current();
606:         } else {
607:             $this->_noMoreImages = true;
608:             return false;
609:         }
610:     }
611: 
612:     /**
613:      * Deterimines if the current iterator item is valid.
614:      *
615:      * @return boolean
616:      */
617:     public function valid()
618:     {
619:         $this->_logDebug('Horde_Image_Imagick#valid:' . print_r(!$this->_noMoreImages, true));
620:         return !$this->_noMoreImages;
621:     }
622: 
623:     /**
624:      * Request a specific image from the collection of images.
625:      *
626:      * @param integer $index  The index to return
627:      *
628:      * @return Horde_Image_Base
629:      */
630:     public function getImageAtIndex($index)
631:     {
632:         if ($index >= $this->_imagick->getNumberImages()) {
633:             throw Horde_Image_Exception('Image index out of bounds.');
634:         }
635: 
636:         $currentIndex = $this->_imagick->getIteratorIndex();
637:         $this->_imagick->setIteratorIndex($index);
638:         $image = $this->current();
639:         $this->_imagick->setIteratorIndex($currentIndex);
640: 
641:         return $image;
642:     }
643: 
644:     /**
645:      * Return the number of image pages available in the image object.
646:      *
647:      * @return integer
648:      */
649:     public function getImagePageCount()
650:     {
651:         return $this->_imagick->getNumberImages();
652:     }
653:  }
654: 
API documentation generated by ApiGen