diff --git a/css/toolbar.css b/css/toolbar.css index 8ece0cbbc..dd7bc04e2 100644 --- a/css/toolbar.css +++ b/css/toolbar.css @@ -108,8 +108,9 @@ .elfinder-button-icon-help { background-position: 0 -480px; } .elfinder-button-icon-resize { background-position: 0 -512px; } .elfinder-button-icon-search { background-position: 0 -561px; } -.elfinder-button-icon-sort { background-position: 0 -577px; } - +.elfinder-button-icon-sort { background-position: 0 -577px; } +.elfinder-button-icon-rotate-r { background-position: 0 -625px; } +.elfinder-button-icon-rotate-l { background-position: 0 -641px; } /* button with dropdown menu*/ .elfinder .elfinder-menubutton { overflow:visible; } diff --git a/img/toolbar.png b/img/toolbar.png index ddfb9bc6f..3b51326db 100644 Binary files a/img/toolbar.png and b/img/toolbar.png differ diff --git a/js/commands/resize.js b/js/commands/resize.js index a4c602494..d8f98aea4 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -31,17 +31,41 @@ elFinder.prototype.commands.resize = function() { rhandlec = $('
'), uiresize = $('
'), uicrop = $('
'), + uibuttonset = '
', + uibutton = '
', + uiseparator = '', + uirotate = $('
'), + uideg270 = $(uibutton).attr('title',fm.i18n('rotate-cw')).append($('') + .click(function(){ + var val = (parseInt(degree.val()) || 0) - 90; + //val = parseInt(val / 90) * 90; + if (val < 0) { + val = 360 + val; + } + degree.val(val); + })), + uideg90 = $(uibutton).attr('title',fm.i18n('rotate-ccw')).append($('') + .click(function(){ + var val = (parseInt(degree.val()) || 0) + 90; + //val = parseInt(val / 90) * 90; + if (val >= 360) { + val = val - 360; + } + degree.val(val); + })), uiprop = $(''), reset = $('
'), uitype = $('
'+fm.i18n('mode')+'
') - .append('') - .append(''), + .append('') + .append('') + .append(''), type = $('input', uitype) .change(function() { uiresize.add(uicrop).toggle(); resetView(); resizable(true); croppable(true); + // TODO rotateable var val = $('input:checked', uitype).val(); if (val == 'resize') { @@ -82,6 +106,7 @@ elFinder.prototype.commands.resize = function() { pointY = $(input), offsetX = $(input), offsetY = $(input), + degree = $(''), ratio = 1, prop = 1, owidth = 0, @@ -95,7 +120,7 @@ elFinder.prototype.commands.resize = function() { owidth = img.width(); oheight = img.height(); - ratio = (owidth/oheight).toFixed(2); + ratio = owidth/oheight; resize.updateView(owidth, oheight); rhandle.append(img.show()).show(); @@ -246,7 +271,7 @@ elFinder.prototype.commands.resize = function() { } }, save = function() { - var w, h, x, y; + var w, h, x, y, d; var mode = $('input:checked', uitype).val(); width.add(height).change(); @@ -259,6 +284,13 @@ elFinder.prototype.commands.resize = function() { h = parseInt(offsetY.val()) || 0; x = parseInt(pointX.val()) || 0; y = parseInt(pointY.val()) || 0; + } else if (mode = 'rotate') { + w = parseInt(offsetX.val()) || 0; + h = parseInt(offsetY.val()) || 0; + d = parseInt(degree.val()) || 0; + if (d < 0 || d > 360) { + return fm.error('Invalid rotate degree'); + } } if (w <= 0 || h <= 0) { @@ -279,6 +311,7 @@ elFinder.prototype.commands.resize = function() { height : h, x : x, y : y, + degree : d, mode : mode }, notify : {type : 'resize', cnt : 1} @@ -307,13 +340,22 @@ elFinder.prototype.commands.resize = function() { uicrop.append($(row).append($(label).text('X')).append(pointX)) .append($(row).append($(label).text('Y')).append(pointY)) .append($(row).append($(label).text(fm.i18n('width'))).append(offsetX)) - .append($(row).append($(label).text(fm.i18n('height'))).append(offsetY)) + .append($(row).append($(label).text(fm.i18n('height'))).append(offsetY)); + + uirotate.append($(row) + .append($(label).text(fm.i18n('rotate'))) + .append($('
') + .append(degree) + .append($('').text(fm.i18n('degree'))) + ).append($(uibuttonset).append(uideg270).append($(uiseparator)).append(uideg90)) + ); dialog.append(uitype); control.append($(row)) .append(uiresize) .append(uicrop.hide()) + .append(uirotate.hide()) .find('input,select').attr('disabled', 'disabled'); rhandle.append('
') diff --git a/js/i18n/elfinder.en.js b/js/i18n/elfinder.en.js index 9e1fd8f57..2be7105ea 100644 --- a/js/i18n/elfinder.en.js +++ b/js/i18n/elfinder.en.js @@ -1,7 +1,7 @@ /** * English translation * @author Troex Nevelin - * @version 2011-07-10 + * @version 2012-02-25 */ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object') { elFinder.prototype.i18.en = { @@ -101,7 +101,7 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'cmdup' : 'Go to parent directory', 'cmdupload' : 'Upload files', 'cmdview' : 'View', - 'cmdresize' : 'Resize image', + 'cmdresize' : 'Resize & Rotate', 'cmdsort' : 'Sort', /*********************************** buttons ***********************************/ @@ -226,9 +226,13 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'scale' : 'Scale', 'width' : 'Width', 'height' : 'Height', - 'mode' : 'Mode', - 'resize' : 'Resize', - 'crop' : 'Crop', + 'mode' : 'Mode', + 'resize' : 'Resize', + 'crop' : 'Crop', + 'rotate' : 'Rotate', + 'rotate-l90' : '90-degree left rotation', + 'rotate-r90' : '90-degree right rotation', + 'deg' : 'Deg', /********************************** mimetypes **********************************/ 'kindUnknown' : 'Unknown', diff --git a/js/i18n/elfinder.jp.js b/js/i18n/elfinder.jp.js index 2cab79c8a..d8b96d905 100644 --- a/js/i18n/elfinder.jp.js +++ b/js/i18n/elfinder.jp.js @@ -1,7 +1,7 @@ /** * Japanese translation * @author Tomoaki Yoshida - * @version 2012-02-22 + * @version 2012-02-25 */ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object') { elFinder.prototype.i18.jp = { @@ -101,7 +101,7 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'cmdup' : '親ディレクトリーへ移動', 'cmdupload' : 'ファイルアップロード', 'cmdview' : 'ビュー', - 'cmdresize' : 'リサイズ', + 'cmdresize' : 'リサイズと回転', 'cmdsort' : 'ソート', /*********************************** buttons ***********************************/ @@ -229,6 +229,10 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'mode' : 'モード', 'resize' : 'リサイズ', 'crop' : '切り抜き', + 'rotate' : '回転', + 'rotate-l90' : '90度左回転', + 'rotate-r90' : '90度右回転', + 'deg' : '度', /********************************** mimetypes **********************************/ 'kindUnknown' : '不明', diff --git a/php/elFinder.class.php b/php/elFinder.class.php index 2de5260dc..2d07327f4 100644 --- a/php/elFinder.class.php +++ b/php/elFinder.class.php @@ -67,7 +67,7 @@ class elFinder { 'search' => array('q' => true, 'mimes' => false), 'info' => array('targets' => true), 'dim' => array('target' => true), - 'resize' => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false) + 'resize' => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false, 'degree' => false) ); /** @@ -1020,13 +1020,15 @@ class elFinder { $x = (int)$args['x']; $y = (int)$args['y']; $mode = $args['mode']; + $bg = null; + $degree = (int)$args['degree']; if (($volume = $this->volume($target)) == false || ($file = $volume->file($target)) == false) { return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND)); } - return ($file = $volume->resize($target, $width, $height, $x, $y, $mode)) + return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree)) ? array('changed' => array($file)) : array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error())); } diff --git a/php/elFinderVolumeDriver.class.php b/php/elFinderVolumeDriver.class.php index 18eebcefd..a264fa9cf 100644 --- a/php/elFinderVolumeDriver.class.php +++ b/php/elFinderVolumeDriver.class.php @@ -1601,12 +1601,16 @@ abstract class elFinderVolumeDriver { * @param string $hash image file * @param int $width new width * @param int $height new height - * @param bool $crop crop image + * @param int $x X start poistion for crop + * @param int $y Y start poistion for crop + * @param string $mode action how to mainpulate image * @return array|false * @author Dmitry (dio) Levashov * @author Alexey Sukhotin + * @author nao-pon + * @author Troex Nevelin **/ - public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg='') { + public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) { if ($this->commandDisabled('resize')) { return $this->setError(elFinder::ERROR_PERM_DENIED); } @@ -1636,14 +1640,18 @@ abstract class elFinderVolumeDriver { break; case 'fitsquare': - $result = $this->imgSquareFit($path, $width, $height, 'center', 'middle', $bg ? $bg : $this->options['tmbBgColor']); + $result = $this->imgSquareFit($path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor'])); break; - + + case 'rotate': + $result = $this->imgRotate($path, $degree, ($bg ? $bg : $this->options['tmbBgColor'])); + break; + default: $result = $this->imgResize($path, $width, $height, false, true); - break; - } - + break; + } + if ($result) { if (!empty($file['tmb']) && $file['tmb'] != "1") { $this->rmTmb($file['tmb']); @@ -2612,7 +2620,8 @@ abstract class elFinderVolumeDriver { * @param bool $resizeByBiggerSide resize image based on bigger side if true * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov, Alexey Sukhotin + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin **/ protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) { if (($s = @getimagesize($path)) == false) { @@ -2718,7 +2727,8 @@ abstract class elFinderVolumeDriver { * @param bool $y crop top offset * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov, Alexey Sukhotin + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin **/ protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) { if (($s = @getimagesize($path)) == false) { @@ -2793,9 +2803,10 @@ abstract class elFinderVolumeDriver { * @param string $bgcolor square background color in #rrggbb format * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov, Alexey Sukhotin + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin **/ - protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null) { + protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null) { if (($s = @getimagesize($path)) == false) { return false; } @@ -2808,11 +2819,9 @@ abstract class elFinderVolumeDriver { switch ($this->imgLib) { case 'imagick': - try { $img = new imagick($path); } catch (Exception $e) { - return false; } @@ -2856,7 +2865,7 @@ abstract class elFinderVolumeDriver { if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) { return false; } - + if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { $result = imagejpeg($tmp, $path, 100); } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { @@ -2869,14 +2878,80 @@ abstract class elFinderVolumeDriver { imagedestroy($tmp); return $result ? $path : false; - } break; } - return false; - } - + return false; + } + + /** + * Rotate image + * + * @param string $path image file + * @param int $degree rotete degrees + * @param string $bgcolor square background color in #rrggbb format + * @param string $destformat image destination format + * @return string|false + * @author nao-pon + * @author Troex Nevelin + **/ + protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) { + if (($s = @getimagesize($path)) == false) { + return false; + } + + $result = false; + + switch ($this->imgLib) { + case 'imagick': + try { + $img = new imagick($path); + } catch (Exception $e) { + return false; + } + + $img->rotateImage(new ImagickPixel($bgcolor), $degree); + $result = $img->writeImage($path); + return $result ? $path : false; + + break; + + case 'gd': + if ($s['mime'] == 'image/jpeg') { + $img = imagecreatefromjpeg($path); + } elseif ($s['mime'] == 'image/png') { + $img = imagecreatefrompng($path); + } elseif ($s['mime'] == 'image/gif') { + $img = imagecreatefromgif($path); + } elseif ($s['mime'] == 'image/xbm') { + $img = imagecreatefromxbm($path); + } + + $degree = 360 - $degree; + list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); + $bgcolor = imagecolorallocate($img, $r, $g, $b); + $tmp = imageRotate($img, $degree, (int)$bgcolor); + + if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { + $result = imagejpeg($tmp, $path, 100); + } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { + $result = imagegif($tmp, $path, 7); + } else { + $result = imagepng($tmp, $path, 7); + } + + imageDestroy($img); + imageDestroy($tmp); + + return $result ? $path : false; + + break; + } + + return false; + } + /** * Execute shell command *