* @link https://getkirby.com * @copyright Bastian Allgeier GmbH * @license https://opensource.org/licenses/MIT */ class Image extends File { /** * optional url where the file is reachable * @var string */ protected $url; /** * @var Exif|null */ protected $exif; /** * @var Dimensions|null */ protected $dimensions; /** * Constructor * * @param string $root * @param string|null $url */ public function __construct(string $root = null, string $url = null) { parent::__construct($root); $this->url = $url; } /** * Improved `var_dump` output * * @return array */ public function __debuginfo(): array { return array_merge($this->toArray(), [ 'dimensions' => $this->dimensions(), 'exif' => $this->exif(), ]); } /** * Returns a full link to this file * Perfect for debugging in connection with echo * * @return string */ public function __toString(): string { return $this->root; } /** * 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'])) { return $this->dimensions = Dimensions::forImage($this->root); } if ($this->extension() === 'svg') { return $this->dimensions = Dimensions::forSvg($this->root); } return $this->dimensions = new Dimensions(0, 0); } /* * Automatically sends all needed headers for the file to be downloaded * and echos the file's content * * @param string|null $filename Optional filename for the download * @return string */ public function download($filename = null): string { return Response::download($this->root, $filename ?? $this->filename()); } /** * Returns the exif object for this file (if image) * * @return Kirby\Image\Exif */ public function exif() { if ($this->exif !== null) { return $this->exif; } $this->exif = new Exif($this); return $this->exif; } /** * Sends an appropriate header for the asset * * @param boolean $send * @return Kirby\Http\Response|string */ public function header(bool $send = true) { $response = new Response(); $response->type($this->mime()); return $send === true ? $response->send() : $response; } /** * Returns the height of the asset * * @return int */ public function height(): int { return $this->dimensions()->height(); } /** * @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); } /** * Checks if the dimensions of the asset are portrait * * @return boolean */ public function isPortrait(): bool { return $this->dimensions()->portrait(); } /** * Checks if the dimensions of the asset are landscape * * @return boolean */ public function isLandscape(): bool { return $this->dimensions()->landscape(); } /** * Checks if the dimensions of the asset are square * * @return boolean */ public function isSquare(): bool { return $this->dimensions()->square(); } /** * Runs a set of validations on the image object * * @return bool */ public function match(array $rules): bool { if (($rules['mime'] ?? null) !== null) { if (Mime::isAccepted($this->mime(), $rules['mime']) !== true) { throw new Exception(sprintf('Invalid mime type: %s', $this->mime())); } } $rules = array_change_key_case($rules); $validations = [ 'maxsize' => ['size', 'max', 'The file is too large'], 'minsize' => ['size', 'min', 'The file is too small'], 'maxwidth' => ['width', 'max', 'The width of the image must not exceed %s pixels'], 'minwidth' => ['width', 'min', 'The width of the image must be at least %s pixels'], 'maxheight' => ['height', 'max', 'The height of the image must not exceed %s pixels'], 'minheight' => ['height', 'min', 'The height of the image must be at least %s pixels'], 'orientation' => ['orientation', 'same', 'The orientation of the image must be "%s"'] ]; foreach ($validations as $key => $arguments) { if (isset($rules[$key]) === true && $rules[$key] !== null) { $property = $arguments[0]; $validator = $arguments[1]; $message = $arguments[2]; if (V::$validator($this->$property(), $rules[$key]) === false) { throw new Exception(sprintf($message, $rules[$key])); } } } return true; } /** * 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(); } /** * Converts the media object to a * plain PHP array * * @return array */ public function toArray(): array { return array_merge(parent::toArray(), [ 'dimensions' => $this->dimensions()->toArray(), 'exif' => $this->exif()->toArray(), ]); } /** * Converts the entire file array into * a json string * * @return string */ public function toJson(): string { return json_encode($this->toArray()); } /** * Returns the url * * @return string */ public function url() { return $this->url; } /** * Returns the width of the asset * * @return int */ public function width(): int { return $this->dimensions()->width(); } }