| | |
| | | } |
| | | |
| | | /** |
| | | * Resize image to a given size |
| | | * 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 |
| | |
| | | $convert = $rcube->config->get('im_convert_path', false); |
| | | $props = $this->props(); |
| | | |
| | | if (empty($props)) { |
| | | return false; |
| | | } |
| | | |
| | | if (!$filename) { |
| | | $filename = $this->image_file; |
| | | } |
| | | |
| | | // 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; |
| | | } |
| | | } |
| | | |
| | | // use GD extension |
| | | $gd_types = array(IMAGETYPE_JPEG, IMAGETYPE_GIF, IMAGETYPE_PNG); |
| | | if ($props['gd_type'] && in_array($props['gd_type'], $gd_types)) { |
| | | if ($props['gd_type'] == IMAGETYPE_JPEG) { |
| | | if ($props['gd_type']) { |
| | | if ($props['gd_type'] == IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) { |
| | | $image = imagecreatefromjpeg($this->image_file); |
| | | $type = 'jpg'; |
| | | } |
| | | elseif($props['gd_type'] == IMAGETYPE_GIF) { |
| | | else if($props['gd_type'] == IMAGETYPE_GIF && function_exists('imagecreatefromgif')) { |
| | | $image = imagecreatefromgif($this->image_file); |
| | | $type = 'gif'; |
| | | } |
| | | elseif($props['gd_type'] == IMAGETYPE_PNG) { |
| | | else if($props['gd_type'] == IMAGETYPE_PNG && function_exists('imagecreatefrompng')) { |
| | | $image = imagecreatefrompng($this->image_file); |
| | | $type = 'png'; |
| | | } |
| | | else { |
| | | // @TODO: print error to the log? |
| | | return false; |
| | | } |
| | | |
| | | $scale = $size / max($props['width'], $props['height']); |
| | | $width = $props['width'] * $scale; |
| | | $height = $props['height'] * $scale; |
| | | |
| | | $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); |
| | | if ($image === false) { |
| | | return false; |
| | | } |
| | | |
| | | imagecopyresampled($new_image, $image, 0, 0, 0, 0, $width, $height, $props['width'], $props['height']); |
| | | $image = $new_image; |
| | | $scale = $size / max($props['width'], $props['height']); |
| | | |
| | | if ($props['gd_type'] == IMAGETYPE_JPEG) { |
| | | $result = imagejpeg($image, $filename, 75); |
| | | $type = 'jpg'; |
| | | // 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) { |
| | | $result = $this->image_file == $filename || copy($this->image_file, $filename); |
| | | } |
| | | elseif($props['gd_type'] == IMAGETYPE_GIF) { |
| | | $result = imagegif($image, $filename); |
| | | $type = 'gid'; |
| | | } |
| | | elseif($props['gd_type'] == IMAGETYPE_PNG) { |
| | | $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS); |
| | | $type = 'png'; |
| | | else { |
| | | $width = intval($props['width'] * $scale); |
| | | $height = intval($props['height'] * $scale); |
| | | $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); |
| | | } |
| | | |
| | | 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 && 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 ($result) { |
| | | @chmod($filename, 0600); |
| | | return $type; |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | // 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); |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | // use GD extension (TIFF isn't supported) |
| | | $props = $this->props(); |
| | | $gd_types = array(IMAGETYPE_JPEG, IMAGETYPE_GIF, IMAGETYPE_PNG); |
| | | // use PHP's Imagick class |
| | | if (class_exists('Imagick', false)) { |
| | | try { |
| | | $image = new Imagick($this->image_file); |
| | | |
| | | if ($props['gd_type'] && in_array($props['gd_type'], $gd_types)) { |
| | | if ($props['gd_type'] == IMAGETYPE_JPEG) { |
| | | $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(); |
| | | |
| | | if ($props['gd_type']) { |
| | | if ($props['gd_type'] == IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) { |
| | | $image = imagecreatefromjpeg($this->image_file); |
| | | } |
| | | else if ($props['gd_type'] == IMAGETYPE_GIF) { |
| | | else if ($props['gd_type'] == IMAGETYPE_GIF && function_exists('imagecreatefromgif')) { |
| | | $image = imagecreatefromgif($this->image_file); |
| | | } |
| | | else if ($props['gd_type'] == IMAGETYPE_PNG) { |
| | | else if ($props['gd_type'] == IMAGETYPE_PNG && function_exists('imagecreatefrompng')) { |
| | | $image = imagecreatefrompng($this->image_file); |
| | | } |
| | | else { |
| | | // @TODO: print error to the log? |
| | | return false; |
| | | } |
| | | |
| | | if ($type == self::TYPE_JPG) { |
| | |
| | | else if ($type == self::TYPE_PNG) { |
| | | $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS); |
| | | } |
| | | |
| | | if ($result) { |
| | | @chmod($filename, 0600); |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | // @TODO: print error to the log? |
| | |
| | | } |
| | | |
| | | /** |
| | | * 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) {} |
| | | } |
| | | } |
| | | } |