[TASK] Use constants for 'Image.CropScaleMask' and 'Image.CropScaleMask'
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Resource / OnlineMedia / Processing / PreviewProcessing.php
1 <?php
2 namespace TYPO3\CMS\Core\Resource\OnlineMedia\Processing;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Imaging\GraphicalFunctions;
18 use TYPO3\CMS\Core\Resource\Driver\AbstractDriver;
19 use TYPO3\CMS\Core\Resource\File;
20 use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperRegistry;
21 use TYPO3\CMS\Core\Resource\ProcessedFile;
22 use TYPO3\CMS\Core\Resource\ProcessedFileRepository;
23 use TYPO3\CMS\Core\Resource\Processing\LocalImageProcessor;
24 use TYPO3\CMS\Core\Resource\Service\FileProcessingService;
25 use TYPO3\CMS\Core\Utility\CommandUtility;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27 use TYPO3\CMS\Core\Utility\MathUtility;
28 use TYPO3\CMS\Frontend\Imaging\GifBuilder;
29
30 /**
31 * Preview of Online Media item Processing
32 */
33 class PreviewProcessing
34 {
35 /**
36 * @var LocalImageProcessor
37 */
38 protected $processor;
39
40 /**
41 * @param ProcessedFile $processedFile
42 * @return bool
43 */
44 protected function needsReprocessing($processedFile)
45 {
46 return $processedFile->isNew()
47 || (!$processedFile->usesOriginalFile() && !$processedFile->exists())
48 || $processedFile->isOutdated();
49 }
50
51 /**
52 * Process file
53 * Create static image preview for Online Media item when possible
54 *
55 * @param FileProcessingService $fileProcessingService
56 * @param AbstractDriver $driver
57 * @param ProcessedFile $processedFile
58 * @param File $file
59 * @param string $taskType
60 * @param array $configuration
61 */
62 public function processFile(FileProcessingService $fileProcessingService, AbstractDriver $driver, ProcessedFile $processedFile, File $file, $taskType, array $configuration)
63 {
64 if ($taskType !== ProcessedFile::CONTEXT_IMAGEPREVIEW && $taskType !== ProcessedFile::CONTEXT_IMAGECROPSCALEMASK) {
65 return;
66 }
67 // Check if processing is needed
68 if (!$this->needsReprocessing($processedFile)) {
69 return;
70 }
71 // Check if there is a OnlineMediaHelper registered for this file type
72 $helper = OnlineMediaHelperRegistry::getInstance()->getOnlineMediaHelper($file);
73 if ($helper === false) {
74 return;
75 }
76 // Check if helper provides a preview image
77 $temporaryFileName = $helper->getPreviewImage($file);
78 if (empty($temporaryFileName) || !file_exists($temporaryFileName)) {
79 return;
80 }
81 $temporaryFileNameForResizedThumb = uniqid(PATH_site . 'typo3temp/var/transient/online_media_' . $file->getHashedIdentifier()) . '.jpg';
82 switch ($taskType) {
83 case ProcessedFile::CONTEXT_IMAGEPREVIEW:
84 // Merge custom configuration with default configuration
85 $configuration = array_merge(['width' => 64, 'height' => 64], $configuration);
86 $configuration['width'] = MathUtility::forceIntegerInRange($configuration['width'], 1, 1000);
87 $configuration['height'] = MathUtility::forceIntegerInRange($configuration['height'], 1, 1000);
88 $this->resizeImage($temporaryFileName, $temporaryFileNameForResizedThumb, $configuration);
89 break;
90
91 case ProcessedFile::CONTEXT_IMAGECROPSCALEMASK:
92 $this->cropScaleImage($temporaryFileName, $temporaryFileNameForResizedThumb, $configuration);
93 break;
94 }
95 GeneralUtility::unlink_tempfile($temporaryFileName);
96 if (is_file($temporaryFileNameForResizedThumb)) {
97 $processedFile->setName($this->getTargetFileName($processedFile));
98 list($width, $height) = getimagesize($temporaryFileNameForResizedThumb);
99 $processedFile->updateProperties(
100 [
101 'width' => $width,
102 'height' => $height,
103 'size' => filesize($temporaryFileNameForResizedThumb),
104 'checksum' => $processedFile->getTask()->getConfigurationChecksum()
105 ]
106 );
107 $processedFile->updateWithLocalFile($temporaryFileNameForResizedThumb);
108 GeneralUtility::unlink_tempfile($temporaryFileNameForResizedThumb);
109
110 /** @var ProcessedFileRepository $processedFileRepository */
111 $processedFileRepository = GeneralUtility::makeInstance(ProcessedFileRepository::class);
112 $processedFileRepository->add($processedFile);
113 }
114 }
115
116 /**
117 * @param ProcessedFile $processedFile
118 * @param string $prefix
119 * @return string
120 */
121 protected function getTargetFileName(ProcessedFile $processedFile, $prefix = 'preview_')
122 {
123 return $prefix . $processedFile->getTask()->getConfigurationChecksum() . '_' . $processedFile->getOriginalFile()->getNameWithoutExtension() . '.jpg';
124 }
125
126 /**
127 * @param string $originalFileName
128 * @param string $temporaryFileName
129 * @param array $configuration
130 */
131 protected function resizeImage($originalFileName, $temporaryFileName, $configuration)
132 {
133 // Create the temporary file
134 if (empty($GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_enabled'])) {
135 return;
136 }
137
138 if (file_exists($originalFileName)) {
139 $arguments = CommandUtility::escapeShellArguments([
140 'width' => $configuration['width'],
141 'height' => $configuration['height'],
142 'originalFileName' => $originalFileName,
143 'temporaryFileName' => $temporaryFileName,
144 ]);
145 $parameters = '-sample ' . $arguments['width'] . 'x' . $arguments['height'] . ' '
146 . $arguments['originalFileName'] . '[0] ' . $arguments['temporaryFileName'];
147
148 $cmd = CommandUtility::imageMagickCommand('convert', $parameters) . ' 2>&1';
149 CommandUtility::exec($cmd);
150 }
151
152 if (!file_exists($temporaryFileName)) {
153 // Create a error image
154 $graphicalFunctions = $this->getGraphicalFunctionsObject();
155 $graphicalFunctions->getTemporaryImageWithText($temporaryFileName, 'No thumb', 'generated!', basename($originalFileName));
156 }
157 }
158
159 /**
160 * cropScaleImage
161 *
162 * @param string $originalFileName
163 * @param string $temporaryFileName
164 * @param array $configuration
165 */
166 protected function cropScaleImage($originalFileName, $temporaryFileName, $configuration)
167 {
168 if (file_exists($originalFileName)) {
169 /** @var $gifBuilder GifBuilder */
170 $gifBuilder = GeneralUtility::makeInstance(GifBuilder::class);
171 $gifBuilder->init();
172
173 $options = $this->getConfigurationForImageCropScaleMask($configuration, $gifBuilder);
174 $info = $gifBuilder->getImageDimensions($originalFileName);
175 $data = $gifBuilder->getImageScale($info, $configuration['width'], $configuration['height'], $options);
176
177 $info[0] = $data[0];
178 $info[1] = $data[1];
179 $frame = '';
180 $params = $gifBuilder->cmds['jpg'];
181
182 // Cropscaling:
183 if ($data['crs']) {
184 if (!$data['origW']) {
185 $data['origW'] = $data[0];
186 }
187 if (!$data['origH']) {
188 $data['origH'] = $data[1];
189 }
190 $offsetX = (int)(($data[0] - $data['origW']) * ($data['cropH'] + 100) / 200);
191 $offsetY = (int)(($data[1] - $data['origH']) * ($data['cropV'] + 100) / 200);
192 $params .= ' -crop ' . $data['origW'] . 'x' . $data['origH'] . '+' . $offsetX . '+' . $offsetY . '! ';
193 }
194 $command = $gifBuilder->scalecmd . ' ' . $info[0] . 'x' . $info[1] . '! ' . $params . ' ';
195 $gifBuilder->imageMagickExec($originalFileName, $temporaryFileName, $command, $frame);
196 }
197 if (!file_exists($temporaryFileName)) {
198 // Create a error image
199 $graphicalFunctions = $this->getGraphicalFunctionsObject();
200 $graphicalFunctions->getTemporaryImageWithText($temporaryFileName, 'No thumb', 'generated!', basename($originalFileName));
201 }
202 }
203
204 /**
205 * Get configuration for ImageCropScaleMask processing
206 *
207 * @param array $configuration
208 * @param GifBuilder $gifBuilder
209 * @return array
210 */
211 protected function getConfigurationForImageCropScaleMask(array $configuration, GifBuilder $gifBuilder)
212 {
213 if (!empty($configuration['useSample'])) {
214 $gifBuilder->scalecmd = '-sample';
215 }
216 $options = [];
217 if (!empty($configuration['maxWidth'])) {
218 $options['maxW'] = $configuration['maxWidth'];
219 }
220 if (!empty($configuration['maxHeight'])) {
221 $options['maxH'] = $configuration['maxHeight'];
222 }
223 if (!empty($configuration['minWidth'])) {
224 $options['minW'] = $configuration['minWidth'];
225 }
226 if (!empty($configuration['minHeight'])) {
227 $options['minH'] = $configuration['minHeight'];
228 }
229
230 $options['noScale'] = $configuration['noScale'];
231
232 return $options;
233 }
234
235 /**
236 * @return LocalImageProcessor
237 */
238 protected function getProcessor()
239 {
240 if (!$this->processor) {
241 $this->processor = GeneralUtility::makeInstance(LocalImageProcessor::class);
242 }
243 return $this->processor;
244 }
245
246 /**
247 * @return GraphicalFunctions
248 */
249 protected function getGraphicalFunctionsObject()
250 {
251 static $graphicalFunctionsObject = null;
252 if ($graphicalFunctionsObject === null) {
253 $graphicalFunctionsObject = GeneralUtility::makeInstance(GraphicalFunctions::class);
254 }
255 return $graphicalFunctionsObject;
256 }
257 }