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