PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/program/lib/Roundcube/rcube_image.php

https://github.com/fretelweb/roundcubemail
PHP | 296 lines | 185 code | 45 blank | 66 comment | 66 complexity | 94c8e456be9526da4052ad1648d3b538 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1
  1. <?php
  2. /*
  3. +-----------------------------------------------------------------------+
  4. | This file is part of the Roundcube Webmail client |
  5. | Copyright (C) 2005-2012, The Roundcube Dev Team |
  6. | Copyright (C) 2011-2012, Kolab Systems AG |
  7. | |
  8. | Licensed under the GNU General Public License version 3 or |
  9. | any later version with exceptions for skins & plugins. |
  10. | See the README file for a full license statement. |
  11. | |
  12. | PURPOSE: |
  13. | Image resizer and converter |
  14. +-----------------------------------------------------------------------+
  15. | Author: Thomas Bruederli <roundcube@gmail.com> |
  16. | Author: Aleksander Machniak <alec@alec.pl> |
  17. +-----------------------------------------------------------------------+
  18. */
  19. /**
  20. * Image resizer and converter
  21. *
  22. * @package Framework
  23. * @subpackage Utils
  24. */
  25. class rcube_image
  26. {
  27. private $image_file;
  28. const TYPE_GIF = 1;
  29. const TYPE_JPG = 2;
  30. const TYPE_PNG = 3;
  31. const TYPE_TIF = 4;
  32. public static $extensions = array(
  33. self::TYPE_GIF => 'gif',
  34. self::TYPE_JPG => 'jpg',
  35. self::TYPE_PNG => 'png',
  36. self::TYPE_TIF => 'tif',
  37. );
  38. function __construct($filename)
  39. {
  40. $this->image_file = $filename;
  41. }
  42. /**
  43. * Get image properties.
  44. *
  45. * @return mixed Hash array with image props like type, width, height
  46. */
  47. public function props()
  48. {
  49. // use GD extension
  50. if (function_exists('getimagesize') && ($imsize = @getimagesize($this->image_file))) {
  51. $width = $imsize[0];
  52. $height = $imsize[1];
  53. $gd_type = $imsize['2'];
  54. $type = image_type_to_extension($imsize['2'], false);
  55. }
  56. // use ImageMagick
  57. if (!$type && ($data = $this->identify())) {
  58. list($type, $width, $height) = $data;
  59. }
  60. if ($type) {
  61. return array(
  62. 'type' => $type,
  63. 'gd_type' => $gd_type,
  64. 'width' => $width,
  65. 'height' => $height,
  66. );
  67. }
  68. }
  69. /**
  70. * Resize image to a given size. Use only to shrink an image.
  71. * If an image is smaller than specified size it will be not resized.
  72. *
  73. * @param int $size Max width/height size
  74. * @param string $filename Output filename
  75. * @param boolean $browser_compat Convert to image type displayable by any browser
  76. *
  77. * @return mixed Output type on success, False on failure
  78. */
  79. public function resize($size, $filename = null, $browser_compat = false)
  80. {
  81. $result = false;
  82. $rcube = rcube::get_instance();
  83. $convert = $rcube->config->get('im_convert_path', false);
  84. $props = $this->props();
  85. if (empty($props)) {
  86. return false;
  87. }
  88. if (!$filename) {
  89. $filename = $this->image_file;
  90. }
  91. // use Imagemagick
  92. if ($convert) {
  93. $p['out'] = $filename;
  94. $p['in'] = $this->image_file;
  95. $p['size'] = $size.'x'.$size;
  96. $type = $props['type'];
  97. if (!$type && ($data = $this->identify())) {
  98. $type = $data[0];
  99. }
  100. $type = strtr($type, array("jpeg" => "jpg", "tiff" => "tif", "ps" => "eps", "ept" => "eps"));
  101. $p['intype'] = $type;
  102. // convert to an image format every browser can display
  103. if ($browser_compat && !in_array($type, array('jpg','gif','png'))) {
  104. $type = 'jpg';
  105. }
  106. $p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif", 'quality' => 75);
  107. $p['-opts'] = array('-resize' => $p['size'].'>');
  108. if (in_array($type, explode(',', $p['types']))) { // Valid type?
  109. $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {intype}:{in} {type}:{out}', $p);
  110. }
  111. if ($result === '') {
  112. @chmod($filename, 0600);
  113. return $type;
  114. }
  115. }
  116. // use GD extension
  117. if ($props['gd_type']) {
  118. if ($props['gd_type'] == IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) {
  119. $image = imagecreatefromjpeg($this->image_file);
  120. $type = 'jpg';
  121. }
  122. else if($props['gd_type'] == IMAGETYPE_GIF && function_exists('imagecreatefromgif')) {
  123. $image = imagecreatefromgif($this->image_file);
  124. $type = 'gid';
  125. }
  126. else if($props['gd_type'] == IMAGETYPE_PNG && function_exists('imagecreatefrompng')) {
  127. $image = imagecreatefrompng($this->image_file);
  128. $type = 'png';
  129. }
  130. else {
  131. // @TODO: print error to the log?
  132. return false;
  133. }
  134. if ($image === false) {
  135. return false;
  136. }
  137. $scale = $size / max($props['width'], $props['height']);
  138. // Imagemagick resize is implemented in shrinking mode (see -resize argument above)
  139. // we do the same here, if an image is smaller than specified size
  140. // we do nothing but copy original file to destination file
  141. if ($scale > 1) {
  142. return $this->image_file == $filename || copy($this->image_file, $filename) ? $type : false;
  143. }
  144. $width = $props['width'] * $scale;
  145. $height = $props['height'] * $scale;
  146. $new_image = imagecreatetruecolor($width, $height);
  147. // Fix transparency of gif/png image
  148. if ($props['gd_type'] != IMAGETYPE_JPEG) {
  149. imagealphablending($new_image, false);
  150. imagesavealpha($new_image, true);
  151. $transparent = imagecolorallocatealpha($new_image, 255, 255, 255, 127);
  152. imagefilledrectangle($new_image, 0, 0, $width, $height, $transparent);
  153. }
  154. imagecopyresampled($new_image, $image, 0, 0, 0, 0, $width, $height, $props['width'], $props['height']);
  155. $image = $new_image;
  156. if ($props['gd_type'] == IMAGETYPE_JPEG) {
  157. $result = imagejpeg($image, $filename, 75);
  158. }
  159. elseif($props['gd_type'] == IMAGETYPE_GIF) {
  160. $result = imagegif($image, $filename);
  161. }
  162. elseif($props['gd_type'] == IMAGETYPE_PNG) {
  163. $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS);
  164. }
  165. if ($result) {
  166. @chmod($filename, 0600);
  167. return $type;
  168. }
  169. }
  170. // @TODO: print error to the log?
  171. return false;
  172. }
  173. /**
  174. * Convert image to a given type
  175. *
  176. * @param int $type Destination file type (see class constants)
  177. * @param string $filename Output filename (if empty, original file will be used
  178. * and filename extension will be modified)
  179. *
  180. * @return bool True on success, False on failure
  181. */
  182. public function convert($type, $filename = null)
  183. {
  184. $rcube = rcube::get_instance();
  185. $convert = $rcube->config->get('im_convert_path', false);
  186. if (!$filename) {
  187. $filename = $this->image_file;
  188. // modify extension
  189. if ($extension = self::$extensions[$type]) {
  190. $filename = preg_replace('/\.[^.]+$/', '', $filename) . '.' . $extension;
  191. }
  192. }
  193. // use ImageMagick
  194. if ($convert) {
  195. $p['in'] = $this->image_file;
  196. $p['out'] = $filename;
  197. $p['type'] = self::$extensions[$type];
  198. $result = rcube::exec($convert . ' 2>&1 -colorspace RGB -quality 75 {in} {type}:{out}', $p);
  199. if ($result === '') {
  200. @chmod($filename, 0600);
  201. return true;
  202. }
  203. }
  204. // use GD extension (TIFF isn't supported)
  205. $props = $this->props();
  206. if ($props['gd_type']) {
  207. if ($props['gd_type'] == IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) {
  208. $image = imagecreatefromjpeg($this->image_file);
  209. }
  210. else if ($props['gd_type'] == IMAGETYPE_GIF && function_exists('imagecreatefromgif')) {
  211. $image = imagecreatefromgif($this->image_file);
  212. }
  213. else if ($props['gd_type'] == IMAGETYPE_PNG && function_exists('imagecreatefrompng')) {
  214. $image = imagecreatefrompng($this->image_file);
  215. }
  216. else {
  217. // @TODO: print error to the log?
  218. return false;
  219. }
  220. if ($type == self::TYPE_JPG) {
  221. $result = imagejpeg($image, $filename, 75);
  222. }
  223. else if ($type == self::TYPE_GIF) {
  224. $result = imagegif($image, $filename);
  225. }
  226. else if ($type == self::TYPE_PNG) {
  227. $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS);
  228. }
  229. if ($result) {
  230. @chmod($filename, 0600);
  231. return true;
  232. }
  233. }
  234. // @TODO: print error to the log?
  235. return false;
  236. }
  237. /**
  238. * Identify command handler.
  239. */
  240. private function identify()
  241. {
  242. $rcube = rcube::get_instance();
  243. if ($cmd = $rcube->config->get('im_identify_path')) {
  244. $args = array('in' => $this->image_file, 'format' => "%m %[fx:w] %[fx:h]");
  245. $id = rcube::exec($cmd. ' 2>/dev/null -format {format} {in}', $args);
  246. if ($id) {
  247. return explode(' ', strtolower($id));
  248. }
  249. }
  250. }
  251. }