[TASK] Use ImageInfo class instead of direct PHP GD call
[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\DriverInterface;
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\Type\File\ImageInfo;
26 use TYPO3\CMS\Core\Utility\CommandUtility;
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
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 DriverInterface $driver
57 * @param ProcessedFile $processedFile
58 * @param File $file
59 * @param string $taskType
60 * @param array $configuration
61 */
62 public function processFile(FileProcessingService $fileProcessingService, DriverInterface $driver, ProcessedFile $processedFile, File $file, $taskType, array $configuration)
63 {
64 if ($taskType !== 'Image.Preview' && $taskType !== 'Image.CropScaleMask') {
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 $configuration = $processedFile->getProcessingConfiguration();
83 switch ($taskType) {
84 case 'Image.Preview':
85 $this->resizeImage($temporaryFileName, $temporaryFileNameForResizedThumb, $configuration);
86 break;
87
88 case 'Image.CropScaleMask':
89 $this->cropScaleImage($temporaryFileName, $temporaryFileNameForResizedThumb, $configuration);
90 break;
91 }
92 GeneralUtility::unlink_tempfile($temporaryFileName);
93 if (is_file($temporaryFileNameForResizedThumb)) {
94 $processedFile->setName($this->getTargetFileName($processedFile));
95 $imageInfo = GeneralUtility::makeInstance(ImageInfo::class, $temporaryFileNameForResizedThumb);
96 $processedFile->updateProperties(
97 [
98 'width' => $imageInfo->getWidth(),
99 'height' => $imageInfo->getHeight(),
100 'size' => filesize($temporaryFileNameForResizedThumb),
101 'checksum' => $processedFile->getTask()->getConfigurationChecksum()
102 ]
103 );
104 $processedFile->updateWithLocalFile($temporaryFileNameForResizedThumb);
105 GeneralUtility::unlink_tempfile($temporaryFileNameForResizedThumb);
106
107 /** @var ProcessedFileRepository $processedFileRepository */
108 $processedFileRepository = GeneralUtility::makeInstance(ProcessedFileRepository::class);
109 $processedFileRepository->add($processedFile);
110 }
111 }
112
113 /**
114 * @param ProcessedFile $processedFile
115 * @param string $prefix
116 * @return string
117 */
118 protected function getTargetFileName(ProcessedFile $processedFile, $prefix = 'preview_')
119 {
120 return $prefix . $processedFile->getTask()->getConfigurationChecksum() . '_' . $processedFile->getOriginalFile()->getNameWithoutExtension() . '.jpg';
121 }
122
123 /**
124 * @param string $originalFileName
125 * @param string $temporaryFileName
126 * @param array $configuration
127 */
128 protected function resizeImage($originalFileName, $temporaryFileName, $configuration)
129 {
130 // Create the temporary file
131 if (empty($GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_enabled'])) {
132 return;
133 }
134
135 if (file_exists($originalFileName)) {
136 $arguments = CommandUtility::escapeShellArguments([
137 'width' => $configuration['width'],
138 'height' => $configuration['height'],
139 'originalFileName' => $originalFileName,
140 'temporaryFileName' => $temporaryFileName,
141 ]);
142 $parameters = '-sample ' . $arguments['width'] . 'x' . $arguments['height'] . ' '
143 . $arguments['originalFileName'] . '[0] ' . $arguments['temporaryFileName'];
144
145 $cmd = CommandUtility::imageMagickCommand('convert', $parameters) . ' 2>&1';
146 CommandUtility::exec($cmd);
147 }
148
149 if (!file_exists($temporaryFileName)) {
150 // Create a error image
151 $graphicalFunctions = $this->getGraphicalFunctionsObject();
152 $graphicalFunctions->getTemporaryImageWithText($temporaryFileName, 'No thumb', 'generated!', basename($originalFileName));
153 }
154 }
155
156 /**
157 * cropScaleImage
158 *
159 * @param string $originalFileName
160 * @param string $temporaryFileName
161 * @param array $configuration
162 */
163 protected function cropScaleImage($originalFileName, $temporaryFileName, $configuration)
164 {
165 if (file_exists($originalFileName)) {
166 /** @var $gifBuilder GifBuilder */
167 $gifBuilder = GeneralUtility::makeInstance(GifBuilder::class);
168 $gifBuilder->init();
169
170 $options = $this->getConfigurationForImageCropScaleMask($configuration, $gifBuilder);
171 $info = $gifBuilder->getImageDimensions($originalFileName);
172 $data = $gifBuilder->getImageScale($info, $configuration['width'], $configuration['height'], $options);
173
174 $info[0] = $data[0];
175 $info[1] = $data[1];
176 $frame = '';
177 $params = $gifBuilder->cmds['jpg'];
178
179 // Cropscaling:
180 if ($data['crs']) {
181 if (!$data['origW']) {
182 $data['origW'] = $data[0];
183 }
184 if (!$data['origH']) {
185 $data['origH'] = $data[1];
186 }
187 $offsetX = (int)(($data[0] - $data['origW']) * ($data['cropH'] + 100) / 200);
188 $offsetY = (int)(($data[1] - $data['origH']) * ($data['cropV'] + 100) / 200);
189 $params .= ' -crop ' . $data['origW'] . 'x' . $data['origH'] . '+' . $offsetX . '+' . $offsetY . '! ';
190 }
191 $command = $gifBuilder->scalecmd . ' ' . $info[0] . 'x' . $info[1] . '! ' . $params . ' ';
192 $gifBuilder->imageMagickExec($originalFileName, $temporaryFileName, $command, $frame);
193 }
194 if (!file_exists($temporaryFileName)) {
195 // Create a error image
196 $graphicalFunctions = $this->getGraphicalFunctionsObject();
197 $graphicalFunctions->getTemporaryImageWithText($temporaryFileName, 'No thumb', 'generated!', basename($originalFileName));
198 }
199 }
200
201 /**
202 * Get configuration for ImageCropScaleMask processing
203 *
204 * @param array $configuration
205 * @param GifBuilder $gifBuilder
206 * @return array
207 */
208 protected function getConfigurationForImageCropScaleMask(array $configuration, GifBuilder $gifBuilder)
209 {
210 if (!empty($configuration['useSample'])) {
211 $gifBuilder->scalecmd = '-sample';
212 }
213 $options = [];
214 if (!empty($configuration['maxWidth'])) {
215 $options['maxW'] = $configuration['maxWidth'];
216 }
217 if (!empty($configuration['maxHeight'])) {
218 $options['maxH'] = $configuration['maxHeight'];
219 }
220 if (!empty($configuration['minWidth'])) {
221 $options['minW'] = $configuration['minWidth'];
222 }
223 if (!empty($configuration['minHeight'])) {
224 $options['minH'] = $configuration['minHeight'];
225 }
226
227 $options['noScale'] = $configuration['noScale'];
228
229 return $options;
230 }
231
232 /**
233 * @return LocalImageProcessor
234 */
235 protected function getProcessor()
236 {
237 if (!$this->processor) {
238 $this->processor = GeneralUtility::makeInstance(LocalImageProcessor::class);
239 }
240 return $this->processor;
241 }
242
243 /**
244 * @return GraphicalFunctions
245 */
246 protected function getGraphicalFunctionsObject()
247 {
248 static $graphicalFunctionsObject = null;
249 if ($graphicalFunctionsObject === null) {
250 $graphicalFunctionsObject = GeneralUtility::makeInstance(GraphicalFunctions::class);
251 $graphicalFunctionsObject->init();
252 }
253 return $graphicalFunctionsObject;
254 }
255 }