2 namespace TYPO3\CMS\Frontend\Imaging
;
5 * This file is part of the TYPO3 CMS project.
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.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use TYPO3\CMS\Core\
Resource\File
;
18 use TYPO3\CMS\Core\
Resource\ProcessedFile
;
19 use TYPO3\CMS\Core\TypoScript\TemplateService
;
20 use TYPO3\CMS\Core\Utility\GeneralUtility
;
21 use TYPO3\CMS\Core\Utility\MathUtility
;
22 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
;
23 use TYPO3\CMS\Core\Imaging\GraphicalFunctions
;
24 use TYPO3\CMS\Core\Utility\File\BasicFileUtility
;
29 * Generating gif/png-files from TypoScript
30 * Used by the menu-objects and imgResource in TypoScript.
32 * This class allows for advanced rendering of images with various layers of images, text and graphical primitives.
33 * The concept is known from TypoScript as "GIFBUILDER" where you can define a "numerical array" (TypoScript term as well) of "GIFBUILDER OBJECTS" (like "TEXT", "IMAGE", etc.) and they will be rendered onto an image one by one.
34 * The name "GIFBUILDER" comes from the time where GIF was the only file format supported. PNG is just as well to create today (configured with TYPO3_CONF_VARS[GFX])
35 * Not all instances of this class is truely building gif/png files by layers; You may also see the class instantiated for the purpose of using the scaling functions in the parent class.
37 * Here is an example of how to use this class (from tslib_content.php, function getImgResource):
39 * $gifCreator = GeneralUtility::makeInstance(\TYPO3\CMS\Frontend\Imaging\GifBuilder::class);
40 * $gifCreator->init();
42 * if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
43 * $gifCreator->start($fileArray, $this->data);
44 * $theImage = $gifCreator->gifBuild();
46 * return $gifCreator->getImageDimensions($theImage);
48 class GifBuilder
extends GraphicalFunctions
{
84 * This holds the operational setup for gifbuilder. Basically this is a TypoScript array with properties.
88 public $setup = array();
91 * Contains all text strings used on this image
95 public $combinedTextStrings = array();
98 * Contains all filenames (basename without extension) used on this image
102 public $combinedFileNames = array();
105 * This is the array from which data->field: [key] is fetched. So this is the current record!
109 public $data = array();
114 public $objBB = array();
119 public $myClassName = 'gifbuilder';
124 public $charRangeMap = array();
129 public $XY = array();
134 public $OFFSET = array();
137 * @var ContentObjectRenderer
144 public $defaultWorkArea = array();
147 * Initialization of the GIFBUILDER objects, in particular TEXT and IMAGE. This includes finding the bounding box, setting dimensions and offset values before the actual rendering is started.
148 * Modifies the ->setup, ->objBB internal arrays
149 * Should be called after the ->init() function which initializes the parent class functions/variables in general.
150 * The class \TYPO3\CMS\Frontend\ContentObject\Menu\GraphicalMenuContentObject also uses gifbuilder and here there is an interesting use since the function findLargestDims() from that class calls the init() and start() functions to find the total dimensions before starting the rendering of the images.
152 * @param array $conf TypoScript properties for the GIFBUILDER session. Stored internally in the variable ->setup
153 * @param array $data The current data record from \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer. Stored internally in the variable ->data
155 * @see ContentObjectRenderer::getImgResource(), \TYPO3\CMS\Frontend\ContentObject\Menu\GraphicalMenuContentObject::makeGifs(), \TYPO3\CMS\Frontend\ContentObject\Menu\GraphicalMenuContentObject::findLargestDims()
157 public function start($conf, $data) {
158 if (is_array($conf)) {
159 $this->setup
= $conf;
161 $this->cObj
= GeneralUtility
::makeInstance(ContentObjectRenderer
::class);
162 $this->cObj
->start($this->data
);
163 // Hook preprocess gifbuilder conf
164 // Added by Julle for 3.8.0
166 // Let's you pre-process the gifbuilder configuration. for
167 // example you can split a string up into lines and render each
168 // line as TEXT obj, see extension julle_gifbconf
169 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_gifbuilder.php']['gifbuilder-ConfPreProcess'])) {
170 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_gifbuilder.php']['gifbuilder-ConfPreProcess'] as $_funcRef) {
171 $_params = $this->setup
;
172 $this->setup
= GeneralUtility
::callUserFunction($_funcRef, $_params, $this);
175 // Initializing global Char Range Map
176 $this->charRangeMap
= array();
177 if (is_array($GLOBALS['TSFE']->tmpl
->setup
['_GIFBUILDER.']['charRangeMap.'])) {
178 foreach ($GLOBALS['TSFE']->tmpl
->setup
['_GIFBUILDER.']['charRangeMap.'] as $cRMcfgkey => $cRMcfg) {
179 if (is_array($cRMcfg)) {
181 $cRMkey = $GLOBALS['TSFE']->tmpl
->setup
['_GIFBUILDER.']['charRangeMap.'][substr($cRMcfgkey, 0, -1)];
182 $this->charRangeMap
[$cRMkey] = array();
183 $this->charRangeMap
[$cRMkey]['charMapConfig'] = $cRMcfg['charMapConfig.'];
184 $this->charRangeMap
[$cRMkey]['cfgKey'] = substr($cRMcfgkey, 0, -1);
185 $this->charRangeMap
[$cRMkey]['multiplicator'] = (double) $cRMcfg['fontSizeMultiplicator'];
186 $this->charRangeMap
[$cRMkey]['pixelSpace'] = (int)$cRMcfg['pixelSpaceFontSizeRef'];
190 // Getting sorted list of TypoScript keys from setup.
191 $sKeyArray = TemplateService
::sortedKeyList($this->setup
);
192 // Setting the background color, passing it through stdWrap
193 if ($conf['backColor.'] ||
$conf['backColor']) {
194 $this->setup
['backColor'] = isset($this->setup
['backColor.']) ?
trim($this->cObj
->stdWrap($this->setup
['backColor'], $this->setup
['backColor.'])) : $this->setup
['backColor'];
196 if (!$this->setup
['backColor']) {
197 $this->setup
['backColor'] = 'white';
199 if ($conf['transparentColor.'] ||
$conf['transparentColor']) {
200 $this->setup
['transparentColor_array'] = isset($this->setup
['transparentColor.']) ?
explode('|', trim($this->cObj
->stdWrap($this->setup
['transparentColor'], $this->setup
['transparentColor.']))) : explode('|', trim($this->setup
['transparentColor']));
202 if (isset($this->setup
['transparentBackground.'])) {
203 $this->setup
['transparentBackground'] = $this->cObj
->stdWrap($this->setup
['transparentBackground'], $this->setup
['transparentBackground.']);
205 if (isset($this->setup
['reduceColors.'])) {
206 $this->setup
['reduceColors'] = $this->cObj
->stdWrap($this->setup
['reduceColors'], $this->setup
['reduceColors.']);
208 // Set default dimensions
209 if (isset($this->setup
['XY.'])) {
210 $this->setup
['XY'] = $this->cObj
->stdWrap($this->setup
['XY'], $this->setup
['XY.']);
212 if (!$this->setup
['XY']) {
213 $this->setup
['XY'] = '120,50';
215 // Checking TEXT and IMAGE objects for files. If any errors the objects are cleared.
216 // The Bounding Box for the objects is stored in an array
217 foreach ($sKeyArray as $theKey) {
218 $theValue = $this->setup
[$theKey];
219 if ((int)$theKey && ($conf = $this->setup
[$theKey . '.'])) {
220 // Swipes through TEXT and IMAGE-objects
223 if ($this->setup
[$theKey . '.'] = $this->checkTextObj($conf)) {
224 // Adjust font width if max size is set:
225 $maxWidth = isset($this->setup
[$theKey . '.']['maxWidth.']) ?
$this->cObj
->stdWrap($this->setup
[$theKey . '.']['maxWidth'], $this->setup
[$theKey . '.']['maxWidth.']) : $this->setup
[$theKey . '.']['maxWidth'];
227 $this->setup
[$theKey . '.']['fontSize'] = $this->fontResize($this->setup
[$theKey . '.']);
229 // Calculate bounding box:
230 $txtInfo = $this->calcBBox($this->setup
[$theKey . '.']);
231 $this->setup
[$theKey . '.']['BBOX'] = $txtInfo;
232 $this->objBB
[$theKey] = $txtInfo;
233 $this->setup
[$theKey . '.']['imgMap'] = 0;
237 $fileInfo = $this->getResource($conf['file'], $conf['file.']);
239 $this->combinedFileNames
[] = preg_replace('/\\.[[:alnum:]]+$/', '', basename($fileInfo[3]));
240 if ($fileInfo['processedFile'] instanceof ProcessedFile
) {
241 // Use processed file, if a FAL file has been processed by GIFBUILDER (e.g. scaled/cropped)
242 $this->setup
[$theKey . '.']['file'] = $fileInfo['processedFile']->getForLocalProcessing(FALSE);
243 } elseif (!isset($fileInfo['origFile']) && $fileInfo['originalFile'] instanceof File
) {
244 // Use FAL file with getForLocalProcessing to circumvent problems with umlauts, if it is a FAL file (origFile not set)
245 /** @var $originalFile File */
246 $originalFile = $fileInfo['originalFile'];
247 $this->setup
[$theKey . '.']['file'] = $originalFile->getForLocalProcessing(FALSE);
249 // 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)
250 $this->setup
[$theKey . '.']['file'] = $fileInfo[3];
252 $this->setup
[$theKey . '.']['BBOX'] = $fileInfo;
253 $this->objBB
[$theKey] = $fileInfo;
255 $maskInfo = $this->getResource($conf['mask'], $conf['mask.']);
257 // the same selection criteria as regarding fileInfo above apply here
258 if ($maskInfo['processedFile'] instanceof ProcessedFile
) {
259 $this->setup
[$theKey . '.']['mask'] = $maskInfo['processedFile']->getForLocalProcessing(FALSE);
260 } elseif (!isset($maskInfo['origFile']) && $maskInfo['originalFile'] instanceof File
) {
261 /** @var $originalFile File */
262 $originalFile = $maskInfo['originalFile'];
263 $this->setup
[$theKey . '.']['mask'] = $originalFile->getForLocalProcessing(FALSE);
265 $this->setup
[$theKey . '.']['mask'] = $maskInfo[3];
268 $this->setup
[$theKey . '.']['mask'] = '';
272 unset($this->setup
[$theKey . '.']);
276 // Checks if disabled is set... (this is also done in menu.php / imgmenu!!)
278 $cObj = GeneralUtility
::makeInstance(ContentObjectRenderer
::class);
279 $cObj->start($this->data
);
280 if (!$cObj->checkIf($conf['if.'])) {
281 unset($this->setup
[$theKey]);
282 unset($this->setup
[$theKey . '.']);
283 unset($this->objBB
[$theKey]);
288 // Calculate offsets on elements
289 $this->setup
['XY'] = $this->calcOffset($this->setup
['XY']);
290 if (isset($this->setup
['offset.'])) {
291 $this->setup
['offset'] = $this->cObj
->stdWrap($this->setup
['offset'], $this->setup
['offset.']);
293 $this->setup
['offset'] = $this->calcOffset($this->setup
['offset']);
294 if (isset($this->setup
['workArea.'])) {
295 $this->setup
['workArea'] = $this->cObj
->stdWrap($this->setup
['workArea'], $this->setup
['workArea.']);
297 $this->setup
['workArea'] = $this->calcOffset($this->setup
['workArea']);
298 foreach ($sKeyArray as $theKey) {
299 $theValue = $this->setup
[$theKey];
300 if ((int)$theKey && ($conf = $this->setup
[$theKey . '.'])) {
305 if (isset($this->setup
[$theKey . '.']['offset.'])) {
306 $this->setup
[$theKey . '.']['offset'] = $this->cObj
->stdWrap($this->setup
[$theKey . '.']['offset'], $this->setup
[$theKey . '.']['offset.']);
307 unset($this->setup
[$theKey . '.']['offset.']);
309 if ($this->setup
[$theKey . '.']['offset']) {
310 $this->setup
[$theKey . '.']['offset'] = $this->calcOffset($this->setup
[$theKey . '.']['offset']);
316 if (isset($this->setup
[$theKey . '.']['dimensions.'])) {
317 $this->setup
[$theKey . '.']['dimensions'] = $this->cObj
->stdWrap($this->setup
[$theKey . '.']['dimensions'], $this->setup
[$theKey . '.']['dimensions.']);
318 unset($this->setup
[$theKey . '.']['dimensions.']);
320 if ($this->setup
[$theKey . '.']['dimensions']) {
321 $this->setup
[$theKey . '.']['dimensions'] = $this->calcOffset($this->setup
[$theKey . '.']['dimensions']);
325 if (isset($this->setup
[$theKey . '.']['set.'])) {
326 $this->setup
[$theKey . '.']['set'] = $this->cObj
->stdWrap($this->setup
[$theKey . '.']['set'], $this->setup
[$theKey . '.']['set.']);
327 unset($this->setup
[$theKey . '.']['set.']);
329 if ($this->setup
[$theKey . '.']['set']) {
330 $this->setup
[$theKey . '.']['set'] = $this->calcOffset($this->setup
[$theKey . '.']['set']);
334 if (isset($this->setup
[$theKey . '.']['crop.'])) {
335 $this->setup
[$theKey . '.']['crop'] = $this->cObj
->stdWrap($this->setup
[$theKey . '.']['crop'], $this->setup
[$theKey . '.']['crop.']);
336 unset($this->setup
[$theKey . '.']['crop.']);
338 if ($this->setup
[$theKey . '.']['crop']) {
339 $this->setup
[$theKey . '.']['crop'] = $this->calcOffset($this->setup
[$theKey . '.']['crop']);
343 if (isset($this->setup
[$theKey . '.']['width.'])) {
344 $this->setup
[$theKey . '.']['width'] = $this->cObj
->stdWrap($this->setup
[$theKey . '.']['width'], $this->setup
[$theKey . '.']['width.']);
345 unset($this->setup
[$theKey . '.']['width.']);
347 if ($this->setup
[$theKey . '.']['width']) {
348 $this->setup
[$theKey . '.']['width'] = $this->calcOffset($this->setup
[$theKey . '.']['width']);
350 if (isset($this->setup
[$theKey . '.']['height.'])) {
351 $this->setup
[$theKey . '.']['height'] = $this->cObj
->stdWrap($this->setup
[$theKey . '.']['height'], $this->setup
[$theKey . '.']['height.']);
352 unset($this->setup
[$theKey . '.']['height.']);
354 if ($this->setup
[$theKey . '.']['height']) {
355 $this->setup
[$theKey . '.']['height'] = $this->calcOffset($this->setup
[$theKey . '.']['height']);
362 $XY = GeneralUtility
::intExplode(',', $this->setup
['XY']);
363 $maxWidth = isset($this->setup
['maxWidth.']) ?
(int)$this->cObj
->stdWrap($this->setup
['maxWidth'], $this->setup
['maxWidth.']) : (int)$this->setup
['maxWidth'];
364 $maxHeight = isset($this->setup
['maxHeight.']) ?
(int)$this->cObj
->stdWrap($this->setup
['maxHeight'], $this->setup
['maxHeight.']) : (int)$this->setup
['maxHeight'];
365 $XY[0] = MathUtility
::forceIntegerInRange($XY[0], 1, $maxWidth ?
: 2000);
366 $XY[1] = MathUtility
::forceIntegerInRange($XY[1], 1, $maxHeight ?
: 2000);
370 $this->OFFSET
= GeneralUtility
::intExplode(',', $this->setup
['offset']);
371 // this sets the workArea
372 $this->setWorkArea($this->setup
['workArea']);
373 // this sets the default to the current;
374 $this->defaultWorkArea
= $this->workArea
;
379 * Initiates the image file generation if ->setup is TRUE and if the file did not exist already.
380 * Gets filename from fileName() and if file exists in typo3temp/ dir it will - of course - not be rendered again.
381 * Otherwise rendering means calling ->make(), then ->output(), then ->destroy()
383 * @return string The filename for the created GIF/PNG file. The filename will be prefixed "GB_
384 * @see make(), fileName()
386 public function gifBuild() {
388 // Relative to PATH_site
389 $gifFileName = $this->fileName('GB/');
391 if (!file_exists($gifFileName)) {
392 // Create temporary directory if not done:
393 $this->createTempSubDir('GB/');
396 $this->output($gifFileName);
405 * The actual rendering of the image file.
406 * Basically sets the dimensions, the background color, the traverses the array of GIFBUILDER objects and finally setting the transparent color if defined.
407 * Creates a GDlib resource in $this->im and works on that
408 * Called by gifBuild()
414 public function make() {
417 // Reset internal properties
418 $this->saveAlphaLayer
= FALSE;
420 $this->im
= imagecreatetruecolor($XY[0], $XY[1]);
423 // Transparent layer as background if set and requirements are met
424 if (!empty($this->setup
['backColor']) && $this->setup
['backColor'] === 'transparent' && $this->png_truecolor
&& !$this->setup
['reduceColors'] && (empty($this->setup
['format']) ||
$this->setup
['format'] === 'png')) {
425 // Set transparency properties
426 imagesavealpha($this->im
, TRUE);
427 // Fill with a transparent background
428 $transparentColor = imagecolorallocatealpha($this->im
, 0, 0, 0, 127);
429 imagefill($this->im
, 0, 0, $transparentColor);
430 // Set internal properties to keep the transparency over the rendering process
431 $this->saveAlphaLayer
= TRUE;
432 // Force PNG in case no format is set
433 $this->setup
['format'] = 'png';
436 // Fill the background with the given color
437 $BGcols = $this->convertColor($this->setup
['backColor']);
438 $Bcolor = ImageColorAllocate($this->im
, $BGcols[0], $BGcols[1], $BGcols[2]);
439 ImageFilledRectangle($this->im
, 0, 0, $XY[0], $XY[1], $Bcolor);
441 // Traverse the GIFBUILDER objects an render each one:
442 if (is_array($this->setup
)) {
443 $sKeyArray = TemplateService
::sortedKeyList($this->setup
);
444 foreach ($sKeyArray as $theKey) {
445 $theValue = $this->setup
[$theKey];
446 if ((int)$theKey && ($conf = $this->setup
[$theKey . '.'])) {
447 // apply stdWrap to all properties, except for TEXT objects
448 // all properties of the TEXT sub-object have already been stdWrap-ped
449 // before in ->checkTextObj()
450 if ($theValue !== 'TEXT') {
451 $isStdWrapped = array();
452 foreach ($conf as $key => $value) {
453 $parameter = rtrim($key, '.');
454 if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
455 $conf[$parameter] = $this->cObj
->stdWrap($conf[$parameter], $conf[$parameter . '.']);
456 $isStdWrapped[$parameter] = 1;
464 $this->maskImageOntoImage($this->im
, $conf, $this->workArea
);
466 $this->copyImageOntoImage($this->im
, $conf, $this->workArea
);
470 if (!$conf['hide']) {
471 if (is_array($conf['shadow.'])) {
472 $isStdWrapped = array();
473 foreach ($conf['shadow.'] as $key => $value) {
474 $parameter = rtrim($key, '.');
475 if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
476 $conf['shadow.'][$parameter] = $this->cObj
->stdWrap($conf[$parameter], $conf[$parameter . '.']);
477 $isStdWrapped[$parameter] = 1;
480 $this->makeShadow($this->im
, $conf['shadow.'], $this->workArea
, $conf);
482 if (is_array($conf['emboss.'])) {
483 $isStdWrapped = array();
484 foreach ($conf['emboss.'] as $key => $value) {
485 $parameter = rtrim($key, '.');
486 if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
487 $conf['emboss.'][$parameter] = $this->cObj
->stdWrap($conf[$parameter], $conf[$parameter . '.']);
488 $isStdWrapped[$parameter] = 1;
491 $this->makeEmboss($this->im
, $conf['emboss.'], $this->workArea
, $conf);
493 if (is_array($conf['outline.'])) {
494 $isStdWrapped = array();
495 foreach ($conf['outline.'] as $key => $value) {
496 $parameter = rtrim($key, '.');
497 if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
498 $conf['outline.'][$parameter] = $this->cObj
->stdWrap($conf[$parameter], $conf[$parameter . '.']);
499 $isStdWrapped[$parameter] = 1;
502 $this->makeOutline($this->im
, $conf['outline.'], $this->workArea
, $conf);
505 $this->makeText($this->im
, $conf, $this->workArea
);
509 if ($this->setup
[$conf['textObjNum']] == 'TEXT' && ($txtConf = $this->checkTextObj($this->setup
[$conf['textObjNum'] . '.']))) {
510 $this->makeOutline($this->im
, $conf, $this->workArea
, $txtConf);
514 if ($this->setup
[$conf['textObjNum']] == 'TEXT' && ($txtConf = $this->checkTextObj($this->setup
[$conf['textObjNum'] . '.']))) {
515 $this->makeEmboss($this->im
, $conf, $this->workArea
, $txtConf);
519 if ($this->setup
[$conf['textObjNum']] == 'TEXT' && ($txtConf = $this->checkTextObj($this->setup
[$conf['textObjNum'] . '.']))) {
520 $this->makeShadow($this->im
, $conf, $this->workArea
, $txtConf);
524 $this->makeBox($this->im
, $conf, $this->workArea
);
527 $this->makeEffect($this->im
, $conf);
530 $this->adjust($this->im
, $conf);
533 $this->crop($this->im
, $conf);
536 $this->scale($this->im
, $conf);
540 // this sets the workArea
541 $this->setWorkArea($conf['set']);
543 if (isset($conf['clear'])) {
544 // This sets the current to the default;
545 $this->workArea
= $this->defaultWorkArea
;
549 $this->makeEllipse($this->im
, $conf, $this->workArea
);
555 // Preserve alpha transparency
556 if (!$this->saveAlphaLayer
) {
557 if ($this->setup
['transparentBackground']) {
558 // Auto transparent background is set
559 $Bcolor = ImageColorClosest($this->im
, $BGcols[0], $BGcols[1], $BGcols[2]);
560 imagecolortransparent($this->im
, $Bcolor);
561 } elseif (is_array($this->setup
['transparentColor_array'])) {
562 // Multiple transparent colors are set. This is done via the trick that all transparent colors get
563 // converted to one color and then this one gets set as transparent as png/gif can just have one
564 // transparent color.
565 $Tcolor = $this->unifyColors($this->im
, $this->setup
['transparentColor_array'], (int)$this->setup
['transparentColor.']['closest']);
567 imagecolortransparent($this->im
, $Tcolor);
573 /*********************************************
575 * Various helper functions
577 ********************************************/
579 * Initializing/Cleaning of TypoScript properties for TEXT GIFBUILDER objects
581 * 'cleans' TEXT-object; Checks fontfile and other vital setup
582 * Finds the title if its a 'variable' (instantiates a cObj and loads it with the ->data record)
583 * Performs caseshift if any.
585 * @param array $conf GIFBUILDER object TypoScript properties
586 * @return array Modified $conf array IF the "text" property is not blank
589 public function checkTextObj($conf) {
590 $cObj = GeneralUtility
::makeInstance(ContentObjectRenderer
::class);
591 $cObj->start($this->data
);
592 $isStdWrapped = array();
593 foreach ($conf as $key => $value) {
594 $parameter = rtrim($key, '.');
595 if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
596 $conf[$parameter] = $cObj->stdWrap($conf[$parameter], $conf[$parameter . '.']);
597 $isStdWrapped[$parameter] = 1;
600 $conf['fontFile'] = $this->checkFile($conf['fontFile']);
601 if (!$conf['fontFile']) {
602 $conf['fontFile'] = 'typo3/sysext/core/Resources/Private/Font/nimbus.ttf';
604 if (!$conf['iterations']) {
605 $conf['iterations'] = 1;
607 if (!$conf['fontSize']) {
608 $conf['fontSize'] = 12;
610 // If any kind of spacing applys, we cannot use angles!!
611 if ($conf['spacing'] ||
$conf['wordSpacing']) {
614 if (!isset($conf['antiAlias'])) {
615 $conf['antiAlias'] = 1;
617 $conf['fontColor'] = trim($conf['fontColor']);
619 if (!$conf['doNotStripHTML']) {
620 $conf['text'] = strip_tags($conf['text']);
622 $this->combinedTextStrings
[] = strip_tags($conf['text']);
623 // Max length = 100 if automatic line braks are not defined:
624 if (!isset($conf['breakWidth']) ||
!$conf['breakWidth']) {
625 $tlen = (int)$conf['textMaxLength'] ?
: 100;
626 if ($this->nativeCharset
) {
627 $conf['text'] = $this->csConvObj
->substr($this->nativeCharset
, $conf['text'], 0, $tlen);
629 $conf['text'] = substr($conf['text'], 0, $tlen);
632 if ((string)$conf['text'] != '') {
633 // Char range map thingie:
634 $fontBaseName = basename($conf['fontFile']);
635 if (is_array($this->charRangeMap
[$fontBaseName])) {
636 // Initialize splitRendering array:
637 if (!is_array($conf['splitRendering.'])) {
638 $conf['splitRendering.'] = array();
640 $cfgK = $this->charRangeMap
[$fontBaseName]['cfgKey'];
641 // Do not impose settings if a splitRendering object already exists:
642 if (!isset($conf['splitRendering.'][$cfgK])) {
643 // Set configuration:
644 $conf['splitRendering.'][$cfgK] = 'charRange';
645 $conf['splitRendering.'][$cfgK . '.'] = $this->charRangeMap
[$fontBaseName]['charMapConfig'];
646 // Multiplicator of fontsize:
647 if ($this->charRangeMap
[$fontBaseName]['multiplicator']) {
648 $conf['splitRendering.'][$cfgK . '.']['fontSize'] = round($conf['fontSize'] * $this->charRangeMap
[$fontBaseName]['multiplicator']);
650 // Multiplicator of pixelSpace:
651 if ($this->charRangeMap
[$fontBaseName]['pixelSpace']) {
652 $travKeys = array('xSpaceBefore', 'xSpaceAfter', 'ySpaceBefore', 'ySpaceAfter');
653 foreach ($travKeys as $pxKey) {
654 if (isset($conf['splitRendering.'][$cfgK . '.'][$pxKey])) {
655 $conf['splitRendering.'][$cfgK . '.'][$pxKey] = round($conf['splitRendering.'][($cfgK . '.')][$pxKey] * ($conf['fontSize'] / $this->charRangeMap
[$fontBaseName]['pixelSpace']));
661 if (is_array($conf['splitRendering.'])) {
662 foreach ($conf['splitRendering.'] as $key => $value) {
663 if (is_array($conf['splitRendering.'][$key])) {
664 if (isset($conf['splitRendering.'][$key]['fontFile'])) {
665 $conf['splitRendering.'][$key]['fontFile'] = $this->checkFile($conf['splitRendering.'][$key]['fontFile']);
676 * Calculation of offset using "splitCalc" and insertion of dimensions from other GIFBUILDER objects.
679 * Input: 2+2, 2*3, 123, [10.w]
680 * Output: 4,6,123,45 (provided that the width of object in position 10 was 45 pixels wide)
682 * @param string $string The string to resolve/calculate the result of. The string is divided by a comma first and each resulting part is calculated into an integer.
683 * @return string The resolved string with each part (separated by comma) returned separated by comma
686 public function calcOffset($string) {
688 $numbers = GeneralUtility
::trimExplode(',', $this->calculateFunctions($string));
689 foreach ($numbers as $key => $val) {
690 if ((string)$val == (string)(int)$val) {
691 $value[$key] = (int)$val;
693 $value[$key] = $this->calculateValue($val);
696 $string = implode(',', $value);
701 * Returns an "imgResource" creating an instance of the ContentObjectRenderer class and calling ContentObjectRenderer::getImgResource
703 * @param string $file Filename value OR the string "GIFBUILDER", see documentation in TSref for the "datatype" called "imgResource
704 * @param array $fileArray TypoScript properties passed to the function. Either GIFBUILDER properties or imgResource properties, depending on the value of $file (whether that is "GIFBUILDER" or a file reference)
705 * @return array|NULL Returns an array with file information from ContentObjectRenderer::getImgResource()
707 * @see ContentObjectRenderer::getImgResource()
709 public function getResource($file, $fileArray) {
710 if (!GeneralUtility
::inList($this->imageFileExt
, $fileArray['ext'])) {
711 $fileArray['ext'] = $this->gifExtension
;
713 /** @var ContentObjectRenderer $cObj */
714 $cObj = GeneralUtility
::makeInstance(ContentObjectRenderer
::class);
715 $cObj->start($this->data
);
716 return $cObj->getImgResource($file, $fileArray);
720 * Returns the reference to a "resource" in TypoScript.
722 * @param string $file The resource value.
723 * @return string Returns the relative filepath
725 * @see TemplateService::getFileName()
727 public function checkFile($file) {
728 return $GLOBALS['TSFE']->tmpl
->getFileName($file);
732 * Calculates the GIFBUILDER output filename/path based on a serialized, hashed value of this->setup
733 * and prefixes the original filename
734 * also, the filename gets an additional prefix (max 100 characters),
735 * something like "GB_MD5HASH_myfilename_is_very_long_and_such.jpg"
737 * @param string $pre Filename prefix, eg. "GB_
738 * @return string The relative filepath (relative to PATH_site)
741 public function fileName($pre) {
742 /** @var $basicFileFunctions \TYPO3\CMS\Core\Utility\File\BasicFileUtility */
743 $basicFileFunctions = GeneralUtility
::makeInstance(BasicFileUtility
::class);
744 $filePrefix = implode('_', array_merge($this->combinedTextStrings
, $this->combinedFileNames
));
745 $filePrefix = $basicFileFunctions->cleanFileName($filePrefix);
747 return $this->tempPath
. $pre . $filePrefix . '_' . GeneralUtility
::shortMD5(serialize($this->setup
)) . '.' . $this->extension();
751 * Returns the file extension used in the filename
753 * @return string Extension; "jpg" or "gif"/"png
756 public function extension() {
757 switch (strtolower($this->setup
['format'])) {
770 return $this->gifExtension
;
775 * Calculates the value concerning the dimensions of objects.
777 * @param string $string The string to be calculated (e.g. "[20.h]+13")
778 * @return int The calculated value (e.g. "23")
781 protected function calculateValue($string) {
782 $calculatedValue = 0;
783 $parts = GeneralUtility
::splitCalc($string, '+-*/%');
784 foreach ($parts as $part) {
787 if (((string)(int)$theVal) == ((string)$theVal)) {
788 $theVal = (int)$theVal;
789 } elseif ('[' . substr($theVal, 1, -1) . ']' == $theVal) {
790 $objParts = explode('.', substr($theVal, 1, -1));
792 if (isset($this->objBB
[$objParts[0]])) {
793 if ($objParts[1] == 'w') {
794 $theVal = $this->objBB
[$objParts[0]][0];
795 } elseif ($objParts[1] == 'h') {
796 $theVal = $this->objBB
[$objParts[0]][1];
797 } elseif ($objParts[1] == 'lineHeight') {
798 $theVal = $this->objBB
[$objParts[0]][2]['lineHeight'];
800 $theVal = (int)$theVal;
802 } elseif (floatval($theVal)) {
803 $theVal = floatval($theVal);
808 $calculatedValue -= $theVal;
809 } elseif ($sign == '+') {
810 $calculatedValue +
= $theVal;
811 } elseif ($sign == '/' && $theVal) {
812 $calculatedValue = $calculatedValue / $theVal;
813 } elseif ($sign == '*') {
814 $calculatedValue = $calculatedValue * $theVal;
815 } elseif ($sign == '%' && $theVal) {
816 $calculatedValue %
= $theVal;
819 return round($calculatedValue);
823 * Calculates special functions:
824 * + max([10.h], [20.h]) -> gets the maximum of the given values
826 * @param string $string The raw string with functions to be calculated
827 * @return string The calculated values
829 protected function calculateFunctions($string) {
830 if (preg_match_all('#max\\(([^)]+)\\)#', $string, $matches)) {
831 foreach ($matches[1] as $index => $maxExpression) {
832 $string = str_replace($matches[0][$index], $this->calculateMaximum($maxExpression), $string);
839 * Calculates the maximum of a set of values defined like "[10.h],[20.h],1000"
841 * @param string $string The string to be used to calculate the maximum (e.g. "[10.h],[20.h],1000")
842 * @return int The maxium value of the given comma separated and calculated values
844 protected function calculateMaximum($string) {
845 $parts = GeneralUtility
::trimExplode(',', $this->calcOffset($string), TRUE);
846 $maximum = !empty($parts) ?
max($parts) : 0;