SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/Drivers/Image/ImageMagick.php

Copyright (C) 2017 Karmabunny Pty Ltd.

This file is a part of SproutCMS.

SproutCMS is free software: you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation, either
version 2 of the License, or (at your option) any later version.

For more information, visit <http://getsproutcms.com>.

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