‪TYPO3CMS  ‪main
GifBuilder.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
30 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
32 
56 {
62  public ‪$combinedTextStrings = [];
63 
69  public ‪$combinedFileNames = [];
70 
76  public ‪$data = [];
77 
81  public ‪$objBB = [];
82 
86  public ‪$myClassName = 'gifbuilder';
87 
91  public ‪$charRangeMap = [];
92 
96  public ‪$XY = [];
97 
98  protected ?ContentObjectRenderer ‪$cObj = null;
99 
103  public ‪$defaultWorkArea = [];
104 
114  public function ‪start($conf, ‪$data)
115  {
116  if (is_array($conf)) {
117  $this->setup = $conf;
118  $this->data = ‪$data;
119  $this->cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
120  $this->cObj->start($this->data);
121  // Hook preprocess gifbuilder conf
122  // Added by Julle for 3.8.0
123  //
124  // Lets you pre-process the gifbuilder configuration. for
125  // example you can split a string up into lines and render each
126  // line as TEXT obj, see extension julle_gifbconf
127  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_gifbuilder.php']['gifbuilder-ConfPreProcess'] ?? [] as $_funcRef) {
128  $_params = ‪$this->setup;
129  $ref = $this; // introduced for phpstan to not lose type information when passing $this into callUserFunction
130  $this->setup = GeneralUtility::callUserFunction($_funcRef, $_params, $ref);
131  }
132  // Initializing Char Range Map
133  $this->charRangeMap = [];
134  foreach (($conf['charRangeMap.'] ?? []) as $cRMcfgkey => $cRMcfg) {
135  if (is_array($cRMcfg)) {
136  $cRMkey = $conf['charRangeMap.'][substr($cRMcfgkey, 0, -1)];
137  $this->charRangeMap[$cRMkey] = [];
138  $this->charRangeMap[$cRMkey]['charMapConfig'] = $cRMcfg['charMapConfig.'] ?? [];
139  $this->charRangeMap[$cRMkey]['cfgKey'] = substr($cRMcfgkey, 0, -1);
140  $this->charRangeMap[$cRMkey]['multiplicator'] = (float)$cRMcfg['fontSizeMultiplicator'];
141  $this->charRangeMap[$cRMkey]['pixelSpace'] = (int)$cRMcfg['pixelSpaceFontSizeRef'];
142  }
143  }
144  // Getting sorted list of TypoScript keys from setup.
145  $sKeyArray = ArrayUtility::filterAndSortByNumericKeys($this->setup);
146  // Setting the background color, passing it through stdWrap
147  $this->setup['backColor'] = $this->cObj->stdWrapValue('backColor', $this->setup ?? []);
148  if (!$this->setup['backColor']) {
149  $this->setup['backColor'] = 'white';
150  }
151  $this->setup['transparentColor_array'] = explode('|', trim((string)$this->cObj->stdWrapValue('transparentColor', $this->setup ?? [])));
152  $this->setup['transparentBackground'] = $this->cObj->stdWrapValue('transparentBackground', $this->setup ?? []);
153  $this->setup['reduceColors'] = $this->cObj->stdWrapValue('reduceColors', $this->setup ?? []);
154  // Set default dimensions
155  $this->setup['XY'] = $this->cObj->stdWrapValue('XY', $this->setup ?? []);
156  if (!$this->setup['XY']) {
157  $this->setup['XY'] = '120,50';
158  }
159  // Checking TEXT and IMAGE objects for files. If any errors the objects are cleared.
160  // The Bounding Box for the objects is stored in an array
161  foreach ($sKeyArray as $theKey) {
162  $theValue = $this->setup[$theKey];
163  if ((int)$theKey && ($conf = $this->setup[$theKey . '.'] ?? [])) {
164  // Swipes through TEXT and IMAGE-objects
165  switch ($theValue) {
166  case 'TEXT':
167  if ($this->setup[$theKey . '.'] = $this->‪checkTextObj($conf)) {
168  // Adjust font width if max size is set:
169  $maxWidth = $this->cObj->stdWrapValue('maxWidth', $this->setup[$theKey . '.'] ?? []);
170  if ($maxWidth) {
171  $this->setup[$theKey . '.']['fontSize'] = $this->‪fontResize($this->setup[$theKey . '.']);
172  }
173  // Calculate bounding box:
174  $txtInfo = $this->‪calcBBox($this->setup[$theKey . '.']);
175  $this->setup[$theKey . '.']['BBOX'] = $txtInfo;
176  $this->objBB[$theKey] = $txtInfo;
177  $this->setup[$theKey . '.']['imgMap'] = 0;
178  }
179  break;
180  case 'IMAGE':
181  $fileInfo = $this->‪getResource($conf['file'] ?? '', $conf['file.'] ?? []);
182  if ($fileInfo) {
183  $this->combinedFileNames[] = preg_replace('/\\.[[:alnum:]]+$/', '', ‪PathUtility::basename($fileInfo[3]));
184  if (($fileInfo['processedFile'] ?? null) instanceof ‪ProcessedFile) {
185  // Use processed file, if a FAL file has been processed by GIFBUILDER (e.g. scaled/cropped)
186  $this->setup[$theKey . '.']['file'] = $fileInfo['processedFile']->getForLocalProcessing(false);
187  } elseif (!isset($fileInfo['origFile']) && ($fileInfo['originalFile'] ?? null) instanceof ‪File) {
188  // Use FAL file with getForLocalProcessing to circumvent problems with umlauts, if it is a FAL file (origFile not set)
189  $originalFile = $fileInfo['originalFile'];
190  $this->setup[$theKey . '.']['file'] = $originalFile->getForLocalProcessing(false);
191  } else {
192  // Use normal path from fileInfo if it is a non-FAL file (even non-FAL files have originalFile set, but only non-FAL files have origFile set)
193  $this->setup[$theKey . '.']['file'] = $fileInfo[3];
194  }
195 
196  // only pass necessary parts of fileInfo further down, to not incorporate facts as
197  // CropScaleMask runs in this request, that may not occur in subsequent calls and change
198  // the md5 of the generated file name
199  $essentialFileInfo = $fileInfo;
200  unset($essentialFileInfo['originalFile'], $essentialFileInfo['processedFile']);
201 
202  $this->setup[$theKey . '.']['BBOX'] = $essentialFileInfo;
203  $this->objBB[$theKey] = $essentialFileInfo;
204  if ($conf['mask'] ?? false) {
205  $maskInfo = $this->‪getResource($conf['mask'], $conf['mask.'] ?? []);
206  if ($maskInfo) {
207  // the same selection criteria as regarding fileInfo above apply here
208  if (($maskInfo['processedFile'] ?? null) instanceof ‪ProcessedFile) {
209  $this->setup[$theKey . '.']['mask'] = $maskInfo['processedFile']->getForLocalProcessing(false);
210  } elseif (!isset($maskInfo['origFile']) && $maskInfo['originalFile'] instanceof ‪File) {
211  $originalFile = $maskInfo['originalFile'];
212  $this->setup[$theKey . '.']['mask'] = $originalFile->getForLocalProcessing(false);
213  } else {
214  $this->setup[$theKey . '.']['mask'] = $maskInfo[3];
215  }
216  } else {
217  $this->setup[$theKey . '.']['mask'] = '';
218  }
219  }
220  } else {
221  unset($this->setup[$theKey . '.']);
222  }
223  break;
224  }
225  // Checks if disabled is set... (this is also done in menu.php / imgmenu!!)
226  if ($conf['if.'] ?? false) {
227  ‪$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
228  ‪$cObj->start($this->data);
229  if (!‪$cObj->checkIf($conf['if.'])) {
230  unset($this->setup[$theKey]);
231  unset($this->setup[$theKey . '.']);
232  unset($this->objBB[$theKey]);
233  }
234  }
235  }
236  }
237  // Calculate offsets on elements
238  $this->setup['XY'] = $this->‪calcOffset($this->setup['XY']);
239  $this->setup['offset'] = (string)$this->cObj->stdWrapValue('offset', $this->setup ?? []);
240  $this->setup['offset'] = $this->‪calcOffset($this->setup['offset']);
241  $this->setup['workArea'] = (string)$this->cObj->stdWrapValue('workArea', $this->setup ?? []);
242  $this->setup['workArea'] = $this->‪calcOffset($this->setup['workArea']);
243  foreach ($sKeyArray as $theKey) {
244  $theValue = $this->setup[$theKey];
245  if ((int)$theKey && ($this->setup[$theKey . '.'] ?? false)) {
246  switch ($theValue) {
247  case 'TEXT':
248 
249  case 'IMAGE':
250  if (isset($this->setup[$theKey . '.']['offset.'])) {
251  $this->setup[$theKey . '.']['offset'] = $this->cObj->stdWrapValue('offset', $this->setup[$theKey . '.']);
252  unset($this->setup[$theKey . '.']['offset.']);
253  }
254  if ($this->setup[$theKey . '.']['offset'] ?? false) {
255  $this->setup[$theKey . '.']['offset'] = $this->‪calcOffset($this->setup[$theKey . '.']['offset']);
256  }
257  break;
258  case 'BOX':
259 
260  case 'ELLIPSE':
261  if (isset($this->setup[$theKey . '.']['dimensions.'])) {
262  $this->setup[$theKey . '.']['dimensions'] = $this->cObj->stdWrapValue('dimensions', $this->setup[$theKey . '.']);
263  unset($this->setup[$theKey . '.']['dimensions.']);
264  }
265  if ($this->setup[$theKey . '.']['dimensions'] ?? false) {
266  $this->setup[$theKey . '.']['dimensions'] = $this->‪calcOffset($this->setup[$theKey . '.']['dimensions']);
267  }
268  break;
269  case 'WORKAREA':
270  if (isset($this->setup[$theKey . '.']['set.'])) {
271  $this->setup[$theKey . '.']['set'] = $this->cObj->stdWrapValue('set', $this->setup[$theKey . '.']);
272  unset($this->setup[$theKey . '.']['set.']);
273  }
274  if ($this->setup[$theKey . '.']['set'] ?? false) {
275  $this->setup[$theKey . '.']['set'] = $this->‪calcOffset($this->setup[$theKey . '.']['set']);
276  }
277  break;
278  case 'CROP':
279  if (isset($this->setup[$theKey . '.']['crop.'])) {
280  $this->setup[$theKey . '.']['crop'] = $this->cObj->stdWrapValue('crop', $this->setup[$theKey . '.']);
281  unset($this->setup[$theKey . '.']['crop.']);
282  }
283  if ($this->setup[$theKey . '.']['crop'] ?? false) {
284  $this->setup[$theKey . '.']['crop'] = $this->‪calcOffset($this->setup[$theKey . '.']['crop']);
285  }
286  break;
287  case 'SCALE':
288  if (isset($this->setup[$theKey . '.']['width.'])) {
289  $this->setup[$theKey . '.']['width'] = $this->cObj->stdWrapValue('width', $this->setup[$theKey . '.']);
290  unset($this->setup[$theKey . '.']['width.']);
291  }
292  if ($this->setup[$theKey . '.']['width'] ?? false) {
293  $this->setup[$theKey . '.']['width'] = $this->‪calcOffset($this->setup[$theKey . '.']['width']);
294  }
295  if (isset($this->setup[$theKey . '.']['height.'])) {
296  $this->setup[$theKey . '.']['height'] = $this->cObj->stdWrapValue('height', $this->setup[$theKey . '.']);
297  unset($this->setup[$theKey . '.']['height.']);
298  }
299  if ($this->setup[$theKey . '.']['height'] ?? false) {
300  $this->setup[$theKey . '.']['height'] = $this->‪calcOffset($this->setup[$theKey . '.']['height']);
301  }
302  break;
303  }
304  }
305  }
306  // Get trivial data
307  ‪$XY = ‪GeneralUtility::intExplode(',', $this->setup['XY']);
308  $maxWidth = (int)$this->cObj->stdWrapValue('maxWidth', $this->setup ?? []);
309  $maxHeight = (int)$this->cObj->stdWrapValue('maxHeight', $this->setup ?? []);
310  ‪$XY[0] = ‪MathUtility::forceIntegerInRange(‪$XY[0], 1, $maxWidth ?: 2000);
311  ‪$XY[1] = ‪MathUtility::forceIntegerInRange(‪$XY[1], 1, $maxHeight ?: 2000);
312  $this->XY = ‪$XY;
313  $this->w = ‪$XY[0];
314  $this->h = ‪$XY[1];
315  $this->OFFSET = ‪GeneralUtility::intExplode(',', $this->setup['offset']);
316  // this sets the workArea
317  $this->‪setWorkArea($this->setup['workArea']);
318  // this sets the default to the current;
319  $this->defaultWorkArea = ‪$this->workArea;
320  }
321  }
322 
332  public function ‪gifBuild()
333  {
334  if (!$this->setup) {
335  return '';
336  }
337 
338  // Relative to Environment::getPublicPath()
339  $gifFileName = $this->‪fileName('assets/images/');
340 
341  if (!file_exists(‪Environment::getPublicPath() . '/' . $gifFileName)) {
342  // Create temporary directory if not done
343  ‪GeneralUtility::mkdir_deep(‪Environment::getPublicPath() . '/typo3temp/assets/images/');
344  // Create file
345  $this->‪make();
346  $this->‪output(‪Environment::getPublicPath() . '/' . $gifFileName);
347  $this->‪destroy();
348  }
349  return $gifFileName;
350  }
351 
361  public function ‪make()
362  {
363  // Get trivial data
365  // Reset internal properties
366  $this->saveAlphaLayer = false;
367  // Gif-start
368  ‪$im = imagecreatetruecolor(‪$XY[0], ‪$XY[1]);
369  if (‪$im === false) {
370  throw new \RuntimeException('imagecreatetruecolor returned false', 1598350445);
371  }
372  $this->im = ‪$im;
373  $this->w = ‪$XY[0];
374  $this->h = ‪$XY[1];
375  // Transparent layer as background if set and requirements are met
376  if (!empty($this->setup['backColor']) && $this->setup['backColor'] === 'transparent' && !$this->setup['reduceColors'] && (empty($this->setup['format']) || $this->setup['format'] === 'png')) {
377  // Set transparency properties
378  imagesavealpha($this->im, true);
379  // Fill with a transparent background
380  $transparentColor = imagecolorallocatealpha($this->im, 0, 0, 0, 127);
381  imagefill($this->im, 0, 0, $transparentColor);
382  // Set internal properties to keep the transparency over the rendering process
383  $this->saveAlphaLayer = true;
384  // Force PNG in case no format is set
385  $this->setup['format'] = 'png';
386  $BGcols = [];
387  } else {
388  // Fill the background with the given color
389  $BGcols = $this->‪convertColor($this->setup['backColor']);
390  $Bcolor = imagecolorallocate($this->im, $BGcols[0], $BGcols[1], $BGcols[2]);
391  imagefilledrectangle($this->im, 0, 0, ‪$XY[0], ‪$XY[1], $Bcolor);
392  }
393  // Traverse the GIFBUILDER objects and render each one:
394  if (is_array($this->setup)) {
395  $sKeyArray = ArrayUtility::filterAndSortByNumericKeys($this->setup);
396  foreach ($sKeyArray as $theKey) {
397  $theValue = $this->setup[$theKey];
398  if ((int)$theKey && ($conf = $this->setup[$theKey . '.'] ?? [])) {
399  // apply stdWrap to all properties, except for TEXT objects
400  // all properties of the TEXT sub-object have already been stdWrap-ped
401  // before in ->checkTextObj()
402  if ($theValue !== 'TEXT') {
403  $isStdWrapped = [];
404  foreach ($conf as $key => $value) {
405  $parameter = rtrim($key, '.');
406  if (!($isStdWrapped[$parameter] ?? false) && isset($conf[$parameter . '.'])) {
407  $conf[$parameter] = $this->cObj->stdWrapValue($parameter, $conf);
408  $isStdWrapped[$parameter] = 1;
409  }
410  }
411  }
412 
413  switch ($theValue) {
414  case 'IMAGE':
415  if ($conf['mask'] ?? false) {
416  $this->‪maskImageOntoImage($this->im, $conf, $this->workArea);
417  } else {
418  $this->‪copyImageOntoImage($this->im, $conf, $this->workArea);
419  }
420  break;
421  case 'TEXT':
422  if (!($conf['hide'] ?? false)) {
423  if (is_array($conf['shadow.'] ?? null)) {
424  $isStdWrapped = [];
425  foreach ($conf['shadow.'] as $key => $value) {
426  $parameter = rtrim($key, '.');
427  if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
428  $conf['shadow.'][$parameter] = $this->cObj->stdWrapValue($parameter, $conf);
429  $isStdWrapped[$parameter] = 1;
430  }
431  }
432  $this->‪makeShadow($this->im, $conf['shadow.'], $this->workArea, $conf);
433  }
434  if (is_array($conf['emboss.'] ?? null)) {
435  $isStdWrapped = [];
436  foreach ($conf['emboss.'] as $key => $value) {
437  $parameter = rtrim($key, '.');
438  if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
439  $conf['emboss.'][$parameter] = $this->cObj->stdWrapValue($parameter, $conf);
440  $isStdWrapped[$parameter] = 1;
441  }
442  }
443  $this->‪makeEmboss($this->im, $conf['emboss.'], $this->workArea, $conf);
444  }
445  if (is_array($conf['outline.'] ?? null)) {
446  $isStdWrapped = [];
447  foreach ($conf['outline.'] as $key => $value) {
448  $parameter = rtrim($key, '.');
449  if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
450  $conf['outline.'][$parameter] = $this->cObj->stdWrapValue($parameter, $conf);
451  $isStdWrapped[$parameter] = 1;
452  }
453  }
454  $this->‪makeOutline($this->im, $conf['outline.'], $this->workArea, $conf);
455  }
456  $conf['imgMap'] = 1;
457  $this->‪makeText($this->im, $conf, $this->workArea);
458  }
459  break;
460  case 'OUTLINE':
461  if ($this->setup[$conf['textObjNum']] === 'TEXT' && ($txtConf = $this->‪checkTextObj($this->setup[$conf['textObjNum'] . '.']))) {
462  $this->‪makeOutline($this->im, $conf, $this->workArea, $txtConf);
463  }
464  break;
465  case 'EMBOSS':
466  if ($this->setup[$conf['textObjNum']] === 'TEXT' && ($txtConf = $this->‪checkTextObj($this->setup[$conf['textObjNum'] . '.']))) {
467  $this->‪makeEmboss($this->im, $conf, $this->workArea, $txtConf);
468  }
469  break;
470  case 'SHADOW':
471  if ($this->setup[$conf['textObjNum']] === 'TEXT' && ($txtConf = $this->‪checkTextObj($this->setup[$conf['textObjNum'] . '.']))) {
472  $this->‪makeShadow($this->im, $conf, $this->workArea, $txtConf);
473  }
474  break;
475  case 'BOX':
476  $this->‪makeBox($this->im, $conf, $this->workArea);
477  break;
478  case 'EFFECT':
479  $this->‪makeEffect($this->im, $conf);
480  break;
481  case 'ADJUST':
482  $this->‪adjust($this->im, $conf);
483  break;
484  case 'CROP':
485  $this->‪crop($this->im, $conf);
486  break;
487  case 'SCALE':
488  $this->‪scale($this->im, $conf);
489  break;
490  case 'WORKAREA':
491  if ($conf['set']) {
492  // this sets the workArea
493  $this->‪setWorkArea($conf['set']);
494  }
495  if (isset($conf['clear'])) {
496  // This sets the current to the default;
497  $this->workArea = ‪$this->defaultWorkArea;
498  }
499  break;
500  case 'ELLIPSE':
501  $this->‪makeEllipse($this->im, $conf, $this->workArea);
502  break;
503  }
504  }
505  }
506  }
507  // Preserve alpha transparency
508  if (!$this->saveAlphaLayer) {
509  if ($this->setup['transparentBackground']) {
510  // Auto transparent background is set
511  $Bcolor = imagecolorclosest($this->im, $BGcols[0], $BGcols[1], $BGcols[2]);
512  imagecolortransparent($this->im, $Bcolor);
513  } elseif (is_array($this->setup['transparentColor_array'])) {
514  // Multiple transparent colors are set. This is done via the trick that all transparent colors get
515  // converted to one color and then this one gets set as transparent as png/gif can just have one
516  // transparent color.
517  $Tcolor = $this->‪unifyColors($this->im, $this->setup['transparentColor_array'], (bool)($this->setup['transparentColor.']['closest'] ?? false));
518  if ($Tcolor >= 0) {
519  imagecolortransparent($this->im, $Tcolor);
520  }
521  }
522  }
523  }
524 
525  /*********************************************
526  *
527  * Various helper functions
528  *
529  ********************************************/
541  public function ‪checkTextObj($conf)
542  {
543  ‪$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
544  ‪$cObj->start($this->data);
545  $isStdWrapped = [];
546  foreach ($conf as $key => $value) {
547  $parameter = rtrim($key, '.');
548  if (!($isStdWrapped[$parameter] ?? false) && isset($conf[$parameter . '.'])) {
549  $conf[$parameter] = ‪$cObj->stdWrapValue($parameter, $conf);
550  $isStdWrapped[$parameter] = 1;
551  }
552  }
553 
554  if (!is_null($conf['fontFile'] ?? null)) {
555  $conf['fontFile'] = $this->‪checkFile($conf['fontFile']);
556  }
557  if (!($conf['fontFile'] ?? false)) {
558  $conf['fontFile'] = $this->‪checkFile('EXT:core/Resources/Private/Font/nimbus.ttf');
559  }
560  if (!($conf['iterations'] ?? false)) {
561  $conf['iterations'] = 1;
562  }
563  if (!($conf['fontSize'] ?? false)) {
564  $conf['fontSize'] = 12;
565  }
566  // If any kind of spacing applies, we cannot use angles!!
567  if (($conf['spacing'] ?? false) || ($conf['wordSpacing'] ?? false)) {
568  $conf['angle'] = 0;
569  }
570  if (!isset($conf['antiAlias'])) {
571  $conf['antiAlias'] = 1;
572  }
573  $conf['fontColor'] = trim($conf['fontColor'] ?? '');
574  // Strip HTML
575  if (!($conf['doNotStripHTML'] ?? false)) {
576  $conf['text'] = strip_tags($conf['text'] ?? '');
577  }
578  $this->combinedTextStrings[] = strip_tags($conf['text'] ?? '');
579  // Max length = 100 if automatic line breaks are not defined:
580  if (!isset($conf['breakWidth']) || !$conf['breakWidth']) {
581  $tlen = (int)($conf['textMaxLength'] ?? 0) ?: 100;
582  $conf['text'] = mb_substr($conf['text'], 0, $tlen, 'utf-8');
583  }
584  if ((string)$conf['text'] != '') {
585  // Char range map thingie:
586  $fontBaseName = ‪PathUtility::basename($conf['fontFile']);
587  if (is_array($this->charRangeMap[$fontBaseName] ?? null)) {
588  // Initialize splitRendering array:
589  if (!is_array($conf['splitRendering.'])) {
590  $conf['splitRendering.'] = [];
591  }
592  $cfgK = $this->charRangeMap[$fontBaseName]['cfgKey'];
593  // Do not impose settings if a splitRendering object already exists:
594  if (!isset($conf['splitRendering.'][$cfgK])) {
595  // Set configuration:
596  $conf['splitRendering.'][$cfgK] = 'charRange';
597  $conf['splitRendering.'][$cfgK . '.'] = $this->charRangeMap[$fontBaseName]['charMapConfig'];
598  // Multiplicator of fontsize:
599  if ($this->charRangeMap[$fontBaseName]['multiplicator']) {
600  $conf['splitRendering.'][$cfgK . '.']['fontSize'] = round($conf['fontSize'] * $this->charRangeMap[$fontBaseName]['multiplicator']);
601  }
602  // Multiplicator of pixelSpace:
603  if ($this->charRangeMap[$fontBaseName]['pixelSpace']) {
604  $travKeys = ['xSpaceBefore', 'xSpaceAfter', 'ySpaceBefore', 'ySpaceAfter'];
605  foreach ($travKeys as $pxKey) {
606  if (isset($conf['splitRendering.'][$cfgK . '.'][$pxKey])) {
607  $conf['splitRendering.'][$cfgK . '.'][$pxKey] = round($conf['splitRendering.'][$cfgK . '.'][$pxKey] * ($conf['fontSize'] / $this->charRangeMap[$fontBaseName]['pixelSpace']));
608  }
609  }
610  }
611  }
612  }
613  if (is_array($conf['splitRendering.'] ?? null)) {
614  foreach ($conf['splitRendering.'] as $key => $value) {
615  if (is_array($conf['splitRendering.'][$key])) {
616  if (isset($conf['splitRendering.'][$key]['fontFile'])) {
617  $conf['splitRendering.'][$key]['fontFile'] = $this->‪checkFile($conf['splitRendering.'][$key]['fontFile']);
618  }
619  }
620  }
621  }
622  return $conf;
623  }
624  return null;
625  }
626 
638  public function ‪calcOffset($string)
639  {
640  $value = [];
641  $numbers = ‪GeneralUtility::trimExplode(',', $this->‪calculateFunctions($string));
642  foreach ($numbers as $key => $val) {
643  if ((string)$val == (string)(int)$val) {
644  $value[$key] = (int)$val;
645  } else {
646  $value[$key] = $this->‪calculateValue($val);
647  }
648  }
649  $string = implode(',', $value);
650  return $string;
651  }
652 
662  public function ‪getResource($file, $fileArray)
663  {
664  $context = GeneralUtility::makeInstance(Context::class);
665  $deferProcessing = !$context->hasAspect('fileProcessing') || $context->getPropertyFromAspect('fileProcessing', 'deferProcessing');
666  $context->setAspect('fileProcessing', new FileProcessingAspect(false));
667  try {
668  if (!in_array($fileArray['ext'] ?? '', $this->imageFileExt, true)) {
669  $fileArray['ext'] = ‪$this->gifExtension;
670  }
671  ‪$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
672  ‪$cObj->start($this->data);
673  return ‪$cObj->getImgResource($file, $fileArray);
674  } finally {
675  $context->setAspect('fileProcessing', new FileProcessingAspect($deferProcessing));
676  }
677  }
678 
686  public function ‪checkFile($file)
687  {
688  try {
689  return GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize($file, true);
690  } catch (‪Exception $e) {
691  return null;
692  }
693  }
694 
705  public function ‪fileName($pre)
706  {
707  $basicFileFunctions = GeneralUtility::makeInstance(BasicFileUtility::class);
708  $filePrefix = implode('_', array_merge($this->combinedTextStrings, $this->combinedFileNames));
709  $filePrefix = $basicFileFunctions->cleanFileName(ltrim($filePrefix, '.'));
710 
711  // shorten prefix to avoid overly long file names
712  $filePrefix = substr($filePrefix, 0, 100);
713 
714  // Only take relevant parameters to ease the pain for json_encode and make the final string short
715  // so shortMD5 is not as slow. see https://forge.typo3.org/issues/64158
716  $hashInputForFileName = [
717  array_keys($this->setup),
718  $filePrefix,
719  ‪$this->im,
720  ‪$this->w,
721  ‪$this->h,
727  ];
728  return 'typo3temp/' . $pre . $filePrefix . '_' . md5((string)json_encode($hashInputForFileName)) . '.' . $this->‪extension();
729  }
730 
737  public function ‪extension()
738  {
739  switch (strtolower($this->setup['format'] ?? '')) {
740  case 'jpg':
741  case 'jpeg':
742  return 'jpg';
743  case 'png':
744  return 'png';
745  case 'gif':
746  return 'gif';
747  default:
748  return ‪$this->gifExtension;
749  }
750  }
751 
759  protected function ‪calculateValue($string)
760  {
761  $calculatedValue = 0;
762  $parts = GeneralUtility::splitCalc($string, '+-*/%');
763  foreach ($parts as $part) {
764  $theVal = $part[1];
765  $sign = $part[0];
766  if (((string)(int)$theVal) == ((string)$theVal)) {
767  $theVal = (int)$theVal;
768  } elseif ('[' . substr($theVal, 1, -1) . ']' == $theVal) {
769  $objParts = explode('.', substr($theVal, 1, -1));
770  $theVal = 0;
771  if (isset($this->objBB[$objParts[0]], $objParts[1])) {
772  if ($objParts[1] === 'w' && isset($this->objBB[$objParts[0]][0])) {
773  $theVal = $this->objBB[$objParts[0]][0];
774  } elseif ($objParts[1] === 'h' && isset($this->objBB[$objParts[0]][1])) {
775  $theVal = $this->objBB[$objParts[0]][1];
776  } elseif ($objParts[1] === 'lineHeight' && isset($this->objBB[$objParts[0]][2]['lineHeight'])) {
777  $theVal = $this->objBB[$objParts[0]][2]['lineHeight'];
778  }
779  $theVal = (int)$theVal;
780  }
781  } elseif ((float)$theVal) {
782  $theVal = (float)$theVal;
783  } else {
784  $theVal = 0;
785  }
786  if ($sign === '-') {
787  $calculatedValue -= $theVal;
788  } elseif ($sign === '+') {
789  $calculatedValue += $theVal;
790  } elseif ($sign === '/' && $theVal) {
791  $calculatedValue /= $theVal;
792  } elseif ($sign === '*') {
793  $calculatedValue *= $theVal;
794  } elseif ($sign === '%' && $theVal) {
795  $calculatedValue %= $theVal;
796  }
797  }
798  return (int)round($calculatedValue);
799  }
800 
808  protected function ‪calculateFunctions($string)
809  {
810  if (preg_match_all('#max\\(([^)]+)\\)#', $string, $matches)) {
811  foreach ($matches[1] as $index => $maxExpression) {
812  $string = str_replace($matches[0][$index], (string)$this->‪calculateMaximum($maxExpression), $string);
813  }
814  }
815  return $string;
816  }
817 
824  protected function ‪calculateMaximum($string)
825  {
826  $parts = ‪GeneralUtility::trimExplode(',', $this->‪calcOffset($string), true);
827  $maximum = !empty($parts) ? max($parts) : 0;
828  return $maximum;
829  }
830 }
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\checkTextObj
‪array null checkTextObj($conf)
Definition: GifBuilder.php:533
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:916
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\calcOffset
‪string calcOffset($string)
Definition: GifBuilder.php:630
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\fontResize
‪int fontResize($conf)
Definition: GraphicalFunctions.php:868
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\checkFile
‪string null checkFile($file)
Definition: GifBuilder.php:678
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\extension
‪string extension()
Definition: GifBuilder.php:729
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\makeEffect
‪makeEffect(&$im, $conf)
Definition: GraphicalFunctions.php:1532
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\fileName
‪string fileName($pre)
Definition: GifBuilder.php:697
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\convertColor
‪array convertColor($string)
Definition: GraphicalFunctions.php:1938
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\scale
‪scale(&$im, $conf)
Definition: GraphicalFunctions.php:1701
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\makeShadow
‪makeShadow(&$im, $conf, $workArea, $txtConf)
Definition: GraphicalFunctions.php:1374
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\setWorkArea
‪setWorkArea($workArea)
Definition: GraphicalFunctions.php:1732
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\makeOutline
‪makeOutline(&$im, $conf, $workArea, $txtConf)
Definition: GraphicalFunctions.php:1291
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\calculateFunctions
‪string calculateFunctions($string)
Definition: GifBuilder.php:800
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\makeBox
‪makeBox(&$im, $conf, $workArea)
Definition: GraphicalFunctions.php:1472
‪TYPO3\CMS\Core\Context\FileProcessingAspect
Definition: FileProcessingAspect.php:29
‪TYPO3\CMS\Core\Utility\File\BasicFileUtility
Definition: BasicFileUtility.php:35
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\output
‪string output($file)
Definition: GraphicalFunctions.php:2656
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\$w
‪int $w
Definition: GraphicalFunctions.php:256
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\$myClassName
‪string $myClassName
Definition: GifBuilder.php:81
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\maskImageOntoImage
‪maskImageOntoImage(&$im, $conf, $workArea)
Definition: GraphicalFunctions.php:332
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static getPublicPath()
Definition: Environment.php:187
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\$workArea
‪array $workArea
Definition: GraphicalFunctions.php:178
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\$h
‪int $h
Definition: GraphicalFunctions.php:260
‪TYPO3\CMS\Frontend\Resource\FilePathSanitizer
Definition: FilePathSanitizer.php:39
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\make
‪make()
Definition: GifBuilder.php:353
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\$map
‪string $map
Definition: GraphicalFunctions.php:245
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:55
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\calculateValue
‪int calculateValue($string)
Definition: GifBuilder.php:751
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static basename(string $path)
Definition: PathUtility.php:219
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\$XY
‪int[] $XY
Definition: GifBuilder.php:89
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\makeEmboss
‪makeEmboss(&$im, $conf, $workArea, $txtConf)
Definition: GraphicalFunctions.php:1350
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions
Definition: GraphicalFunctions.php:37
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\calcBBox
‪array calcBBox($conf)
Definition: GraphicalFunctions.php:699
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\adjust
‪adjust(&$im, $conf)
Definition: GraphicalFunctions.php:1628
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir_deep
‪static mkdir_deep($directory)
Definition: GeneralUtility.php:1753
‪TYPO3\CMS\Frontend\Imaging
Definition: GifBuilder.php:16
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\makeText
‪makeText(&$im, $conf, $workArea)
Definition: GraphicalFunctions.php:547
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:24
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\$defaultWorkArea
‪array $defaultWorkArea
Definition: GifBuilder.php:95
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\unifyColors
‪int unifyColors(&$img, $colArr, $closest=false)
Definition: GraphicalFunctions.php:2819
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\destroy
‪destroy()
Definition: GraphicalFunctions.php:2698
‪TYPO3\CMS\Frontend\Imaging\GifBuilder
Definition: GifBuilder.php:56
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\getResource
‪array null getResource($file, $fileArray)
Definition: GifBuilder.php:654
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\$data
‪array $data
Definition: GifBuilder.php:73
‪TYPO3\CMS\Core\Resource\ProcessedFile
Definition: ProcessedFile.php:45
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\$objBB
‪array $objBB
Definition: GifBuilder.php:77
‪TYPO3\CMS\Core\Resource\Exception
Definition: Exception.php:22
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\$gifExtension
‪string $gifExtension
Definition: GraphicalFunctions.php:48
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\start
‪start($conf, $data)
Definition: GifBuilder.php:106
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static int[] intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:842
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\$setup
‪array $setup
Definition: GraphicalFunctions.php:252
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\$cObj
‪ContentObjectRenderer $cObj
Definition: GifBuilder.php:91
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\$im
‪resource GdImage $im
Definition: GraphicalFunctions.php:268
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\$combinedTextStrings
‪array $combinedTextStrings
Definition: GifBuilder.php:61
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\gifBuild
‪string gifBuild()
Definition: GifBuilder.php:324
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\$charRangeMap
‪array $charRangeMap
Definition: GifBuilder.php:85
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange(mixed $theInt, int $min, int $max=2000000000, int $defaultValue=0)
Definition: MathUtility.php:34
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\$combinedFileNames
‪array $combinedFileNames
Definition: GifBuilder.php:67
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\makeEllipse
‪makeEllipse(&$im, array $conf, array $workArea)
Definition: GraphicalFunctions.php:1511
‪TYPO3\CMS\Core\Resource\Exception
Definition: AbstractFileOperationException.php:16
‪TYPO3\CMS\Frontend\Imaging\GifBuilder\calculateMaximum
‪int calculateMaximum($string)
Definition: GifBuilder.php:816
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\copyImageOntoImage
‪copyImageOntoImage(&$im, $conf, $workArea)
Definition: GraphicalFunctions.php:417
‪TYPO3\CMS\Core\Imaging\GraphicalFunctions\crop
‪crop(&$im, $conf)
Definition: GraphicalFunctions.php:1663