1<?php 2 3declare(strict_types=1); 4 5/* 6 * This file is part of the TYPO3 CMS project. 7 * 8 * It is free software; you can redistribute it and/or modify it under 9 * the terms of the GNU General Public License, either version 2 10 * of the License, or any later version. 11 * 12 * For the full copyright and license information, please read the 13 * LICENSE.txt file that was distributed with this source code. 14 * 15 * The TYPO3 project - inspiring people to share! 16 */ 17 18namespace TYPO3\CMS\Core\Imaging\ImageManipulation; 19 20use TYPO3\CMS\Core\Resource\FileInterface; 21 22class CropVariant 23{ 24 /** 25 * @var string 26 */ 27 protected $id; 28 29 /** 30 * @var string 31 */ 32 protected $title; 33 34 /** 35 * @var Area 36 */ 37 protected $cropArea; 38 39 /** 40 * @var Ratio[] 41 */ 42 protected $allowedAspectRatios; 43 44 /** 45 * @var string 46 */ 47 protected $selectedRatio; 48 49 /** 50 * @var Area|null 51 */ 52 protected $focusArea; 53 54 /** 55 * @var Area[]|null 56 */ 57 protected $coverAreas; 58 59 /** 60 * @param string $id 61 * @param string $title 62 * @param Area $cropArea 63 * @param Ratio[] $allowedAspectRatios 64 * @param string|null $selectedRatio 65 * @param Area|null $focusArea 66 * @param Area[]|null $coverAreas 67 * @throws InvalidConfigurationException 68 */ 69 public function __construct( 70 string $id, 71 string $title, 72 Area $cropArea, 73 array $allowedAspectRatios = null, 74 string $selectedRatio = null, 75 Area $focusArea = null, 76 array $coverAreas = null 77 ) { 78 $this->id = $id; 79 $this->title = $title; 80 $this->cropArea = $cropArea; 81 if ($allowedAspectRatios) { 82 $this->setAllowedAspectRatios(...$allowedAspectRatios); 83 if ($selectedRatio && isset($this->allowedAspectRatios[$selectedRatio])) { 84 $this->selectedRatio = $selectedRatio; 85 } else { 86 $this->selectedRatio = current($this->allowedAspectRatios)->getId(); 87 } 88 } 89 $this->focusArea = $focusArea; 90 if ($coverAreas !== null) { 91 $this->setCoverAreas(...$coverAreas); 92 } 93 } 94 95 /** 96 * @param string $id 97 * @param array $config 98 * @return CropVariant 99 * @throws InvalidConfigurationException 100 */ 101 public static function createFromConfiguration(string $id, array $config): CropVariant 102 { 103 try { 104 return new self( 105 $id, 106 $config['title'] ?? '', 107 Area::createFromConfiguration($config['cropArea']), 108 isset($config['allowedAspectRatios']) ? Ratio::createMultipleFromConfiguration($config['allowedAspectRatios']) : null, 109 $config['selectedRatio'] ?? null, 110 isset($config['focusArea']) ? Area::createFromConfiguration($config['focusArea']) : null, 111 isset($config['coverAreas']) ? Area::createMultipleFromConfiguration($config['coverAreas']) : null 112 ); 113 } catch (\Throwable $throwable) { 114 throw new InvalidConfigurationException(sprintf('Invalid type in configuration for crop variant: %s', $throwable->getMessage()), 1485278693, $throwable); 115 } 116 } 117 118 /** 119 * @return array 120 * @internal 121 */ 122 public function asArray(): array 123 { 124 $coverAreasAsArray = null; 125 $allowedAspectRatiosAsArray = []; 126 foreach ($this->allowedAspectRatios ?? [] as $id => $allowedAspectRatio) { 127 $allowedAspectRatiosAsArray[$id] = $allowedAspectRatio->asArray(); 128 } 129 if ($this->coverAreas !== null) { 130 $coverAreasAsArray = []; 131 foreach ($this->coverAreas as $coverArea) { 132 $coverAreasAsArray[] = $coverArea->asArray(); 133 } 134 } 135 return [ 136 'id' => $this->id, 137 'title' => $this->title, 138 'cropArea' => $this->cropArea->asArray(), 139 'allowedAspectRatios' => $allowedAspectRatiosAsArray, 140 'selectedRatio' => $this->selectedRatio, 141 'focusArea' => $this->focusArea ? $this->focusArea->asArray() : null, 142 'coverAreas' => $coverAreasAsArray ?? null, 143 ]; 144 } 145 146 /** 147 * @return string 148 */ 149 public function getId(): string 150 { 151 return $this->id; 152 } 153 154 /** 155 * @return Area 156 */ 157 public function getCropArea(): Area 158 { 159 return $this->cropArea; 160 } 161 162 /** 163 * @return Area|null 164 */ 165 public function getFocusArea() 166 { 167 return $this->focusArea; 168 } 169 170 /** 171 * @param FileInterface $file 172 * @return CropVariant 173 */ 174 public function applyRatioRestrictionToSelectedCropArea(FileInterface $file): CropVariant 175 { 176 if (!$this->selectedRatio) { 177 return $this; 178 } 179 $newVariant = clone $this; 180 $newArea = $this->cropArea->makeAbsoluteBasedOnFile($file); 181 $newArea = $newArea->applyRatioRestriction($this->allowedAspectRatios[$this->selectedRatio]); 182 $newVariant->cropArea = $newArea->makeRelativeBasedOnFile($file); 183 return $newVariant; 184 } 185 186 /** 187 * @param Ratio ...$ratios 188 * @throws InvalidConfigurationException 189 */ 190 protected function setAllowedAspectRatios(Ratio ...$ratios) 191 { 192 $this->allowedAspectRatios = []; 193 foreach ($ratios as $ratio) { 194 $this->addAllowedAspectRatio($ratio); 195 } 196 } 197 198 /** 199 * @param Ratio $ratio 200 * @throws InvalidConfigurationException 201 */ 202 protected function addAllowedAspectRatio(Ratio $ratio) 203 { 204 if (isset($this->allowedAspectRatios[$ratio->getId()])) { 205 throw new InvalidConfigurationException(sprintf('Ratio with with duplicate ID (%s) is configured. Make sure all configured ratios have different ids.', $ratio->getId()), 1485274618); 206 } 207 $this->allowedAspectRatios[$ratio->getId()] = $ratio; 208 } 209 210 /** 211 * @param Area ...$areas 212 * @throws InvalidConfigurationException 213 */ 214 protected function setCoverAreas(Area ...$areas) 215 { 216 $this->coverAreas = []; 217 foreach ($areas as $area) { 218 $this->addCoverArea($area); 219 } 220 } 221 222 /** 223 * @param Area $area 224 * @throws InvalidConfigurationException 225 */ 226 protected function addCoverArea(Area $area) 227 { 228 $this->coverAreas[] = $area; 229 } 230} 231