Update Kirby and dependencies
This commit is contained in:
parent
503b339974
commit
399fa20902
439 changed files with 66915 additions and 64442 deletions
|
@ -13,81 +13,81 @@ namespace Kirby\Image;
|
|||
*/
|
||||
class Camera
|
||||
{
|
||||
/**
|
||||
* Make exif data
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $make;
|
||||
/**
|
||||
* Make exif data
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $make;
|
||||
|
||||
/**
|
||||
* Model exif data
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $model;
|
||||
/**
|
||||
* Model exif data
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $exif
|
||||
*/
|
||||
public function __construct(array $exif)
|
||||
{
|
||||
$this->make = $exif['Make'] ?? null;
|
||||
$this->model = $exif['Model'] ?? null;
|
||||
}
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $exif
|
||||
*/
|
||||
public function __construct(array $exif)
|
||||
{
|
||||
$this->make = $exif['Make'] ?? null;
|
||||
$this->model = $exif['Model'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the make of the camera
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function make(): ?string
|
||||
{
|
||||
return $this->make;
|
||||
}
|
||||
/**
|
||||
* Returns the make of the camera
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function make(): ?string
|
||||
{
|
||||
return $this->make;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the camera model
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function model(): ?string
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
/**
|
||||
* Returns the camera model
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function model(): ?string
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object into a nicely readable array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'make' => $this->make,
|
||||
'model' => $this->model
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Converts the object into a nicely readable array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'make' => $this->make,
|
||||
'model' => $this->model
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full make + model name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return trim($this->make . ' ' . $this->model);
|
||||
}
|
||||
/**
|
||||
* Returns the full make + model name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return trim($this->make . ' ' . $this->model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,145 +16,145 @@ use Exception;
|
|||
*/
|
||||
class Darkroom
|
||||
{
|
||||
public static $types = [
|
||||
'gd' => 'Kirby\Image\Darkroom\GdLib',
|
||||
'im' => 'Kirby\Image\Darkroom\ImageMagick'
|
||||
];
|
||||
public static $types = [
|
||||
'gd' => 'Kirby\Image\Darkroom\GdLib',
|
||||
'im' => 'Kirby\Image\Darkroom\ImageMagick'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $settings = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $settings = [];
|
||||
|
||||
/**
|
||||
* Darkroom constructor
|
||||
*
|
||||
* @param array $settings
|
||||
*/
|
||||
public function __construct(array $settings = [])
|
||||
{
|
||||
$this->settings = array_merge($this->defaults(), $settings);
|
||||
}
|
||||
/**
|
||||
* Darkroom constructor
|
||||
*
|
||||
* @param array $settings
|
||||
*/
|
||||
public function __construct(array $settings = [])
|
||||
{
|
||||
$this->settings = array_merge($this->defaults(), $settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Darkroom instance for the given
|
||||
* type/driver
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $settings
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function factory(string $type, array $settings = [])
|
||||
{
|
||||
if (isset(static::$types[$type]) === false) {
|
||||
throw new Exception('Invalid Darkroom type');
|
||||
}
|
||||
/**
|
||||
* Creates a new Darkroom instance for the given
|
||||
* type/driver
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $settings
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function factory(string $type, array $settings = [])
|
||||
{
|
||||
if (isset(static::$types[$type]) === false) {
|
||||
throw new Exception('Invalid Darkroom type');
|
||||
}
|
||||
|
||||
$class = static::$types[$type];
|
||||
return new $class($settings);
|
||||
}
|
||||
$class = static::$types[$type];
|
||||
return new $class($settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default thumb settings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function defaults(): array
|
||||
{
|
||||
return [
|
||||
'autoOrient' => true,
|
||||
'blur' => false,
|
||||
'crop' => false,
|
||||
'format' => null,
|
||||
'grayscale' => false,
|
||||
'height' => null,
|
||||
'quality' => 90,
|
||||
'scaleHeight' => null,
|
||||
'scaleWidth' => null,
|
||||
'width' => null,
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns the default thumb settings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function defaults(): array
|
||||
{
|
||||
return [
|
||||
'autoOrient' => true,
|
||||
'blur' => false,
|
||||
'crop' => false,
|
||||
'format' => null,
|
||||
'grayscale' => false,
|
||||
'height' => null,
|
||||
'quality' => 90,
|
||||
'scaleHeight' => null,
|
||||
'scaleWidth' => null,
|
||||
'width' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes all thumb options
|
||||
*
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
protected function options(array $options = []): array
|
||||
{
|
||||
$options = array_merge($this->settings, $options);
|
||||
/**
|
||||
* Normalizes all thumb options
|
||||
*
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
protected function options(array $options = []): array
|
||||
{
|
||||
$options = array_merge($this->settings, $options);
|
||||
|
||||
// normalize the crop option
|
||||
if ($options['crop'] === true) {
|
||||
$options['crop'] = 'center';
|
||||
}
|
||||
// normalize the crop option
|
||||
if ($options['crop'] === true) {
|
||||
$options['crop'] = 'center';
|
||||
}
|
||||
|
||||
// normalize the blur option
|
||||
if ($options['blur'] === true) {
|
||||
$options['blur'] = 10;
|
||||
}
|
||||
// normalize the blur option
|
||||
if ($options['blur'] === true) {
|
||||
$options['blur'] = 10;
|
||||
}
|
||||
|
||||
// normalize the greyscale option
|
||||
if (isset($options['greyscale']) === true) {
|
||||
$options['grayscale'] = $options['greyscale'];
|
||||
unset($options['greyscale']);
|
||||
}
|
||||
// normalize the greyscale option
|
||||
if (isset($options['greyscale']) === true) {
|
||||
$options['grayscale'] = $options['greyscale'];
|
||||
unset($options['greyscale']);
|
||||
}
|
||||
|
||||
// normalize the bw option
|
||||
if (isset($options['bw']) === true) {
|
||||
$options['grayscale'] = $options['bw'];
|
||||
unset($options['bw']);
|
||||
}
|
||||
// normalize the bw option
|
||||
if (isset($options['bw']) === true) {
|
||||
$options['grayscale'] = $options['bw'];
|
||||
unset($options['bw']);
|
||||
}
|
||||
|
||||
if ($options['quality'] === null) {
|
||||
$options['quality'] = $this->settings['quality'];
|
||||
}
|
||||
if ($options['quality'] === null) {
|
||||
$options['quality'] = $this->settings['quality'];
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the dimensions of the final thumb based
|
||||
* on the given options and returns a full array with
|
||||
* all the final options to be used for the image generator
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function preprocess(string $file, array $options = [])
|
||||
{
|
||||
$options = $this->options($options);
|
||||
$image = new Image($file);
|
||||
/**
|
||||
* Calculates the dimensions of the final thumb based
|
||||
* on the given options and returns a full array with
|
||||
* all the final options to be used for the image generator
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function preprocess(string $file, array $options = [])
|
||||
{
|
||||
$options = $this->options($options);
|
||||
$image = new Image($file);
|
||||
|
||||
$dimensions = $image->dimensions();
|
||||
$thumbDimensions = $dimensions->thumb($options);
|
||||
$dimensions = $image->dimensions();
|
||||
$thumbDimensions = $dimensions->thumb($options);
|
||||
|
||||
$sourceWidth = $image->width();
|
||||
$sourceHeight = $image->height();
|
||||
$sourceWidth = $image->width();
|
||||
$sourceHeight = $image->height();
|
||||
|
||||
$options['width'] = $thumbDimensions->width();
|
||||
$options['height'] = $thumbDimensions->height();
|
||||
$options['width'] = $thumbDimensions->width();
|
||||
$options['height'] = $thumbDimensions->height();
|
||||
|
||||
// scale ratio compared to the source dimensions
|
||||
$options['scaleWidth'] = $sourceWidth ? $options['width'] / $sourceWidth : null;
|
||||
$options['scaleHeight'] = $sourceHeight ? $options['height'] / $sourceHeight : null;
|
||||
// scale ratio compared to the source dimensions
|
||||
$options['scaleWidth'] = $sourceWidth ? $options['width'] / $sourceWidth : null;
|
||||
$options['scaleHeight'] = $sourceHeight ? $options['height'] / $sourceHeight : null;
|
||||
|
||||
return $options;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method must be replaced by the driver to run the
|
||||
* actual image processing job.
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function process(string $file, array $options = []): array
|
||||
{
|
||||
return $this->preprocess($file, $options);
|
||||
}
|
||||
/**
|
||||
* This method must be replaced by the driver to run the
|
||||
* actual image processing job.
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function process(string $file, array $options = []): array
|
||||
{
|
||||
return $this->preprocess($file, $options);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,108 +17,108 @@ use Kirby\Image\Darkroom;
|
|||
*/
|
||||
class GdLib extends Darkroom
|
||||
{
|
||||
/**
|
||||
* Processes the image with the SimpleImage library
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function process(string $file, array $options = []): array
|
||||
{
|
||||
$options = $this->preprocess($file, $options);
|
||||
$mime = $this->mime($options);
|
||||
/**
|
||||
* Processes the image with the SimpleImage library
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function process(string $file, array $options = []): array
|
||||
{
|
||||
$options = $this->preprocess($file, $options);
|
||||
$mime = $this->mime($options);
|
||||
|
||||
$image = new SimpleImage();
|
||||
$image->fromFile($file);
|
||||
$image = new SimpleImage();
|
||||
$image->fromFile($file);
|
||||
|
||||
$image = $this->resize($image, $options);
|
||||
$image = $this->autoOrient($image, $options);
|
||||
$image = $this->blur($image, $options);
|
||||
$image = $this->grayscale($image, $options);
|
||||
$image = $this->resize($image, $options);
|
||||
$image = $this->autoOrient($image, $options);
|
||||
$image = $this->blur($image, $options);
|
||||
$image = $this->grayscale($image, $options);
|
||||
|
||||
$image->toFile($file, $mime, $options['quality']);
|
||||
$image->toFile($file, $mime, $options['quality']);
|
||||
|
||||
return $options;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the autoOrient option in SimpleImage
|
||||
* unless this is deactivated
|
||||
*
|
||||
* @param \claviska\SimpleImage $image
|
||||
* @param $options
|
||||
* @return \claviska\SimpleImage
|
||||
*/
|
||||
protected function autoOrient(SimpleImage $image, $options)
|
||||
{
|
||||
if ($options['autoOrient'] === false) {
|
||||
return $image;
|
||||
}
|
||||
/**
|
||||
* Activates the autoOrient option in SimpleImage
|
||||
* unless this is deactivated
|
||||
*
|
||||
* @param \claviska\SimpleImage $image
|
||||
* @param $options
|
||||
* @return \claviska\SimpleImage
|
||||
*/
|
||||
protected function autoOrient(SimpleImage $image, $options)
|
||||
{
|
||||
if ($options['autoOrient'] === false) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
return $image->autoOrient();
|
||||
}
|
||||
return $image->autoOrient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around SimpleImage's resize and crop methods
|
||||
*
|
||||
* @param \claviska\SimpleImage $image
|
||||
* @param array $options
|
||||
* @return \claviska\SimpleImage
|
||||
*/
|
||||
protected function resize(SimpleImage $image, array $options)
|
||||
{
|
||||
if ($options['crop'] === false) {
|
||||
return $image->resize($options['width'], $options['height']);
|
||||
}
|
||||
/**
|
||||
* Wrapper around SimpleImage's resize and crop methods
|
||||
*
|
||||
* @param \claviska\SimpleImage $image
|
||||
* @param array $options
|
||||
* @return \claviska\SimpleImage
|
||||
*/
|
||||
protected function resize(SimpleImage $image, array $options)
|
||||
{
|
||||
if ($options['crop'] === false) {
|
||||
return $image->resize($options['width'], $options['height']);
|
||||
}
|
||||
|
||||
return $image->thumbnail($options['width'], $options['height'] ?? $options['width'], $options['crop']);
|
||||
}
|
||||
return $image->thumbnail($options['width'], $options['height'] ?? $options['width'], $options['crop']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the correct blur settings for SimpleImage
|
||||
*
|
||||
* @param \claviska\SimpleImage $image
|
||||
* @param array $options
|
||||
* @return \claviska\SimpleImage
|
||||
*/
|
||||
protected function blur(SimpleImage $image, array $options)
|
||||
{
|
||||
if ($options['blur'] === false) {
|
||||
return $image;
|
||||
}
|
||||
/**
|
||||
* Applies the correct blur settings for SimpleImage
|
||||
*
|
||||
* @param \claviska\SimpleImage $image
|
||||
* @param array $options
|
||||
* @return \claviska\SimpleImage
|
||||
*/
|
||||
protected function blur(SimpleImage $image, array $options)
|
||||
{
|
||||
if ($options['blur'] === false) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
return $image->blur('gaussian', (int)$options['blur']);
|
||||
}
|
||||
return $image->blur('gaussian', (int)$options['blur']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies grayscale conversion if activated in the options.
|
||||
*
|
||||
* @param \claviska\SimpleImage $image
|
||||
* @param array $options
|
||||
* @return \claviska\SimpleImage
|
||||
*/
|
||||
protected function grayscale(SimpleImage $image, array $options)
|
||||
{
|
||||
if ($options['grayscale'] === false) {
|
||||
return $image;
|
||||
}
|
||||
/**
|
||||
* Applies grayscale conversion if activated in the options.
|
||||
*
|
||||
* @param \claviska\SimpleImage $image
|
||||
* @param array $options
|
||||
* @return \claviska\SimpleImage
|
||||
*/
|
||||
protected function grayscale(SimpleImage $image, array $options)
|
||||
{
|
||||
if ($options['grayscale'] === false) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
return $image->desaturate();
|
||||
}
|
||||
return $image->desaturate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns mime type based on `format` option
|
||||
*
|
||||
* @param array $options
|
||||
* @return string|null
|
||||
*/
|
||||
protected function mime(array $options): ?string
|
||||
{
|
||||
if ($options['format'] === null) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Returns mime type based on `format` option
|
||||
*
|
||||
* @param array $options
|
||||
* @return string|null
|
||||
*/
|
||||
protected function mime(array $options): ?string
|
||||
{
|
||||
if ($options['format'] === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Mime::fromExtension($options['format']);
|
||||
}
|
||||
return Mime::fromExtension($options['format']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,238 +17,238 @@ use Kirby\Image\Darkroom;
|
|||
*/
|
||||
class ImageMagick extends Darkroom
|
||||
{
|
||||
/**
|
||||
* Activates imagemagick's auto-orient feature unless
|
||||
* it is deactivated via the options
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function autoOrient(string $file, array $options)
|
||||
{
|
||||
if ($options['autoOrient'] === true) {
|
||||
return '-auto-orient';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Activates imagemagick's auto-orient feature unless
|
||||
* it is deactivated via the options
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function autoOrient(string $file, array $options)
|
||||
{
|
||||
if ($options['autoOrient'] === true) {
|
||||
return '-auto-orient';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the blur settings
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function blur(string $file, array $options)
|
||||
{
|
||||
if ($options['blur'] !== false) {
|
||||
return '-blur ' . escapeshellarg('0x' . $options['blur']);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Applies the blur settings
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function blur(string $file, array $options)
|
||||
{
|
||||
if ($options['blur'] !== false) {
|
||||
return '-blur ' . escapeshellarg('0x' . $options['blur']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep animated gifs
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function coalesce(string $file, array $options)
|
||||
{
|
||||
if (F::extension($file) === 'gif') {
|
||||
return '-coalesce';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Keep animated gifs
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function coalesce(string $file, array $options)
|
||||
{
|
||||
if (F::extension($file) === 'gif') {
|
||||
return '-coalesce';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the convert command with the right path to the binary file
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function convert(string $file, array $options): string
|
||||
{
|
||||
$command = escapeshellarg($options['bin']);
|
||||
/**
|
||||
* Creates the convert command with the right path to the binary file
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function convert(string $file, array $options): string
|
||||
{
|
||||
$command = escapeshellarg($options['bin']);
|
||||
|
||||
// limit to single-threading to keep CPU usage sane
|
||||
$command .= ' -limit thread 1';
|
||||
// limit to single-threading to keep CPU usage sane
|
||||
$command .= ' -limit thread 1';
|
||||
|
||||
// add JPEG size hint to optimize CPU and memory usage
|
||||
if (F::mime($file) === 'image/jpeg') {
|
||||
// add hint only when downscaling
|
||||
if ($options['scaleWidth'] < 1 && $options['scaleHeight'] < 1) {
|
||||
$command .= ' -define ' . escapeshellarg(sprintf('jpeg:size=%dx%d', $options['width'], $options['height']));
|
||||
}
|
||||
}
|
||||
// add JPEG size hint to optimize CPU and memory usage
|
||||
if (F::mime($file) === 'image/jpeg') {
|
||||
// add hint only when downscaling
|
||||
if ($options['scaleWidth'] < 1 && $options['scaleHeight'] < 1) {
|
||||
$command .= ' -define ' . escapeshellarg(sprintf('jpeg:size=%dx%d', $options['width'], $options['height']));
|
||||
}
|
||||
}
|
||||
|
||||
// append input file
|
||||
return $command . ' ' . escapeshellarg($file);
|
||||
}
|
||||
// append input file
|
||||
return $command . ' ' . escapeshellarg($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns additional default parameters for imagemagick
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function defaults(): array
|
||||
{
|
||||
return parent::defaults() + [
|
||||
'bin' => 'convert',
|
||||
'interlace' => false,
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns additional default parameters for imagemagick
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function defaults(): array
|
||||
{
|
||||
return parent::defaults() + [
|
||||
'bin' => 'convert',
|
||||
'interlace' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the correct settings for grayscale images
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function grayscale(string $file, array $options)
|
||||
{
|
||||
if ($options['grayscale'] === true) {
|
||||
return '-colorspace gray';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Applies the correct settings for grayscale images
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function grayscale(string $file, array $options)
|
||||
{
|
||||
if ($options['grayscale'] === true) {
|
||||
return '-colorspace gray';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the correct settings for interlaced JPEGs if
|
||||
* activated via options
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function interlace(string $file, array $options)
|
||||
{
|
||||
if ($options['interlace'] === true) {
|
||||
return '-interlace line';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Applies the correct settings for interlaced JPEGs if
|
||||
* activated via options
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function interlace(string $file, array $options)
|
||||
{
|
||||
if ($options['interlace'] === true) {
|
||||
return '-interlace line';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and runs the full imagemagick command
|
||||
* to process the image
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function process(string $file, array $options = []): array
|
||||
{
|
||||
$options = $this->preprocess($file, $options);
|
||||
$command = [];
|
||||
/**
|
||||
* Creates and runs the full imagemagick command
|
||||
* to process the image
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function process(string $file, array $options = []): array
|
||||
{
|
||||
$options = $this->preprocess($file, $options);
|
||||
$command = [];
|
||||
|
||||
$command[] = $this->convert($file, $options);
|
||||
$command[] = $this->strip($file, $options);
|
||||
$command[] = $this->interlace($file, $options);
|
||||
$command[] = $this->coalesce($file, $options);
|
||||
$command[] = $this->grayscale($file, $options);
|
||||
$command[] = $this->autoOrient($file, $options);
|
||||
$command[] = $this->resize($file, $options);
|
||||
$command[] = $this->quality($file, $options);
|
||||
$command[] = $this->blur($file, $options);
|
||||
$command[] = $this->save($file, $options);
|
||||
$command[] = $this->convert($file, $options);
|
||||
$command[] = $this->strip($file, $options);
|
||||
$command[] = $this->interlace($file, $options);
|
||||
$command[] = $this->coalesce($file, $options);
|
||||
$command[] = $this->grayscale($file, $options);
|
||||
$command[] = $this->autoOrient($file, $options);
|
||||
$command[] = $this->resize($file, $options);
|
||||
$command[] = $this->quality($file, $options);
|
||||
$command[] = $this->blur($file, $options);
|
||||
$command[] = $this->save($file, $options);
|
||||
|
||||
// remove all null values and join the parts
|
||||
$command = implode(' ', array_filter($command));
|
||||
// remove all null values and join the parts
|
||||
$command = implode(' ', array_filter($command));
|
||||
|
||||
// try to execute the command
|
||||
exec($command, $output, $return);
|
||||
// try to execute the command
|
||||
exec($command, $output, $return);
|
||||
|
||||
// log broken commands
|
||||
if ($return !== 0) {
|
||||
throw new Exception('The imagemagick convert command could not be executed: ' . $command);
|
||||
}
|
||||
// log broken commands
|
||||
if ($return !== 0) {
|
||||
throw new Exception('The imagemagick convert command could not be executed: ' . $command);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the correct JPEG compression quality settings
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function quality(string $file, array $options): string
|
||||
{
|
||||
return '-quality ' . escapeshellarg($options['quality']);
|
||||
}
|
||||
/**
|
||||
* Applies the correct JPEG compression quality settings
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function quality(string $file, array $options): string
|
||||
{
|
||||
return '-quality ' . escapeshellarg($options['quality']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the correct options to crop or resize the image
|
||||
* and translates the crop positions for imagemagick
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function resize(string $file, array $options): string
|
||||
{
|
||||
// simple resize
|
||||
if ($options['crop'] === false) {
|
||||
return '-thumbnail ' . escapeshellarg(sprintf('%sx%s!', $options['width'], $options['height']));
|
||||
}
|
||||
/**
|
||||
* Creates the correct options to crop or resize the image
|
||||
* and translates the crop positions for imagemagick
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function resize(string $file, array $options): string
|
||||
{
|
||||
// simple resize
|
||||
if ($options['crop'] === false) {
|
||||
return '-thumbnail ' . escapeshellarg(sprintf('%sx%s!', $options['width'], $options['height']));
|
||||
}
|
||||
|
||||
$gravities = [
|
||||
'top left' => 'NorthWest',
|
||||
'top' => 'North',
|
||||
'top right' => 'NorthEast',
|
||||
'left' => 'West',
|
||||
'center' => 'Center',
|
||||
'right' => 'East',
|
||||
'bottom left' => 'SouthWest',
|
||||
'bottom' => 'South',
|
||||
'bottom right' => 'SouthEast'
|
||||
];
|
||||
$gravities = [
|
||||
'top left' => 'NorthWest',
|
||||
'top' => 'North',
|
||||
'top right' => 'NorthEast',
|
||||
'left' => 'West',
|
||||
'center' => 'Center',
|
||||
'right' => 'East',
|
||||
'bottom left' => 'SouthWest',
|
||||
'bottom' => 'South',
|
||||
'bottom right' => 'SouthEast'
|
||||
];
|
||||
|
||||
// translate the gravity option into something imagemagick understands
|
||||
$gravity = $gravities[$options['crop']] ?? 'Center';
|
||||
// translate the gravity option into something imagemagick understands
|
||||
$gravity = $gravities[$options['crop']] ?? 'Center';
|
||||
|
||||
$command = '-thumbnail ' . escapeshellarg(sprintf('%sx%s^', $options['width'], $options['height']));
|
||||
$command .= ' -gravity ' . escapeshellarg($gravity);
|
||||
$command .= ' -crop ' . escapeshellarg(sprintf('%sx%s+0+0', $options['width'], $options['height']));
|
||||
$command = '-thumbnail ' . escapeshellarg(sprintf('%sx%s^', $options['width'], $options['height']));
|
||||
$command .= ' -gravity ' . escapeshellarg($gravity);
|
||||
$command .= ' -crop ' . escapeshellarg(sprintf('%sx%s+0+0', $options['width'], $options['height']));
|
||||
|
||||
return $command;
|
||||
}
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the option for the output file
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function save(string $file, array $options): string
|
||||
{
|
||||
if ($options['format'] !== null) {
|
||||
$file = pathinfo($file, PATHINFO_DIRNAME) . '/' . pathinfo($file, PATHINFO_FILENAME) . '.' . $options['format'];
|
||||
}
|
||||
/**
|
||||
* Creates the option for the output file
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function save(string $file, array $options): string
|
||||
{
|
||||
if ($options['format'] !== null) {
|
||||
$file = pathinfo($file, PATHINFO_DIRNAME) . '/' . pathinfo($file, PATHINFO_FILENAME) . '.' . $options['format'];
|
||||
}
|
||||
|
||||
return escapeshellarg($file);
|
||||
}
|
||||
return escapeshellarg($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all metadata from the image
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function strip(string $file, array $options): string
|
||||
{
|
||||
if (F::extension($file) === 'png') {
|
||||
// ImageMagick does not support keeping ICC profiles while
|
||||
// stripping other privacy- and security-related information,
|
||||
// such as GPS data; so discard all color profiles for PNG files
|
||||
// (tested with ImageMagick 7.0.11-14 Q16 x86_64 2021-05-31)
|
||||
return '-strip';
|
||||
}
|
||||
/**
|
||||
* Removes all metadata from the image
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function strip(string $file, array $options): string
|
||||
{
|
||||
if (F::extension($file) === 'png') {
|
||||
// ImageMagick does not support keeping ICC profiles while
|
||||
// stripping other privacy- and security-related information,
|
||||
// such as GPS data; so discard all color profiles for PNG files
|
||||
// (tested with ImageMagick 7.0.11-14 Q16 x86_64 2021-05-31)
|
||||
return '-strip';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,415 +16,415 @@ namespace Kirby\Image;
|
|||
*/
|
||||
class Dimensions
|
||||
{
|
||||
/**
|
||||
* the height of the parent object
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $height = 0;
|
||||
/**
|
||||
* the height of the parent object
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $height = 0;
|
||||
|
||||
/**
|
||||
* the width of the parent object
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $width = 0;
|
||||
/**
|
||||
* the width of the parent object
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $width = 0;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
*/
|
||||
public function __construct(int $width, int $height)
|
||||
{
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
}
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
*/
|
||||
public function __construct(int $width, int $height)
|
||||
{
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Echos the dimensions as width × height
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->width . ' × ' . $this->height;
|
||||
}
|
||||
/**
|
||||
* Echos the dimensions as width × height
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->width . ' × ' . $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crops the dimensions by width and height
|
||||
*
|
||||
* @param int $width
|
||||
* @param int|null $height
|
||||
* @return $this
|
||||
*/
|
||||
public function crop(int $width, int $height = null)
|
||||
{
|
||||
$this->width = $width;
|
||||
$this->height = $width;
|
||||
/**
|
||||
* Crops the dimensions by width and height
|
||||
*
|
||||
* @param int $width
|
||||
* @param int|null $height
|
||||
* @return $this
|
||||
*/
|
||||
public function crop(int $width, int $height = null)
|
||||
{
|
||||
$this->width = $width;
|
||||
$this->height = $width;
|
||||
|
||||
if ($height !== 0 && $height !== null) {
|
||||
$this->height = $height;
|
||||
}
|
||||
if ($height !== 0 && $height !== null) {
|
||||
$this->height = $height;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function height()
|
||||
{
|
||||
return $this->height;
|
||||
}
|
||||
/**
|
||||
* Returns the height
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function height()
|
||||
{
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates the width and height to fit into the given box.
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* $dimensions = new Dimensions(1200, 768);
|
||||
* $dimensions->fit(500);
|
||||
*
|
||||
* echo $dimensions->width();
|
||||
* // output: 500
|
||||
*
|
||||
* echo $dimensions->height();
|
||||
* // output: 320
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param int $box the max width and/or height
|
||||
* @param bool $force If true, the dimensions will be
|
||||
* upscaled to fit the box if smaller
|
||||
* @return $this object with recalculated dimensions
|
||||
*/
|
||||
public function fit(int $box, bool $force = false)
|
||||
{
|
||||
if ($this->width === 0 || $this->height === 0) {
|
||||
$this->width = $box;
|
||||
$this->height = $box;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Recalculates the width and height to fit into the given box.
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* $dimensions = new Dimensions(1200, 768);
|
||||
* $dimensions->fit(500);
|
||||
*
|
||||
* echo $dimensions->width();
|
||||
* // output: 500
|
||||
*
|
||||
* echo $dimensions->height();
|
||||
* // output: 320
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param int $box the max width and/or height
|
||||
* @param bool $force If true, the dimensions will be
|
||||
* upscaled to fit the box if smaller
|
||||
* @return $this object with recalculated dimensions
|
||||
*/
|
||||
public function fit(int $box, bool $force = false)
|
||||
{
|
||||
if ($this->width === 0 || $this->height === 0) {
|
||||
$this->width = $box;
|
||||
$this->height = $box;
|
||||
return $this;
|
||||
}
|
||||
|
||||
$ratio = $this->ratio();
|
||||
$ratio = $this->ratio();
|
||||
|
||||
if ($this->width > $this->height) {
|
||||
// wider than tall
|
||||
if ($this->width > $box || $force === true) {
|
||||
$this->width = $box;
|
||||
}
|
||||
$this->height = (int)round($this->width / $ratio);
|
||||
} elseif ($this->height > $this->width) {
|
||||
// taller than wide
|
||||
if ($this->height > $box || $force === true) {
|
||||
$this->height = $box;
|
||||
}
|
||||
$this->width = (int)round($this->height * $ratio);
|
||||
} elseif ($this->width > $box) {
|
||||
// width = height but bigger than box
|
||||
$this->width = $box;
|
||||
$this->height = $box;
|
||||
}
|
||||
if ($this->width > $this->height) {
|
||||
// wider than tall
|
||||
if ($this->width > $box || $force === true) {
|
||||
$this->width = $box;
|
||||
}
|
||||
$this->height = (int)round($this->width / $ratio);
|
||||
} elseif ($this->height > $this->width) {
|
||||
// taller than wide
|
||||
if ($this->height > $box || $force === true) {
|
||||
$this->height = $box;
|
||||
}
|
||||
$this->width = (int)round($this->height * $ratio);
|
||||
} elseif ($this->width > $box) {
|
||||
// width = height but bigger than box
|
||||
$this->width = $box;
|
||||
$this->height = $box;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates the width and height to fit the given height
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* $dimensions = new Dimensions(1200, 768);
|
||||
* $dimensions->fitHeight(500);
|
||||
*
|
||||
* echo $dimensions->width();
|
||||
* // output: 781
|
||||
*
|
||||
* echo $dimensions->height();
|
||||
* // output: 500
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param int|null $fit the max height
|
||||
* @param bool $force If true, the dimensions will be
|
||||
* upscaled to fit the box if smaller
|
||||
* @return $this object with recalculated dimensions
|
||||
*/
|
||||
public function fitHeight(int $fit = null, bool $force = false)
|
||||
{
|
||||
return $this->fitSize('height', $fit, $force);
|
||||
}
|
||||
/**
|
||||
* Recalculates the width and height to fit the given height
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* $dimensions = new Dimensions(1200, 768);
|
||||
* $dimensions->fitHeight(500);
|
||||
*
|
||||
* echo $dimensions->width();
|
||||
* // output: 781
|
||||
*
|
||||
* echo $dimensions->height();
|
||||
* // output: 500
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param int|null $fit the max height
|
||||
* @param bool $force If true, the dimensions will be
|
||||
* upscaled to fit the box if smaller
|
||||
* @return $this object with recalculated dimensions
|
||||
*/
|
||||
public function fitHeight(int $fit = null, bool $force = false)
|
||||
{
|
||||
return $this->fitSize('height', $fit, $force);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for fitWidth and fitHeight methods
|
||||
*
|
||||
* @param string $ref reference (width or height)
|
||||
* @param int|null $fit the max width
|
||||
* @param bool $force If true, the dimensions will be
|
||||
* upscaled to fit the box if smaller
|
||||
* @return $this object with recalculated dimensions
|
||||
*/
|
||||
protected function fitSize(string $ref, int $fit = null, bool $force = false)
|
||||
{
|
||||
if ($fit === 0 || $fit === null) {
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Helper for fitWidth and fitHeight methods
|
||||
*
|
||||
* @param string $ref reference (width or height)
|
||||
* @param int|null $fit the max width
|
||||
* @param bool $force If true, the dimensions will be
|
||||
* upscaled to fit the box if smaller
|
||||
* @return $this object with recalculated dimensions
|
||||
*/
|
||||
protected function fitSize(string $ref, int $fit = null, bool $force = false)
|
||||
{
|
||||
if ($fit === 0 || $fit === null) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->$ref <= $fit && !$force) {
|
||||
return $this;
|
||||
}
|
||||
if ($this->$ref <= $fit && !$force) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$ratio = $this->ratio();
|
||||
$mode = $ref === 'width';
|
||||
$this->width = $mode ? $fit : (int)round($fit * $ratio);
|
||||
$this->height = !$mode ? $fit : (int)round($fit / $ratio);
|
||||
$ratio = $this->ratio();
|
||||
$mode = $ref === 'width';
|
||||
$this->width = $mode ? $fit : (int)round($fit * $ratio);
|
||||
$this->height = !$mode ? $fit : (int)round($fit / $ratio);
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates the width and height to fit the given width
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* $dimensions = new Dimensions(1200, 768);
|
||||
* $dimensions->fitWidth(500);
|
||||
*
|
||||
* echo $dimensions->width();
|
||||
* // output: 500
|
||||
*
|
||||
* echo $dimensions->height();
|
||||
* // output: 320
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param int|null $fit the max width
|
||||
* @param bool $force If true, the dimensions will be
|
||||
* upscaled to fit the box if smaller
|
||||
* @return $this object with recalculated dimensions
|
||||
*/
|
||||
public function fitWidth(int $fit = null, bool $force = false)
|
||||
{
|
||||
return $this->fitSize('width', $fit, $force);
|
||||
}
|
||||
/**
|
||||
* Recalculates the width and height to fit the given width
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* $dimensions = new Dimensions(1200, 768);
|
||||
* $dimensions->fitWidth(500);
|
||||
*
|
||||
* echo $dimensions->width();
|
||||
* // output: 500
|
||||
*
|
||||
* echo $dimensions->height();
|
||||
* // output: 320
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param int|null $fit the max width
|
||||
* @param bool $force If true, the dimensions will be
|
||||
* upscaled to fit the box if smaller
|
||||
* @return $this object with recalculated dimensions
|
||||
*/
|
||||
public function fitWidth(int $fit = null, bool $force = false)
|
||||
{
|
||||
return $this->fitSize('width', $fit, $force);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates the dimensions by the width and height
|
||||
*
|
||||
* @param int|null $width the max height
|
||||
* @param int|null $height the max width
|
||||
* @param bool $force
|
||||
* @return $this
|
||||
*/
|
||||
public function fitWidthAndHeight(int $width = null, int $height = null, bool $force = false)
|
||||
{
|
||||
if ($this->width > $this->height) {
|
||||
$this->fitWidth($width, $force);
|
||||
/**
|
||||
* Recalculates the dimensions by the width and height
|
||||
*
|
||||
* @param int|null $width the max height
|
||||
* @param int|null $height the max width
|
||||
* @param bool $force
|
||||
* @return $this
|
||||
*/
|
||||
public function fitWidthAndHeight(int $width = null, int $height = null, bool $force = false)
|
||||
{
|
||||
if ($this->width > $this->height) {
|
||||
$this->fitWidth($width, $force);
|
||||
|
||||
// do another check for the max height
|
||||
if ($this->height > $height) {
|
||||
$this->fitHeight($height);
|
||||
}
|
||||
} else {
|
||||
$this->fitHeight($height, $force);
|
||||
// do another check for the max height
|
||||
if ($this->height > $height) {
|
||||
$this->fitHeight($height);
|
||||
}
|
||||
} else {
|
||||
$this->fitHeight($height, $force);
|
||||
|
||||
// do another check for the max width
|
||||
if ($this->width > $width) {
|
||||
$this->fitWidth($width);
|
||||
}
|
||||
}
|
||||
// do another check for the max width
|
||||
if ($this->width > $width) {
|
||||
$this->fitWidth($width);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the dimensions for an image file
|
||||
*
|
||||
* @param string $root
|
||||
* @return static
|
||||
*/
|
||||
public static function forImage(string $root)
|
||||
{
|
||||
if (file_exists($root) === false) {
|
||||
return new static(0, 0);
|
||||
}
|
||||
/**
|
||||
* Detect the dimensions for an image file
|
||||
*
|
||||
* @param string $root
|
||||
* @return static
|
||||
*/
|
||||
public static function forImage(string $root)
|
||||
{
|
||||
if (file_exists($root) === false) {
|
||||
return new static(0, 0);
|
||||
}
|
||||
|
||||
$size = getimagesize($root);
|
||||
return new static($size[0] ?? 0, $size[1] ?? 1);
|
||||
}
|
||||
$size = getimagesize($root);
|
||||
return new static($size[0] ?? 0, $size[1] ?? 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the dimensions for a svg file
|
||||
*
|
||||
* @param string $root
|
||||
* @return static
|
||||
*/
|
||||
public static function forSvg(string $root)
|
||||
{
|
||||
// avoid xml errors
|
||||
libxml_use_internal_errors(true);
|
||||
/**
|
||||
* Detect the dimensions for a svg file
|
||||
*
|
||||
* @param string $root
|
||||
* @return static
|
||||
*/
|
||||
public static function forSvg(string $root)
|
||||
{
|
||||
// avoid xml errors
|
||||
libxml_use_internal_errors(true);
|
||||
|
||||
$content = file_get_contents($root);
|
||||
$height = 0;
|
||||
$width = 0;
|
||||
$xml = simplexml_load_string($content);
|
||||
$content = file_get_contents($root);
|
||||
$height = 0;
|
||||
$width = 0;
|
||||
$xml = simplexml_load_string($content);
|
||||
|
||||
if ($xml !== false) {
|
||||
$attr = $xml->attributes();
|
||||
$width = (int)($attr->width);
|
||||
$height = (int)($attr->height);
|
||||
if (($width === 0 || $height === 0) && empty($attr->viewBox) === false) {
|
||||
$box = explode(' ', $attr->viewBox);
|
||||
$width = (int)($box[2] ?? 0);
|
||||
$height = (int)($box[3] ?? 0);
|
||||
}
|
||||
}
|
||||
if ($xml !== false) {
|
||||
$attr = $xml->attributes();
|
||||
$width = (int)($attr->width);
|
||||
$height = (int)($attr->height);
|
||||
if (($width === 0 || $height === 0) && empty($attr->viewBox) === false) {
|
||||
$box = explode(' ', $attr->viewBox);
|
||||
$width = (int)($box[2] ?? 0);
|
||||
$height = (int)($box[3] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
return new static($width, $height);
|
||||
}
|
||||
return new static($width, $height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the dimensions are landscape
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function landscape(): bool
|
||||
{
|
||||
return $this->width > $this->height;
|
||||
}
|
||||
/**
|
||||
* Checks if the dimensions are landscape
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function landscape(): bool
|
||||
{
|
||||
return $this->width > $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the orientation
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function orientation()
|
||||
{
|
||||
if (!$this->ratio()) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Returns a string representation of the orientation
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function orientation()
|
||||
{
|
||||
if (!$this->ratio()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->portrait()) {
|
||||
return 'portrait';
|
||||
}
|
||||
if ($this->portrait()) {
|
||||
return 'portrait';
|
||||
}
|
||||
|
||||
if ($this->landscape()) {
|
||||
return 'landscape';
|
||||
}
|
||||
if ($this->landscape()) {
|
||||
return 'landscape';
|
||||
}
|
||||
|
||||
return 'square';
|
||||
}
|
||||
return 'square';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the dimensions are portrait
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function portrait(): bool
|
||||
{
|
||||
return $this->height > $this->width;
|
||||
}
|
||||
/**
|
||||
* Checks if the dimensions are portrait
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function portrait(): bool
|
||||
{
|
||||
return $this->height > $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and returns the ratio
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* $dimensions = new Dimensions(1200, 768);
|
||||
* echo $dimensions->ratio();
|
||||
* // output: 1.5625
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function ratio(): float
|
||||
{
|
||||
if ($this->width !== 0 && $this->height !== 0) {
|
||||
return $this->width / $this->height;
|
||||
}
|
||||
/**
|
||||
* Calculates and returns the ratio
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* $dimensions = new Dimensions(1200, 768);
|
||||
* echo $dimensions->ratio();
|
||||
* // output: 1.5625
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function ratio(): float
|
||||
{
|
||||
if ($this->width !== 0 && $this->height !== 0) {
|
||||
return $this->width / $this->height;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $width
|
||||
* @param int|null $height
|
||||
* @param bool $force
|
||||
* @return $this
|
||||
*/
|
||||
public function resize(int $width = null, int $height = null, bool $force = false)
|
||||
{
|
||||
return $this->fitWidthAndHeight($width, $height, $force);
|
||||
}
|
||||
/**
|
||||
* @param int|null $width
|
||||
* @param int|null $height
|
||||
* @param bool $force
|
||||
* @return $this
|
||||
*/
|
||||
public function resize(int $width = null, int $height = null, bool $force = false)
|
||||
{
|
||||
return $this->fitWidthAndHeight($width, $height, $force);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the dimensions are square
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function square(): bool
|
||||
{
|
||||
return $this->width === $this->height;
|
||||
}
|
||||
/**
|
||||
* Checks if the dimensions are square
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function square(): bool
|
||||
{
|
||||
return $this->width === $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize and crop
|
||||
*
|
||||
* @param array $options
|
||||
* @return $this
|
||||
*/
|
||||
public function thumb(array $options = [])
|
||||
{
|
||||
$width = $options['width'] ?? null;
|
||||
$height = $options['height'] ?? null;
|
||||
$crop = $options['crop'] ?? false;
|
||||
$method = $crop !== false ? 'crop' : 'resize';
|
||||
/**
|
||||
* Resize and crop
|
||||
*
|
||||
* @param array $options
|
||||
* @return $this
|
||||
*/
|
||||
public function thumb(array $options = [])
|
||||
{
|
||||
$width = $options['width'] ?? null;
|
||||
$height = $options['height'] ?? null;
|
||||
$crop = $options['crop'] ?? false;
|
||||
$method = $crop !== false ? 'crop' : 'resize';
|
||||
|
||||
if ($width === null && $height === null) {
|
||||
return $this;
|
||||
}
|
||||
if ($width === null && $height === null) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->$method($width, $height);
|
||||
}
|
||||
return $this->$method($width, $height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the dimensions object
|
||||
* to a plain PHP array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'width' => $this->width(),
|
||||
'height' => $this->height(),
|
||||
'ratio' => $this->ratio(),
|
||||
'orientation' => $this->orientation(),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Converts the dimensions object
|
||||
* to a plain PHP array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'width' => $this->width(),
|
||||
'height' => $this->height(),
|
||||
'ratio' => $this->ratio(),
|
||||
'orientation' => $this->orientation(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function width(): int
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
/**
|
||||
* Returns the width
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function width(): int
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,284 +15,284 @@ use Kirby\Toolkit\V;
|
|||
*/
|
||||
class Exif
|
||||
{
|
||||
/**
|
||||
* the parent image object
|
||||
* @var \Kirby\Image\Image
|
||||
*/
|
||||
protected $image;
|
||||
/**
|
||||
* the parent image object
|
||||
* @var \Kirby\Image\Image
|
||||
*/
|
||||
protected $image;
|
||||
|
||||
/**
|
||||
* the raw exif array
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
/**
|
||||
* the raw exif array
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* the camera object with model and make
|
||||
* @var Camera
|
||||
*/
|
||||
protected $camera;
|
||||
/**
|
||||
* the camera object with model and make
|
||||
* @var Camera
|
||||
*/
|
||||
protected $camera;
|
||||
|
||||
/**
|
||||
* the location object
|
||||
* @var Location
|
||||
*/
|
||||
protected $location;
|
||||
/**
|
||||
* the location object
|
||||
* @var Location
|
||||
*/
|
||||
protected $location;
|
||||
|
||||
/**
|
||||
* the timestamp
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $timestamp;
|
||||
/**
|
||||
* the timestamp
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $timestamp;
|
||||
|
||||
/**
|
||||
* the exposure value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $exposure;
|
||||
/**
|
||||
* the exposure value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $exposure;
|
||||
|
||||
/**
|
||||
* the aperture value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $aperture;
|
||||
/**
|
||||
* the aperture value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $aperture;
|
||||
|
||||
/**
|
||||
* iso value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $iso;
|
||||
/**
|
||||
* iso value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $iso;
|
||||
|
||||
/**
|
||||
* focal length
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $focalLength;
|
||||
/**
|
||||
* focal length
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $focalLength;
|
||||
|
||||
/**
|
||||
* color or black/white
|
||||
* @var bool
|
||||
*/
|
||||
protected $isColor;
|
||||
/**
|
||||
* color or black/white
|
||||
* @var bool
|
||||
*/
|
||||
protected $isColor;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Kirby\Image\Image $image
|
||||
*/
|
||||
public function __construct(Image $image)
|
||||
{
|
||||
$this->image = $image;
|
||||
$this->data = $this->read();
|
||||
$this->parse();
|
||||
}
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Kirby\Image\Image $image
|
||||
*/
|
||||
public function __construct(Image $image)
|
||||
{
|
||||
$this->image = $image;
|
||||
$this->data = $this->read();
|
||||
$this->parse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data array from the parser
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
/**
|
||||
* Returns the raw data array from the parser
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Camera object
|
||||
*
|
||||
* @return \Kirby\Image\Camera|null
|
||||
*/
|
||||
public function camera()
|
||||
{
|
||||
if ($this->camera !== null) {
|
||||
return $this->camera;
|
||||
}
|
||||
/**
|
||||
* Returns the Camera object
|
||||
*
|
||||
* @return \Kirby\Image\Camera|null
|
||||
*/
|
||||
public function camera()
|
||||
{
|
||||
if ($this->camera !== null) {
|
||||
return $this->camera;
|
||||
}
|
||||
|
||||
return $this->camera = new Camera($this->data);
|
||||
}
|
||||
return $this->camera = new Camera($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location object
|
||||
*
|
||||
* @return \Kirby\Image\Location|null
|
||||
*/
|
||||
public function location()
|
||||
{
|
||||
if ($this->location !== null) {
|
||||
return $this->location;
|
||||
}
|
||||
/**
|
||||
* Returns the location object
|
||||
*
|
||||
* @return \Kirby\Image\Location|null
|
||||
*/
|
||||
public function location()
|
||||
{
|
||||
if ($this->location !== null) {
|
||||
return $this->location;
|
||||
}
|
||||
|
||||
return $this->location = new Location($this->data);
|
||||
}
|
||||
return $this->location = new Location($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function timestamp()
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
/**
|
||||
* Returns the timestamp
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function timestamp()
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exposure
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function exposure()
|
||||
{
|
||||
return $this->exposure;
|
||||
}
|
||||
/**
|
||||
* Returns the exposure
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function exposure()
|
||||
{
|
||||
return $this->exposure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the aperture
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function aperture()
|
||||
{
|
||||
return $this->aperture;
|
||||
}
|
||||
/**
|
||||
* Returns the aperture
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function aperture()
|
||||
{
|
||||
return $this->aperture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the iso value
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function iso()
|
||||
{
|
||||
return $this->iso;
|
||||
}
|
||||
/**
|
||||
* Returns the iso value
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function iso()
|
||||
{
|
||||
return $this->iso;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this is a color picture
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function isColor()
|
||||
{
|
||||
return $this->isColor;
|
||||
}
|
||||
/**
|
||||
* Checks if this is a color picture
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function isColor()
|
||||
{
|
||||
return $this->isColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this is a bw picture
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function isBW(): ?bool
|
||||
{
|
||||
return ($this->isColor !== null) ? $this->isColor === false : null;
|
||||
}
|
||||
/**
|
||||
* Checks if this is a bw picture
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function isBW(): ?bool
|
||||
{
|
||||
return ($this->isColor !== null) ? $this->isColor === false : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the focal length
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function focalLength()
|
||||
{
|
||||
return $this->focalLength;
|
||||
}
|
||||
/**
|
||||
* Returns the focal length
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function focalLength()
|
||||
{
|
||||
return $this->focalLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the exif data of the image object if possible
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function read(): array
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if (function_exists('exif_read_data') === false) {
|
||||
return [];
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
/**
|
||||
* Read the exif data of the image object if possible
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function read(): array
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if (function_exists('exif_read_data') === false) {
|
||||
return [];
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$data = @exif_read_data($this->image->root());
|
||||
return is_array($data) ? $data : [];
|
||||
}
|
||||
$data = @exif_read_data($this->image->root());
|
||||
return is_array($data) ? $data : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all computed data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function computed(): array
|
||||
{
|
||||
return $this->data['COMPUTED'] ?? [];
|
||||
}
|
||||
/**
|
||||
* Get all computed data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function computed(): array
|
||||
{
|
||||
return $this->data['COMPUTED'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Pareses and stores all relevant exif data
|
||||
*/
|
||||
protected function parse()
|
||||
{
|
||||
$this->timestamp = $this->parseTimestamp();
|
||||
$this->exposure = $this->data['ExposureTime'] ?? null;
|
||||
$this->iso = $this->data['ISOSpeedRatings'] ?? null;
|
||||
$this->focalLength = $this->parseFocalLength();
|
||||
$this->aperture = $this->computed()['ApertureFNumber'] ?? null;
|
||||
$this->isColor = V::accepted($this->computed()['IsColor'] ?? null);
|
||||
}
|
||||
/**
|
||||
* Pareses and stores all relevant exif data
|
||||
*/
|
||||
protected function parse()
|
||||
{
|
||||
$this->timestamp = $this->parseTimestamp();
|
||||
$this->exposure = $this->data['ExposureTime'] ?? null;
|
||||
$this->iso = $this->data['ISOSpeedRatings'] ?? null;
|
||||
$this->focalLength = $this->parseFocalLength();
|
||||
$this->aperture = $this->computed()['ApertureFNumber'] ?? null;
|
||||
$this->isColor = V::accepted($this->computed()['IsColor'] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the timestamp when the picture has been taken
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
protected function parseTimestamp()
|
||||
{
|
||||
if (isset($this->data['DateTimeOriginal']) === true) {
|
||||
return strtotime($this->data['DateTimeOriginal']);
|
||||
}
|
||||
/**
|
||||
* Return the timestamp when the picture has been taken
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
protected function parseTimestamp()
|
||||
{
|
||||
if (isset($this->data['DateTimeOriginal']) === true) {
|
||||
return strtotime($this->data['DateTimeOriginal']);
|
||||
}
|
||||
|
||||
return $this->data['FileDateTime'] ?? $this->image->modified();
|
||||
}
|
||||
return $this->data['FileDateTime'] ?? $this->image->modified();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the focal length
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function parseFocalLength()
|
||||
{
|
||||
return $this->data['FocalLength'] ?? $this->data['FocalLengthIn35mmFilm'] ?? null;
|
||||
}
|
||||
/**
|
||||
* Return the focal length
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function parseFocalLength()
|
||||
{
|
||||
return $this->data['FocalLength'] ?? $this->data['FocalLengthIn35mmFilm'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object into a nicely readable array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'camera' => $this->camera() ? $this->camera()->toArray() : null,
|
||||
'location' => $this->location() ? $this->location()->toArray() : null,
|
||||
'timestamp' => $this->timestamp(),
|
||||
'exposure' => $this->exposure(),
|
||||
'aperture' => $this->aperture(),
|
||||
'iso' => $this->iso(),
|
||||
'focalLength' => $this->focalLength(),
|
||||
'isColor' => $this->isColor()
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Converts the object into a nicely readable array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'camera' => $this->camera() ? $this->camera()->toArray() : null,
|
||||
'location' => $this->location() ? $this->location()->toArray() : null,
|
||||
'timestamp' => $this->timestamp(),
|
||||
'exposure' => $this->exposure(),
|
||||
'aperture' => $this->aperture(),
|
||||
'iso' => $this->iso(),
|
||||
'focalLength' => $this->focalLength(),
|
||||
'isColor' => $this->isColor()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return array_merge($this->toArray(), [
|
||||
'camera' => $this->camera(),
|
||||
'location' => $this->location()
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return array_merge($this->toArray(), [
|
||||
'camera' => $this->camera(),
|
||||
'location' => $this->location()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,230 +22,230 @@ use Kirby\Toolkit\Html;
|
|||
*/
|
||||
class Image extends File
|
||||
{
|
||||
/**
|
||||
* @var \Kirby\Image\Exif|null
|
||||
*/
|
||||
protected $exif;
|
||||
/**
|
||||
* @var \Kirby\Image\Exif|null
|
||||
*/
|
||||
protected $exif;
|
||||
|
||||
/**
|
||||
* @var \Kirby\Image\Dimensions|null
|
||||
*/
|
||||
protected $dimensions;
|
||||
/**
|
||||
* @var \Kirby\Image\Dimensions|null
|
||||
*/
|
||||
protected $dimensions;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $resizableTypes = [
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'gif',
|
||||
'png',
|
||||
'webp'
|
||||
];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $resizableTypes = [
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'gif',
|
||||
'png',
|
||||
'webp'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $viewableTypes = [
|
||||
'avif',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'gif',
|
||||
'png',
|
||||
'svg',
|
||||
'webp'
|
||||
];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $viewableTypes = [
|
||||
'avif',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'gif',
|
||||
'png',
|
||||
'svg',
|
||||
'webp'
|
||||
];
|
||||
|
||||
/**
|
||||
* Validation rules to be used for `::match()`
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $validations = [
|
||||
'maxsize' => ['size', 'max'],
|
||||
'minsize' => ['size', 'min'],
|
||||
'maxwidth' => ['width', 'max'],
|
||||
'minwidth' => ['width', 'min'],
|
||||
'maxheight' => ['height', 'max'],
|
||||
'minheight' => ['height', 'min'],
|
||||
'orientation' => ['orientation', 'same']
|
||||
];
|
||||
/**
|
||||
* Validation rules to be used for `::match()`
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $validations = [
|
||||
'maxsize' => ['size', 'max'],
|
||||
'minsize' => ['size', 'min'],
|
||||
'maxwidth' => ['width', 'max'],
|
||||
'minwidth' => ['width', 'min'],
|
||||
'maxheight' => ['height', 'max'],
|
||||
'minheight' => ['height', 'min'],
|
||||
'orientation' => ['orientation', 'same']
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns the `<img>` tag for the image object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->html();
|
||||
}
|
||||
/**
|
||||
* Returns the `<img>` tag for the image object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dimensions of the file if possible
|
||||
*
|
||||
* @return \Kirby\Image\Dimensions
|
||||
*/
|
||||
public function dimensions()
|
||||
{
|
||||
if ($this->dimensions !== null) {
|
||||
return $this->dimensions;
|
||||
}
|
||||
/**
|
||||
* Returns the dimensions of the file if possible
|
||||
*
|
||||
* @return \Kirby\Image\Dimensions
|
||||
*/
|
||||
public function dimensions()
|
||||
{
|
||||
if ($this->dimensions !== null) {
|
||||
return $this->dimensions;
|
||||
}
|
||||
|
||||
if (in_array($this->mime(), [
|
||||
'image/jpeg',
|
||||
'image/jp2',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp'
|
||||
])) {
|
||||
return $this->dimensions = Dimensions::forImage($this->root);
|
||||
}
|
||||
if (in_array($this->mime(), [
|
||||
'image/jpeg',
|
||||
'image/jp2',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp'
|
||||
])) {
|
||||
return $this->dimensions = Dimensions::forImage($this->root);
|
||||
}
|
||||
|
||||
if ($this->extension() === 'svg') {
|
||||
return $this->dimensions = Dimensions::forSvg($this->root);
|
||||
}
|
||||
if ($this->extension() === 'svg') {
|
||||
return $this->dimensions = Dimensions::forSvg($this->root);
|
||||
}
|
||||
|
||||
return $this->dimensions = new Dimensions(0, 0);
|
||||
}
|
||||
return $this->dimensions = new Dimensions(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exif object for this file (if image)
|
||||
*
|
||||
* @return \Kirby\Image\Exif
|
||||
*/
|
||||
public function exif()
|
||||
{
|
||||
return $this->exif ??= new Exif($this);
|
||||
}
|
||||
/**
|
||||
* Returns the exif object for this file (if image)
|
||||
*
|
||||
* @return \Kirby\Image\Exif
|
||||
*/
|
||||
public function exif()
|
||||
{
|
||||
return $this->exif ??= new Exif($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the asset
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function height(): int
|
||||
{
|
||||
return $this->dimensions()->height();
|
||||
}
|
||||
/**
|
||||
* Returns the height of the asset
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function height(): int
|
||||
{
|
||||
return $this->dimensions()->height();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the file to html
|
||||
*
|
||||
* @param array $attr
|
||||
* @return string
|
||||
*/
|
||||
public function html(array $attr = []): string
|
||||
{
|
||||
return Html::img($this->url(), $attr);
|
||||
}
|
||||
/**
|
||||
* Converts the file to html
|
||||
*
|
||||
* @param array $attr
|
||||
* @return string
|
||||
*/
|
||||
public function html(array $attr = []): string
|
||||
{
|
||||
return Html::img($this->url(), $attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PHP imagesize array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function imagesize(): array
|
||||
{
|
||||
return getimagesize($this->root);
|
||||
}
|
||||
/**
|
||||
* Returns the PHP imagesize array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function imagesize(): array
|
||||
{
|
||||
return getimagesize($this->root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the dimensions of the asset are portrait
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPortrait(): bool
|
||||
{
|
||||
return $this->dimensions()->portrait();
|
||||
}
|
||||
/**
|
||||
* Checks if the dimensions of the asset are portrait
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPortrait(): bool
|
||||
{
|
||||
return $this->dimensions()->portrait();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the dimensions of the asset are landscape
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLandscape(): bool
|
||||
{
|
||||
return $this->dimensions()->landscape();
|
||||
}
|
||||
/**
|
||||
* Checks if the dimensions of the asset are landscape
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLandscape(): bool
|
||||
{
|
||||
return $this->dimensions()->landscape();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the dimensions of the asset are square
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSquare(): bool
|
||||
{
|
||||
return $this->dimensions()->square();
|
||||
}
|
||||
/**
|
||||
* Checks if the dimensions of the asset are square
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSquare(): bool
|
||||
{
|
||||
return $this->dimensions()->square();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the file is a resizable image
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isResizable(): bool
|
||||
{
|
||||
return in_array($this->extension(), static::$resizableTypes) === true;
|
||||
}
|
||||
/**
|
||||
* Checks if the file is a resizable image
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isResizable(): bool
|
||||
{
|
||||
return in_array($this->extension(), static::$resizableTypes) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a preview can be displayed for the file
|
||||
* in the Panel or in the frontend
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isViewable(): bool
|
||||
{
|
||||
return in_array($this->extension(), static::$viewableTypes) === true;
|
||||
}
|
||||
/**
|
||||
* Checks if a preview can be displayed for the file
|
||||
* in the Panel or in the frontend
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isViewable(): bool
|
||||
{
|
||||
return in_array($this->extension(), static::$viewableTypes) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ratio of the asset
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function ratio(): float
|
||||
{
|
||||
return $this->dimensions()->ratio();
|
||||
}
|
||||
/**
|
||||
* Returns the ratio of the asset
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function ratio(): float
|
||||
{
|
||||
return $this->dimensions()->ratio();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the orientation as string
|
||||
* landscape | portrait | square
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function orientation(): string
|
||||
{
|
||||
return $this->dimensions()->orientation();
|
||||
}
|
||||
/**
|
||||
* Returns the orientation as string
|
||||
* landscape | portrait | square
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function orientation(): string
|
||||
{
|
||||
return $this->dimensions()->orientation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = array_merge(parent::toArray(), [
|
||||
'dimensions' => $this->dimensions()->toArray(),
|
||||
'exif' => $this->exif()->toArray(),
|
||||
]);
|
||||
/**
|
||||
* Converts the object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = array_merge(parent::toArray(), [
|
||||
'dimensions' => $this->dimensions()->toArray(),
|
||||
'exif' => $this->exif()->toArray(),
|
||||
]);
|
||||
|
||||
ksort($array);
|
||||
ksort($array);
|
||||
|
||||
return $array;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the asset
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function width(): int
|
||||
{
|
||||
return $this->dimensions()->width();
|
||||
}
|
||||
/**
|
||||
* Returns the width of the asset
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function width(): int
|
||||
{
|
||||
return $this->dimensions()->width();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,123 +14,123 @@ namespace Kirby\Image;
|
|||
*/
|
||||
class Location
|
||||
{
|
||||
/**
|
||||
* latitude
|
||||
*
|
||||
* @var float|null
|
||||
*/
|
||||
protected $lat;
|
||||
/**
|
||||
* latitude
|
||||
*
|
||||
* @var float|null
|
||||
*/
|
||||
protected $lat;
|
||||
|
||||
/**
|
||||
* longitude
|
||||
*
|
||||
* @var float|null
|
||||
*/
|
||||
protected $lng;
|
||||
/**
|
||||
* longitude
|
||||
*
|
||||
* @var float|null
|
||||
*/
|
||||
protected $lng;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $exif The entire exif array
|
||||
*/
|
||||
public function __construct(array $exif)
|
||||
{
|
||||
if (isset($exif['GPSLatitude']) === true &&
|
||||
isset($exif['GPSLatitudeRef']) === true &&
|
||||
isset($exif['GPSLongitude']) === true &&
|
||||
isset($exif['GPSLongitudeRef']) === true
|
||||
) {
|
||||
$this->lat = $this->gps($exif['GPSLatitude'], $exif['GPSLatitudeRef']);
|
||||
$this->lng = $this->gps($exif['GPSLongitude'], $exif['GPSLongitudeRef']);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $exif The entire exif array
|
||||
*/
|
||||
public function __construct(array $exif)
|
||||
{
|
||||
if (isset($exif['GPSLatitude']) === true &&
|
||||
isset($exif['GPSLatitudeRef']) === true &&
|
||||
isset($exif['GPSLongitude']) === true &&
|
||||
isset($exif['GPSLongitudeRef']) === true
|
||||
) {
|
||||
$this->lat = $this->gps($exif['GPSLatitude'], $exif['GPSLatitudeRef']);
|
||||
$this->lng = $this->gps($exif['GPSLongitude'], $exif['GPSLongitudeRef']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latitude
|
||||
*
|
||||
* @return float|null
|
||||
*/
|
||||
public function lat()
|
||||
{
|
||||
return $this->lat;
|
||||
}
|
||||
/**
|
||||
* Returns the latitude
|
||||
*
|
||||
* @return float|null
|
||||
*/
|
||||
public function lat()
|
||||
{
|
||||
return $this->lat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the longitude
|
||||
*
|
||||
* @return float|null
|
||||
*/
|
||||
public function lng()
|
||||
{
|
||||
return $this->lng;
|
||||
}
|
||||
/**
|
||||
* Returns the longitude
|
||||
*
|
||||
* @return float|null
|
||||
*/
|
||||
public function lng()
|
||||
{
|
||||
return $this->lng;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the gps coordinates
|
||||
*
|
||||
* @param string|array $coord
|
||||
* @param string $hemi
|
||||
* @return float
|
||||
*/
|
||||
protected function gps($coord, string $hemi): float
|
||||
{
|
||||
$degrees = count($coord) > 0 ? $this->num($coord[0]) : 0;
|
||||
$minutes = count($coord) > 1 ? $this->num($coord[1]) : 0;
|
||||
$seconds = count($coord) > 2 ? $this->num($coord[2]) : 0;
|
||||
/**
|
||||
* Converts the gps coordinates
|
||||
*
|
||||
* @param string|array $coord
|
||||
* @param string $hemi
|
||||
* @return float
|
||||
*/
|
||||
protected function gps($coord, string $hemi): float
|
||||
{
|
||||
$degrees = count($coord) > 0 ? $this->num($coord[0]) : 0;
|
||||
$minutes = count($coord) > 1 ? $this->num($coord[1]) : 0;
|
||||
$seconds = count($coord) > 2 ? $this->num($coord[2]) : 0;
|
||||
|
||||
$hemi = strtoupper($hemi);
|
||||
$flip = ($hemi === 'W' || $hemi === 'S') ? -1 : 1;
|
||||
$hemi = strtoupper($hemi);
|
||||
$flip = ($hemi === 'W' || $hemi === 'S') ? -1 : 1;
|
||||
|
||||
return $flip * ($degrees + $minutes / 60 + $seconds / 3600);
|
||||
}
|
||||
return $flip * ($degrees + $minutes / 60 + $seconds / 3600);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts coordinates to floats
|
||||
*
|
||||
* @param string $part
|
||||
* @return float
|
||||
*/
|
||||
protected function num(string $part): float
|
||||
{
|
||||
$parts = explode('/', $part);
|
||||
/**
|
||||
* Converts coordinates to floats
|
||||
*
|
||||
* @param string $part
|
||||
* @return float
|
||||
*/
|
||||
protected function num(string $part): float
|
||||
{
|
||||
$parts = explode('/', $part);
|
||||
|
||||
if (count($parts) === 1) {
|
||||
return (float)$parts[0];
|
||||
}
|
||||
if (count($parts) === 1) {
|
||||
return (float)$parts[0];
|
||||
}
|
||||
|
||||
return (float)($parts[0]) / (float)($parts[1]);
|
||||
}
|
||||
return (float)($parts[0]) / (float)($parts[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object into a nicely readable array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'lat' => $this->lat(),
|
||||
'lng' => $this->lng()
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Converts the object into a nicely readable array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'lat' => $this->lat(),
|
||||
'lng' => $this->lng()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Echos the entire location as lat, lng
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return trim(trim($this->lat() . ', ' . $this->lng(), ','));
|
||||
}
|
||||
/**
|
||||
* Echos the entire location as lat, lng
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return trim(trim($this->lat() . ', ' . $this->lng(), ','));
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue