Implement auto-rotation of uploaded images

This commit is contained in:
Kijin Sung 2019-08-01 15:25:12 +09:00
parent f3761fd934
commit 8c242327a8
7 changed files with 141 additions and 61 deletions

View file

@ -480,9 +480,10 @@ class FileHandler
* @param string $target_type If $target_type is set (gif, jpg, png, bmp), result image will be saved as target type
* @param string $thumbnail_type Thumbnail type(crop, ratio)
* @param int $quality Compression ratio (0~9)
* @param int $rotate Rotation degrees (0~360)
* @return bool TRUE: success, FALSE: failed
*/
public static function createImageFile($source_file, $target_file, $resize_width = 0, $resize_height = 0, $target_type = '', $thumbnail_type = 'crop', $quality = 100)
public static function createImageFile($source_file, $target_file, $resize_width = 0, $resize_height = 0, $target_type = '', $thumbnail_type = 'crop', $quality = 100, $rotate = 0)
{
// check params
if (($source_file = self::exists($source_file)) === FALSE)
@ -544,49 +545,6 @@ class FileHandler
}
$target_type = strtolower($target_type);
// if original image is larger than specified size to resize, calculate the ratio
$width_per = ($resize_width > 0 && $width >= $resize_width) ? $resize_width / $width : 1;
$height_per = ($resize_height > 0 && $height >= $resize_height) ? $resize_height / $height : 1;
$per = NULL;
if($thumbnail_type == 'ratio')
{
$per = ($width_per > $height_per) ? $height_per : $width_per;
$resize_width = $width * $per;
$resize_height = $height * $per;
}
else
{
$per = ($width_per < $height_per) ? $height_per : $width_per;
}
// create temporary image with target size
$thumb = NULL;
if(function_exists('imagecreateTRUEcolor'))
{
$thumb = imagecreateTRUEcolor($resize_width, $resize_height);
}
else if(function_exists('imagecreate'))
{
$thumb = imagecreate($resize_width, $resize_height);
}
if(!$thumb)
{
return FALSE;
}
if($target_type == 'png' && function_exists('imagecolorallocatealpha') && function_exists('imagesavealpha') && function_exists('imagealphablending'))
{
imagefill($thumb, 0, 0, imagecolorallocatealpha($thumb, 0, 0, 0, 127));
imagesavealpha($thumb, TRUE);
imagealphablending($thumb, TRUE);
}
else
{
imagefilledrectangle($thumb, 0, 0, $resize_width - 1, $resize_height - 1, imagecolorallocate($thumb, 255, 255, 255));
}
// create temporary image having original type
$source = NULL;
switch($type)
@ -636,27 +594,86 @@ class FileHandler
return FALSE;
}
// resize original image and put it into temporary image
$new_width = (int) ($width * $per);
$new_height = (int) ($height * $per);
$x = 0;
$y = 0;
if($thumbnail_type == 'crop')
// Rotate image
if ($rotate)
{
$x = (int) ($resize_width / 2 - $new_width / 2);
$y = (int) ($resize_height / 2 - $new_height / 2);
$source = imagerotate($source, $rotate, 0);
$width = imagesx($source);
$height = imagesy($source);
}
if(function_exists('imagecopyresampled'))
// If resize not needed, skip thumbnail generation
if ($width == $resize_width && $height == $resize_height)
{
imagecopyresampled($thumb, $source, $x, $y, 0, 0, $new_width, $new_height, $width, $height);
$thumb = &$source;
}
else
{
imagecopyresized($thumb, $source, $x, $y, 0, 0, $new_width, $new_height, $width, $height);
}
// if original image is larger than specified size to resize, calculate the ratio
$width_per = ($resize_width > 0 && $width >= $resize_width) ? $resize_width / $width : 1;
$height_per = ($resize_height > 0 && $height >= $resize_height) ? $resize_height / $height : 1;
$per = NULL;
if($thumbnail_type == 'ratio')
{
$per = ($width_per > $height_per) ? $height_per : $width_per;
$resize_width = $width * $per;
$resize_height = $height * $per;
}
else
{
$per = ($width_per < $height_per) ? $height_per : $width_per;
}
// create temporary image with target size
$thumb = NULL;
if(function_exists('imagecreateTRUEcolor'))
{
$thumb = imagecreateTRUEcolor($resize_width, $resize_height);
}
else if(function_exists('imagecreate'))
{
$thumb = imagecreate($resize_width, $resize_height);
}
if(!$thumb)
{
return FALSE;
}
if($target_type == 'png' && function_exists('imagecolorallocatealpha') && function_exists('imagesavealpha') && function_exists('imagealphablending'))
{
imagefill($thumb, 0, 0, imagecolorallocatealpha($thumb, 0, 0, 0, 127));
imagesavealpha($thumb, TRUE);
imagealphablending($thumb, TRUE);
}
else
{
imagefilledrectangle($thumb, 0, 0, $resize_width - 1, $resize_height - 1, imagecolorallocate($thumb, 255, 255, 255));
}
// resize original image and put it into temporary image
$new_width = (int) ($width * $per);
$new_height = (int) ($height * $per);
$x = 0;
$y = 0;
if($thumbnail_type == 'crop')
{
$x = (int) ($resize_width / 2 - $new_width / 2);
$y = (int) ($resize_height / 2 - $new_height / 2);
}
if(function_exists('imagecopyresampled'))
{
imagecopyresampled($thumb, $source, $x, $y, 0, 0, $new_width, $new_height, $width, $height);
}
else
{
imagecopyresized($thumb, $source, $x, $y, 0, 0, $new_width, $new_height, $width, $height);
}
}
// create directory
self::makeDir(dirname($target_file));

View file

@ -73,6 +73,8 @@ class fileAdminController extends file
$config->image_autoconv['bmp2jpg'] = Context::get('image_autoconv_bmp2jpg') === 'Y' ? true : false;
$config->image_autoconv['webp2jpg'] = Context::get('image_autoconv_webp2jpg') === 'Y' ? true : false;
$config->image_autoconv_quality = max(50, min(100, intval(Context::get('image_autoconv_quality'))));
$config->image_autorotate = Context::get('image_autorotate') === 'Y' ? true : false;
$config->image_autorotate_quality = max(50, min(100, intval(Context::get('image_autorotate_quality'))));
// Check maximum file size
if (PHP_INT_SIZE < 8)

View file

@ -1051,11 +1051,41 @@ class fileController extends file
// Check image type
if($config->image_autoconv['bmp2jpg'] && function_exists('imagebmp') && $image_type === 6)
{
$convert = array($image_width, $image_height, 'jpg', $config->image_autoconv_quality ?: 75);
$convert = array($image_width, $image_height, 'jpg', $config->image_autoconv_quality ?: 75, 0);
}
if($config->image_autoconv['webp2jpg'] && function_exists('imagewebp') && $image_type === 18)
{
$convert = array($image_width, $image_height, 'jpg', $config->image_autoconv_quality ?: 75);
$convert = array($image_width, $image_height, 'jpg', $config->image_autoconv_quality ?: 75, 0);
}
// Check image rotation
if($config->image_autorotate && function_exists('exif_read_data'))
{
$exif = @exif_read_data($file_info['tmp_name']);
if($exif && isset($exif['Orientation']))
{
switch ($exif['Orientation'])
{
case 3: $rotate = 180; break;
case 6: $rotate = 270; break;
case 8: $rotate = 90; break;
default: $rotate = 0;
}
if ($rotate)
{
$convert = $convert ?: array($image_width, $image_height, $file_info['extension']);
if ($rotate == 90 || $rotate == 270)
{
$image_height = $convert[0];
$image_width = $convert[1];
$convert[0] = $image_width;
$convert[1] = $image_height;
}
$convert[3] = $config->image_autorotate_quality ?: 75;
$convert[4] = $rotate;
}
}
unset($exif);
}
// Check image size
@ -1108,7 +1138,8 @@ class fileController extends file
$resize_height = $config->max_image_height;
}
$target_type = in_array($image_type, array(6, 8, 18)) ? 'jpg' : $file_info['extension'];
$convert = array(intval($resize_width), intval($resize_height), $target_type, $config->max_image_size_quality ?: 75);
$rotate = ($convert && $convert[4]) ? $convert[4] : 0;
$convert = array(intval($resize_width), intval($resize_height), $target_type, $config->max_image_size_quality ?: 75, $rotate);
}
}
}
@ -1116,12 +1147,13 @@ class fileController extends file
// Convert image if necessary
if ($convert)
{
$result = FileHandler::createImageFile($file_info['tmp_name'], $file_info['tmp_name'] . '.conv', $convert[0], $convert[1], $convert[2], 'crop', $convert[3]);
$result = FileHandler::createImageFile($file_info['tmp_name'], $file_info['tmp_name'] . '.conv', $convert[0], $convert[1], $convert[2], 'crop', $convert[3], $convert[4]);
if ($result)
{
$file_info['name'] = preg_replace('/\.' . preg_quote($file_info['extension'], '/') . '$/i', '.' . $convert[2], $file_info['name']);
$file_info['tmp_name'] = $file_info['tmp_name'] . '.conv';
$file_info['size'] = filesize($file_info['tmp_name']);
$file_info['extension'] = $convert[2];
$file_info['converted'] = true;
}
}

View file

@ -195,6 +195,8 @@ class fileModel extends file
$config->max_image_size_quality = $file_config->max_image_size_quality;
$config->image_autoconv = $file_config->image_autoconv;
$config->image_autoconv_quality = $file_config->image_autoconv_quality;
$config->image_autorotate = $file_config->image_autorotate;
$config->image_autorotate_quality = $file_config->image_autorotate_quality;
$config->download_grant = $file_config->download_grant;
$config->allow_outlink = $file_config->allow_outlink;
$config->allow_outlink_site = $file_config->allow_outlink_site;
@ -215,6 +217,8 @@ class fileModel extends file
if(!$config->max_image_size_quality) $config->max_image_size_quality = $file_module_config->max_image_size_quality;
if(!$config->image_autoconv) $config->image_autoconv = $file_module_config->image_autoconv;
if(!$config->image_autoconv_quality) $config->image_autoconv_quality = $file_module_config->image_autoconv_quality;
if(!$config->image_autorotate) $config->image_autorotate = $file_module_config->image_autorotate;
if(!$config->image_autorotate_quality) $config->image_autorotate_quality = $file_module_config->image_autorotate_quality;
// Default setting if not exists
if(!$config->allowed_filesize) $config->allowed_filesize = '2';
@ -226,6 +230,7 @@ class fileModel extends file
if(!$config->max_image_size_quality) $config->max_image_size_quality = 75;
if(!$config->image_autoconv) $config->image_autoconv = array();
if(!$config->image_autoconv_quality) $config->image_autoconv_quality = 75;
if(!$config->image_autorotate_quality) $config->image_autorotate_quality = 75;
return $config;
}

View file

@ -25,6 +25,7 @@ $lang->image_resize_quality = 'Quality';
$lang->image_autoconv = 'Auto-Convert Image';
$lang->image_autoconv_bmp2jpg = 'BMP → JPG';
$lang->image_autoconv_webp2jpg = 'WebP → JPG';
$lang->image_autorotate = 'Auto-Rotate Image';
$lang->inline_download_format = 'Open in current window';
$lang->inline_download_image = 'Image';
$lang->inline_download_audio = 'Audio';
@ -44,6 +45,7 @@ $lang->about_allowed_size_limits = 'The file size will be limited to the value s
$lang->about_allowed_filetypes = 'To allow an extension, use "*.[extention]". To allow multiple extensions, use ";" between each extension. ex) *.* or *.jpg;*.gif; ';
$lang->about_max_image_size = 'You can limit the maximum width and/or height of uploaded images.<br />This limit does not apply to files uploaded by the administrator.';
$lang->about_image_autoconv = 'Automatically convert types of images that often cause trouble or waste disk space into other types.<br />This also works for WebP images that incorrectly have the JPG extension.<br />If enabled, this feature also applies to images uploaded by the administrator.';
$lang->about_image_autorotate = 'Automatically correct images that are rotated by mobile devices.<br />If enabled, this feature also applies to images uploaded by the administrator.';
$lang->cmd_delete_checked_file = 'Delete Selected Item(s)';
$lang->cmd_move_to_document = 'Move to Document';
$lang->cmd_download = 'Download';

View file

@ -25,6 +25,7 @@ $lang->image_resize_quality = '화질';
$lang->image_autoconv = '이미지 자동 변환';
$lang->image_autoconv_bmp2jpg = 'BMP → JPG';
$lang->image_autoconv_webp2jpg = 'WebP → JPG';
$lang->image_autorotate = '이미지 자동 회전';
$lang->inline_download_format = '다운로드시 현재 창 사용';
$lang->inline_download_image = '이미지';
$lang->inline_download_audio = '오디오';
@ -44,6 +45,7 @@ $lang->about_allowed_size_limits = 'IE9 이하, 구버전 안드로이드 등에
$lang->about_allowed_filetypes = '"*.확장자"로 지정할 수 있고 ";" 으로 여러 개 지정이 가능합니다. 예) *.* or *.jpg;*.gif;';
$lang->about_max_image_size = '이미지 파일의 가로, 세로, 또는 가로세로 크기를 모두 제한할 수 있습니다.<br />관리자가 업로드한 파일에는 적용되지 않습니다.';
$lang->about_image_autoconv = '종종 문제를 일으키거나 용량을 낭비하는 이미지 타입을 다른 타입으로 자동 변환합니다.<br />WebP 이미지에 JPG 확장자가 잘못 부여된 경우에도 변환할 수 있습니다.<br />관리자가 업로드한 파일에도 적용됩니다.';
$lang->about_image_autorotate = '모바일 기기 등에서 잘못 회전된 이미지를 바로잡습니다.<br />관리자가 업로드한 파일에도 적용됩니다.';
$lang->cmd_delete_checked_file = '선택항목 삭제';
$lang->cmd_move_to_document = '문서로 이동';
$lang->cmd_download = '다운로드';

View file

@ -61,6 +61,26 @@
<p class="x_help-block">{$lang->about_image_autoconv}</p>
</div>
</div>
<div class="x_control-group">
<label class="x_control-label">{$lang->image_autorotate}</label>
<div class="x_controls">
<label for="image_autorotate_Y" class="x_inline">
<input type="radio" name="image_autorotate" id="image_autorotate_Y" value="Y" checked="checked"|cond="$config->image_autorotate === true" disabled="disabled"|cond="!function_exists('exif_read_data')" />
{$lang->cmd_yes}
</label>
<label for="image_autorotate_N" class="x_inline">
<input type="radio" name="image_autorotate" id="image_autorotate_N" value="N" checked="checked"|cond="$config->image_autorotate !== true" disabled="disabled"|cond="!function_exists('exif_read_data')" />
{$lang->cmd_no}
</label>
<select name="image_autorotate_quality" id="image_autorotate_quality" style="width:100px;min-width:100px">
{@ $config->image_autorotate_quality = $config->image_autorotate_quality ?: 75}
<!--@for($q = 50; $q <= 100; $q += 5)-->
<option value="{$q}" selected="selected"|cond="$config->image_autorotate_quality == $q">{$lang->image_resize_quality} {$q}%</option>
<!--@endfor-->
</select>
<p class="x_help-block">{$lang->about_image_autorotate}</p>
</div>
</div>
<div class="x_control-group">
<label for="allowedFiletypes" class="x_control-label">{$lang->allowed_filetypes}</label>
<div class="x_controls">