sfImageMagickAdapter.class.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) 2004-2007 Fabien Potencier <fabien.potencier@symfony-project.com>
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * sfImageMagickAdapter provides a mechanism for creating thumbnail images.
  11. * @see http://www.imagemagick.org
  12. *
  13. * @package sfThumbnailPlugin
  14. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  15. * @author Benjamin Meynell <bmeynell@colorado.edu>
  16. */
  17. class sfImageMagickAdapter
  18. {
  19. protected
  20. $sourceWidth,
  21. $sourceHeight,
  22. $sourceMime,
  23. $maxWidth,
  24. $maxHeight,
  25. $scale,
  26. $inflate,
  27. $quality,
  28. $source,
  29. $magickCommands;
  30. /**
  31. * Mime types this adapter supports
  32. */
  33. protected $imgTypes = array(
  34. 'application/pdf',
  35. 'application/postscript',
  36. 'application/vnd.palm',
  37. 'application/x-icb',
  38. 'application/x-mif',
  39. 'image/dcx',
  40. 'image/g3fax',
  41. 'image/gif',
  42. 'image/jng',
  43. 'image/jpeg',
  44. 'image/pbm',
  45. 'image/pcd',
  46. 'image/pict',
  47. 'image/pjpeg',
  48. 'image/png',
  49. 'image/ras',
  50. 'image/sgi',
  51. 'image/svg',
  52. 'image/tga',
  53. 'image/tiff',
  54. 'image/vda',
  55. 'image/vnd.wap.wbmp',
  56. 'image/vst',
  57. 'image/x-fits',
  58. 'image/x-ms-bmp',
  59. 'image/x-otb',
  60. 'image/x-palm',
  61. 'image/x-pcx',
  62. 'image/x-pgm',
  63. 'image/x-photoshop',
  64. 'image/x-ppm',
  65. 'image/x-ptiff',
  66. 'image/x-viff',
  67. 'image/x-win-bitmap',
  68. 'image/x-xbitmap',
  69. 'image/x-xv',
  70. 'image/xpm',
  71. 'image/xwd',
  72. 'text/plain',
  73. 'video/mng',
  74. 'video/mpeg',
  75. 'video/mpeg2',
  76. );
  77. /**
  78. * Imagemagick-specific Type to Mime type map
  79. */
  80. protected $mimeMap = array(
  81. 'bmp' => 'image/bmp',
  82. 'bmp2' => 'image/bmp',
  83. 'bmp3' => 'image/bmp',
  84. 'cur' => 'image/x-win-bitmap',
  85. 'dcx' => 'image/dcx',
  86. 'epdf' => 'application/pdf',
  87. 'epi' => 'application/postscript',
  88. 'eps' => 'application/postscript',
  89. 'eps2' => 'application/postscript',
  90. 'eps3' => 'application/postscript',
  91. 'epsf' => 'application/postscript',
  92. 'epsi' => 'application/postscript',
  93. 'ept' => 'application/postscript',
  94. 'ept2' => 'application/postscript',
  95. 'ept3' => 'application/postscript',
  96. 'fax' => 'image/g3fax',
  97. 'fits' => 'image/x-fits',
  98. 'g3' => 'image/g3fax',
  99. 'gif' => 'image/gif',
  100. 'gif87' => 'image/gif',
  101. 'icb' => 'application/x-icb',
  102. 'ico' => 'image/x-win-bitmap',
  103. 'icon' => 'image/x-win-bitmap',
  104. 'jng' => 'image/jng',
  105. 'jpeg' => 'image/jpeg',
  106. 'jpg' => 'image/jpeg',
  107. 'm2v' => 'video/mpeg2',
  108. 'miff' => 'application/x-mif',
  109. 'mng' => 'video/mng',
  110. 'mpeg' => 'video/mpeg',
  111. 'mpg' => 'video/mpeg',
  112. 'otb' => 'image/x-otb',
  113. 'p7' => 'image/x-xv',
  114. 'palm' => 'image/x-palm',
  115. 'pbm' => 'image/pbm',
  116. 'pcd' => 'image/pcd',
  117. 'pcds' => 'image/pcd',
  118. 'pcl' => 'application/pcl',
  119. 'pct' => 'image/pict',
  120. 'pcx' => 'image/x-pcx',
  121. 'pdb' => 'application/vnd.palm',
  122. 'pdf' => 'application/pdf',
  123. 'pgm' => 'image/x-pgm',
  124. 'picon' => 'image/xpm',
  125. 'pict' => 'image/pict',
  126. 'pjpeg' => 'image/pjpeg',
  127. 'png' => 'image/png',
  128. 'png24' => 'image/png',
  129. 'png32' => 'image/png',
  130. );
  131. public function __construct($maxWidth, $maxHeight, $scale, $inflate, $quality, $options)
  132. {
  133. $this->magickCommands = array();
  134. $this->magickCommands['convert'] = isset($options['convert']) ? escapeshellcmd($options['convert']) : 'convert';
  135. $this->magickCommands['identify'] = isset($options['identify']) ? escapeshellcmd($options['identify']) : 'identify';
  136. exec($this->magickCommands['convert'], $stdout);
  137. if (strpos($stdout[0], 'ImageMagick') === false)
  138. {
  139. throw new Exception(sprintf("ImageMagick convert command not found"));
  140. }
  141. exec($this->magickCommands['identify'], $stdout);
  142. if (strpos($stdout[0], 'ImageMagick') === false)
  143. {
  144. throw new Exception(sprintf("ImageMagick identify command not found"));
  145. }
  146. $this->maxWidth = $maxWidth;
  147. $this->maxHeight = $maxHeight;
  148. $this->scale = $scale;
  149. $this->inflate = $inflate;
  150. $this->quality = $quality;
  151. $this->options = $options;
  152. }
  153. public function toString($thumbnail, $targetMime = null)
  154. {
  155. ob_start();
  156. $this->save($thumbnail, null, $targetMime);
  157. return ob_get_clean();
  158. }
  159. public function toResource()
  160. {
  161. throw new Exception('The ImageMagick adapter does not support the toResource method.');
  162. }
  163. public function loadFile($thumbnail, $image)
  164. {
  165. // try and use getimagesize()
  166. // on failure, use identify instead
  167. $imgData = @getimagesize($image);
  168. if (!$imgData)
  169. {
  170. exec($this->magickCommands['identify'].' '.escapeshellarg($image), $stdout, $retval);
  171. if ($retval === 1)
  172. {
  173. throw new Exception('Image could not be identified.');
  174. }
  175. else
  176. {
  177. // get image data via identify
  178. list($img, $type, $dimen) = explode(' ', $stdout[0]);
  179. list($width, $height) = explode('x', $dimen);
  180. $this->sourceWidth = $width;
  181. $this->sourceHeight = $height;
  182. $this->sourceMime = $this->mimeMap[strtolower($type)];
  183. }
  184. }
  185. else
  186. {
  187. // use image data from getimagesize()
  188. $this->sourceWidth = $imgData[0];
  189. $this->sourceHeight = $imgData[1];
  190. $this->sourceMime = $imgData['mime'];
  191. }
  192. $this->image = $image;
  193. // open file resource
  194. $source = fopen($image, 'r');
  195. $this->source = $source;
  196. $thumbnail->initThumb($this->sourceWidth, $this->sourceHeight, $this->maxWidth, $this->maxHeight, $this->scale, $this->inflate);
  197. return true;
  198. }
  199. public function loadData($thumbnail, $image, $mime)
  200. {
  201. throw new Exception('This function is not yet implemented. Try a different adapter.');
  202. }
  203. public function save($thumbnail, $thumbDest, $targetMime = null)
  204. {
  205. $command = '';
  206. $width = $this->sourceWidth;
  207. $height = $this->sourceHeight;
  208. $x = $y = 0;
  209. switch (@$this->options['method'])
  210. {
  211. case "shave_all":
  212. $proportion['source'] = $width / $height;
  213. $proportion['thumb'] = $thumbnail->getThumbWidth() / $thumbnail->getThumbHeight();
  214. if ($proportion['source'] > 1 && $proportion['thumb'] < 1)
  215. {
  216. $x = ($width - $height * $proportion['thumb']) / 2;
  217. }
  218. else
  219. {
  220. if ($proportion['source'] > $proportion['thumb'])
  221. {
  222. $x = ($width - $height * $proportion['thumb']) / 2;
  223. }
  224. else
  225. {
  226. $y = ($height - $width / $proportion['thumb']) / 2;
  227. }
  228. }
  229. $command = sprintf(" -shave %dx%d", $x, $y);
  230. break;
  231. case "shave_bottom":
  232. if ($width > $height)
  233. {
  234. $x = ceil(($width - $height) / 2 );
  235. $width = $height;
  236. }
  237. elseif ($height > $width)
  238. {
  239. $y = 0;
  240. $height = $width;
  241. }
  242. if (is_null($thumbDest))
  243. {
  244. $command = sprintf(
  245. " -crop %dx%d+%d+%d %s '-' | %s",
  246. $width, $height,
  247. $x, $y,
  248. escapeshellarg($this->image),
  249. $this->magickCommands['convert']
  250. );
  251. $this->image = '-';
  252. }
  253. else
  254. {
  255. $command = sprintf(
  256. " -crop %dx%d+%d+%d %s %s && %s",
  257. $width, $height,
  258. $x, $y,
  259. escapeshellarg($this->image), escapeshellarg($thumbDest),
  260. $this->magickCommands['convert']
  261. );
  262. $this->image = $thumbDest;
  263. }
  264. break;
  265. case 'custom':
  266. $coords = $this->options['coords'];
  267. if (empty($coords)) break;
  268. $x = $coords['x1'];
  269. $y = $coords['y1'];
  270. $width = $coords['x2'] - $coords['x1'];
  271. $height = $coords['y2'] - $coords['y1'];
  272. if (is_null($thumbDest))
  273. {
  274. $command = sprintf(
  275. " -crop %dx%d+%d+%d %s '-' | %s",
  276. $width, $height,
  277. $x, $y,
  278. escapeshellarg($this->image),
  279. $this->magickCommands['convert']
  280. );
  281. $this->image = '-';
  282. }
  283. else
  284. {
  285. $command = sprintf(
  286. " -crop %dx%d+%d+%d %s %s && %s",
  287. $width, $height,
  288. $x, $y,
  289. escapeshellarg($this->image), escapeshellarg($thumbDest),
  290. $this->magickCommands['convert']
  291. );
  292. $this->image = $thumbDest;
  293. }
  294. break;
  295. } // end switch
  296. $command .= ' -thumbnail ';
  297. $command .= $thumbnail->getThumbWidth().'x'.$thumbnail->getThumbHeight();
  298. // absolute sizing
  299. if (!$this->scale)
  300. {
  301. $command .= '!';
  302. }
  303. if ($this->quality && $targetMime == 'image/jpeg')
  304. {
  305. $command .= ' -quality '.$this->quality.'% ';
  306. }
  307. // extract images such as pages from a pdf doc
  308. $extract = '';
  309. if (isset($this->options['extract']) && is_int($this->options['extract']))
  310. {
  311. if ($this->options['extract'] > 0)
  312. {
  313. $this->options['extract']--;
  314. }
  315. $extract = '['.escapeshellarg($this->options['extract']).'] ';
  316. }
  317. $output = (is_null($thumbDest))?'-':$thumbDest;
  318. $output = (($mime = array_search($targetMime, $this->mimeMap))?$mime.':':'').$output;
  319. $cmd = $this->magickCommands['convert'].' '.$command.' '.escapeshellarg($this->image).$extract.' '.escapeshellarg($output);
  320. (is_null($thumbDest))?passthru($cmd):exec($cmd);
  321. }
  322. public function freeSource()
  323. {
  324. if (is_resource($this->source))
  325. {
  326. fclose($this->source);
  327. }
  328. }
  329. public function freeThumb()
  330. {
  331. return true;
  332. }
  333. public function getSourceMime()
  334. {
  335. return $this->sourceMime;
  336. }
  337. }