1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13: class Horde_Image_Imagick extends Horde_Image_Base
14: {
15: 16: 17: 18: 19:
20: protected $_imagick;
21:
22: 23: 24: 25: 26: 27:
28: private $_noMoreImages = false;
29:
30: 31: 32: 33: 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: 47: 48: 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: 81: 82: 83: 84: 85: 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: 103: 104: 105: 106: 107: 108: 109:
110: public function loadFile($filename)
111: {
112:
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: 127: 128: 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:
137: }
138:
139: return $old;
140: }
141:
142: 143: 144: 145: 146: 147: 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: 167: 168: 169: 170: 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: 188: 189: 190: 191: 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:
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: 214: 215: 216: 217: 218: 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: 233: 234: 235: 236: 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: 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: 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: 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: 286: 287: 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: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 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: 334: 335: 336: 337: 338: 339: 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: 357: 358: 359: 360: 361: 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: 379: 380: 381: 382: 383: 384: 385: 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: 403: 404: 405: 406: 407: 408: 409: 410: 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: 428: 429: 430: 431: 432: 433: 434: 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: 452: 453: 454: 455: 456: 457: 458: 459: 460: 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: 479: 480: 481: 482: 483: 484: 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: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 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:
522: }
523:
524: public function __get($property)
525: {
526: switch ($property) {
527: case "imagick":
528: return $this->_imagick;
529: }
530: }
531:
532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542:
543: static public function frameImage(&$image, $color, $width, $height)
544: {
545:
546:
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: 561: 562: 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: 573: 574: 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: 587: 588: 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: 598: 599: 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: 614: 615: 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: 625: 626: 627: 628: 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: 646: 647: 648:
649: public function getImagePageCount()
650: {
651: return $this->_imagick->getNumberImages();
652: }
653: }
654: