<?php
namespace CodeIgniter\Images\Handlers;
use CodeIgniter\Images\Exceptions\ImageException;
use CodeIgniter\Images\Image;
use CodeIgniter\Images\ImageHandlerInterface;
abstract class BaseHandler implements ImageHandlerInterface
{
protected $config;
protected $image = null;
protected $width = 0;
protected $height = 0;
protected $filePermissions = 0644;
protected $xAxis = 0;
protected $yAxis = 0;
protected $masterDim = 'auto';
protected $textDefaults = [
'fontPath' => null,
'fontSize' => 16,
'color' => 'ffffff',
'opacity' => 1.0,
'vAlign' => 'bottom',
'hAlign' => 'center',
'vOffset' => 0,
'hOffset' => 0,
'padding' => 0,
'withShadow' => false,
'shadowColor' => '000000',
'shadowOffset' => 3,
];
protected $resource;
public function __construct($config = null)
{
$this->config = $config;
}
public function withFile(string $path)
{
$this->resource = null;
$this->image = new Image($path, true);
$this->image->getProperties(false);
$this->width = $this->image->origWidth;
$this->height = $this->image->origHeight;
return $this;
}
protected function ensureResource()
{
if ($this->resource === null)
{
$path = $this->image->getPathname();
switch ($this->image->imageType)
{
case IMAGETYPE_GIF:
$this->resource = imagecreatefromgif($path);
break;
case IMAGETYPE_JPEG:
$this->resource = imagecreatefromjpeg($path);
break;
case IMAGETYPE_PNG:
$this->resource = imagecreatefrompng($path);
break;
}
}
}
public function getFile()
{
return $this->image;
}
public function getResource()
{
$this->ensureResource();
return $this->resource;
}
public function resize(int $width, int $height, bool $maintainRatio = false, string $masterDim = 'auto')
{
if ($this->image->origWidth === $width && $this->image->origHeight === $height)
{
return $this;
}
$this->width = $width;
$this->height = $height;
if ($maintainRatio)
{
$this->masterDim = $masterDim;
$this->reproportion();
}
return $this->_resize($maintainRatio);
}
public function crop(int $width = null, int $height = null, int $x = null, int $y = null, bool $maintainRatio = false, string $masterDim = 'auto')
{
$this->width = $width;
$this->height = $height;
$this->xAxis = $x;
$this->yAxis = $y;
if ($maintainRatio)
{
$this->masterDim = $masterDim;
$this->reproportion();
}
$result = $this->_crop();
$this->xAxis = null;
$this->yAxis = null;
return $result;
}
public function convert(int $imageType)
{
$this->image->imageType = $imageType;
return $this;
}
public function rotate(float $angle)
{
$degs = [
90,
180,
270,
];
if ($angle === '' || ! in_array($angle, $degs))
{
throw ImageException::forMissingAngle();
}
$angle = (int) $angle;
if ($angle === 90 || $angle === 270)
{
$temp = $this->height;
$this->width = $this->height;
$this->height = $temp;
}
$this->_rotate($angle);
return $this;
}
public function flatten(int $red = 255, int $green = 255, int $blue = 255)
{
$this->width = $this->image->origWidth;
$this->height = $this->image->origHeight;
return $this->_flatten();
}
protected abstract function _flatten(int $red = 255, int $green = 255, int $blue = 255);
protected abstract function _rotate(int $angle);
public function flip(string $dir = 'vertical')
{
$dir = strtolower($dir);
if ($dir !== 'vertical' && $dir !== 'horizontal')
{
throw ImageException::forInvalidDirection($dir);
}
return $this->_flip($dir);
}
protected abstract function _flip(string $direction);
public function text(string $text, array $options = [])
{
$options = array_merge($this->textDefaults, $options);
$options['color'] = trim($options['color'], '# ');
$options['shadowColor'] = trim($options['shadowColor'], '# ');
$this->_text($text, $options);
return $this;
}
protected abstract function _text(string $text, array $options = []);
public function reorient(bool $silent = false)
{
$orientation = $this->getEXIF('Orientation', $silent);
switch ($orientation)
{
case 2:
return $this->flip('horizontal');
break;
case 3:
return $this->rotate(180);
break;
case 4:
return $this->rotate(180)
->flip('horizontal');
break;
case 5:
return $this->rotate(270)
->flip('horizontal');
break;
case 6:
return $this->rotate(270);
break;
case 7:
return $this->rotate(90)
->flip('horizontal');
break;
case 8:
return $this->rotate(90);
break;
default:
return $this;
}
}
public function getEXIF(string $key = null, bool $silent = false)
{
if (! function_exists('exif_read_data'))
{
if ($silent)
{
return null;
}
}
$exif = null; switch ($this->image->imageType)
{
case IMAGETYPE_JPEG:
case IMAGETYPE_TIFF_II:
$exif = exif_read_data($this->image->getPathname());
if (! is_null($key) && is_array($exif))
{
$exif = $exif[$key] ?? false;
}
}
return $exif;
}
public function fit(int $width, int $height = null, string $position = 'center')
{
$origWidth = $this->image->origWidth;
$origHeight = $this->image->origHeight;
list($cropWidth, $cropHeight) = $this->calcAspectRatio($width, $height, $origWidth, $origHeight);
if (is_null($height))
{
$height = ceil(($width / $cropWidth) * $cropHeight);
}
list($x, $y) = $this->calcCropCoords($cropWidth, $cropHeight, $origWidth, $origHeight, $position);
return $this->crop($cropWidth, $cropHeight, $x, $y)
->resize($width, $height);
}
protected function calcAspectRatio($width, $height = null, $origWidth, $origHeight): array
{
if (is_null($height))
{
$height = ($width / $origWidth) * $origHeight;
return [
$width,
(int) $height,
];
}
$xRatio = $width / $origWidth;
$yRatio = $height / $origHeight;
if ($xRatio > $yRatio)
{
return [
$origWidth,
(int) ($origWidth * $height / $width),
];
}
return [
(int) ($origHeight * $width / $height),
$origHeight,
];
}
protected function calcCropCoords($width, $height, $origWidth, $origHeight, $position): array
{
$position = strtolower($position);
$x = $y = 0;
switch ($position)
{
case 'top-left':
$x = 0;
$y = 0;
break;
case 'top':
$x = floor(($origWidth - $width) / 2);
$y = 0;
break;
case 'top-right':
$x = $origWidth - $width;
$y = 0;
break;
case 'left':
$x = 0;
$y = floor(($origHeight - $height) / 2);
break;
case 'center':
$x = floor(($origWidth - $width) / 2);
$y = floor(($origHeight - $height) / 2);
break;
case 'right':
$x = ($origWidth - $width);
$y = floor(($origHeight - $height) / 2);
break;
case 'bottom-left':
$x = 0;
$y = $origHeight - $height;
break;
case 'bottom':
$x = floor(($origWidth - $width) / 2);
$y = $origHeight - $height;
break;
case 'bottom-right':
$x = ($origWidth - $width);
$y = $origHeight - $height;
break;
}
return [
$x,
$y,
];
}
public abstract function getVersion();
public abstract function save(string $target = null, int $quality = 90);
protected abstract function process(string $action);
public function __call(string $name, array $args = [])
{
if (method_exists($this->image, $name))
{
return $this->image->$name(...$args);
}
}
protected function reproportion()
{
if (($this->width === 0 && $this->height === 0) ||
$this->image->origWidth === 0 ||
$this->image->origHeight === 0 ||
( ! ctype_digit((string) $this->width) && ! ctype_digit((string) $this->height)) ||
! ctype_digit((string) $this->image->origWidth) ||
! ctype_digit((string) $this->image->origHeight)
)
{
return;
}
$this->width = (int) $this->width;
$this->height = (int) $this->height;
if ($this->masterDim !== 'width' && $this->masterDim !== 'height')
{
if ($this->width > 0 && $this->height > 0)
{
$this->masterDim = ((($this->image->origHeight / $this->image->origWidth) - ($this->height / $this->width)) < 0) ? 'width' : 'height';
}
else
{
$this->masterDim = ($this->height === 0) ? 'width' : 'height';
}
}
elseif (($this->masterDim === 'width' && $this->width === 0) || ($this->masterDim === 'height' && $this->height === 0)
)
{
return;
}
if ($this->masterDim === 'width')
{
$this->height = (int) ceil($this->width * $this->image->origHeight / $this->image->origWidth);
}
else
{
$this->width = (int) ceil($this->image->origWidth * $this->height / $this->image->origHeight);
}
}
public function getWidth()
{
return ($this->resource !== null) ? $this->_getWidth() : $this->width;
}
public function getHeight()
{
return ($this->resource !== null) ? $this->_getHeight() : $this->height;
}
}