
This small fix resolves issues where a user will upload an image taken on a device that sets the "Orientation" tag in the EXIF metadata or rotated using another tool on the desktop and that orientation information will be ignored.
273 lines
7.0 KiB
PHP
273 lines
7.0 KiB
PHP
<?php defined('BLUDIT') or die('Bludit CMS.');
|
|
|
|
class Image {
|
|
|
|
private $image;
|
|
private $width;
|
|
private $height;
|
|
private $imageResized;
|
|
|
|
public function setImage($fileName, $newWidth, $newHeight, $option="auto")
|
|
{
|
|
// *** Open up the file
|
|
$this->image = $this->openImage($fileName);
|
|
|
|
// *** Fix issues with orientation being set in metadata
|
|
$this->fixOrientation($fileName);
|
|
|
|
// *** Get width and height
|
|
$this->width = imagesx($this->image);
|
|
$this->height = imagesy($this->image);
|
|
|
|
$this->resizeImage($newWidth, $newHeight, $option);
|
|
}
|
|
|
|
private function fixOrientation($filename) {
|
|
$exif = exif_read_data($filename);
|
|
|
|
if (!empty($exif['Orientation'])) {
|
|
switch ($exif['Orientation']) {
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
$this->image = imageflip($this->image, IMG_FLIP_HORIZONTAL);
|
|
break;
|
|
case 3:
|
|
$this->image = imagerotate($this->image, 180, 0);
|
|
break;
|
|
case 4:
|
|
$this->image = imageflip($this->image, IMG_FLIP_VERTICAL);
|
|
break;
|
|
case 5:
|
|
$this->image = imageflip($this->image, IMG_FLIP_VERTICAL);
|
|
$this->image = imagerotate($this->image, -90, 0);
|
|
break;
|
|
case 6:
|
|
$this->image = imagerotate($this->image, -90, 0);
|
|
break;
|
|
case 7:
|
|
$this->image = imageflip($this->image, IMG_FLIP_HORIZONTAL);
|
|
$this->image = imagerotate($this->image, -90, 0);
|
|
break;
|
|
case 8:
|
|
$this->image = imagerotate($this->image, 90, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public function saveImage($savePath, $imageQuality="100", $forceJPG=false, $forcePNG=false)
|
|
{
|
|
$extension = strtolower(pathinfo($savePath, PATHINFO_EXTENSION));
|
|
|
|
// Remove the extension
|
|
$filename = substr($savePath, 0,strrpos($savePath,'.'));
|
|
|
|
$path_complete = $filename.'.'.$extension;
|
|
|
|
if($forcePNG) {
|
|
$extension = 'png';
|
|
}
|
|
elseif($forceJPG) {
|
|
$extension = 'jpg';
|
|
}
|
|
|
|
switch($extension)
|
|
{
|
|
case 'jpg':
|
|
case 'jpeg':
|
|
// Checking for JPG support
|
|
if (imagetypes() & IMG_JPG) {
|
|
imagejpeg($this->imageResized, $path_complete, $imageQuality);
|
|
}
|
|
break;
|
|
|
|
case 'gif':
|
|
// Checking for GIF support
|
|
if (imagetypes() & IMG_GIF) {
|
|
imagegif($this->imageResized, $path_complete);
|
|
}
|
|
break;
|
|
|
|
case 'png':
|
|
// *** Scale quality from 0-100 to 0-9
|
|
$scaleQuality = round(($imageQuality/100) * 9);
|
|
|
|
// *** Invert quality setting as 0 is best, not 9
|
|
$invertScaleQuality = 9 - $scaleQuality;
|
|
|
|
// Checking for PNG support
|
|
if (imagetypes() & IMG_PNG) {
|
|
imagepng($this->imageResized, $path_complete, $invertScaleQuality);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Fail extension detection
|
|
break;
|
|
}
|
|
|
|
imagedestroy($this->imageResized);
|
|
}
|
|
|
|
private function openImage($file)
|
|
{
|
|
// *** Get extension
|
|
$extension = strtolower(strrchr($file, '.'));
|
|
|
|
switch($extension)
|
|
{
|
|
case '.jpg':
|
|
case '.jpeg':
|
|
$img = imagecreatefromjpeg($file);
|
|
break;
|
|
case '.gif':
|
|
$img = imagecreatefromgif($file);
|
|
break;
|
|
case '.png':
|
|
$img = imagecreatefrompng($file);
|
|
break;
|
|
default:
|
|
$img = false;
|
|
break;
|
|
}
|
|
return $img;
|
|
}
|
|
|
|
private function resizeImage($newWidth, $newHeight, $option)
|
|
{
|
|
// *** Get optimal width and height - based on $option
|
|
$optionArray = $this->getDimensions($newWidth, $newHeight, $option);
|
|
|
|
$optimalWidth = $optionArray['optimalWidth'];
|
|
$optimalHeight = $optionArray['optimalHeight'];
|
|
|
|
|
|
// *** Resample - create image canvas of x, y size
|
|
$this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);
|
|
imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height);
|
|
|
|
|
|
// *** if option is 'crop', then crop too
|
|
if ($option == 'crop') {
|
|
$this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight);
|
|
}
|
|
}
|
|
|
|
private function getDimensions($newWidth, $newHeight, $option)
|
|
{
|
|
|
|
if( ($this->width < $newWidth) and ($this->height < $newHeight) )
|
|
{
|
|
return array('optimalWidth' => $this->width, 'optimalHeight' => $this->height);
|
|
}
|
|
|
|
switch ($option)
|
|
{
|
|
case 'exact':
|
|
$optimalWidth = $newWidth;
|
|
$optimalHeight= $newHeight;
|
|
break;
|
|
case 'portrait':
|
|
$optimalWidth = $this->getSizeByFixedHeight($newHeight);
|
|
$optimalHeight= $newHeight;
|
|
break;
|
|
case 'landscape':
|
|
$optimalWidth = $newWidth;
|
|
$optimalHeight= $this->getSizeByFixedWidth($newWidth);
|
|
break;
|
|
case 'auto':
|
|
$optionArray = $this->getSizeByAuto($newWidth, $newHeight);
|
|
$optimalWidth = $optionArray['optimalWidth'];
|
|
$optimalHeight = $optionArray['optimalHeight'];
|
|
break;
|
|
case 'crop':
|
|
$optionArray = $this->getOptimalCrop($newWidth, $newHeight);
|
|
$optimalWidth = $optionArray['optimalWidth'];
|
|
$optimalHeight = $optionArray['optimalHeight'];
|
|
break;
|
|
}
|
|
|
|
return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
|
|
}
|
|
|
|
private function getSizeByFixedHeight($newHeight)
|
|
{
|
|
$ratio = $this->width / $this->height;
|
|
$newWidth = $newHeight * $ratio;
|
|
return $newWidth;
|
|
}
|
|
|
|
private function getSizeByFixedWidth($newWidth)
|
|
{
|
|
$ratio = $this->height / $this->width;
|
|
$newHeight = $newWidth * $ratio;
|
|
return $newHeight;
|
|
}
|
|
|
|
private function getSizeByAuto($newWidth, $newHeight)
|
|
{
|
|
if ($this->height < $this->width)
|
|
// *** Image to be resized is wider (landscape)
|
|
{
|
|
$optimalWidth = $newWidth;
|
|
$optimalHeight= $this->getSizeByFixedWidth($newWidth);
|
|
}
|
|
elseif ($this->height > $this->width)
|
|
// *** Image to be resized is taller (portrait)
|
|
{
|
|
$optimalWidth = $this->getSizeByFixedHeight($newHeight);
|
|
$optimalHeight= $newHeight;
|
|
}
|
|
else
|
|
// *** Image to be resizerd is a square
|
|
{
|
|
if ($newHeight < $newWidth) {
|
|
$optimalWidth = $newWidth;
|
|
$optimalHeight= $this->getSizeByFixedWidth($newWidth);
|
|
} else if ($newHeight > $newWidth) {
|
|
$optimalWidth = $this->getSizeByFixedHeight($newHeight);
|
|
$optimalHeight= $newHeight;
|
|
} else {
|
|
// *** Sqaure being resized to a square
|
|
$optimalWidth = $newWidth;
|
|
$optimalHeight= $newHeight;
|
|
}
|
|
}
|
|
|
|
return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
|
|
}
|
|
|
|
private function getOptimalCrop($newWidth, $newHeight)
|
|
{
|
|
|
|
$heightRatio = $this->height / $newHeight;
|
|
$widthRatio = $this->width / $newWidth;
|
|
|
|
if ($heightRatio < $widthRatio) {
|
|
$optimalRatio = $heightRatio;
|
|
} else {
|
|
$optimalRatio = $widthRatio;
|
|
}
|
|
|
|
$optimalHeight = $this->height / $optimalRatio;
|
|
$optimalWidth = $this->width / $optimalRatio;
|
|
|
|
return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
|
|
}
|
|
|
|
private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight)
|
|
{
|
|
// *** Find center - this will be used for the crop
|
|
$cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 );
|
|
$cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 );
|
|
|
|
$crop = $this->imageResized;
|
|
//imagedestroy($this->imageResized);
|
|
|
|
// *** Now crop from center to exact requested size
|
|
$this->imageResized = imagecreatetruecolor($newWidth , $newHeight);
|
|
imagecopyresampled($this->imageResized, $crop , 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight , $newWidth, $newHeight);
|
|
}
|
|
}
|