057ed584a3dcdd3223fa7f88fb4a68c748d41412
[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 imagealphablending($this->im, FALSE);
369 imagesavealpha($this->im, TRUE);
370
371 // Fill with a transparent background
372 $transparentColor = imagecolorallocatealpha($this->im, 0, 0, 0, 127);
373 imagefill($this->im, 0, 0, $transparentColor);
374
375 // Set internal properties to keep the transparency over the rendering process
376 $this->saveAlphaLayer = TRUE;
377 // Force PNG in case no format is set
378 $this->setup['format'] = 'png';
379 } else {
380
381 // Fill the background with the given color
382 $BGcols = $this->convertColor($this->setup['backColor']);
383 $Bcolor = ImageColorAllocate($this->im, $BGcols[0], $BGcols[1], $BGcols[2]);
384 ImageFilledRectangle($this->im, 0, 0, $XY[0], $XY[1], $Bcolor);
385 }
386
387 // Traverse the GIFBUILDER objects an render each one:
388 if (is_array($this->setup)) {
389 $sKeyArray=t3lib_TStemplate::sortedKeyList($this->setup);
390 foreach($sKeyArray as $theKey) {
391 $theValue=$this->setup[$theKey];
392 if (intval($theKey) && $conf=$this->setup[$theKey.'.']) {
393 $isStdWrapped = array();
394 foreach($conf as $key => $value) {
395 $parameter = rtrim($key, '.');
396 if (!$isStdWrapped[$parameter] && isset($conf[$parameter.'.'])) {
397 $conf[$parameter] = $this->cObj->stdWrap($conf[$parameter], $conf[$parameter.'.']);
398 $isStdWrapped[$parameter] = 1;
399 }
400 }
401 switch($theValue) {
402 // Images
403 case 'IMAGE':
404 if ($conf['mask']) {
405 $this->maskImageOntoImage($this->im, $conf, $this->workArea);
406 } else {
407 $this->copyImageOntoImage($this->im, $conf, $this->workArea);
408 }
409 break;
410
411 // Text
412 case 'TEXT':
413 if (!$conf['hide']) {
414 if (is_array($conf['shadow.'])) {
415 $isStdWrapped = array();
416 foreach($conf['shadow.'] as $key => $value) {
417 $parameter = rtrim($key, '.');
418 if (!$isStdWrapped[$parameter] && isset($conf[$parameter.'.'])) {
419 $conf['shadow.'][$parameter] = $this->cObj->stdWrap($conf[$parameter], $conf[$parameter.'.']);
420 $isStdWrapped[$parameter] = 1;
421 }
422 }
423 $this->makeShadow($this->im, $conf['shadow.'], $this->workArea, $conf);
424 }
425 if (is_array($conf['emboss.'])) {
426 $isStdWrapped = array();
427 foreach($conf['emboss.'] as $key => $value) {
428 $parameter = rtrim($key, '.');
429 if (!$isStdWrapped[$parameter] && isset($conf[$parameter.'.'])) {
430 $conf['emboss.'][$parameter] = $this->cObj->stdWrap($conf[$parameter], $conf[$parameter.'.']);
431 $isStdWrapped[$parameter] = 1;
432 }
433 }
434 $this->makeEmboss($this->im, $conf['emboss.'], $this->workArea, $conf);
435 }
436 if (is_array($conf['outline.'])) {
437 $isStdWrapped = array();
438 foreach($conf['outline.'] as $key => $value) {
439 $parameter = rtrim($key, '.');
440 if (!$isStdWrapped[$parameter] && isset($conf[$parameter.'.'])) {
441 $conf['outline.'][$parameter] = $this->cObj->stdWrap($conf[$parameter], $conf[$parameter.'.']);
442 $isStdWrapped[$parameter] = 1;
443 }
444 }
445 $this->makeOutline($this->im, $conf['outline.'], $this->workArea, $conf);
446 }
447 $conf['imgMap']=1;
448 $this->makeText($this->im, $conf, $this->workArea);
449 }
450 break;
451
452 // Text effects:
453 case 'OUTLINE':
454 if ($this->setup[$conf['textObjNum']]=='TEXT' && $txtConf=$this->checkTextObj($this->setup[$conf['textObjNum'].'.'])) {
455 $this->makeOutline($this->im, $conf, $this->workArea, $txtConf);
456 }
457 break;
458 case 'EMBOSS':
459 if ($this->setup[$conf['textObjNum']]=='TEXT' && $txtConf=$this->checkTextObj($this->setup[$conf['textObjNum'].'.'])) {
460 $this->makeEmboss($this->im, $conf, $this->workArea, $txtConf);
461 }
462 break;
463 case 'SHADOW':
464 if ($this->setup[$conf['textObjNum']]=='TEXT' && $txtConf=$this->checkTextObj($this->setup[$conf['textObjNum'].'.'])) {
465 $this->makeShadow($this->im, $conf, $this->workArea, $txtConf);
466 }
467 break;
468
469 // Other
470 case 'BOX':
471 $this->makeBox($this->im, $conf, $this->workArea);
472 break;
473 case 'EFFECT':
474 $this->makeEffect($this->im, $conf);
475 break;
476 case 'ADJUST':
477 $this->adjust($this->im, $conf);
478 break;
479 case 'CROP':
480 $this->crop($this->im, $conf);
481 break;
482 case 'SCALE':
483 $this->scale($this->im, $conf);
484 break;
485 case 'WORKAREA':
486 if ($conf['set']) {
487 // this sets the workArea
488 $this->setWorkArea($conf['set']);
489 }
490 if (isset($conf['clear'])) {
491 // This sets the current to the default;
492 $this->workArea = $this->defaultWorkArea;
493 }
494 break;
495 case 'ELLIPSE':
496 $this->makeEllipse($this->im, $conf, $this->workArea);
497 break;
498 }
499 }
500 }
501 }
502
503 // Preserve alpha transparency
504 if (!$this->saveAlphaLayer) {
505 if ($this->setup['transparentBackground']) {
506 // Auto transparent background is set
507 $Bcolor = ImageColorClosest($this->im, $BGcols[0], $BGcols[1], $BGcols[2]);
508 imagecolortransparent($this->im, $Bcolor);
509 } elseif (is_array($this->setup['transparentColor_array'])) {
510 // Multiple transparent colors are set. This is done via the trick that all transparent colors get
511 // converted to one color and then this one gets set as transparent as png/gif can just have one
512 // transparent color.
513 $Tcolor = $this->unifyColors(
514 $this->im,
515 $this->setup['transparentColor_array'],
516 intval($this->setup['transparentColor.']['closest'])
517 );
518 if ($Tcolor >= 0) {
519 imagecolortransparent($this->im, $Tcolor);
520 }
521 }
522 }
523 }
524
525 /*********************************************
526 *
527 * Various helper functions
528 *
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 */
542 function checkTextObj($conf) {
543 $cObj = t3lib_div::makeInstance('tslib_cObj');
544 $cObj->start($this->data);
545
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'] = 't3lib/fonts/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'])){$conf['antiAlias']=1;}
569
570 $conf['fontColor'] = trim($conf['fontColor']);
571 // Strip HTML
572 if (!$conf['doNotStripHTML']) {
573 $conf['text'] = strip_tags($conf['text']);
574 }
575 $this->combinedTextStrings[] = strip_tags($conf['text']);
576
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
588 // Char range map thingie:
589 $fontBaseName = basename($conf['fontFile']);
590 if (is_array($this->charRangeMap[$fontBaseName])) {
591
592 // Initialize splitRendering array:
593 if (!is_array($conf['splitRendering.'])) {
594 $conf['splitRendering.'] = array();
595 }
596
597 $cfgK = $this->charRangeMap[$fontBaseName]['cfgKey'];
598 // Do not impose settings if a splitRendering object already exists:
599 if (!isset($conf['splitRendering.'][$cfgK])) {
600 // Set configuration:
601 $conf['splitRendering.'][$cfgK] = 'charRange';
602 $conf['splitRendering.'][$cfgK.'.'] = $this->charRangeMap[$fontBaseName]['charMapConfig'];
603
604 // Multiplicator of fontsize:
605 if ($this->charRangeMap[$fontBaseName]['multiplicator']) {
606 $conf['splitRendering.'][$cfgK.'.']['fontSize'] = round($conf['fontSize'] * $this->charRangeMap[$fontBaseName]['multiplicator']);
607 }
608 // Multiplicator of pixelSpace:
609 if ($this->charRangeMap[$fontBaseName]['pixelSpace']) {
610 $travKeys = array('xSpaceBefore', 'xSpaceAfter', 'ySpaceBefore', 'ySpaceAfter');
611 foreach($travKeys as $pxKey) {
612 if (isset($conf['splitRendering.'][$cfgK.'.'][$pxKey])) {
613 $conf['splitRendering.'][$cfgK.'.'][$pxKey] = round($conf['splitRendering.'][$cfgK.'.'][$pxKey] * ($conf['fontSize'] / $this->charRangeMap[$fontBaseName]['pixelSpace']));
614 }
615 }
616 }
617 }
618 }
619 if (is_array($conf['splitRendering.'])) {
620 foreach($conf['splitRendering.'] as $key => $value) {
621 if (is_array($conf['splitRendering.'][$key])) {
622 if (isset($conf['splitRendering.'][$key]['fontFile'])) {
623 $conf['splitRendering.'][$key]['fontFile'] = $this->checkFile($conf['splitRendering.'][$key]['fontFile']);
624 }
625 }
626 }
627 }
628
629 return $conf;
630 }
631 }
632
633 /**
634 * Calculation of offset using "splitCalc" and insertion of dimensions from other GIFBUILDER objects.
635 *
636 * Example:
637 * Input: 2+2, 2*3, 123, [10.w]
638 * Output: 4,6,123,45 (provided that the width of object in position 10 was 45 pixels wide)
639 *
640 * @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.
641 * @return string The resolved string with each part (separated by comma) returned separated by comma
642 * @access private
643 */
644 function calcOffset($string) {
645 $value = array();
646 $numbers = t3lib_div::trimExplode(',', $this->calculateFunctions($string));
647
648 foreach ($numbers as $key => $val) {
649 if ((string)$val == (string)intval($val)) {
650 $value[$key] = intval($val);
651 } else {
652 $value[$key] = $this->calculateValue($val);
653 }
654 }
655
656 $string = implode(',', $value);
657 return $string;
658 }
659
660 /**
661 * Returns an "imgResource" creating an instance of the tslib_cObj class and calling tslib_cObj::getImgResource
662 *
663 * @param string $file Filename value OR the string "GIFBUILDER", see documentation in TSref for the "datatype" called "imgResource"
664 * @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)
665 * @return array Returns an array with file information if an image was returned. Otherwise FALSE.
666 * @access private
667 * @see tslib_cObj::getImgResource()
668 */
669 function getResource($file, $fileArray) {
670 if (!t3lib_div::inList($this->imageFileExt, $fileArray['ext'])) {
671 $fileArray['ext'] = $this->gifExtension;
672 }
673 $cObj =t3lib_div::makeInstance('tslib_cObj');
674 $cObj->start($this->data);
675 return $cObj->getImgResource($file, $fileArray);
676 }
677
678 /**
679 * Returns the reference to a "resource" in TypoScript.
680 *
681 * @param string $file The resource value.
682 * @return string Returns the relative filepath
683 * @access private
684 * @see t3lib_TStemplate::getFileName()
685 */
686 function checkFile($file) {
687 return $GLOBALS['TSFE']->tmpl->getFileName($file);
688 }
689
690 /**
691 * Calculates the GIFBUILDER output filename/path based on a serialized, hashed value of this->setup
692 *
693 * @param string $pre Filename prefix, eg. "GB_"
694 * @return string The relative filepath (relative to PATH_site)
695 * @access private
696 */
697 function fileName($pre) {
698
699 $meaningfulPrefix = '';
700
701 if ($GLOBALS['TSFE']->config['config']['meaningfulTempFilePrefix']) {
702 /** @var $basicFileFunctions t3lib_basicFileFunctions */
703 $basicFileFunctions = t3lib_div::makeInstance('t3lib_basicFileFunctions');
704
705 $meaningfulPrefix = implode('_', array_merge($this->combinedTextStrings, $this->combinedFileNames));
706 $meaningfulPrefix = $basicFileFunctions->cleanFileName($meaningfulPrefix);
707 $meaningfulPrefixLength = intval($GLOBALS['TSFE']->config['config']['meaningfulTempFilePrefix']);
708 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
709 /** @var $t3libCsInstance t3lib_cs */
710 $t3libCsInstance = t3lib_div::makeInstance('t3lib_cs');
711 $meaningfulPrefix = $t3libCsInstance->substr('utf-8', $meaningfulPrefix, 0, $meaningfulPrefixLength);
712 } else {
713 $meaningfulPrefix = substr($meaningfulPrefix, 0, $meaningfulPrefixLength);
714 }
715 $meaningfulPrefix .= '_';
716 }
717
718 // WARNING: In PHP5 I discovered that rendering with freetype of Japanese letters was totally corrupt.
719 // Not only the wrong glyphs are printed but also some memory stack overflow resulted in strange additional
720 // chars - and finally the reason for this investigation: The Bounding box data was changing all the time
721 // resulting in new images being generated all the time. With PHP4 it works fine.
722 return $this->tempPath .
723 $pre .
724 $meaningfulPrefix .
725 t3lib_div::shortMD5(serialize($this->setup)) .
726 '.' . $this->extension();
727 }
728
729 /**
730 * Returns the file extension used in the filename
731 *
732 * @return string Extension; "jpg" or "gif"/"png"
733 * @access private
734 */
735 function extension() {
736 switch(strtolower($this->setup['format'])) {
737 case 'jpg':
738 case 'jpeg':
739 return 'jpg';
740 break;
741 case 'png':
742 return 'png';
743 break;
744 case 'gif':
745 return 'gif';
746 break;
747 default:
748 return $this->gifExtension;
749 break;
750 }
751 }
752
753 /**
754 * Calculates the value concerning the dimensions of objects.
755 *
756 * @param string $string The string to be calculated (e.g. "[20.h]+13")
757 * @return integer The calculated value (e.g. "23")
758 * @see calcOffset()
759 */
760 protected function calculateValue($string) {
761 $calculatedValue = 0;
762 $parts = t3lib_div::splitCalc($string, '+-*/%');
763
764 foreach ($parts as $part) {
765 $theVal = $part[1];
766 $sign = $part[0];
767
768 if ((string)intval($theVal) == (string)$theVal) {
769 $theVal = intval($theVal);
770 } elseif ('[' . substr($theVal, 1, -1) . ']' == $theVal) {
771 $objParts = explode('.', substr($theVal, 1, -1));
772 $theVal = 0;
773 if (isset($this->objBB[$objParts[0]])) {
774 if ($objParts[1] == 'w') {
775 $theVal = $this->objBB[$objParts[0]][0];
776 } elseif ($objParts[1] == 'h') {
777 $theVal = $this->objBB[$objParts[0]][1];
778 } elseif ($objParts[1] == 'lineHeight') {
779 $theVal = $this->objBB[$objParts[0]][2]['lineHeight'];
780 }
781 $theVal = intval($theVal);
782 }
783 } elseif (floatval($theVal)) {
784 $theVal = floatval($theVal);
785 } else {
786 $theVal = 0;
787 }
788
789 if ($sign == '-') {
790 $calculatedValue-= $theVal;
791 } elseif ($sign == '+') {
792 $calculatedValue+= $theVal;
793 } elseif ($sign == '/' && $theVal) {
794 $calculatedValue = $calculatedValue / $theVal;
795 } elseif ($sign == '*') {
796 $calculatedValue = $calculatedValue * $theVal;
797 } elseif ($sign == '%' && $theVal) {
798 $calculatedValue%= $theVal;
799 }
800 }
801
802 return round($calculatedValue);
803 }
804
805 /**
806 * Calculates special functions:
807 * + max([10.h], [20.h]) -> gets the maximum of the given values
808 *
809 * @param string $string The raw string with functions to be calculated
810 * @return string The calculated values
811 */
812 protected function calculateFunctions($string) {
813 if (preg_match_all('#max\(([^)]+)\)#', $string, $matches)) {
814 foreach ($matches[1] as $index => $maxExpression) {
815 $string = str_replace(
816 $matches[0][$index],
817 $this->calculateMaximum(
818 $maxExpression
819 ),
820 $string
821 );
822 }
823 }
824
825 return $string;
826 }
827
828 /**
829 * Calculates the maximum of a set of values defined like "[10.h],[20.h],1000"
830 *
831 * @param string $string The string to be used to calculate the maximum (e.g. "[10.h],[20.h],1000")
832 * @return integer The maxium value of the given comma separated and calculated values
833 */
834 protected function calculateMaximum($string) {
835 $parts = t3lib_div::trimExplode(',', $this->calcOffset($string), TRUE);
836 $maximum = (count($parts) ? max($parts) : 0);
837 return $maximum;
838 }
839 }
840 ?>