diff --git a/Classes/Domain/Model/FalImage.php b/Classes/Domain/Model/FalImage.php index 6243e60..0a60d85 100644 --- a/Classes/Domain/Model/FalImage.php +++ b/Classes/Domain/Model/FalImage.php @@ -3,11 +3,18 @@ namespace SMS\FluidComponents\Domain\Model; use SMS\FluidComponents\Domain\Model\Traits\FalFileTrait; +use SMS\FluidComponents\Interfaces\ImageWithCropVariants; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; +use SMS\FluidComponents\Interfaces\ProcessableImage; +use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; +use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Service\ImageService; /** * Data structure as a wrapper around a FAL object to be passed to a component. */ -class FalImage extends Image +class FalImage extends Image implements ImageWithDimensions, ImageWithCropVariants, ProcessableImage { use FalFileTrait; @@ -27,13 +34,37 @@ public function getCopyright(): ?string return parent::getCopyright() ?? $this->file->getProperty('copyright'); } - public function getHeight() + public function getHeight(): int { - return $this->file->getProperty('height'); + return (int) $this->file->getProperty('height'); } - public function getWidth() + public function getWidth(): int { - return $this->file->getProperty('width'); + return (int) $this->file->getProperty('width'); + } + + public function getDefaultCrop(): Area + { + $cropVariantCollection = CropVariantCollection::create((string)$this->file->getProperty('crop')); + return $cropVariantCollection->getCropArea(); + } + + public function getCropVariant(string $name): Area + { + $cropVariantCollection = CropVariantCollection::create((string)$this->file->getProperty('crop')); + return $cropVariantCollection->getCropArea($name); + } + + public function process(int $width, int $height, ?string $format, Area $cropArea): FalImage + { + $imageService = GeneralUtility::makeInstance(ImageService::class); + $processedImage = $imageService->applyProcessingInstructions($this->getFile(), [ + 'width' => $width, + 'height' => $height, + 'fileExtension' => $format, + 'crop' => ($cropArea->isEmpty()) ? null : $cropArea->makeAbsoluteBasedOnFile($this->getFile()) + ]); + return new FalImage($processedImage); } } diff --git a/Classes/Domain/Model/LocalImage.php b/Classes/Domain/Model/LocalImage.php index cefe15e..76388af 100644 --- a/Classes/Domain/Model/LocalImage.php +++ b/Classes/Domain/Model/LocalImage.php @@ -2,16 +2,20 @@ namespace SMS\FluidComponents\Domain\Model; -use SMS\FluidComponents\Exception\InvalidFilePathException; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\PathUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; +use SMS\FluidComponents\Exception\InvalidFilePathException; +use SMS\FluidComponents\Interfaces\ProcessableImage; +use TYPO3\CMS\Core\Imaging\GraphicalFunctions; +use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; /** * Data structure for a local image resource to be passed to a component. * * @deprecated, use FalImage instead */ -class LocalImage extends Image +class LocalImage extends Image implements ImageWithDimensions, ProcessableImage { /** * Type of image to differentiate implementations in Fluid templates. @@ -23,6 +27,16 @@ class LocalImage extends Image */ protected string $filePath = ''; + /** + * Image width, will be determined at first access + */ + protected int $width = 0; + + /** + * Image height, will be determined at first access + */ + protected int $height = 0; + /** * Creates an image object for a local image resource. * @@ -59,4 +73,41 @@ public function getPublicUrl(): string { return PathUtility::getAbsoluteWebPath($this->filePath); } + + public function getHeight(): int + { + if (!isset($this->height)) { + $this->getImageDimensions(); + } + return $this->height; + } + + public function getWidth(): int + { + if (!isset($this->height)) { + $this->getImageDimensions(); + } + return $this->width; + } + + protected function getImageDimensions(): void + { + $graphicalFunctions = GeneralUtility::makeInstance(GraphicalFunctions::class); + $imageDimensions = $graphicalFunctions->getImageDimensions($this->getFilePath()); + $this->width = (int) $imageDimensions[0]; + $this->height = (int) $imageDimensions[1]; + } + + public function process(int $width, int $height, ?string $format, Area $cropArea): ProcessableImage + { + $imageService = GeneralUtility::makeInstance(ImageService::class); + $file = $imageService->getImage($this->getFilePath(), null, false); + $processedImage = $imageService->applyProcessingInstructions($file, [ + 'width' => $width, + 'height' => $height, + 'fileExtension' => $format, + 'crop' => ($cropArea->isEmpty()) ? null : $cropArea->makeAbsoluteBasedOnFile($file) + ]); + return new FalImage($processedImage); + } } diff --git a/Classes/Domain/Model/PlaceholderImage.php b/Classes/Domain/Model/PlaceholderImage.php index f9436b4..70e2220 100644 --- a/Classes/Domain/Model/PlaceholderImage.php +++ b/Classes/Domain/Model/PlaceholderImage.php @@ -2,10 +2,14 @@ namespace SMS\FluidComponents\Domain\Model; +use SMS\FluidComponents\Interfaces\ImageWithDimensions; +use SMS\FluidComponents\Interfaces\ProcessableImage; +use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; + /** * Data structure for a placeholder image to be passed to a component. */ -class PlaceholderImage extends Image +class PlaceholderImage extends Image implements ImageWithDimensions, ProcessableImage { /** * Type of image to differentiate implementations in Fluid templates. @@ -22,13 +26,19 @@ class PlaceholderImage extends Image */ protected int $height = 0; + /** + * Image format of the image + */ + protected string $format = 'gif'; + /** * Creates an image object for a placeholder image. */ - public function __construct(int $width, int $height) + public function __construct(int $width, int $height, string $format = 'gif') { $this->width = $width; $this->height = $height; + $this->format = $format; } public function getWidth(): int @@ -41,8 +51,22 @@ public function getHeight(): int return $this->height; } + public function getFormat(): string + { + return $this->format; + } + public function getPublicUrl(): string { - return 'https://via.placeholder.com/' . $this->width . 'x' . $this->height; + return 'https://via.placeholder.com/' . $this->width . 'x' . $this->height . '.' . $this->format; + } + + public function process(int $width, int $height, ?string $format, Area $cropArea): ProcessableImage + { + return new PlaceholderImage( + (int) round($cropArea->getWidth() * $width), + (int) round($cropArea->getHeight() * $height), + $format ?: $this->format + ); } } diff --git a/Classes/Interfaces/ImageWithCropVariants.php b/Classes/Interfaces/ImageWithCropVariants.php new file mode 100644 index 0000000..83abac8 --- /dev/null +++ b/Classes/Interfaces/ImageWithCropVariants.php @@ -0,0 +1,11 @@ +conversionInterfaces[$givenType][0]) && + if (isset($this->conversionInterfaces[$givenType]) && is_subclass_of($toType, $this->conversionInterfaces[$givenType][0]) ) { $conversionInfo = $this->conversionInterfaces[$givenType];