PageRenderTime 57ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/ImageManager.php

https://github.com/netplayer/PrestaShop
PHP | 466 lines | 274 code | 49 blank | 143 comment | 72 complexity | a2de36f1d283257eb4cbedf1225c4295 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, LGPL-3.0
  1. <?php
  2. /*
  3. * 2007-2014 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2014 PrestaShop SA
  23. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  24. * International Registered Trademark & Property of PrestaShop SA
  25. */
  26. /**
  27. * This class includes functions for image manipulation
  28. *
  29. * @since 1.5.0
  30. */
  31. class ImageManagerCore
  32. {
  33. const ERROR_FILE_NOT_EXIST = 1;
  34. const ERROR_FILE_WIDTH = 2;
  35. const ERROR_MEMORY_LIMIT = 3;
  36. /**
  37. * Generate a cached thumbnail for object lists (eg. carrier, order statuses...etc)
  38. *
  39. * @param string $image Real image filename
  40. * @param string $cache_image Cached filename
  41. * @param int $size Desired size
  42. * @param string $image_type Image type
  43. * @param bool $disable_cache When turned on a timestamp will be added to the image URI to disable the HTTP cache
  44. * @param bool $regenerate When turned on and the file already exist, the file will be regenerated
  45. * @return string
  46. */
  47. public static function thumbnail($image, $cache_image, $size, $image_type = 'jpg', $disable_cache = true, $regenerate = false)
  48. {
  49. if (!file_exists($image))
  50. return '';
  51. if (file_exists(_PS_TMP_IMG_DIR_.$cache_image) && $regenerate)
  52. @unlink(_PS_TMP_IMG_DIR_.$cache_image);
  53. if ($regenerate || !file_exists(_PS_TMP_IMG_DIR_.$cache_image))
  54. {
  55. $infos = getimagesize($image);
  56. // Evaluate the memory required to resize the image: if it's too much, you can't resize it.
  57. if (!ImageManager::checkImageMemoryLimit($image))
  58. return false;
  59. $x = $infos[0];
  60. $y = $infos[1];
  61. $max_x = $size * 3;
  62. // Size is already ok
  63. if ($y < $size && $x <= $max_x)
  64. copy($image, _PS_TMP_IMG_DIR_.$cache_image);
  65. // We need to resize */
  66. else
  67. {
  68. $ratio_x = $x / ($y / $size);
  69. if ($ratio_x > $max_x)
  70. {
  71. $ratio_x = $max_x;
  72. $size = $y / ($x / $max_x);
  73. }
  74. ImageManager::resize($image, _PS_TMP_IMG_DIR_.$cache_image, $ratio_x, $size, $image_type);
  75. }
  76. }
  77. // Relative link will always work, whatever the base uri set in the admin
  78. if (Context::getContext()->controller->controller_type == 'admin')
  79. return '<img src="../img/tmp/'.$cache_image.($disable_cache ? '?time='.time() : '').'" alt="" class="imgm img-thumbnail" />';
  80. else
  81. return '<img src="'._PS_TMP_IMG_.$cache_image.($disable_cache ? '?time='.time() : '').'" alt="" class="imgm img-thumbnail" />';
  82. }
  83. /**
  84. * Check if memory limit is too long or not
  85. *
  86. * @static
  87. * @param $image
  88. * @return bool
  89. */
  90. public static function checkImageMemoryLimit($image)
  91. {
  92. $infos = @getimagesize($image);
  93. $memory_limit = Tools::getMemoryLimit();
  94. // memory_limit == -1 => unlimited memory
  95. if (function_exists('memory_get_usage') && (int)$memory_limit != -1)
  96. {
  97. $current_memory = memory_get_usage();
  98. $channel = isset($infos['channels']) ? ($infos['channels'] / 8) : 1;
  99. // Evaluate the memory required to resize the image: if it's too much, you can't resize it.
  100. if (($infos[0] * $infos[1] * $infos['bits'] * $channel + pow(2, 16)) * 1.8 + $current_memory > $memory_limit - 1024 * 1024)
  101. return false;
  102. }
  103. return true;
  104. }
  105. /**
  106. * Resize, cut and optimize image
  107. *
  108. * @param string $src_file Image object from $_FILE
  109. * @param string $dst_file Destination filename
  110. * @param integer $dst_width Desired width (optional)
  111. * @param integer $dst_height Desired height (optional)
  112. * @param string $file_type
  113. * @return boolean Operation result
  114. */
  115. public static function resize($src_file, $dst_file, $dst_width = null, $dst_height = null, $file_type = 'jpg', $force_type = false, &$error = 0)
  116. {
  117. if (PHP_VERSION_ID < 50300)
  118. clearstatcache();
  119. else
  120. clearstatcache(true, $src_file);
  121. if (!file_exists($src_file) || !filesize($src_file))
  122. return !($error = self::ERROR_FILE_NOT_EXIST);
  123. list($src_width, $src_height, $type) = getimagesize($src_file);
  124. // If PS_IMAGE_QUALITY is activated, the generated image will be a PNG with .jpg as a file extension.
  125. // This allow for higher quality and for transparency. JPG source files will also benefit from a higher quality
  126. // because JPG reencoding by GD, even with max quality setting, degrades the image.
  127. if (Configuration::get('PS_IMAGE_QUALITY') == 'png_all'
  128. || (Configuration::get('PS_IMAGE_QUALITY') == 'png' && $type == IMAGETYPE_PNG) && !$force_type)
  129. $file_type = 'png';
  130. if (!$src_width)
  131. return !($error = self::ERROR_FILE_WIDTH);
  132. if (!$dst_width)
  133. $dst_width = $src_width;
  134. if (!$dst_height)
  135. $dst_height = $src_height;
  136. $src_image = ImageManager::create($type, $src_file);
  137. $width_diff = $dst_width / $src_width;
  138. $height_diff = $dst_height / $src_height;
  139. if ($width_diff > 1 && $height_diff > 1)
  140. {
  141. $next_width = $src_width;
  142. $next_height = $src_height;
  143. }
  144. else
  145. {
  146. if (Configuration::get('PS_IMAGE_GENERATION_METHOD') == 2 || (!Configuration::get('PS_IMAGE_GENERATION_METHOD') && $width_diff > $height_diff))
  147. {
  148. $next_height = $dst_height;
  149. $next_width = round(($src_width * $next_height) / $src_height);
  150. $dst_width = (int)(!Configuration::get('PS_IMAGE_GENERATION_METHOD') ? $dst_width : $next_width);
  151. }
  152. else
  153. {
  154. $next_width = $dst_width;
  155. $next_height = round($src_height * $dst_width / $src_width);
  156. $dst_height = (int)(!Configuration::get('PS_IMAGE_GENERATION_METHOD') ? $dst_height : $next_height);
  157. }
  158. }
  159. if (!ImageManager::checkImageMemoryLimit($src_file))
  160. return !($error = self::ERROR_MEMORY_LIMIT);
  161. $dest_image = imagecreatetruecolor($dst_width, $dst_height);
  162. // If image is a PNG and the output is PNG, fill with transparency. Else fill with white background.
  163. if ($file_type == 'png' && $type == IMAGETYPE_PNG)
  164. {
  165. imagealphablending($dest_image, false);
  166. imagesavealpha($dest_image, true);
  167. $transparent = imagecolorallocatealpha($dest_image, 255, 255, 255, 127);
  168. imagefilledrectangle($dest_image, 0, 0, $dst_width, $dst_height, $transparent);
  169. }
  170. else
  171. {
  172. $white = imagecolorallocate($dest_image, 255, 255, 255);
  173. imagefilledrectangle ($dest_image, 0, 0, $dst_width, $dst_height, $white);
  174. }
  175. imagecopyresampled($dest_image, $src_image, (int)(($dst_width - $next_width) / 2), (int)(($dst_height - $next_height) / 2), 0, 0, $next_width, $next_height, $src_width, $src_height);
  176. return (ImageManager::write($file_type, $dest_image, $dst_file));
  177. }
  178. /**
  179. * Check if file is a real image
  180. *
  181. * @param string $filename File path to check
  182. * @param string $file_mime_type File known mime type (generally from $_FILES)
  183. * @param array $mime_type_list Allowed MIME types
  184. * @return bool
  185. */
  186. public static function isRealImage($filename, $file_mime_type = null, $mime_type_list = null)
  187. {
  188. // Detect mime content type
  189. $mime_type = false;
  190. if (!$mime_type_list)
  191. $mime_type_list = array('image/gif', 'image/jpg', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png');
  192. // Try 4 different methods to determine the mime type
  193. if (function_exists('getimagesize'))
  194. {
  195. $image_info = @getimagesize($filename);
  196. if ($image_info)
  197. $mime_type = $image_info['mime'];
  198. else
  199. $file_mime_type = false;
  200. }
  201. else if (function_exists('finfo_open'))
  202. {
  203. $const = defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME;
  204. $finfo = finfo_open($const);
  205. $mime_type = finfo_file($finfo, $filename);
  206. finfo_close($finfo);
  207. }
  208. elseif (function_exists('mime_content_type'))
  209. $mime_type = mime_content_type($filename);
  210. elseif (function_exists('exec'))
  211. {
  212. $mime_type = trim(exec('file -b --mime-type '.escapeshellarg($filename)));
  213. if (!$mime_type)
  214. $mime_type = trim(exec('file --mime '.escapeshellarg($filename)));
  215. if (!$mime_type)
  216. $mime_type = trim(exec('file -bi '.escapeshellarg($filename)));
  217. }
  218. if ($file_mime_type && (empty($mime_type) || $mime_type == 'regular file' || $mime_type == 'text/plain'))
  219. $mime_type = $file_mime_type;
  220. // For each allowed MIME type, we are looking for it inside the current MIME type
  221. foreach ($mime_type_list as $type)
  222. if (strstr($mime_type, $type))
  223. return true;
  224. return false;
  225. }
  226. /**
  227. * Check if image file extension is correct
  228. *
  229. * @static
  230. * @param $filename real filename
  231. * @return bool true if it's correct
  232. */
  233. public static function isCorrectImageFileExt($filename, $authorized_extensions = null)
  234. {
  235. // Filter on file extension
  236. if ($authorized_extensions === null)
  237. $authorized_extensions = array('gif', 'jpg', 'jpeg', 'jpe', 'png');
  238. $name_explode = explode('.', $filename);
  239. if (count($name_explode) >= 2)
  240. {
  241. $current_extension = strtolower($name_explode[count($name_explode) - 1]);
  242. if (!in_array($current_extension, $authorized_extensions))
  243. return false;
  244. }
  245. else
  246. return false;
  247. return true;
  248. }
  249. /**
  250. * Validate image upload (check image type and weight)
  251. *
  252. * @param array $file Upload $_FILE value
  253. * @param integer $max_file_size Maximum upload size
  254. * @return bool|string Return false if no error encountered
  255. */
  256. public static function validateUpload($file, $max_file_size = 0, $types = null)
  257. {
  258. if ((int)$max_file_size > 0 && $file['size'] > (int)$max_file_size)
  259. return sprintf(Tools::displayError('Image is too large (%1$d kB). Maximum allowed: %2$d kB'), $file['size'] / 1024, $max_file_size / 1024);
  260. if (!ImageManager::isRealImage($file['tmp_name'], $file['type']) || !ImageManager::isCorrectImageFileExt($file['name'], $types) || preg_match('/\%00/', $file['name']))
  261. return Tools::displayError('Image format not recognized, allowed formats are: .gif, .jpg, .png');
  262. if ($file['error'])
  263. return sprintf(Tools::displayError('Error while uploading image; please change your server\'s settings. (Error code: %s)'), $file['error']);
  264. return false;
  265. }
  266. /**
  267. * Validate icon upload
  268. *
  269. * @param array $file Upload $_FILE value
  270. * @param int $max_file_size Maximum upload size
  271. * @return bool|string Return false if no error encountered
  272. */
  273. public static function validateIconUpload($file, $max_file_size = 0)
  274. {
  275. if ((int)$max_file_size > 0 && $file['size'] > $max_file_size)
  276. return sprintf(
  277. Tools::displayError('Image is too large (%1$d kB). Maximum allowed: %2$d kB'),
  278. $file['size'] / 1000,
  279. $max_file_size / 1000
  280. );
  281. if (substr($file['name'], -4) != '.ico')
  282. return Tools::displayError('Image format not recognized, allowed formats are: .ico');
  283. if ($file['error'])
  284. return Tools::displayError('Error while uploading image; please change your server\'s settings.');
  285. return false;
  286. }
  287. /**
  288. * Cut image
  289. *
  290. * @param array $src_file Origin filename
  291. * @param string $dst_file Destination filename
  292. * @param integer $dst_width Desired width
  293. * @param integer $dst_height Desired height
  294. * @param string $file_type
  295. * @param int $dst_x
  296. * @param int $dst_y
  297. *
  298. * @return bool Operation result
  299. */
  300. public static function cut($src_file, $dst_file, $dst_width = null, $dst_height = null, $file_type = 'jpg', $dst_x = 0, $dst_y = 0)
  301. {
  302. if (!file_exists($src_file))
  303. return false;
  304. // Source information
  305. $src_info = getimagesize($src_file);
  306. $src = array(
  307. 'width' => $src_info[0],
  308. 'height' => $src_info[1],
  309. 'ressource' => ImageManager::create($src_info[2], $src_file),
  310. );
  311. // Destination information
  312. $dest = array();
  313. $dest['x'] = $dst_x;
  314. $dest['y'] = $dst_y;
  315. $dest['width'] = !is_null($dst_width) ? $dst_width : $src['width'];
  316. $dest['height'] = !is_null($dst_height) ? $dst_height : $src['height'];
  317. $dest['ressource'] = ImageManager::createWhiteImage($dest['width'], $dest['height']);
  318. $white = imagecolorallocate($dest['ressource'], 255, 255, 255);
  319. imagecopyresampled($dest['ressource'], $src['ressource'], 0, 0, $dest['x'], $dest['y'], $dest['width'], $dest['height'], $dest['width'], $dest['height']);
  320. imagecolortransparent($dest['ressource'], $white);
  321. $return = ImageManager::write($file_type, $dest['ressource'], $dst_file);
  322. return $return;
  323. }
  324. /**
  325. * Create an image with GD extension from a given type
  326. *
  327. * @param string $type
  328. * @param string $filename
  329. * @return resource
  330. */
  331. public static function create($type, $filename)
  332. {
  333. switch ($type)
  334. {
  335. case IMAGETYPE_GIF :
  336. return imagecreatefromgif($filename);
  337. break;
  338. case IMAGETYPE_PNG :
  339. return imagecreatefrompng($filename);
  340. break;
  341. case IMAGETYPE_JPEG :
  342. default:
  343. return imagecreatefromjpeg($filename);
  344. break;
  345. }
  346. }
  347. /**
  348. * Create an empty image with white background
  349. *
  350. * @param int $width
  351. * @param int $height
  352. * @return resource
  353. */
  354. public static function createWhiteImage($width, $height)
  355. {
  356. $image = imagecreatetruecolor($width, $height);
  357. $white = imagecolorallocate($image, 255, 255, 255);
  358. imagefill($image, 0, 0, $white);
  359. return $image;
  360. }
  361. /**
  362. * Generate and write image
  363. *
  364. * @param string $type
  365. * @param resource $resource
  366. * @param string $filename
  367. * @return bool
  368. */
  369. public static function write($type, $resource, $filename)
  370. {
  371. switch ($type)
  372. {
  373. case 'gif':
  374. $success = imagegif($resource, $filename);
  375. break;
  376. case 'png':
  377. $quality = (Configuration::get('PS_PNG_QUALITY') === false ? 7 : Configuration::get('PS_PNG_QUALITY'));
  378. $success = imagepng($resource, $filename, (int)$quality);
  379. break;
  380. case 'jpg':
  381. case 'jpeg':
  382. default:
  383. $quality = (Configuration::get('PS_JPEG_QUALITY') === false ? 90 : Configuration::get('PS_JPEG_QUALITY'));
  384. imageinterlace($resource,1); /// make it PROGRESSIVE
  385. $success = imagejpeg($resource, $filename, (int)$quality);
  386. break;
  387. }
  388. imagedestroy($resource);
  389. @chmod($filename, 0664);
  390. return $success;
  391. }
  392. /**
  393. * Return the mime type by the file extension
  394. *
  395. * @param string $file_name
  396. * @return string
  397. */
  398. public static function getMimeTypeByExtension($file_name)
  399. {
  400. $types = array(
  401. 'image/gif' => array('gif'),
  402. 'image/jpeg' => array('jpg', 'jpeg'),
  403. 'image/png' => array('png')
  404. );
  405. $extension = substr($file_name, strrpos($file_name, '.') + 1);
  406. $mime_type = null;
  407. foreach ($types as $mime => $exts)
  408. if (in_array($extension, $exts))
  409. {
  410. $mime_type = $mime;
  411. break;
  412. }
  413. if ($mime_type === null)
  414. $mime_type = 'image/jpeg';
  415. return $mime_type;
  416. }
  417. }