SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/Drivers/Image/GraphicsMagick.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.  * GraphicsMagick Image Driver.
  26.  */
  27. class GraphicsMagick extends ImageDriver
  28. {
  29.  
  30. // Directory that GM 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 GraphicsMagick 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 GM by using "which" (only works for *nix!)
  51. if ( ! is_file($path = exec('which gm')))
  52. throw new Kohana_Exception('image.graphicsmagick.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']).'/gm'.$this->ext))
  62. throw new Kohana_Exception('image.graphicsmagick.not_found', 'gm'.$this->ext);
  63.  
  64.  
  65. // Set the installation directory
  66. $this->dir = str_replace('\\', '/', realpath($config['directory'])).'/';
  67. }
  68.  
  69. /**
  70.   * Creates a temporary image and executes the given actions. By creating a
  71.   * temporary copy of the image before manipulating it, this process is atomic.
  72.   */
  73. public function process($image, $actions, $dir, $file, $render = FALSE)
  74. {
  75. // We only need the filename
  76. $image = $image['file'];
  77.  
  78. // Unique temporary filename
  79. $this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.'));
  80.  
  81. // Copy the image to the temporary file
  82. copy($image, $this->tmp_image);
  83.  
  84. // Quality change is done last
  85. $quality = (int) $actions['quality'];
  86. unset($actions['quality']);
  87.  
  88. // Use 95 for the default quality
  89. empty($quality) and $quality = 95;
  90.  
  91. // All calls to these will need to be escaped, so do it now
  92. $this->cmd_image = escapeshellarg($this->tmp_image);
  93. $this->new_image = ($render)? $this->cmd_image : escapeshellarg($dir.$file);
  94.  
  95. if ($status = $this->execute($actions))
  96. {
  97. // Use convert to change the image into its final version. This is
  98. // done to allow the file type to change correctly, and to handle
  99. // the quality conversion in the most effective way possible.
  100. if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image))
  101. {
  102. $this->errors[] = $error;
  103. }
  104. else
  105. {
  106. // Output the image directly to the browser
  107. if ($render !== FALSE)
  108. {
  109. $contents = file_get_contents($this->tmp_image);
  110. switch (substr($file, strrpos($file, '.') + 1))
  111. {
  112. case 'jpg':
  113. case 'jpeg':
  114. header('Content-Type: image/jpeg');
  115. break;
  116. case 'gif':
  117. header('Content-Type: image/gif');
  118. break;
  119. case 'png':
  120. header('Content-Type: image/png');
  121. break;
  122. }
  123. echo $contents;
  124. }
  125. }
  126. }
  127.  
  128. // Remove the temporary image
  129. unlink($this->tmp_image);
  130. $this->tmp_image = '';
  131.  
  132. return $status;
  133. }
  134.  
  135. public function crop($prop)
  136. {
  137. // Sanitize and normalize the properties into geometry
  138. $this->sanitizeGeometry($prop);
  139.  
  140. // Set the IM geometry based on the properties
  141. $geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']);
  142.  
  143. if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -crop '.$geometry.' '.$this->cmd_image.' '.$this->cmd_image))
  144. {
  145. $this->errors[] = $error;
  146. return FALSE;
  147. }
  148.  
  149. return TRUE;
  150. }
  151.  
  152. public function flip($dir)
  153. {
  154. // Convert the direction into a GM command
  155. $dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip';
  156.  
  157. if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' '.$dir.' '.$this->cmd_image.' '.$this->cmd_image))
  158. {
  159. $this->errors[] = $error;
  160. return FALSE;
  161. }
  162.  
  163. return TRUE;
  164. }
  165.  
  166. public function resize($prop)
  167. {
  168. switch ($prop['master'])
  169. {
  170. case Image::WIDTH: // Wx
  171. $dim = escapeshellarg($prop['width'].'x');
  172. break;
  173. case Image::HEIGHT: // xH
  174. $dim = escapeshellarg('x'.$prop['height']);
  175. break;
  176. case Image::AUTO: // WxH
  177. $dim = escapeshellarg($prop['width'].'x'.$prop['height']);
  178. break;
  179. case Image::NONE: // WxH!
  180. $dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!');
  181. break;
  182. }
  183.  
  184. // Use "convert" to change the width and height
  185. if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image))
  186. {
  187. $this->errors[] = $error;
  188. return FALSE;
  189. }
  190.  
  191. return TRUE;
  192. }
  193.  
  194. public function rotate($amt)
  195. {
  196. if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image))
  197. {
  198. $this->errors[] = $error;
  199. return FALSE;
  200. }
  201.  
  202. return TRUE;
  203. }
  204.  
  205. public function sharpen($amount)
  206. {
  207. // Set the sigma, radius, and amount. The amount formula allows a nice
  208. // spread between 1 and 100 without pixelizing the image badly.
  209. $sigma = 0.5;
  210. $radius = $sigma * 2;
  211. $amount = round(($amount / 80) * 3.14, 2);
  212.  
  213. // Convert the amount to an GM command
  214. $sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0');
  215.  
  216. if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image))
  217. {
  218. $this->errors[] = $error;
  219. return FALSE;
  220. }
  221.  
  222. return TRUE;
  223. }
  224.  
  225. protected function properties()
  226. {
  227. return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE);
  228. }
  229.  
  230. } // End Image GraphicsMagick Driver
  231.