See my changelog comment from 2004-06-28 + locallang-XML changes (done to german...
[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/'); // Relative to PATH_site
277 if (!@file_exists($gifFileName)) { // File exists
278
279 // Create temporary directory if not done:
280 $this->createTempSubDir('GB/');
281
282 // Create file:
283 $this->make();
284 $this->output($gifFileName);
285 $this->destroy();
286 }
287 return $gifFileName;
288 }
289 }
290
291 /**
292 * The actual rendering of the image file.
293 * Basically sets the dimensions, the background color, the traverses the array of GIFBUILDER objects and finally setting the transparent color if defined.
294 * Creates a GDlib resource in $this->im and works on that
295 * Called by gifBuild()
296 *
297 * @return void
298 * @access private
299 * @see gifBuild()
300 * @link http://typo3.org/doc.0.html?&tx_extrepmgm_pi1[extUid]=270&tx_extrepmgm_pi1[tocEl]=378&cHash=3c2ae4a1ab
301 */
302 function make() {
303 // Get trivial data
304 $XY = $this->XY;
305
306 // Gif-start
307 $this->im = imagecreate($XY[0],$XY[1]);
308 $this->w = $XY[0];
309 $this->h = $XY[1];
310
311 // backColor is set
312 $cols=$this->convertColor($this->setup['backColor']);
313 ImageColorAllocate($this->im, $cols[0],$cols[1],$cols[2]);
314
315 // Traverse the GIFBUILDER objects an render each one:
316 if (is_array($this->setup)) {
317 $sKeyArray=t3lib_TStemplate::sortedKeyList($this->setup);
318 foreach($sKeyArray as $theKey) {
319 $theValue=$this->setup[$theKey];
320
321 if (intval($theKey) && $conf=$this->setup[$theKey.'.']) {
322 switch($theValue) {
323 // Images
324 case 'IMAGE':
325 if ($conf['mask']) {
326 $this->maskImageOntoImage($this->im,$conf,$this->workArea);
327 } else {
328 $this->copyImageOntoImage($this->im,$conf,$this->workArea);
329 }
330 break;
331
332 // Text
333 case 'TEXT':
334 if (!$conf['hide']) {
335 if (is_array($conf['shadow.'])) {
336 $this->makeShadow($this->im,$conf['shadow.'],$this->workArea,$conf);
337 }
338 if (is_array($conf['emboss.'])) {
339 $this->makeEmboss($this->im,$conf['emboss.'],$this->workArea,$conf);
340 }
341 if (is_array($conf['outline.'])) {
342 $this->makeOutline($this->im,$conf['outline.'],$this->workArea,$conf);
343 }
344 $conf['imgMap']=1;
345 $this->makeText($this->im,$conf,$this->workArea);
346 }
347 break;
348
349 // Text effects:
350 case 'OUTLINE':
351 if ($this->setup[$conf['textObjNum']]=='TEXT' && $txtConf=$this->checkTextObj($this->setup[$conf['textObjNum'].'.'])) {
352 $this->makeOutline($this->im,$conf,$this->workArea,$txtConf);
353 }
354 break;
355 case 'EMBOSS':
356 if ($this->setup[$conf['textObjNum']]=='TEXT' && $txtConf=$this->checkTextObj($this->setup[$conf['textObjNum'].'.'])) {
357 $this->makeEmboss($this->im,$conf,$this->workArea,$txtConf);
358 }
359 break;
360 case 'SHADOW':
361 if ($this->setup[$conf['textObjNum']]=='TEXT' && $txtConf=$this->checkTextObj($this->setup[$conf['textObjNum'].'.'])) {
362 $this->makeShadow($this->im,$conf,$this->workArea,$txtConf);
363 }
364 break;
365
366 // Other
367 case 'BOX':
368 $this->makeBox($this->im,$conf,$this->workArea);
369 break;
370 case 'EFFECT':
371 $this->makeEffect($this->im,$conf);
372 break;
373 case 'ADJUST':
374 $this->adjust($this->im,$conf);
375 break;
376 case 'CROP':
377 $this->crop($this->im,$conf);
378 break;
379 case 'SCALE':
380 $this->scale($this->im,$conf);
381 break;
382 case 'WORKAREA':
383 if ($conf['set']) {
384 $this->setWorkArea($conf['set']); // this sets the workArea
385 }
386 if (isset($conf['clear'])) {
387 $this->workArea = $this->defaultWorkArea; // this sets the current to the default;
388 }
389 break;
390 }
391 }
392 }
393 }
394 // Auto transparent background is set
395 if ($this->setup['transparentBackground']) {
396 imagecolortransparent($this->im, imagecolorat($this->im, 0, 0));
397 }
398 // TransparentColors are set
399 if (is_array($this->setup['transparentColor_array'])) {
400 reset($this->setup['transparentColor_array']);
401 while(list(,$transparentColor)=each($this->setup['transparentColor_array'])) {
402 $cols=$this->convertColor($transparentColor);
403 if ($this->setup['transparentColor.']['closest']) {
404 $colIndex = ImageColorClosest ($this->im, $cols[0],$cols[1],$cols[2]);
405 } else {
406 $colIndex = ImageColorExact ($this->im, $cols[0],$cols[1],$cols[2]);
407 }
408 if ($colIndex > -1) {
409 ImageColorTransparent($this->im, $colIndex);
410 } else {
411 ImageColorTransparent($this->im, ImageColorAllocate($this->im, $cols[0],$cols[1],$cols[2]));
412 }
413 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.
414 }
415 }
416 }
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435 /*********************************************
436 *
437 * Various helper functions
438 *
439 ********************************************/
440
441
442 /**
443 * Initializing/Cleaning of TypoScript properties for TEXT GIFBUILDER objects
444 *
445 * 'cleans' TEXT-object; Checks fontfile and other vital setup
446 * Finds the title if its a 'variable' (instantiates a cObj and loads it with the ->data record)
447 * Performs caseshift if any.
448 *
449 * @param array GIFBUILDER object TypoScript properties
450 * @return array Modified $conf array IF the "text" property is not blank
451 * @access private
452 */
453 function checkTextObj($conf) {
454 $conf['fontFile']=$this->checkFile($conf['fontFile']);
455 if (!$conf['fontFile']){$conf['fontFile']='t3lib/fonts/arial.ttf';}
456 if (!$conf['iterations']){$conf['iterations'] = 1;}
457 if (!$conf['fontSize']){$conf['fontSize']=12;}
458 if ($conf['spacing'] || $conf['wordSpacing']) { // If any kind of spacing applys, we cannot use angles!!
459 $conf['angle']=0;
460 }
461 if (!isset($conf['antiAlias'])){$conf['antiAlias']=1;}
462 $cObj =t3lib_div::makeInstance('tslib_cObj');
463 $cObj->start($this->data);
464
465 $conf['text']=$cObj->stdWrap($conf['text'],$conf['text.']);
466 // Strip HTML
467 if (!$conf['doNotStripHTML']) {
468 $conf['text'] = strip_tags($conf['text']);
469 }
470 // Max length = 100
471 $tlen = intval($conf['textMaxLength']) ? intval($conf['textMaxLength']) : 100;
472 $conf['text'] = substr($conf['text'],0,$tlen);
473 if ((string)$conf['text']!='') {
474 return $conf;
475 }
476 }
477
478 /**
479 * Calculation of offset using "splitCalc" and insertion of dimensions from other GIFBUILDER objects.
480 *
481 * Example:
482 * Input: 2+2, 2*3, 123, [10.w]
483 * Output: 4,6,123,45 (provided that the width of object in position 10 was 45 pixels wide)
484 *
485 * @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.
486 * @return string The resolved string with each part (separated by comma) returned separated by comma
487 * @access private
488 */
489 function calcOffset($string) {
490 $numbers=explode(',',$string);
491 while(list($key,$val)=each($numbers)) {
492 $val = trim($val);
493 if ((string)$val==(string)intval($val)) {
494 $value[$key]=intval($val);
495 } else {
496 $parts= t3lib_div::splitCalc($val,'+-*/%');
497 $value[$key]=0;
498 reset($parts);
499 while(list(,$part)=each($parts)) {
500 $theVal = $part[1];
501 $sign = $part[0];
502 if ((string)intval($theVal)==(string)$theVal) {
503 $theVal = intval($theVal);
504 } elseif ('['.substr($theVal,1,-1).']'==$theVal) {
505 $objParts=explode('.',substr($theVal,1,-1));
506 $theVal=0;
507 if (isset($this->objBB[$objParts[0]])) {
508 if ($objParts[1]=='w') {$theVal=intval($this->objBB[$objParts[0]][0]);}
509 if ($objParts[1]=='h') {$theVal=intval($this->objBB[$objParts[0]][1]);}
510 }
511 } else {
512 $theVal =0;
513 }
514 if ($sign=='-') {$value[$key]-=$theVal;}
515 if ($sign=='+') {$value[$key]+=$theVal;}
516 if ($sign=='/') {if (intval($theVal)) $value[$key]/=intval($theVal);}
517 if ($sign=='*') {$value[$key]*=$theVal;}
518 if ($sign=='%') {if (intval($theVal)) $value[$key]%=intval($theVal);}
519 }
520 $value[$key]=intval($value[$key]);
521 }
522 }
523 $string = implode($value,',');
524 return $string;
525 }
526
527 /**
528 * Returns an "imgResource" creating an instance of the tslib_cObj class and calling tslib_cObj::getImgResource
529 *
530 * @param string Filename value OR the string "GIFBUILDER", see documentation in TSref for the "datatype" called "imgResource"
531 * @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)
532 * @return array Returns an array with file information if an image was returned. Otherwise false.
533 * @access private
534 * @see tslib_cObj::getImgResource()
535 * @link http://typo3.org/doc.0.html?&tx_extrepmgm_pi1[extUid]=270&tx_extrepmgm_pi1[tocEl]=315&cHash=63b593a934
536 * @link http://typo3.org/doc.0.html?&tx_extrepmgm_pi1[extUid]=270&tx_extrepmgm_pi1[tocEl]=282&cHash=831a95115d
537 */
538 function getResource($file,$fileArray) {
539 $fileArray['ext']= $this->gifExtension;
540 $cObj =t3lib_div::makeInstance('tslib_cObj');
541 $cObj->start($this->data);
542 return $cObj->getImgResource($file,$fileArray);
543 }
544
545 /**
546 * Returns the reference to a "resource" in TypoScript.
547 *
548 * @param string The resource value.
549 * @return string Returns the relative filepath
550 * @access private
551 * @see t3lib_TStemplate::getFileName()
552 */
553 function checkFile($file) {
554 return $GLOBALS['TSFE']->tmpl->getFileName($file);
555 }
556
557 /**
558 * Calculates the GIFBUILDER output filename/path based on a serialized, hashed value of this->setup
559 *
560 * @param string Filename prefix, eg. "GB_"
561 * @return string The relative filepath (relative to PATH_site)
562 * @access private
563 */
564 function fileName($pre) {
565 return $this->tempPath.$pre.t3lib_div::shortMD5(serialize($this->setup)).'.'.$this->extension();
566 }
567
568 /**
569 * Returns the file extension used in the filename
570 *
571 * @return string Extension; "jpg" or "gif"/"png"
572 * @access private
573 */
574 function extension() {
575 switch(strtolower($this->setup['format'])) {
576 case 'jpg':
577 case 'jpeg':
578 return 'jpg';
579 break;
580 default:
581 return $this->gifExtension;
582 break;
583 }
584 }
585 }
586
587
588 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['tslib/class.tslib_gifbuilder.php']) {
589 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['tslib/class.tslib_gifbuilder.php']);
590 }
591
592 ?>