| | |
| | | <?php |
| | | |
| | | /* |
| | | /** |
| | | +-----------------------------------------------------------------------+ |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2005-2012, The Roundcube Dev Team | |
| | |
| | | ); |
| | | |
| | | |
| | | /** |
| | | * Class constructor |
| | | * |
| | | * @param string $filename Image file name/path |
| | | */ |
| | | function __construct($filename) |
| | | { |
| | | $this->image_file = $filename; |
| | |
| | | $height = $imsize[1]; |
| | | $gd_type = $imsize['2']; |
| | | $type = image_type_to_extension($imsize['2'], false); |
| | | $channels = $imsize['channels']; |
| | | } |
| | | |
| | | // use ImageMagick |
| | | if (!$type && ($data = $this->identify())) { |
| | | list($type, $width, $height) = $data; |
| | | $channels = null; |
| | | } |
| | | |
| | | if ($type) { |
| | |
| | | 'gd_type' => $gd_type, |
| | | 'width' => $width, |
| | | 'height' => $height, |
| | | 'channels' => $channels, |
| | | ); |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * Resize image to a given size. Use only to shrink an image. |
| | | * If an image is smaller than specified size it will be not resized. |
| | | * |
| | | * @param int $size Max width/height size |
| | | * @param string $filename Output filename |
| | | * @param boolean $browser_compat Convert to image type displayable by any browser |
| | | * @param int $size Max width/height size |
| | | * @param string $filename Output filename |
| | | * @param boolean $browser_compat Convert to image type displayable by any browser |
| | | * |
| | | * @return mixed Output type on success, False on failure |
| | | */ |
| | |
| | | } |
| | | |
| | | // use Imagemagick |
| | | if ($convert) { |
| | | $p['out'] = $filename; |
| | | $p['in'] = $this->image_file; |
| | | $p['size'] = $size.'x'.$size; |
| | | $type = $props['type']; |
| | | if ($convert || class_exists('Imagick', false)) { |
| | | $p['out'] = $filename; |
| | | $p['in'] = $this->image_file; |
| | | $type = $props['type']; |
| | | |
| | | if (!$type && ($data = $this->identify())) { |
| | | $type = $data[0]; |
| | |
| | | $type = 'jpg'; |
| | | } |
| | | |
| | | $p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif", 'quality' => 75); |
| | | $p['-opts'] = array('-resize' => $p['size'].'>'); |
| | | // If only one dimension is greater than the limit convert doesn't |
| | | // work as expected, we need to calculate new dimensions |
| | | $scale = $size / max($props['width'], $props['height']); |
| | | |
| | | if (in_array($type, explode(',', $p['types']))) { // Valid type? |
| | | $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {intype}:{in} {type}:{out}', $p); |
| | | // if file is smaller than the limit, we do nothing |
| | | // but copy original file to destination file |
| | | if ($scale >= 1 && $p['intype'] == $type) { |
| | | $result = ($this->image_file == $filename || copy($this->image_file, $filename)) ? '' : false; |
| | | } |
| | | else { |
| | | $valid_types = "bmp,eps,gif,jp2,jpg,png,svg,tif"; |
| | | |
| | | if (in_array($type, explode(',', $valid_types))) { // Valid type? |
| | | if ($scale >= 1) { |
| | | $width = $props['width']; |
| | | $height = $props['height']; |
| | | } |
| | | else { |
| | | $width = intval($props['width'] * $scale); |
| | | $height = intval($props['height'] * $scale); |
| | | } |
| | | |
| | | // use ImageMagick in command line |
| | | if ($convert) { |
| | | $p += array( |
| | | 'type' => $type, |
| | | 'quality' => 75, |
| | | 'size' => $width . 'x' . $height, |
| | | ); |
| | | |
| | | $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace sRGB -strip' |
| | | . ' -quality {quality} -resize {size} {intype}:{in} {type}:{out}', $p); |
| | | } |
| | | // use PHP's Imagick class |
| | | else { |
| | | try { |
| | | $image = new Imagick($this->image_file); |
| | | $image = $image->flattenImages(); |
| | | |
| | | $image->setImageColorspace(Imagick::COLORSPACE_SRGB); |
| | | $image->setImageCompressionQuality(75); |
| | | $image->setImageFormat($type); |
| | | $image->stripImage(); |
| | | $image->scaleImage($width, $height); |
| | | |
| | | if ($image->writeImage($filename)) { |
| | | $result = ''; |
| | | } |
| | | } |
| | | catch (Exception $e) { |
| | | rcube::raise_error($e, true, false); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | if ($result === '') { |
| | | @chmod($filename, 0600); |
| | | return $type; |
| | | } |
| | | } |
| | | |
| | | // do we have enough memory? (#1489937) |
| | | if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && !$this->mem_check($props)) { |
| | | return false; |
| | | } |
| | | |
| | | // use GD extension |
| | |
| | | } |
| | | else if($props['gd_type'] == IMAGETYPE_GIF && function_exists('imagecreatefromgif')) { |
| | | $image = imagecreatefromgif($this->image_file); |
| | | $type = 'gid'; |
| | | $type = 'gif'; |
| | | } |
| | | else if($props['gd_type'] == IMAGETYPE_PNG && function_exists('imagecreatefrompng')) { |
| | | $image = imagecreatefrompng($this->image_file); |
| | |
| | | // Imagemagick resize is implemented in shrinking mode (see -resize argument above) |
| | | // we do the same here, if an image is smaller than specified size |
| | | // we do nothing but copy original file to destination file |
| | | if ($scale > 1) { |
| | | return $this->image_file == $filename || copy($this->image_file, $filename) ? $type : false; |
| | | if ($scale >= 1) { |
| | | $result = $this->image_file == $filename || copy($this->image_file, $filename); |
| | | } |
| | | else { |
| | | $width = intval($props['width'] * $scale); |
| | | $height = intval($props['height'] * $scale); |
| | | $new_image = imagecreatetruecolor($width, $height); |
| | | |
| | | $width = $props['width'] * $scale; |
| | | $height = $props['height'] * $scale; |
| | | if ($new_image === false) { |
| | | return false; |
| | | } |
| | | |
| | | $new_image = imagecreatetruecolor($width, $height); |
| | | // Fix transparency of gif/png image |
| | | if ($props['gd_type'] != IMAGETYPE_JPEG) { |
| | | imagealphablending($new_image, false); |
| | | imagesavealpha($new_image, true); |
| | | $transparent = imagecolorallocatealpha($new_image, 255, 255, 255, 127); |
| | | imagefilledrectangle($new_image, 0, 0, $width, $height, $transparent); |
| | | } |
| | | |
| | | // Fix transparency of gif/png image |
| | | if ($props['gd_type'] != IMAGETYPE_JPEG) { |
| | | imagealphablending($new_image, false); |
| | | imagesavealpha($new_image, true); |
| | | $transparent = imagecolorallocatealpha($new_image, 255, 255, 255, 127); |
| | | imagefilledrectangle($new_image, 0, 0, $width, $height, $transparent); |
| | | } |
| | | imagecopyresampled($new_image, $image, 0, 0, 0, 0, $width, $height, $props['width'], $props['height']); |
| | | $image = $new_image; |
| | | |
| | | imagecopyresampled($new_image, $image, 0, 0, 0, 0, $width, $height, $props['width'], $props['height']); |
| | | $image = $new_image; |
| | | // fix rotation of image if EXIF data exists and specifies rotation (GD strips the EXIF data) |
| | | if ($this->image_file && $type == 'jpg' && function_exists('exif_read_data')) { |
| | | $exif = exif_read_data($this->image_file); |
| | | if ($exif && $exif['Orientation']) { |
| | | switch ($exif['Orientation']) { |
| | | case 3: |
| | | $image = imagerotate($image, 180, 0); |
| | | break; |
| | | case 6: |
| | | $image = imagerotate($image, -90, 0); |
| | | break; |
| | | case 8: |
| | | $image = imagerotate($image, 90, 0); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if ($props['gd_type'] == IMAGETYPE_JPEG) { |
| | | $result = imagejpeg($image, $filename, 75); |
| | | } |
| | | elseif($props['gd_type'] == IMAGETYPE_GIF) { |
| | | $result = imagegif($image, $filename); |
| | | } |
| | | elseif($props['gd_type'] == IMAGETYPE_PNG) { |
| | | $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS); |
| | | if ($props['gd_type'] == IMAGETYPE_JPEG) { |
| | | $result = imagejpeg($image, $filename, 75); |
| | | } |
| | | elseif($props['gd_type'] == IMAGETYPE_GIF) { |
| | | $result = imagegif($image, $filename); |
| | | } |
| | | elseif($props['gd_type'] == IMAGETYPE_PNG) { |
| | | $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS); |
| | | } |
| | | } |
| | | |
| | | if ($result) { |
| | |
| | | /** |
| | | * Convert image to a given type |
| | | * |
| | | * @param int $type Destination file type (see class constants) |
| | | * @param string $filename Output filename (if empty, original file will be used |
| | | * and filename extension will be modified) |
| | | * @param int $type Destination file type (see class constants) |
| | | * @param string $filename Output filename (if empty, original file will be used |
| | | * and filename extension will be modified) |
| | | * |
| | | * @return bool True on success, False on failure |
| | | */ |
| | |
| | | } |
| | | } |
| | | |
| | | // use ImageMagick |
| | | // use ImageMagick in command line |
| | | if ($convert) { |
| | | $p['in'] = $this->image_file; |
| | | $p['out'] = $filename; |
| | | $p['type'] = self::$extensions[$type]; |
| | | |
| | | $result = rcube::exec($convert . ' 2>&1 -colorspace RGB -quality 75 {in} {type}:{out}', $p); |
| | | $result = rcube::exec($convert . ' 2>&1 -colorspace sRGB -strip -quality 75 {in} {type}:{out}', $p); |
| | | |
| | | if ($result === '') { |
| | | @chmod($filename, 0600); |
| | | chmod($filename, 0600); |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | // use PHP's Imagick class |
| | | if (class_exists('Imagick', false)) { |
| | | try { |
| | | $image = new Imagick($this->image_file); |
| | | |
| | | $image->setImageColorspace(Imagick::COLORSPACE_SRGB); |
| | | $image->setImageCompressionQuality(75); |
| | | $image->setImageFormat(self::$extensions[$type]); |
| | | $image->stripImage(); |
| | | |
| | | if ($image->writeImage($filename)) { |
| | | @chmod($filename, 0600); |
| | | return true; |
| | | } |
| | | } |
| | | catch (Exception $e) { |
| | | rcube::raise_error($e, true, false); |
| | | } |
| | | } |
| | | |
| | | // use GD extension (TIFF isn't supported) |
| | | $props = $this->props(); |
| | | |
| | | // do we have enough memory? (#1489937) |
| | | if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && !$this->mem_check($props)) { |
| | | return false; |
| | | } |
| | | |
| | | |
| | | if ($props['gd_type']) { |
| | | if ($props['gd_type'] == IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Identify command handler. |
| | | * Checks if image format conversion is supported |
| | | * |
| | | * @return boolean True if specified format can be converted to another format |
| | | */ |
| | | public static function is_convertable($mimetype = null) |
| | | { |
| | | $rcube = rcube::get_instance(); |
| | | |
| | | // @TODO: check if specified mimetype is really supported |
| | | return class_exists('Imagick', false) || $rcube->config->get('im_convert_path'); |
| | | } |
| | | |
| | | /** |
| | | * ImageMagick based image properties read. |
| | | */ |
| | | private function identify() |
| | | { |
| | | $rcube = rcube::get_instance(); |
| | | |
| | | // use ImageMagick in command line |
| | | if ($cmd = $rcube->config->get('im_identify_path')) { |
| | | $args = array('in' => $this->image_file, 'format' => "%m %[fx:w] %[fx:h]"); |
| | | $id = rcube::exec($cmd. ' 2>/dev/null -format {format} {in}', $args); |
| | |
| | | return explode(' ', strtolower($id)); |
| | | } |
| | | } |
| | | |
| | | // use PHP's Imagick class |
| | | if (class_exists('Imagick', false)) { |
| | | try { |
| | | $image = new Imagick($this->image_file); |
| | | |
| | | return array( |
| | | strtolower($image->getImageFormat()), |
| | | $image->getImageWidth(), |
| | | $image->getImageHeight(), |
| | | ); |
| | | } |
| | | catch (Exception $e) {} |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Check if we have enough memory to load specified image |
| | | */ |
| | | private function mem_check($props) |
| | | { |
| | | // image size is unknown, we can't calculate required memory |
| | | if (!$props['width']) { |
| | | return true; |
| | | } |
| | | |
| | | // channels: CMYK - 4, RGB - 3 |
| | | $multip = ($props['channels'] ?: 3) + 1; |
| | | |
| | | // calculate image size in memory (in bytes) |
| | | $size = $props['width'] * $props['height'] * $multip; |
| | | return rcube_utils::mem_check($size); |
| | | } |
| | | } |