Initial Kohana install
[speedfreak] / Server / system / libraries / drivers / Image / ImageMagick.php
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
2 /**
3  * ImageMagick Image Driver.
4  *
5  * $Id: ImageMagick.php 3769 2008-12-15 00:48:56Z zombor $
6  *
7  * @package    Image
8  * @author     Kohana Team
9  * @copyright  (c) 2007-2008 Kohana Team
10  * @license    http://kohanaphp.com/license.html
11  */
12 class Image_ImageMagick_Driver extends Image_Driver {
13
14         // Directory that IM is installed in
15         protected $dir = '';
16
17         // Command extension (exe for windows)
18         protected $ext = '';
19
20         // Temporary image filename
21         protected $tmp_image;
22
23         /**
24          * Attempts to detect the ImageMagick installation directory.
25          *
26          * @throws  Kohana_Exception
27          * @param   array   configuration
28          * @return  void
29          */
30         public function __construct($config)
31         {
32                 if (empty($config['directory']))
33                 {
34                         // Attempt to locate IM by using "which" (only works for *nix!)
35                         if ( ! is_file($path = exec('which convert')))
36                                 throw new Kohana_Exception('image.imagemagick.not_found');
37
38                         $config['directory'] = dirname($path);
39                 }
40
41                 // Set the command extension
42                 $this->ext = (PHP_SHLIB_SUFFIX === 'dll') ? '.exe' : '';
43
44                 // Check to make sure the provided path is correct
45                 if ( ! is_file(realpath($config['directory']).'/convert'.$this->ext))
46                         throw new Kohana_Exception('image.imagemagick.not_found', 'convert'.$this->ext);
47
48                 // Set the installation directory
49                 $this->dir = str_replace('\\', '/', realpath($config['directory'])).'/';
50         }
51
52         /**
53          * Creates a temporary image and executes the given actions. By creating a
54          * temporary copy of the image before manipulating it, this process is atomic.
55          */
56         public function process($image, $actions, $dir, $file, $render = FALSE)
57         {
58                 // We only need the filename
59                 $image = $image['file'];
60
61                 // Unique temporary filename
62                 $this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.'));
63
64                 // Copy the image to the temporary file
65                 copy($image, $this->tmp_image);
66
67                 // Quality change is done last
68                 $quality = (int) arr::remove('quality', $actions);
69
70                 // Use 95 for the default quality
71                 empty($quality) and $quality = 95;
72
73                 // All calls to these will need to be escaped, so do it now
74                 $this->cmd_image = escapeshellarg($this->tmp_image);
75                 $this->new_image = ($render)? $this->cmd_image : escapeshellarg($dir.$file);
76
77                 if ($status = $this->execute($actions))
78                 {
79                         // Use convert to change the image into its final version. This is
80                         // done to allow the file type to change correctly, and to handle
81                         // the quality conversion in the most effective way possible.
82                         if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image))
83                         {
84                                 $this->errors[] = $error;
85                         }
86                         else
87                         {
88                                 // Output the image directly to the browser
89                                 if ($render !== FALSE)
90                                 {
91                                         $contents = file_get_contents($this->tmp_image);
92                                         switch (substr($file, strrpos($file, '.') + 1))
93                                         {
94                                                 case 'jpg':
95                                                 case 'jpeg':
96                                                         header('Content-Type: image/jpeg');
97                                                 break;
98                                                 case 'gif':
99                                                         header('Content-Type: image/gif');
100                                                 break;
101                                                 case 'png':
102                                                         header('Content-Type: image/png');
103                                                 break;
104                                         }
105                                         echo $contents;
106                                 }
107                         }
108                 }
109
110                 // Remove the temporary image
111                 unlink($this->tmp_image);
112                 $this->tmp_image = '';
113
114                 return $status;
115         }
116
117         public function crop($prop)
118         {
119                 // Sanitize and normalize the properties into geometry
120                 $this->sanitize_geometry($prop);
121
122                 // Set the IM geometry based on the properties
123                 $geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']);
124
125                 if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -crop '.$geometry.' '.$this->cmd_image.' '.$this->cmd_image))
126                 {
127                         $this->errors[] = $error;
128                         return FALSE;
129                 }
130
131                 return TRUE;
132         }
133
134         public function flip($dir)
135         {
136                 // Convert the direction into a IM command
137                 $dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip';
138
139                 if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' '.$dir.' '.$this->cmd_image.' '.$this->cmd_image))
140                 {
141                         $this->errors[] = $error;
142                         return FALSE;
143                 }
144
145                 return TRUE;
146         }
147
148         public function resize($prop)
149         {
150                 switch ($prop['master'])
151                 {
152                         case Image::WIDTH:  // Wx
153                                 $dim = escapeshellarg($prop['width'].'x');
154                         break;
155                         case Image::HEIGHT: // xH
156                                 $dim = escapeshellarg('x'.$prop['height']);
157                         break;
158                         case Image::AUTO:   // WxH
159                                 $dim = escapeshellarg($prop['width'].'x'.$prop['height']);
160                         break;
161                         case Image::NONE:   // WxH!
162                                 $dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!');
163                         break;
164                 }
165
166                 // Use "convert" to change the width and height
167                 if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image))
168                 {
169                         $this->errors[] = $error;
170                         return FALSE;
171                 }
172
173                 return TRUE;
174         }
175
176         public function rotate($amt)
177         {
178                 if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image))
179                 {
180                         $this->errors[] = $error;
181                         return FALSE;
182                 }
183
184                 return TRUE;
185         }
186
187         public function sharpen($amount)
188         {
189                 // Set the sigma, radius, and amount. The amount formula allows a nice
190                 // spread between 1 and 100 without pixelizing the image badly.
191                 $sigma  = 0.5;
192                 $radius = $sigma * 2;
193                 $amount = round(($amount / 80) * 3.14, 2);
194
195                 // Convert the amount to an IM command
196                 $sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0');
197
198                 if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image))
199                 {
200                         $this->errors[] = $error;
201                         return FALSE;
202                 }
203
204                 return TRUE;
205         }
206
207         protected function properties()
208         {
209                 return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE);
210         }
211
212 } // End Image ImageMagick Driver