[TASK] Use short version ?: in ternary operator (PHP 5.3+)
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Imaging / GifBuilder.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Imaging;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 1999-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the text file GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31
32 /**
33 * GIFBUILDER
34 *
35 * Generating gif/png-files from TypoScript
36 * Used by the menu-objects and imgResource in TypoScript.
37 *
38 * This class allows for advanced rendering of images with various layers of images, text and graphical primitives.
39 * 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.
40 * 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])
41 * 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.
42 *
43 * Here is an example of how to use this class (from tslib_content.php, function getImgResource):
44 *
45 * $gifCreator = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Imaging\\GifBuilder');
46 * $gifCreator->init();
47 * $theImage='';
48 * if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
49 * $gifCreator->start($fileArray, $this->data);
50 * $theImage = $gifCreator->gifBuild();
51 * }
52 * return $gifCreator->getImageDimensions($theImage);
53 *
54 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
55 */
56 class GifBuilder extends \TYPO3\CMS\Core\Imaging\GraphicalFunctions {
57
58 // Internal
59 // the main image
60 /**
61 * @todo Define visibility
62 */
63 public $im = '';
64
65 // the image-width
66 /**
67 * @todo Define visibility
68 */
69 public $w = 0;
70
71 // the image-height
72 /**
73 * @todo Define visibility
74 */
75 public $h = 0;
76
77 // map-data
78 /**
79 * @todo Define visibility
80 */
81 public $map;
82
83 /**
84 * @todo Define visibility
85 */
86 public $workArea;
87
88 // This holds the operational setup for gifbuilder. Basically this is a TypoScript array with properties.
89 /**
90 * @todo Define visibility
91 */
92 public $setup = array();
93
94 // Contains all text strings used on this image
95 /**
96 * @todo Define visibility
97 */
98 public $combinedTextStrings = array();
99
100 // Contains all filenames (basename without extension) used on this image
101 /**
102 * @todo Define visibility
103 */
104 public $combinedFileNames = array();
105
106 // This is the array from which data->field: [key] is fetched. So this is the current record!
107 /**
108 * @todo Define visibility
109 */
110 public $data = array();
111
112 /**
113 * @todo Define visibility
114 */
115 public $objBB = array();
116
117 /**
118 * @todo Define visibility
119 */
120 public $myClassName = 'gifbuilder';
121
122 /**
123 * @todo Define visibility
124 */
125 public $charRangeMap = array();
126
127 /**
128 * 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.
129 * Modifies the ->setup, ->objBB internal arrays
130 * Should be called after the ->init() function which initializes the parent class functions/variables in general.
131 * 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.
132 *
133 * @param array $conf TypoScript properties for the GIFBUILDER session. Stored internally in the variable ->setup
134 * @param array $data The current data record from \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer. Stored internally in the variable ->data
135 * @return void
136 * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::getImgResource(), \TYPO3\CMS\Frontend\ContentObject\Menu\GraphicalMenuContentObject::makeGifs(), \TYPO3\CMS\Frontend\ContentObject\Menu\GraphicalMenuContentObject::findLargestDims()
137 * @todo Define visibility
138 */
139 public function start($conf, $data) {
140 if (is_array($conf)) {
141 $this->setup = $conf;
142 $this->data = $data;
143 $this->cObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer');
144 $this->cObj->start($this->data);
145 // Hook preprocess gifbuilder conf
146 // Added by Julle for 3.8.0
147 //
148 // Let's you pre-process the gifbuilder configuration. for
149 // example you can split a string up into lines and render each
150 // line as TEXT obj, see extension julle_gifbconf
151 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_gifbuilder.php']['gifbuilder-ConfPreProcess'])) {
152 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_gifbuilder.php']['gifbuilder-ConfPreProcess'] as $_funcRef) {
153 $_params = $this->setup;
154 $this->setup = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
155 }
156 }
157 // Initializing global Char Range Map
158 $this->charRangeMap = array();
159 if (is_array($GLOBALS['TSFE']->tmpl->setup['_GIFBUILDER.']['charRangeMap.'])) {
160 foreach ($GLOBALS['TSFE']->tmpl->setup['_GIFBUILDER.']['charRangeMap.'] as $cRMcfgkey => $cRMcfg) {
161 if (is_array($cRMcfg)) {
162 // Initializing:
163 $cRMkey = $GLOBALS['TSFE']->tmpl->setup['_GIFBUILDER.']['charRangeMap.'][substr($cRMcfgkey, 0, -1)];
164 $this->charRangeMap[$cRMkey] = array();
165 $this->charRangeMap[$cRMkey]['charMapConfig'] = $cRMcfg['charMapConfig.'];
166 $this->charRangeMap[$cRMkey]['cfgKey'] = substr($cRMcfgkey, 0, -1);
167 $this->charRangeMap[$cRMkey]['multiplicator'] = (double) $cRMcfg['fontSizeMultiplicator'];
168 $this->charRangeMap[$cRMkey]['pixelSpace'] = intval($cRMcfg['pixelSpaceFontSizeRef']);
169 }
170 }
171 }
172 // Getting sorted list of TypoScript keys from setup.
173 $sKeyArray = \TYPO3\CMS\Core\TypoScript\TemplateService::sortedKeyList($this->setup);
174 // Setting the background color, passing it through stdWrap
175 if ($conf['backColor.'] || $conf['backColor']) {
176 $this->setup['backColor'] = isset($this->setup['backColor.']) ? trim($this->cObj->stdWrap($this->setup['backColor'], $this->setup['backColor.'])) : $this->setup['backColor'];
177 }
178 if (!$this->setup['backColor']) {
179 $this->setup['backColor'] = 'white';
180 }
181 if ($conf['transparentColor.'] || $conf['transparentColor']) {
182 $this->setup['transparentColor_array'] = isset($this->setup['transparentColor.']) ? explode('|', trim($this->cObj->stdWrap($this->setup['transparentColor'], $this->setup['transparentColor.']))) : explode('|', trim($this->setup['transparentColor']));
183 }
184 if (isset($this->setup['transparentBackground.'])) {
185 $this->setup['transparentBackground'] = $this->cObj->stdWrap($this->setup['transparentBackground'], $this->setup['transparentBackground.']);
186 }
187 if (isset($this->setup['reduceColors.'])) {
188 $this->setup['reduceColors'] = $this->cObj->stdWrap($this->setup['reduceColors'], $this->setup['reduceColors.']);
189 }
190 // Set default dimensions
191 if (isset($this->setup['XY.'])) {
192 $this->setup['XY'] = $this->cObj->stdWrap($this->setup['XY'], $this->setup['XY.']);
193 }
194 if (!$this->setup['XY']) {
195 $this->setup['XY'] = '120,50';
196 }
197 // Checking TEXT and IMAGE objects for files. If any errors the objects are cleared.
198 // The Bounding Box for the objects is stored in an array
199 foreach ($sKeyArray as $theKey) {
200 $theValue = $this->setup[$theKey];
201 if (intval($theKey) && ($conf = $this->setup[$theKey . '.'])) {
202 // Swipes through TEXT and IMAGE-objects
203 switch ($theValue) {
204 case 'TEXT':
205 if ($this->setup[$theKey . '.'] = $this->checkTextObj($conf)) {
206 // Adjust font width if max size is set:
207 $maxWidth = isset($this->setup[$theKey . '.']['maxWidth.']) ? $this->cObj->stdWrap($this->setup[$theKey . '.']['maxWidth'], $this->setup[$theKey . '.']['maxWidth.']) : $this->setup[$theKey . '.']['maxWidth'];
208 if ($maxWidth) {
209 $this->setup[$theKey . '.']['fontSize'] = $this->fontResize($this->setup[$theKey . '.']);
210 }
211 // Calculate bounding box:
212 $txtInfo = $this->calcBBox($this->setup[$theKey . '.']);
213 $this->setup[$theKey . '.']['BBOX'] = $txtInfo;
214 $this->objBB[$theKey] = $txtInfo;
215 $this->setup[$theKey . '.']['imgMap'] = 0;
216 }
217 break;
218 case 'IMAGE':
219 $fileInfo = $this->getResource($conf['file'], $conf['file.']);
220 if ($fileInfo) {
221 $this->combinedFileNames[] = preg_replace('/\\.[[:alnum:]]+$/', '', basename($fileInfo[3]));
222 $this->setup[$theKey . '.']['file'] = $fileInfo[3];
223 $this->setup[$theKey . '.']['BBOX'] = $fileInfo;
224 $this->objBB[$theKey] = $fileInfo;
225 if ($conf['mask']) {
226 $maskInfo = $this->getResource($conf['mask'], $conf['mask.']);
227 if ($maskInfo) {
228 $this->setup[$theKey . '.']['mask'] = $maskInfo[3];
229 } else {
230 $this->setup[$theKey . '.']['mask'] = '';
231 }
232 }
233 } else {
234 unset($this->setup[$theKey . '.']);
235 }
236 break;
237 }
238 // Checks if disabled is set... (this is also done in menu.php / imgmenu!!)
239 if ($conf['if.']) {
240 $cObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer');
241 $cObj->start($this->data);
242 if (!$cObj->checkIf($conf['if.'])) {
243 unset($this->setup[$theKey]);
244 unset($this->setup[$theKey . '.']);
245 }
246 }
247 }
248 }
249 // Calculate offsets on elements
250 $this->setup['XY'] = $this->calcOffset($this->setup['XY']);
251 if (isset($this->setup['offset.'])) {
252 $this->setup['offset'] = $this->cObj->stdWrap($this->setup['offset'], $this->setup['offset.']);
253 }
254 $this->setup['offset'] = $this->calcOffset($this->setup['offset']);
255 if (isset($this->setup['workArea.'])) {
256 $this->setup['workArea'] = $this->cObj->stdWrap($this->setup['workArea'], $this->setup['workArea.']);
257 }
258 $this->setup['workArea'] = $this->calcOffset($this->setup['workArea']);
259 foreach ($sKeyArray as $theKey) {
260 $theValue = $this->setup[$theKey];
261 if (intval($theKey) && ($conf = $this->setup[$theKey . '.'])) {
262 switch ($theValue) {
263 case 'TEXT':
264
265 case 'IMAGE':
266 if (isset($this->setup[$theKey . '.']['offset.'])) {
267 $this->setup[$theKey . '.']['offset'] = $this->cObj->stdWrap($this->setup[$theKey . '.']['offset'], $this->setup[$theKey . '.']['offset.']);
268 }
269 if ($this->setup[$theKey . '.']['offset']) {
270 $this->setup[$theKey . '.']['offset'] = $this->calcOffset($this->setup[$theKey . '.']['offset']);
271 }
272 break;
273 case 'BOX':
274
275 case 'ELLIPSE':
276 if (isset($this->setup[$theKey . '.']['dimensions.'])) {
277 $this->setup[$theKey . '.']['dimensions'] = $this->cObj->stdWrap($this->setup[$theKey . '.']['dimensions'], $this->setup[$theKey . '.']['dimensions.']);
278 }
279 if ($this->setup[$theKey . '.']['dimensions']) {
280 $this->setup[$theKey . '.']['dimensions'] = $this->calcOffset($this->setup[$theKey . '.']['dimensions']);
281 }
282 break;
283 case 'WORKAREA':
284 if (isset($this->setup[$theKey . '.']['set.'])) {
285 $this->setup[$theKey . '.']['set'] = $this->cObj->stdWrap($this->setup[$theKey . '.']['set'], $this->setup[$theKey . '.']['set.']);
286 }
287 if ($this->setup[$theKey . '.']['set']) {
288 $this->setup[$theKey . '.']['set'] = $this->calcOffset($this->setup[$theKey . '.']['set']);
289 }
290 break;
291 case 'CROP':
292 if (isset($this->setup[$theKey . '.']['crop.'])) {
293 $this->setup[$theKey . '.']['crop'] = $this->cObj->stdWrap($this->setup[$theKey . '.']['crop'], $this->setup[$theKey . '.']['crop.']);
294 }
295 if ($this->setup[$theKey . '.']['crop']) {
296 $this->setup[$theKey . '.']['crop'] = $this->calcOffset($this->setup[$theKey . '.']['crop']);
297 }
298 break;
299 case 'SCALE':
300 if (isset($this->setup[$theKey . '.']['width.'])) {
301 $this->setup[$theKey . '.']['width'] = $this->cObj->stdWrap($this->setup[$theKey . '.']['width'], $this->setup[$theKey . '.']['width.']);
302 }
303 if ($this->setup[$theKey . '.']['width']) {
304 $this->setup[$theKey . '.']['width'] = $this->calcOffset($this->setup[$theKey . '.']['width']);
305 }
306 if (isset($this->setup[$theKey . '.']['height.'])) {
307 $this->setup[$theKey . '.']['height'] = $this->cObj->stdWrap($this->setup[$theKey . '.']['height'], $this->setup[$theKey . '.']['height.']);
308 }
309 if ($this->setup[$theKey . '.']['height']) {
310 $this->setup[$theKey . '.']['height'] = $this->calcOffset($this->setup[$theKey . '.']['height']);
311 }
312 break;
313 }
314 }
315 }
316 // Get trivial data
317 $XY = GeneralUtility::intExplode(',', $this->setup['XY']);
318 $maxWidth = isset($this->setup['maxWidth.']) ? intval($this->cObj->stdWrap($this->setup['maxWidth'], $this->setup['maxWidth.'])) : intval($this->setup['maxWidth']);
319 $maxHeight = isset($this->setup['maxHeight.']) ? intval($this->cObj->stdWrap($this->setup['maxHeight'], $this->setup['maxHeight.'])) : intval($this->setup['maxHeight']);
320 $XY[0] = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($XY[0], 1, $maxWidth ?: 2000);
321 $XY[1] = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($XY[1], 1, $maxHeight ?: 2000);
322 $this->XY = $XY;
323 $this->w = $XY[0];
324 $this->h = $XY[1];
325 $this->OFFSET = GeneralUtility::intExplode(',', $this->setup['offset']);
326 // this sets the workArea
327 $this->setWorkArea($this->setup['workArea']);
328 // this sets the default to the current;
329 $this->defaultWorkArea = $this->workArea;
330 }
331 }
332
333 /**
334 * Initiates the image file generation if ->setup is TRUE and if the file did not exist already.
335 * Gets filename from fileName() and if file exists in typo3temp/ dir it will - of course - not be rendered again.
336 * Otherwise rendering means calling ->make(), then ->output(), then ->destroy()
337 *
338 * @return string The filename for the created GIF/PNG file. The filename will be prefixed "GB_
339 * @see make(), fileName()
340 * @todo Define visibility
341 */
342 public function gifBuild() {
343 if ($this->setup) {
344 // Relative to PATH_site
345 $gifFileName = $this->fileName('GB/');
346 // File exists
347 if (!file_exists($gifFileName)) {
348 // Create temporary directory if not done:
349 $this->createTempSubDir('GB/');
350 // Create file:
351 $this->make();
352 $this->output($gifFileName);
353 $this->destroy();
354 }
355 return $gifFileName;
356 }
357 }
358
359 /**
360 * The actual rendering of the image file.
361 * Basically sets the dimensions, the background color, the traverses the array of GIFBUILDER objects and finally setting the transparent color if defined.
362 * Creates a GDlib resource in $this->im and works on that
363 * Called by gifBuild()
364 *
365 * @return void
366 * @access private
367 * @see gifBuild()
368 * @todo Define visibility
369 */
370 public function make() {
371 // Get trivial data
372 $XY = $this->XY;
373 // Reset internal properties
374 $this->saveAlphaLayer = FALSE;
375 // Gif-start
376 $this->im = imagecreatetruecolor($XY[0], $XY[1]);
377 $this->w = $XY[0];
378 $this->h = $XY[1];
379 // Transparent layer as background if set and requirements are met
380 if (!empty($this->setup['backColor']) && $this->setup['backColor'] === 'transparent' && $this->png_truecolor && !$this->setup['reduceColors'] && (empty($this->setup['format']) || $this->setup['format'] === 'png')) {
381 // Set transparency properties
382 imagesavealpha($this->im, TRUE);
383 // Fill with a transparent background
384 $transparentColor = imagecolorallocatealpha($this->im, 0, 0, 0, 127);
385 imagefill($this->im, 0, 0, $transparentColor);
386 // Set internal properties to keep the transparency over the rendering process
387 $this->saveAlphaLayer = TRUE;
388 // Force PNG in case no format is set
389 $this->setup['format'] = 'png';
390 } else {
391 // Fill the background with the given color
392 $BGcols = $this->convertColor($this->setup['backColor']);
393 $Bcolor = ImageColorAllocate($this->im, $BGcols[0], $BGcols[1], $BGcols[2]);
394 ImageFilledRectangle($this->im, 0, 0, $XY[0], $XY[1], $Bcolor);
395 }
396 // Traverse the GIFBUILDER objects an render each one:
397 if (is_array($this->setup)) {
398 $sKeyArray = \TYPO3\CMS\Core\TypoScript\TemplateService::sortedKeyList($this->setup);
399 foreach ($sKeyArray as $theKey) {
400 $theValue = $this->setup[$theKey];
401 if (intval($theKey) && ($conf = $this->setup[$theKey . '.'])) {
402 // apply stdWrap to all properties, except for TEXT objects
403 // all properties of the TEXT sub-object have already been stdWrap-ped
404 // before in ->checkTextObj()
405 if ($theValue !== 'TEXT') {
406 $isStdWrapped = array();
407 foreach ($conf as $key => $value) {
408 $parameter = rtrim($key, '.');
409 if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
410 $conf[$parameter] = $this->cObj->stdWrap($conf[$parameter], $conf[$parameter . '.']);
411 $isStdWrapped[$parameter] = 1;
412 }
413 }
414 }
415
416 switch ($theValue) {
417 case 'IMAGE':
418 if ($conf['mask']) {
419 $this->maskImageOntoImage($this->im, $conf, $this->workArea);
420 } else {
421 $this->copyImageOntoImage($this->im, $conf, $this->workArea);
422 }
423 break;
424 case 'TEXT':
425 if (!$conf['hide']) {
426 if (is_array($conf['shadow.'])) {
427 $isStdWrapped = array();
428 foreach ($conf['shadow.'] as $key => $value) {
429 $parameter = rtrim($key, '.');
430 if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
431 $conf['shadow.'][$parameter] = $this->cObj->stdWrap($conf[$parameter], $conf[$parameter . '.']);
432 $isStdWrapped[$parameter] = 1;
433 }
434 }
435 $this->makeShadow($this->im, $conf['shadow.'], $this->workArea, $conf);
436 }
437 if (is_array($conf['emboss.'])) {
438 $isStdWrapped = array();
439 foreach ($conf['emboss.'] as $key => $value) {
440 $parameter = rtrim($key, '.');
441 if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
442 $conf['emboss.'][$parameter] = $this->cObj->stdWrap($conf[$parameter], $conf[$parameter . '.']);
443 $isStdWrapped[$parameter] = 1;
444 }
445 }
446 $this->makeEmboss($this->im, $conf['emboss.'], $this->workArea, $conf);
447 }
448 if (is_array($conf['outline.'])) {
449 $isStdWrapped = array();
450 foreach ($conf['outline.'] as $key => $value) {
451 $parameter = rtrim($key, '.');
452 if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
453 $conf['outline.'][$parameter] = $this->cObj->stdWrap($conf[$parameter], $conf[$parameter . '.']);
454 $isStdWrapped[$parameter] = 1;
455 }
456 }
457 $this->makeOutline($this->im, $conf['outline.'], $this->workArea, $conf);
458 }
459 $conf['imgMap'] = 1;
460 $this->makeText($this->im, $conf, $this->workArea);
461 }
462 break;
463 case 'OUTLINE':
464 if ($this->setup[$conf['textObjNum']] == 'TEXT' && ($txtConf = $this->checkTextObj($this->setup[$conf['textObjNum'] . '.']))) {
465 $this->makeOutline($this->im, $conf, $this->workArea, $txtConf);
466 }
467 break;
468 case 'EMBOSS':
469 if ($this->setup[$conf['textObjNum']] == 'TEXT' && ($txtConf = $this->checkTextObj($this->setup[$conf['textObjNum'] . '.']))) {
470 $this->makeEmboss($this->im, $conf, $this->workArea, $txtConf);
471 }
472 break;
473 case 'SHADOW':
474 if ($this->setup[$conf['textObjNum']] == 'TEXT' && ($txtConf = $this->checkTextObj($this->setup[$conf['textObjNum'] . '.']))) {
475 $this->makeShadow($this->im, $conf, $this->workArea, $txtConf);
476 }
477 break;
478 case 'BOX':
479 $this->makeBox($this->im, $conf, $this->workArea);
480 break;
481 case 'EFFECT':
482 $this->makeEffect($this->im, $conf);
483 break;
484 case 'ADJUST':
485 $this->adjust($this->im, $conf);
486 break;
487 case 'CROP':
488 $this->crop($this->im, $conf);
489 break;
490 case 'SCALE':
491 $this->scale($this->im, $conf);
492 break;
493 case 'WORKAREA':
494 if ($conf['set']) {
495 // this sets the workArea
496 $this->setWorkArea($conf['set']);
497 }
498 if (isset($conf['clear'])) {
499 // This sets the current to the default;
500 $this->workArea = $this->defaultWorkArea;
501 }
502 break;
503 case 'ELLIPSE':
504 $this->makeEllipse($this->im, $conf, $this->workArea);
505 break;
506 }
507 }
508 }
509 }
510 // Preserve alpha transparency
511 if (!$this->saveAlphaLayer) {
512 if ($this->setup['transparentBackground']) {
513 // Auto transparent background is set
514 $Bcolor = ImageColorClosest($this->im, $BGcols[0], $BGcols[1], $BGcols[2]);
515 imagecolortransparent($this->im, $Bcolor);
516 } elseif (is_array($this->setup['transparentColor_array'])) {
517 // Multiple transparent colors are set. This is done via the trick that all transparent colors get
518 // converted to one color and then this one gets set as transparent as png/gif can just have one
519 // transparent color.
520 $Tcolor = $this->unifyColors($this->im, $this->setup['transparentColor_array'], intval($this->setup['transparentColor.']['closest']));
521 if ($Tcolor >= 0) {
522 imagecolortransparent($this->im, $Tcolor);
523 }
524 }
525 }
526 }
527
528 /*********************************************
529 *
530 * Various helper functions
531 *
532 ********************************************/
533 /**
534 * Initializing/Cleaning of TypoScript properties for TEXT GIFBUILDER objects
535 *
536 * 'cleans' TEXT-object; Checks fontfile and other vital setup
537 * Finds the title if its a 'variable' (instantiates a cObj and loads it with the ->data record)
538 * Performs caseshift if any.
539 *
540 * @param array $conf GIFBUILDER object TypoScript properties
541 * @return array Modified $conf array IF the "text" property is not blank
542 * @access private
543 * @todo Define visibility
544 */
545 public function checkTextObj($conf) {
546 $cObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer');
547 $cObj->start($this->data);
548 $isStdWrapped = array();
549 foreach ($conf as $key => $value) {
550 $parameter = rtrim($key, '.');
551 if (!$isStdWrapped[$parameter] && isset($conf[$parameter . '.'])) {
552 $conf[$parameter] = $cObj->stdWrap($conf[$parameter], $conf[$parameter . '.']);
553 $isStdWrapped[$parameter] = 1;
554 }
555 }
556 $conf['fontFile'] = $this->checkFile($conf['fontFile']);
557 if (!$conf['fontFile']) {
558 $conf['fontFile'] = 'typo3/sysext/core/Resources/Private/Font/nimbus.ttf';
559 }
560 if (!$conf['iterations']) {
561 $conf['iterations'] = 1;
562 }
563 if (!$conf['fontSize']) {
564 $conf['fontSize'] = 12;
565 }
566 // If any kind of spacing applys, we cannot use angles!!
567 if ($conf['spacing'] || $conf['wordSpacing']) {
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']) {
576 $conf['text'] = strip_tags($conf['text']);
577 }
578 $this->combinedTextStrings[] = strip_tags($conf['text']);
579 // Max length = 100 if automatic line braks are not defined:
580 if (!isset($conf['breakWidth']) || !$conf['breakWidth']) {
581 $tlen = intval($conf['textMaxLength']) ?: 100;
582 if ($this->nativeCharset) {
583 $conf['text'] = $this->csConvObj->substr($this->nativeCharset, $conf['text'], 0, $tlen);
584 } else {
585 $conf['text'] = substr($conf['text'], 0, $tlen);
586 }
587 }
588 if ((string) $conf['text'] != '') {
589 // Char range map thingie:
590 $fontBaseName = basename($conf['fontFile']);
591 if (is_array($this->charRangeMap[$fontBaseName])) {
592 // Initialize splitRendering array:
593 if (!is_array($conf['splitRendering.'])) {
594 $conf['splitRendering.'] = array();
595 }
596 $cfgK = $this->charRangeMap[$fontBaseName]['cfgKey'];
597 // Do not impose settings if a splitRendering object already exists:
598 if (!isset($conf['splitRendering.'][$cfgK])) {
599 // Set configuration:
600 $conf['splitRendering.'][$cfgK] = 'charRange';
601 $conf['splitRendering.'][$cfgK . '.'] = $this->charRangeMap[$fontBaseName]['charMapConfig'];
602 // Multiplicator of fontsize:
603 if ($this->charRangeMap[$fontBaseName]['multiplicator']) {
604 $conf['splitRendering.'][$cfgK . '.']['fontSize'] = round($conf['fontSize'] * $this->charRangeMap[$fontBaseName]['multiplicator']);
605 }
606 // Multiplicator of pixelSpace:
607 if ($this->charRangeMap[$fontBaseName]['pixelSpace']) {
608 $travKeys = array('xSpaceBefore', 'xSpaceAfter', 'ySpaceBefore', 'ySpaceAfter');
609 foreach ($travKeys as $pxKey) {
610 if (isset($conf['splitRendering.'][$cfgK . '.'][$pxKey])) {
611 $conf['splitRendering.'][$cfgK . '.'][$pxKey] = round($conf['splitRendering.'][($cfgK . '.')][$pxKey] * ($conf['fontSize'] / $this->charRangeMap[$fontBaseName]['pixelSpace']));
612 }
613 }
614 }
615 }
616 }
617 if (is_array($conf['splitRendering.'])) {
618 foreach ($conf['splitRendering.'] as $key => $value) {
619 if (is_array($conf['splitRendering.'][$key])) {
620 if (isset($conf['splitRendering.'][$key]['fontFile'])) {
621 $conf['splitRendering.'][$key]['fontFile'] = $this->checkFile($conf['splitRendering.'][$key]['fontFile']);
622 }
623 }
624 }
625 }
626 return $conf;
627 }
628 }
629
630 /**
631 * Calculation of offset using "splitCalc" and insertion of dimensions from other GIFBUILDER objects.
632 *
633 * Example:
634 * Input: 2+2, 2*3, 123, [10.w]
635 * Output: 4,6,123,45 (provided that the width of object in position 10 was 45 pixels wide)
636 *
637 * @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.
638 * @return string The resolved string with each part (separated by comma) returned separated by comma
639 * @access private
640 * @todo Define visibility
641 */
642 public function calcOffset($string) {
643 $value = array();
644 $numbers = GeneralUtility::trimExplode(',', $this->calculateFunctions($string));
645 foreach ($numbers as $key => $val) {
646 if ((string) $val == (string) intval($val)) {
647 $value[$key] = intval($val);
648 } else {
649 $value[$key] = $this->calculateValue($val);
650 }
651 }
652 $string = implode(',', $value);
653 return $string;
654 }
655
656 /**
657 * Returns an "imgResource" creating an instance of the ContentObjectRenderer class and calling ContentObjectRenderer::getImgResource
658 *
659 * @param string $file Filename value OR the string "GIFBUILDER", see documentation in TSref for the "datatype" called "imgResource
660 * @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)
661 * @return array Returns an array with file information if an image was returned. Otherwise FALSE.
662 * @access private
663 * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::getImgResource()
664 * @todo Define visibility
665 */
666 public function getResource($file, $fileArray) {
667 if (!GeneralUtility::inList($this->imageFileExt, $fileArray['ext'])) {
668 $fileArray['ext'] = $this->gifExtension;
669 }
670 $cObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer');
671 $cObj->start($this->data);
672 return $cObj->getImgResource($file, $fileArray);
673 }
674
675 /**
676 * Returns the reference to a "resource" in TypoScript.
677 *
678 * @param string $file The resource value.
679 * @return string Returns the relative filepath
680 * @access private
681 * @see \TYPO3\CMS\Core\TypoScript\TemplateService::getFileName()
682 * @todo Define visibility
683 */
684 public function checkFile($file) {
685 return $GLOBALS['TSFE']->tmpl->getFileName($file);
686 }
687
688 /**
689 * Calculates the GIFBUILDER output filename/path based on a serialized, hashed value of this->setup
690 *
691 * @param string $pre Filename prefix, eg. "GB_
692 * @return string The relative filepath (relative to PATH_site)
693 * @access private
694 * @todo Define visibility
695 */
696 public function fileName($pre) {
697 $meaningfulPrefix = '';
698 if ($GLOBALS['TSFE']->config['config']['meaningfulTempFilePrefix']) {
699 /** @var $basicFileFunctions \TYPO3\CMS\Core\Utility\File\BasicFileUtility */
700 $basicFileFunctions = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Utility\\File\\BasicFileUtility');
701 $meaningfulPrefix = implode('_', array_merge($this->combinedTextStrings, $this->combinedFileNames));
702 $meaningfulPrefix = $basicFileFunctions->cleanFileName($meaningfulPrefix);
703 $meaningfulPrefixLength = intval($GLOBALS['TSFE']->config['config']['meaningfulTempFilePrefix']);
704 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
705 /** @var $t3libCsInstance \TYPO3\CMS\Core\Charset\CharsetConverter */
706 $t3libCsInstance = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter');
707 $meaningfulPrefix = $t3libCsInstance->substr('utf-8', $meaningfulPrefix, 0, $meaningfulPrefixLength);
708 } else {
709 $meaningfulPrefix = substr($meaningfulPrefix, 0, $meaningfulPrefixLength);
710 }
711 $meaningfulPrefix .= '_';
712 }
713 // WARNING: In PHP5 I discovered that rendering with freetype of Japanese letters was totally corrupt.
714 // Not only the wrong glyphs are printed but also some memory stack overflow resulted in strange additional
715 // chars - and finally the reason for this investigation: The Bounding box data was changing all the time
716 // resulting in new images being generated all the time. With PHP4 it works fine.
717 return $this->tempPath . $pre . $meaningfulPrefix . GeneralUtility::shortMD5(serialize($this->setup)) . '.' . $this->extension();
718 }
719
720 /**
721 * Returns the file extension used in the filename
722 *
723 * @return string Extension; "jpg" or "gif"/"png
724 * @access private
725 * @todo Define visibility
726 */
727 public function extension() {
728 switch (strtolower($this->setup['format'])) {
729 case 'jpg':
730
731 case 'jpeg':
732 return 'jpg';
733 break;
734 case 'png':
735 return 'png';
736 break;
737 case 'gif':
738 return 'gif';
739 break;
740 default:
741 return $this->gifExtension;
742 }
743 }
744
745 /**
746 * Calculates the value concerning the dimensions of objects.
747 *
748 * @param string $string The string to be calculated (e.g. "[20.h]+13")
749 * @return integer The calculated value (e.g. "23")
750 * @see calcOffset()
751 */
752 protected function calculateValue($string) {
753 $calculatedValue = 0;
754 $parts = GeneralUtility::splitCalc($string, '+-*/%');
755 foreach ($parts as $part) {
756 $theVal = $part[1];
757 $sign = $part[0];
758 if ((string) intval($theVal) == (string) $theVal) {
759 $theVal = intval($theVal);
760 } elseif ('[' . substr($theVal, 1, -1) . ']' == $theVal) {
761 $objParts = explode('.', substr($theVal, 1, -1));
762 $theVal = 0;
763 if (isset($this->objBB[$objParts[0]])) {
764 if ($objParts[1] == 'w') {
765 $theVal = $this->objBB[$objParts[0]][0];
766 } elseif ($objParts[1] == 'h') {
767 $theVal = $this->objBB[$objParts[0]][1];
768 } elseif ($objParts[1] == 'lineHeight') {
769 $theVal = $this->objBB[$objParts[0]][2]['lineHeight'];
770 }
771 $theVal = intval($theVal);
772 }
773 } elseif (floatval($theVal)) {
774 $theVal = floatval($theVal);
775 } else {
776 $theVal = 0;
777 }
778 if ($sign == '-') {
779 $calculatedValue -= $theVal;
780 } elseif ($sign == '+') {
781 $calculatedValue += $theVal;
782 } elseif ($sign == '/' && $theVal) {
783 $calculatedValue = $calculatedValue / $theVal;
784 } elseif ($sign == '*') {
785 $calculatedValue = $calculatedValue * $theVal;
786 } elseif ($sign == '%' && $theVal) {
787 $calculatedValue %= $theVal;
788 }
789 }
790 return round($calculatedValue);
791 }
792
793 /**
794 * Calculates special functions:
795 * + max([10.h], [20.h]) -> gets the maximum of the given values
796 *
797 * @param string $string The raw string with functions to be calculated
798 * @return string The calculated values
799 */
800 protected function calculateFunctions($string) {
801 if (preg_match_all('#max\\(([^)]+)\\)#', $string, $matches)) {
802 foreach ($matches[1] as $index => $maxExpression) {
803 $string = str_replace($matches[0][$index], $this->calculateMaximum($maxExpression), $string);
804 }
805 }
806 return $string;
807 }
808
809 /**
810 * Calculates the maximum of a set of values defined like "[10.h],[20.h],1000"
811 *
812 * @param string $string The string to be used to calculate the maximum (e.g. "[10.h],[20.h],1000")
813 * @return integer The maxium value of the given comma separated and calculated values
814 */
815 protected function calculateMaximum($string) {
816 $parts = GeneralUtility::trimExplode(',', $this->calcOffset($string), TRUE);
817 $maximum = count($parts) ? max($parts) : 0;
818 return $maximum;
819 }
820
821 }