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 CropVariantCollection 23{ 24 /** 25 * @var CropVariant[] 26 */ 27 protected $cropVariants; 28 29 /** 30 * @param CropVariant[] $cropVariants 31 * @throws \TYPO3\CMS\Core\Imaging\ImageManipulation\InvalidConfigurationException 32 */ 33 public function __construct(array $cropVariants) 34 { 35 $this->setCropVariants(...$cropVariants); 36 } 37 38 /** 39 * @param string $jsonString 40 * @param array $tcaConfig 41 * @return CropVariantCollection 42 */ 43 public static function create(string $jsonString, array $tcaConfig = []): CropVariantCollection 44 { 45 $persistedCollectionConfig = empty($jsonString) ? [] : json_decode($jsonString, true); 46 if (empty($persistedCollectionConfig) && empty($tcaConfig)) { 47 return self::createEmpty(); 48 } 49 try { 50 if ($tcaConfig === []) { 51 $tcaConfig = (array)$persistedCollectionConfig; 52 } else { 53 if (!is_array($persistedCollectionConfig)) { 54 $persistedCollectionConfig = []; 55 } 56 // Merge selected areas with crop tool configuration 57 reset($persistedCollectionConfig); 58 foreach ($tcaConfig as $id => &$cropVariantConfig) { 59 if (!isset($persistedCollectionConfig[$id])) { 60 $id = key($persistedCollectionConfig); 61 next($persistedCollectionConfig); 62 } 63 if (isset($persistedCollectionConfig[$id]['cropArea'])) { 64 $cropVariantConfig['cropArea'] = $persistedCollectionConfig[$id]['cropArea']; 65 } 66 if (isset($persistedCollectionConfig[$id]['focusArea'], $cropVariantConfig['focusArea'])) { 67 $cropVariantConfig['focusArea'] = $persistedCollectionConfig[$id]['focusArea']; 68 } 69 if (isset($persistedCollectionConfig[$id]['selectedRatio'], $cropVariantConfig['allowedAspectRatios'][$persistedCollectionConfig[$id]['selectedRatio']])) { 70 $cropVariantConfig['selectedRatio'] = $persistedCollectionConfig[$id]['selectedRatio']; 71 } 72 } 73 unset($cropVariantConfig); 74 } 75 $cropVariants = []; 76 foreach ($tcaConfig as $id => $cropVariantConfig) { 77 $cropVariants[] = CropVariant::createFromConfiguration($id, $cropVariantConfig); 78 } 79 return new self($cropVariants); 80 } catch (\Throwable $throwable) { 81 return self::createEmpty(); 82 } 83 } 84 85 /** 86 * @return array 87 * @internal 88 */ 89 public function asArray(): array 90 { 91 $cropVariantsAsArray = []; 92 foreach ($this->cropVariants as $id => $cropVariant) { 93 $cropVariantsAsArray[$id] = $cropVariant->asArray(); 94 } 95 return $cropVariantsAsArray; 96 } 97 98 /** 99 * @param FileInterface $file 100 * @return CropVariantCollection 101 */ 102 public function applyRatioRestrictionToSelectedCropArea(FileInterface $file): CropVariantCollection 103 { 104 $newCollection = clone $this; 105 foreach ($this->cropVariants as $id => $cropVariant) { 106 $newCollection->cropVariants[$id] = $cropVariant->applyRatioRestrictionToSelectedCropArea($file); 107 } 108 return $newCollection; 109 } 110 111 public function __toString() 112 { 113 $filterNonPersistentKeys = function ($key) { 114 if (in_array($key, ['id', 'title', 'allowedAspectRatios', 'coverAreas'], true)) { 115 return false; 116 } 117 return true; 118 }; 119 $cropVariantsAsArray = []; 120 foreach ($this->cropVariants as $id => $cropVariant) { 121 $cropVariantsAsArray[$id] = array_filter($cropVariant->asArray(), $filterNonPersistentKeys, ARRAY_FILTER_USE_KEY); 122 } 123 return json_encode($cropVariantsAsArray) ?: '[]'; 124 } 125 126 /** 127 * @param string $id 128 * @return Area 129 */ 130 public function getCropArea(string $id = 'default'): Area 131 { 132 if (isset($this->cropVariants[$id])) { 133 return $this->cropVariants[$id]->getCropArea(); 134 } 135 return Area::createEmpty(); 136 } 137 138 /** 139 * @param string $id 140 * @return Area 141 */ 142 public function getFocusArea(string $id = 'default'): Area 143 { 144 if (isset($this->cropVariants[$id]) && $this->cropVariants[$id]->getFocusArea() !== null) { 145 return $this->cropVariants[$id]->getFocusArea(); 146 } 147 return Area::createEmpty(); 148 } 149 150 /** 151 * @return CropVariantCollection 152 */ 153 protected static function createEmpty(): CropVariantCollection 154 { 155 return new self([]); 156 } 157 158 /** 159 * @param CropVariant ...$cropVariants 160 * @throws \TYPO3\CMS\Core\Imaging\ImageManipulation\InvalidConfigurationException 161 */ 162 protected function setCropVariants(CropVariant ...$cropVariants) 163 { 164 $this->cropVariants = []; 165 foreach ($cropVariants as $cropVariant) { 166 $this->addCropVariant($cropVariant); 167 } 168 } 169 170 /** 171 * @param CropVariant $cropVariant 172 * @throws InvalidConfigurationException 173 */ 174 protected function addCropVariant(CropVariant $cropVariant) 175 { 176 if (isset($this->cropVariants[$cropVariant->getId()])) { 177 throw new InvalidConfigurationException(sprintf('Crop variant with with duplicate ID (%s) is configured. Make sure all configured cropVariants have different ids.', $cropVariant->getId()), 1485284352); 178 } 179 $this->cropVariants[$cropVariant->getId()] = $cropVariant; 180 } 181} 182