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