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