This commit was manufactured by cvs2svn to create tag
[Packages/TYPO3.CMS.git] / typo3 / sysext / cms / tslib / class.tslib_gifbuilder.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2004 Kasper Skaarhoj (kasper@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 * $Id$
32 * Revised for TYPO3 3.6 June/2003 by Kasper Skaarhoj
33 *
34 * @author Kasper Skaarhoj <kasper@typo3.com>
35 */
36 /**
37 * [CLASS/FUNCTION INDEX of SCRIPT]
38 *
39 *
40 *
41 * 102: class tslib_gifBuilder extends t3lib_stdGraphic
42 * 126: function start($conf,$data)
43 * 274: function gifBuild()
44 * 297: function make()
45 *
46 * SECTION: Various helper functions
47 * 448: function checkTextObj($conf)
48 * 484: function calcOffset($string)
49 * 533: function getResource($file,$fileArray)
50 * 548: function checkFile($file)
51 * 559: function fileName($pre)
52 * 569: function extension()
53 *
54 * TOTAL FUNCTIONS: 9
55 * (This index is automatically created/updated by the extension "extdeveval")
56 *
57 */
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 /**
80 * GIFBUILDER extension class.
81 * This class allows for advanced rendering of images with various layers of images, text and graphical primitives.
82 * 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.
83 * 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])
84 * 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.
85 *
86 * Here is an example of how to use this class (from tslib_content.php, function getImgResource):
87 *
88 * $gifCreator = t3lib_div::makeInstance('tslib_gifbuilder');
89 * $gifCreator->init();
90 * $theImage='';
91 * if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
92 * $gifCreator->start($fileArray,$this->data);
93 * $theImage = $gifCreator->gifBuild();
94 * }
95 * return $gifCreator->getImageDimensions($theImage);
96 *
97 * @author Kasper Skaarhoj <kasper@typo3.com>
98 * @package TYPO3
99 * @subpackage tslib
100 * @link http://typo3.org/doc.0.html?&tx_extrepmgm_pi1[extUid]=270&tx_extrepmgm_pi1[tocEl]=377&cHash=e00ac666f3
101 */
102 class tslib_gifBuilder extends t3lib_stdGraphic {
103
104 // Internal
105 var $im = ''; // the main image
106 var $w = 0; // the image-width
107 var $h = 0; // the image-height
108 var $map; // map-data
109 var $workArea;
110 var $setup = Array (); // This holds the operational setup for gifbuilder. Basically this is a TypoScript array with properties.
111 var $data = Array(); // This is the array from which data->field: [key] is fetched. So this is the current record!
112 var $objBB = Array();
113 var $myClassName = 'gifbuilder';
114
115 /**
116 * 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.
117 * Modifies the ->setup, ->objBB internal arrays
118 * Should be called after the ->init() function which initializes the parent class functions/variables in general.
119 * 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.
120 *
121 * @param array TypoScript properties for the GIFBUILDER session. Stored internally in the variable ->setup
122 * @param array The current data record from tslib_cObj. Stored internally in the variable ->data
123 * @return void
124 * @see tslib_cObj::getImgResource(), tslib_gmenu::makeGifs(), tslib_gmenu::findLargestDims()
125 */
126 function start($conf,$data) {
127
128 if (is_array($conf)) {
129 $this->setup = $conf;
130 $this->data = $data;
131
132 // Getting sorted list of TypoScript keys from setup.
133 $sKeyArray=t3lib_TStemplate::sortedKeyList($this->setup);
134
135 // Setting the background color, passing it through stdWrap
136 if ($conf['backColor.'] || $conf['backColor']) {
137 $cObj =t3lib_div::makeInstance('tslib_cObj');
138 $cObj->start($this->data);
139 $this->setup['backColor'] = trim($cObj->stdWrap($this->setup['backColor'], $this->setup['backColor.']));
140 }
141 if (!$this->setup['backColor']) {$this->setup['backColor']='white';}
142
143 // Transparent GIFs
144 // not working with reduceColors
145 // there's an option for IM: -transparent colors
146 if ($conf['transparentColor.'] || $conf['transparentColor']) {
147 $cObj =t3lib_div::makeInstance('tslib_cObj');
148 $cObj->start($this->data);
149 $this->setup['transparentColor_array'] = explode('|', trim($cObj->stdWrap($this->setup['transparentColor'], $this->setup['transparentColor.'])));
150 }
151
152 // Set default dimensions
153 if (!$this->setup['XY']) {$this->setup['XY']='120,50';}
154
155
156 // Checking TEXT and IMAGE objects for files. If any errors the objects are cleared.
157 // The Bounding Box for the objects is stored in an array
158 foreach($sKeyArray as $theKey) {
159 $theValue=$this->setup[$theKey];
160
161 if (intval($theKey) && $conf=$this->setup[$theKey.'.']) {
162 // Swipes through TEXT and IMAGE-objects
163 switch($theValue) {
164 case 'TEXT':
165 if ($this->setup[$theKey.'.']=$this->checkTextObj($conf)) {
166 if ($this->setup[$theKey.'.']['maxWidth']) {
167 $this->setup[$theKey.'.']['fontSize'] = $this->fontResize($this->setup[$theKey.'.']); //RTF - this has to be done before calcBBox
168 }
169 $txtInfo=$this->calcBBox($this->setup[$theKey.'.']);
170 $this->setup[$theKey.'.']['BBOX']=$txtInfo;
171 $this->objBB[$theKey]=$txtInfo;
172 $this->setup[$theKey.'.']['imgMap']=0;
173 }
174 break;
175 case 'IMAGE':
176 $fileInfo = $this->getResource($conf['file'],$conf['file.']);
177 if ($fileInfo) {
178 $this->setup[$theKey.'.']['file']=$fileInfo[3];
179 $this->setup[$theKey.'.']['BBOX']=$fileInfo;
180 $this->objBB[$theKey]=$fileInfo;
181 if ($conf['mask']) {
182 $maskInfo = $this->getResource($conf['mask'],$conf['mask.']);
183 if ($maskInfo) {
184 $this->setup[$theKey.'.']['mask']=$maskInfo[3];
185 } else {
186 $this->setup[$theKey.'.']['mask'] = '';
187 }
188 }
189 } else {
190 unset($this->setup[$theKey.'.']);
191 }
192 break;
193 }
194 // Checks if disabled is set... (this is also done in menu.php / imgmenu!!)
195 if ($conf['if.']) {
196 $cObj =t3lib_div::makeInstance('tslib_cObj');
197 $cObj->start($this->data);
198
199 if (!$cObj->checkIf($conf['if.'])) {
200 unset($this->setup[$theKey]);
201 unset($this->setup[$theKey.'.']);
202 }
203 }
204 }
205 }
206
207 // Calculate offsets on elements
208 $this->setup['XY'] = $this->calcOffset($this->setup['XY']);
209 $this->setup['offset'] = $this->calcOffset($this->setup['offset']);
210 $this->setup['workArea'] = $this->calcOffset($this->setup['workArea']);
211
212 foreach ($sKeyArray as $theKey) {
213 $theValue=$this->setup[$theKey];
214
215 if (intval($theKey) && $conf=$this->setup[$theKey.'.']) {
216 switch($theValue) {
217 case 'TEXT':
218 case 'IMAGE':
219 if ($this->setup[$theKey.'.']['offset']) {
220 $this->setup[$theKey.'.']['offset'] = $this->calcOffset($this->setup[$theKey.'.']['offset']);
221 }
222 break;
223 case 'BOX':
224 if ($this->setup[$theKey.'.']['dimensions']) {
225 $this->setup[$theKey.'.']['dimensions'] = $this->calcOffset($this->setup[$theKey.'.']['dimensions']);
226 }
227 break;
228 case 'WORKAREA':
229 if ($this->setup[$theKey.'.']['set']) {
230 $this->setup[$theKey.'.']['set'] = $this->calcOffset($this->setup[$theKey.'.']['set']);
231 }
232 break;
233 case 'CROP':
234 if ($this->setup[$theKey.'.']['crop']) {
235 $this->setup[$theKey.'.']['crop'] = $this->calcOffset($this->setup[$theKey.'.']['crop']);
236 }
237 break;
238 case 'SCALE':
239 if ($this->setup[$theKey.'.']['width']) {
240 $this->setup[$theKey.'.']['width'] = $this->calcOffset($this->setup[$theKey.'.']['width']);
241 }
242 if ($this->setup[$theKey.'.']['height']) {
243 $this->setup[$theKey.'.']['height'] = $this->calcOffset($this->setup[$theKey.'.']['height']);
244 }
245 break;
246 }
247 }
248 }
249 // Get trivial data
250 $XY = t3lib_div::intExplode(',',$this->setup['XY']);
251 $maxWidth = intval($this->setup['maxWidth']);
252 $maxHeight = intval($this->setup['maxHeight']);
253
254 $XY[0] = t3lib_div::intInRange($XY[0],1, $maxWidth?$maxWidth:2000);
255 $XY[1] = t3lib_div::intInRange($XY[1],1, $maxHeight?$maxHeight:2000);
256 $this->XY = $XY;
257 $this->w = $XY[0];
258 $this->h = $XY[1];
259 $this->OFFSET = t3lib_div::intExplode(',',$this->setup['offset']);
260
261 $this->setWorkArea($this->setup['workArea']); // this sets the workArea
262 $this->defaultWorkArea = $this->workArea; // this sets the default to the current;
263 }
264 }
265
266 /**
267 * Initiates the image file generation if ->setup is true and if the file did not exist already.
268 * Gets filename from fileName() and if file exists in typo3temp/ dir it will - of course - not be rendered again.
269 * Otherwise rendering means calling ->make(), then ->output(), then ->destroy()
270 *
271 * @return string The filename for the created GIF/PNG file. The filename will be prefixed "GB_"
272 * @see make(), fileName()
273 */
274 function gifBuild() {
275 if ($this->setup) {
276 $gifFileName = $this->fileName('GB_');
277 if (!@file_exists($gifFileName)) { // File exists
278 $this->make();
279 $this->output($gifFileName);
280 $this->destroy();
281 }
282 return $gifFileName;
283 }
284 }
285
286 /**
287 * The actual rendering of the image file.
288 * Basically sets the dimensions, the background color, the traverses the array of GIFBUILDER objects and finally setting the transparent color if defined.
289 * Creates a GDlib resource in $this->im and works on that
290 * Called by gifBuild()
291 *
292 * @return void
293 * @access private
294 * @see gifBuild()
295 * @link http://typo3.org/doc.0.html?&tx_extrepmgm_pi1[extUid]=270&tx_extrepmgm_pi1[tocEl]=378&cHash=3c2ae4a1ab
296 */
297 function make() {
298 // Get trivial data
299 $XY = $this->XY;
300
301 // Gif-start
302 $this->im = imagecreate($XY[0],$XY[1]);
303 $this->w = $XY[0];
304 $this->h = $XY[1];
305
306 // backColor is set
307 $cols=$this->convertColor($this->setup['backColor']);
308 ImageColorAllocate($this->im, $cols[0],$cols[1],$cols[2]);
309
310 // Traverse the GIFBUILDER objects an render each one:
311 if (is_array($this->setup)) {
312 $sKeyArray=t3lib_TStemplate::sortedKeyList($this->setup);
313 foreach($sKeyArray as $theKey) {
314 $theValue=$this->setup[$theKey];
315
316 if (intval($theKey) && $conf=$this->setup[$theKey.'.']) {
317 switch($theValue) {
318 // Images
319 case 'IMAGE':
320 if ($conf['mask']) {
321 $this->maskImageOntoImage($this->im,$conf,$this->workArea);
322 } else {
323 $this->copyImageOntoImage($this->im,$conf,$this->workArea);
324 }
325 break;
326
327 // Text
328 case 'TEXT':
329 if (!$conf['hide']) {
330 if (is_array($conf['shadow.'])) {
331 $this->makeShadow($this->im,$conf['shadow.'],$this->workArea,$conf);
332 }
333 if (is_array($conf['emboss.'])) {
334 $this->makeEmboss($this->im,$conf['emboss.'],$this->workArea,$conf);
335 }
336 if (is_array($conf['outline.'])) {
337 $this->makeOutline($this->im,$conf['outline.'],$this->workArea,$conf);
338 }
339 $conf['imgMap']=1;
340 $this->makeText($this->im,$conf,$this->workArea);
341 }
342 break;
343
344 // Text effects:
345 case 'OUTLINE':
346 if ($this->setup[$conf['textObjNum']]=='TEXT' && $txtConf=$this->checkTextObj($this->setup[$conf['textObjNum'].'.'])) {
347 $this->makeOutline($this->im,$conf,$this->workArea,$txtConf);
348 }
349 break;
350 case 'EMBOSS':
351 if ($this->setup[$conf['textObjNum']]=='TEXT' && $txtConf=$this->checkTextObj($this->setup[$conf['textObjNum'].'.'])) {
352 $this->makeEmboss($this->im,$conf,$this->workArea,$txtConf);
353 }
354 break;
355 case 'SHADOW':
356 if ($this->setup[$conf['textObjNum']]=='TEXT' && $txtConf=$this->checkTextObj($this->setup[$conf['textObjNum'].'.'])) {
357 $this->makeShadow($this->im,$conf,$this->workArea,$txtConf);
358 }
359 break;
360
361 // Other
362 case 'BOX':
363 $this->makeBox($this->im,$conf,$this->workArea);
364 break;
365 case 'EFFECT':
366 $this->makeEffect($this->im,$conf);
367 break;
368 case 'ADJUST':
369 $this->adjust($this->im,$conf);
370 break;
371 case 'CROP':
372 $this->crop($this->im,$conf);
373 break;
374 case 'SCALE':
375 $this->scale($this->im,$conf);
376 break;
377 case 'WORKAREA':
378 if ($conf['set']) {
379 $this->setWorkArea($conf['set']); // this sets the workArea
380 }
381 if (isset($conf['clear'])) {
382 $this->workArea = $this->defaultWorkArea; // this sets the current to the default;
383 }
384 break;
385 }
386 }
387 }
388 }
389 // Auto transparent background is set
390 if ($this->setup['transparentBackground']) {
391 imagecolortransparent($this->im, imagecolorat($this->im, 0, 0));
392 }
393 // TransparentColors are set
394 if (is_array($this->setup['transparentColor_array'])) {
395 reset($this->setup['transparentColor_array']);
396 while(list(,$transparentColor)=each($this->setup['transparentColor_array'])) {
397 $cols=$this->convertColor($transparentColor);
398 if ($this->setup['transparentColor.']['closest']) {
399 $colIndex = ImageColorClosest ($this->im, $cols[0],$cols[1],$cols[2]);
400 } else {
401 $colIndex = ImageColorExact ($this->im, $cols[0],$cols[1],$cols[2]);
402 }
403 if ($colIndex > -1) {
404 ImageColorTransparent($this->im, $colIndex);
405 } else {
406 ImageColorTransparent($this->im, ImageColorAllocate($this->im, $cols[0],$cols[1],$cols[2]));
407 }
408 break; // Originally we thought of letting many colors be defined as transparent, but GDlib seems to accept only one definition. Therefore we break here. Maybe in the future this 'break' will be cancelled if a method of truly defining many transparent colors could be found.
409 }
410 }
411 }
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430 /*********************************************
431 *
432 * Various helper functions
433 *
434 ********************************************/
435
436
437 /**
438 * Initializing/Cleaning of TypoScript properties for TEXT GIFBUILDER objects
439 *
440 * 'cleans' TEXT-object; Checks fontfile and other vital setup
441 * Finds the title if its a 'variable' (instantiates a cObj and loads it with the ->data record)
442 * Performs caseshift if any.
443 *
444 * @param array GIFBUILDER object TypoScript properties
445 * @return array Modified $conf array IF the "text" property is not blank
446 * @access private
447 */
448 function checkTextObj($conf) {
449 $conf['fontFile']=$this->checkFile($conf['fontFile']);
450 if (!$conf['fontFile']){$conf['fontFile']='t3lib/fonts/arial.ttf';}
451 if (!$conf['iterations']){$conf['iterations'] = 1;}
452 if (!$conf['fontSize']){$conf['fontSize']=12;}
453 if ($conf['spacing'] || $conf['wordSpacing']) { // If any kind of spacing applys, we cannot use angles!!
454 $conf['angle']=0;
455 }
456 if (!isset($conf['antiAlias'])){$conf['antiAlias']=1;}
457 $cObj =t3lib_div::makeInstance('tslib_cObj');
458 $cObj->start($this->data);
459
460 $conf['text']=$cObj->stdWrap($conf['text'],$conf['text.']);
461 // Strip HTML
462 if (!$conf['doNotStripHTML']) {
463 $conf['text'] = strip_tags($conf['text']);
464 }
465 // Max length = 100
466 $tlen = intval($conf['textMaxLength']) ? intval($conf['textMaxLength']) : 100;
467 $conf['text'] = substr($conf['text'],0,$tlen);
468 if ((string)$conf['text']!='') {
469 return $conf;
470 }
471 }
472
473 /**
474 * Calculation of offset using "splitCalc" and insertion of dimensions from other GIFBUILDER objects.
475 *
476 * Example:
477 * Input: 2+2, 2*3, 123, [10.w]
478 * Output: 4,6,123,45 (provided that the width of object in position 10 was 45 pixels wide)
479 *
480 * @param 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.
481 * @return string The resolved string with each part (separated by comma) returned separated by comma
482 * @access private
483 */
484 function calcOffset($string) {
485 $numbers=explode(',',$string);
486 while(list($key,$val)=each($numbers)) {
487 $val = trim($val);
488 if ((string)$val==(string)intval($val)) {
489 $value[$key]=intval($val);
490 } else {
491 $parts= t3lib_div::splitCalc($val,'+-*/%');
492 $value[$key]=0;
493 reset($parts);
494 while(list(,$part)=each($parts)) {
495 $theVal = $part[1];
496 $sign = $part[0];
497 if ((string)intval($theVal)==(string)$theVal) {
498 $theVal = intval($theVal);
499 } elseif ('['.substr($theVal,1,-1).']'==$theVal) {
500 $objParts=explode('.',substr($theVal,1,-1));
501 $theVal=0;
502 if (isset($this->objBB[$objParts[0]])) {
503 if ($objParts[1]=='w') {$theVal=intval($this->objBB[$objParts[0]][0]);}
504 if ($objParts[1]=='h') {$theVal=intval($this->objBB[$objParts[0]][1]);}
505 }
506 } else {
507 $theVal =0;
508 }
509 if ($sign=='-') {$value[$key]-=$theVal;}
510 if ($sign=='+') {$value[$key]+=$theVal;}
511 if ($sign=='/') {if (intval($theVal)) $value[$key]/=intval($theVal);}
512 if ($sign=='*') {$value[$key]*=$theVal;}
513 if ($sign=='%') {if (intval($theVal)) $value[$key]%=intval($theVal);}
514 }
515 $value[$key]=intval($value[$key]);
516 }
517 }
518 $string = implode($value,',');
519 return $string;
520 }
521
522 /**
523 * Returns an "imgResource" creating an instance of the tslib_cObj class and calling tslib_cObj::getImgResource
524 *
525 * @param string Filename value OR the string "GIFBUILDER", see documentation in TSref for the "datatype" called "imgResource"
526 * @param array 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)
527 * @return array Returns an array with file information if an image was returned. Otherwise false.
528 * @access private
529 * @see tslib_cObj::getImgResource()
530 * @link http://typo3.org/doc.0.html?&tx_extrepmgm_pi1[extUid]=270&tx_extrepmgm_pi1[tocEl]=315&cHash=63b593a934
531 * @link http://typo3.org/doc.0.html?&tx_extrepmgm_pi1[extUid]=270&tx_extrepmgm_pi1[tocEl]=282&cHash=831a95115d
532 */
533 function getResource($file,$fileArray) {
534 $fileArray['ext']= $this->gifExtension;
535 $cObj =t3lib_div::makeInstance('tslib_cObj');
536 $cObj->start($this->data);
537 return $cObj->getImgResource($file,$fileArray);
538 }
539
540 /**
541 * Returns the reference to a "resource" in TypoScript.
542 *
543 * @param string The resource value.
544 * @return string Returns the relative filepath
545 * @access private
546 * @see t3lib_TStemplate::getFileName()
547 */
548 function checkFile($file) {
549 return $GLOBALS['TSFE']->tmpl->getFileName($file);
550 }
551
552 /**
553 * Calculates the GIFBUILDER output filename/path based on a serialized, hashed value of this->setup
554 *
555 * @param string Filename prefix, eg. "GB_"
556 * @return string The relative filepath
557 * @access private
558 */
559 function fileName($pre) {
560 return $this->tempPath.$pre.t3lib_div::shortMD5(serialize($this->setup)).'.'.$this->extension();
561 }
562
563 /**
564 * Returns the file extension used in the filename
565 *
566 * @return string Extension; "jpg" or "gif"/"png"
567 * @access private
568 */
569 function extension() {
570 switch(strtolower($this->setup['format'])) {
571 case 'jpg':
572 case 'jpeg':
573 return 'jpg';
574 break;
575 default:
576 return $this->gifExtension;
577 break;
578 }
579 }
580 }
581
582
583 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['tslib/class.tslib_gifbuilder.php']) {
584 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['tslib/class.tslib_gifbuilder.php']);
585 }
586
587 ?>