Added $Id$ keywords, cleaned up comment tags
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_stdgraphic.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2003 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 * Standard graphical functions
29 *
30 * $Id$
31 * Revised for TYPO3 3.6 July/2003 by Kasper Skaarhoj
32 *
33 * @author Kasper Skaarhoj <kasper@typo3.com>
34 */
35 /**
36 * [CLASS/FUNCTION INDEX of SCRIPT]
37 *
38 *
39 *
40 * 141: class t3lib_stdGraphic
41 * 216: function init()
42 *
43 * SECTION: Layering images / "IMAGE" GIFBUILDER object
44 * 315: function maskImageOntoImage(&$im,$conf,$workArea)
45 * 375: function copyImageOntoImage(&$im,$conf,$workArea)
46 * 397: function copyGifOntoGif(&$im,$cpImg,$conf,$workArea)
47 * 476: function imagecopyresized(&$im, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h)
48 *
49 * SECTION: Text / "TEXT" GIFBUILDER object
50 * 525: function makeText(&$im,$conf,$workArea)
51 * 641: function txtPosition($conf,$workArea,$BB)
52 * 695: function calcBBox($conf)
53 * 764: function addToMap($cords,$conf)
54 * 779: function calcTextCordsForMap($cords,$offset, $conf)
55 * 812: function SpacedImageTTFText(&$im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $text, $spacing, $wordSpacing)
56 * 845: function fontResize($conf)
57 *
58 * SECTION: Other GIFBUILDER objects related to TEXT
59 * 907: function makeOutline(&$im,$conf,$workArea,$txtConf)
60 * 936: function circleOffset($distance, $iterations)
61 * 960: function makeEmboss(&$im,$conf,$workArea,$txtConf)
62 * 982: function makeShadow(&$im,$conf,$workArea,$txtConf)
63 *
64 * SECTION: Other GIFBUILDER objects
65 * 1112: function makeBox(&$im,$conf,$workArea)
66 * 1131: function makeEffect(&$im, $conf)
67 * 1146: function IMparams($setup)
68 * 1229: function adjust(&$im, $conf)
69 * 1261: function crop(&$im,$conf)
70 * 1291: function scale(&$im,$conf)
71 * 1323: function setWorkArea($workArea)
72 *
73 * SECTION: Adjustment functions
74 * 1364: function autolevels(&$im)
75 * 1395: function outputLevels(&$im,$low,$high,$swap='')
76 * 1427: function inputLevels(&$im,$low,$high,$swap='')
77 * 1458: function reduceColors(&$im,$limit, $cols)
78 *
79 * SECTION: GIFBUILDER Helper functions
80 * 1496: function prependAbsolutePath($fontFile)
81 * 1510: function v5_sharpen($factor)
82 * 1529: function v5_blur($factor)
83 * 1546: function randomName()
84 * 1558: function applyOffset($cords,$OFFSET)
85 * 1571: function convertColor($string)
86 * 1621: function recodeString($string)
87 * 1642: function singleChars($theText)
88 * 1665: function objPosition($conf,$workArea,$BB)
89 *
90 * SECTION: Scaling, Dimensions of images
91 * 1744: function imageMagickConvert($imagefile,$newExt='',$w='',$h='',$params='',$frame='',$options='',$mustCreate=0)
92 * 1827: function getImageDimensions($imagefile)
93 * 1849: function getImageScale($info,$w,$h,$options)
94 * 1937: function file_exists_typo3temp_file($output,$orig='')
95 *
96 * SECTION: ImageMagick API functions
97 * 1994: function imageMagickIdentify($imagefile)
98 * 2027: function imageMagickExec($input,$output,$params)
99 * 2045: function combineExec($input,$overlay,$mask,$output)
100 * 2060: function wrapFileName($inputName)
101 *
102 * SECTION: Various IO functions
103 * 2101: function checkFile($file)
104 * 2116: function applyImageMagickToPHPGif(&$im, $command)
105 * 2142: function gif_or_jpg($type,$w,$h)
106 * 2159: function output($file)
107 * 2203: function destroy()
108 * 2213: function imgTag ($imgInfo)
109 * 2225: function ImageGif($destImg, $theImage)
110 * 2242: function imageCreateFromGif($sourceImg)
111 *
112 * TOTAL FUNCTIONS: 52
113 * (This index is automatically created/updated by the extension "extdeveval")
114 *
115 */
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 /**
133 * Class contains a bunch of cool functions for manipulating graphics with GDlib/Freetype and ImageMagick
134 * VERY OFTEN used with gifbuilder that extends this class and provides a TypoScript API to using these functions
135 *
136 * @author Kasper Skaarhoj <kasper@typo3.com>
137 * @package TYPO3
138 * @subpackage t3lib
139 * @see tslib_gifBuilder
140 */
141 class t3lib_stdGraphic {
142
143 // Internal configuration, set in init()
144 var $combineScript = 'combine'; // The ImageMagick filename used for combining two images. This name changed during the versions.
145 var $noFramePrepended=0; // If set, there is no frame pointer prepended to the filenames.
146 var $GD2=0; // Set, if the GDlib used is version 2.
147 var $gifExtension = 'gif'; // This should be changed to 'png' if you want this class to read/make PNG-files instead!
148 var $TTFLocaleConv = ''; // Used to recode input to TTF-functions for other charsets.
149 var $enable_typo3temp_db_tracking = 0; // If set, then all files in typo3temp will be logged in a database table. In addition to being a log of the files with original filenames, it also serves to secure that the same image is not rendered simultaneously by two different processes.
150 var $imageMagickPath = ''; // path to imageMagick, eg: /usr/lib/
151 var $imageFileExt = 'gif,jpg,jpeg,png,tif,bmp,tga,pcx,ai,pdf'; // Commalist of file extensions perceived as images by TYPO3. List should be set to 'gif,png,jpeg,jpg' if IM is not available. Lowercase and no spaces between!
152 var $webImageExt = 'gif,jpg,jpeg,png'; // Commalist of web image extensions (can be shown by a webbrowser)
153 var $maskNegate = ''; // Will be ' -negate' if ImageMagick ver 5.2+. See init();
154 var $NO_IM_EFFECTS = '';
155 var $cmds = Array (
156 'jpg' => '-colorspace RGB -sharpen 50 -quality 60',
157 'jpeg' => '-colorspace RGB -sharpen 50 -quality 60',
158 'gif' => '-colors 64',
159 'png' => '-colors 64'
160 );
161 var $NO_IMAGE_MAGICK = '';
162 var $V5_EFFECTS = 0;
163 var $mayScaleUp = 1;
164
165 // Variables for testing, alternative usage etc.
166 var $filenamePrefix=''; // Filename prefix for images scaled in imageMagickConvert()
167 var $imageMagickConvert_forceFileNameBody=''; // Forcing the output filename of imageMagickConvert() to this value. However after calling imageMagickConvert() it will be set blank again.
168 var $dontCheckForExistingTempFile = 0; // This flag should always be false. If set true, imageMagickConvert will always write a new file to the tempdir! Used for debugging.
169 var $dontCompress=0; // Prevents imageMagickConvert() from compressing the gif-files with t3lib_div::gif_compress()
170 var $dontUnlinkTempFiles=0; // For debugging ONLY!
171 var $alternativeOutputKey=''; // For debugging only. Filenames will not be based on mtime and only filename (not path) will be used. This key is also included in the hash of the filename...
172
173 // Internal:
174 var $IM_commands = Array(); // All ImageMagick commands executed is stored in this array for tracking. Used by the Install Tools Image section
175 var $workArea = Array();
176
177 // Constants:
178 var $tempPath = 'typo3temp/'; // The temp-directory where to store the files. Relative to PATH_site.
179 var $scalecmd = '-geometry'; // ImageMagick scaling command; "-geometry" eller "-sample". Used in makeText() and imageMagickConvert()
180 var $im5fx_blurSteps='1x2,2x2,3x2,4x3,5x3,5x4,6x4,7x5,8x5,9x5'; // Used by v5_blur() to simulate 10 continuous steps of blurring
181 var $im5fx_sharpenSteps='1x2,2x2,3x2,2x3,3x3,4x3,3x4,4x4,4x5,5x5'; // Used by v5_sharpen() to simulate 10 continuous steps of sharpening.
182 var $pixelLimitGif = 10000; // This is the limit for the number of pixels in an image before it will be rendered as JPG instead of GIF/PNG
183 var $colMap = Array ( // Array mapping HTML color names to RGB values.
184 'aqua' => Array(0,255,255),
185 'black' => Array(0,0,0),
186 'blue' => Array(0,0,255),
187 'fuchsia' => Array(255,0,255),
188 'gray' => Array(128,128,128),
189 'green' => Array(0,128,0),
190 'lime' => Array(0,255,0),
191 'maroon' => Array(128,0,0),
192 'navy' => Array(0,0,128),
193 'olive' => Array(128,128,0),
194 'purple' => Array(128,0,128),
195 'red' => Array(255,0,0),
196 'silver' => Array(192,192,192),
197 'teal' => Array(0,128,128),
198 'yellow' => Array(255,255,0),
199 'white' => Array(255,255,255)
200 );
201
202 // Charset conversion object:
203 var $csConvObj;
204 var $nativeCharset=''; // Is set to the native character set of the input strings.
205
206
207
208
209
210 /**
211 * Init function. Must always call this when using the class.
212 * This function will read the configuration information from $GLOBALS['TYPO3_CONF_VARS']['GFX'] can set some values in internal variables.
213 *
214 * @return void
215 */
216 function init() {
217 $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX'];
218
219 if ($gfxConf['im_combine_filename']) $this->combineScript=$gfxConf['im_combine_filename'];
220 if ($gfxConf['im_noFramePrepended']) $this->noFramePrepended=1;
221
222 if ($gfxConf['gdlib_2']) {
223 $this->GD2=1;
224 }
225 if ($gfxConf['gdlib_png']) {
226 $this->gifExtension='png';
227 }
228 if ($gfxConf['TTFLocaleConv']) {
229 $this->TTFLocaleConv = $gfxConf['TTFLocaleConv'];
230 }
231 if ($gfxConf['enable_typo3temp_db_tracking']) {
232 $this->enable_typo3temp_db_tracking = $gfxConf['enable_typo3temp_db_tracking'];
233 }
234
235 $this->imageMagickPath = $gfxConf['im_path'];
236 $this->imageFileExt = $gfxConf['imagefile_ext'];
237
238 // This should be set if ImageMagick ver. 5+ is used.
239 if ($gfxConf['im_negate_mask']) {
240 // Boolean. Indicates if the mask images should be inverted first.
241 // This depends of the ImageMagick version. Below ver. 5.1 this should be false.
242 // Above ImageMagick version 5.2+ it should be true.
243 // Just set the flag if the masks works opposite the intension!
244 $this->maskNegate = ' -negate';
245 }
246 if ($gfxConf['im_no_effects']) {
247 // Boolean. This is necessary if using ImageMagick 5+.
248 // Approved version for using effects is version 4.2.9.
249 // Effects in Imagemagick 5+ tends to render very slowly!!
250 // - therefore must be disabled in order not to perform sharpen, blurring and such.
251 $this->NO_IM_EFFECTS = 1;
252
253 $this->cmds['jpg'] = '-colorspace RGB -quality 60';
254 $this->cmds['jpeg'] = '-colorspace RGB -quality 60';
255 }
256 // ... but if 'im_v5effects' is set, dont care about 'im_no_effects'
257 if ($gfxConf['im_v5effects']) {
258 $this->NO_IM_EFFECTS = 0;
259 $this->V5_EFFECTS = 1;
260
261 if ($gfxConf['im_v5effects']>0) {
262 $this->cmds['jpg'] = '-colorspace RGB -quality 60'.$this->v5_sharpen(10);
263 $this->cmds['jpeg'] = '-colorspace RGB -quality 60'.$this->v5_sharpen(10);
264 }
265 }
266
267 if (!$gfxConf['im']) {
268 $this->NO_IMAGE_MAGICK = 1;
269 }
270 // Secures that images are not scaled up.
271 if ($gfxConf['im_noScaleUp']) {
272 $this->mayScaleUp=0;
273 }
274
275 if (TYPO3_MODE=='FE') {
276 $this->csConvObj = &$GLOBALS['TSFE']->csConvObj;
277 } else { // BE assumed:
278 $this->csConvObj = &$GLOBALS['LANG']->csConvObj;
279 }
280 $this->nativeCharset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'];
281 }
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298 /*************************************************
299 *
300 * Layering images / "IMAGE" GIFBUILDER object
301 *
302 *************************************************/
303
304 /**
305 * Implements the "IMAGE" GIFBUILDER object, when the "mask" property is true.
306 * It reads the two images defined by $conf['file'] and $conf['mask'] and copies the $conf['file'] onto the input image pointer image using the $conf['mask'] as a grayscale mask
307 * The operation involves ImageMagick for combining.
308 *
309 * @param pointer GDlib image pointer
310 * @param array TypoScript array with configuration for the GIFBUILDER object.
311 * @param array The current working area coordinates.
312 * @return void
313 * @see tslib_gifBuilder::make()
314 */
315 function maskImageOntoImage(&$im,$conf,$workArea) {
316 if ($conf['file'] && $conf['mask']) {
317 $BBimage = $this->imageMagickConvert($conf['file'],$this->gifExtension,'','','','','');
318 $BBmask = $this->imageMagickConvert($conf['mask'],$this->gifExtension,'','','','','');
319 if ($BBimage && $BBmask) {
320 $w = imagesx($im);
321 $h = imagesy($im);
322 $tmpStr = $this->randomName();
323 $theImage = $tmpStr.'_img.'.$this->gifExtension;
324 $theDest = $tmpStr.'_dest.'.$this->gifExtension;
325 $theMask = $tmpStr.'_mask.'.$this->gifExtension;
326 $theMask2 = $tmpStr.'_mask2.'.trim($GLOBALS['TYPO3_CONF_VARS']['GFX']['im_mask_temp_ext_noloss']);
327 // prepare overlay image
328 $cpImg = $this->imageCreateFromGif($BBimage[3]);
329 $destImg = imagecreate($w,$h);
330 ImageColorAllocate($destImg, 0,0,0);
331 $this->copyGifOntoGif($destImg,$cpImg,$conf,$workArea);
332 $this->ImageGif($destImg, $theImage);
333 imageDestroy($cpImg);
334 imageDestroy($destImg);
335 // prepare mask image
336 $cpImg = $this->imageCreateFromGif($BBmask[3]);
337 $destImg = imagecreate($w,$h);
338 ImageColorAllocate($destImg, 0,0,0);
339 $this->copyGifOntoGif($destImg,$cpImg,$conf,$workArea);
340 $this->ImageGif($destImg, $theMask);
341 imageDestroy($cpImg);
342 imageDestroy($destImg);
343 // treat the mask
344 $this->imageMagickExec($theMask,$theMask2,'-colorspace GRAY'.$this->maskNegate);
345 // mask the images
346 $this->ImageGif($im, $theDest);
347
348 $this->combineExec($theDest,$theImage,$theMask2,$theDest);
349
350 $backIm = $this->imageCreateFromGif($theDest); // The main image is loaded again...
351 if ($backIm) { // ... and if nothing went wrong we load it onto the old one.
352 ImageColorTransparent($backIm,-1);
353 $im = $backIm;
354 }
355 // unlink files from process
356 if (!$this->dontUnlinkTempFiles) {
357 unlink($theDest);
358 unlink($theImage);
359 unlink($theMask);
360 unlink($theMask2);
361 }
362 }
363 }
364 }
365
366 /**
367 * Implements the "IMAGE" GIFBUILDER object, when the "mask" property is false (using only $conf['file'])
368 *
369 * @param pointer GDlib image pointer
370 * @param array TypoScript array with configuration for the GIFBUILDER object.
371 * @param array The current working area coordinates.
372 * @return void
373 * @see tslib_gifBuilder::make(), maskImageOntoImage()
374 */
375 function copyImageOntoImage(&$im,$conf,$workArea) {
376 if ($conf['file']) {
377 if ($conf['BBOX'][2]!=$this->gifExtension) {
378 $conf['BBOX']=$this->imageMagickConvert($conf['BBOX'][3],$this->gifExtension,'','','','','');
379 $conf['file']=$conf['BBOX'][3];
380 }
381 $cpImg = $this->imageCreateFromGif($conf['file']);
382 $this->copyGifOntoGif($im,$cpImg,$conf,$workArea);
383 imageDestroy($cpImg);
384 }
385 }
386
387 /**
388 * Copies two GDlib image pointers onto each other, using TypoScript configuration from $conf and the input $workArea definition.
389 *
390 * @param pointer GDlib image pointer, destination (bottom image)
391 * @param pointer GDlib image pointer, source (top image)
392 * @param array TypoScript array with the properties for the IMAGE GIFBUILDER object. Only used for the "tile" property value.
393 * @param array Work area
394 * @return void Works on the $im image pointer
395 * @access private
396 */
397 function copyGifOntoGif(&$im,$cpImg,$conf,$workArea) {
398 $cpW = imagesx($cpImg);
399 $cpH = imagesy($cpImg);
400 $tile = t3lib_div::intExplode(',',$conf['tile']);
401 $tile[0] = t3lib_div::intInRange($tile[0],1,20);
402 $tile[1] = t3lib_div::intInRange($tile[1],1,20);
403 $cpOff = $this->objPosition($conf,$workArea,Array($cpW*$tile[0],$cpH*$tile[1]));
404
405 for ($xt=0;$xt<$tile[0];$xt++) {
406 $Xstart=$cpOff[0]+$cpW*$xt;
407 if ($Xstart+$cpW > $workArea[0]) { // if this image is inside of the workArea, then go on
408 // X:
409 if ($Xstart < $workArea[0]) {
410 $cpImgCutX = $workArea[0]-$Xstart;
411 $Xstart = $workArea[0];
412 } else {
413 $cpImgCutX = 0;
414 }
415 $w = $cpW-$cpImgCutX;
416 if ($Xstart > $workArea[0]+$workArea[2]-$w) {
417 $w = $workArea[0]+$workArea[2]-$Xstart;
418 }
419 if ($Xstart < $workArea[0]+$workArea[2]) { // if this image is inside of the workArea, then go on
420 // Y:
421 for ($yt=0;$yt<$tile[1];$yt++) {
422 $Ystart=$cpOff[1]+$cpH*$yt;
423 if ($Ystart+$cpH > $workArea[1]) { // if this image is inside of the workArea, then go on
424 if ($Ystart < $workArea[1]) {
425 $cpImgCutY = $workArea[1]-$Ystart;
426 $Ystart = $workArea[1];
427 } else {
428 $cpImgCutY = 0;
429 }
430 $h = $cpH-$cpImgCutY;
431 if ($Ystart > $workArea[1]+$workArea[3]-$h) {
432 $h = $workArea[1]+$workArea[3]-$Ystart;
433 }
434 if ($Ystart < $workArea[1]+$workArea[3]) { // if this image is inside of the workArea, then go on
435 $this->imagecopyresized($im, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h);
436 }
437 }
438 } // Y:
439 }
440 }
441 }
442 }
443
444 /**
445 * Alternative function for using the similat PHP function imagecopyresized(). Used for GD2 only.
446 *
447 * OK, the reason for this stupid fix is the following story:
448 * GD1.x was capable of copying two images together and combining their palettes! GD2 is apparently not.
449 * With GD2 only the palette of the dest-image is used which mostly results in totally black images when trying to
450 * copy a color-ful image onto the destination.
451 * The GD2-fix is to
452 * 1) Create a blank TRUE-COLOR image
453 * 2) Copy the destination image onto that one
454 * 3) Then do the actual operation; Copying the source (top image) onto that
455 * 4) ... and return the result pointer.
456 * 5) Reduce colors (if we do not, the result may become strange!)
457 * It works, but the resulting images is now a true-color PNG which may be very large.
458 * So, why not use 'imagetruecolortopalette ($im, TRUE, 256)' - well because it does NOT WORK! So simple is that.
459 *
460 * For parameters, see PHP function "imagecopyresized()"
461 *
462 * @param pointer
463 * @param pointer
464 * @param integer
465 * @param integer
466 * @param integer
467 * @param integer
468 * @param integer
469 * @param integer
470 * @param integer
471 * @param integer
472 * @return void
473 * @access private
474 * @see t3lib_iconWorks::imagecopyresized()
475 */
476 function imagecopyresized(&$im, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h) {
477 if ($this->GD2) {
478 $im_base = imagecreatetruecolor(imagesx($im), imagesy($im)); // Make true color image
479 imagecopyresized($im_base, $im, 0,0,0,0, imagesx($im),imagesy($im),imagesx($im),imagesy($im)); // Copy the source image onto that
480 imagecopyresized($im_base, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h); // Then copy the $cpImg onto that (the actual operation!)
481 $im = $im_base; // Set pointer
482 $this->makeEffect($im, Array('value'=>'colors=256')); // Reduce colors to 256 - make SURE that IM is working then!
483 } else {
484 imagecopyresized($im, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h);
485 }
486 }
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510 /********************************
511 *
512 * Text / "TEXT" GIFBUILDER object
513 *
514 ********************************/
515
516 /**
517 * Implements the "TEXT" GIFBUILDER object
518 *
519 * @param pointer GDlib image pointer
520 * @param array TypoScript array with configuration for the GIFBUILDER object.
521 * @param array The current working area coordinates.
522 * @return void
523 * @see tslib_gifBuilder::make()
524 */
525 function makeText(&$im,$conf,$workArea) {
526 // Spacing
527 $spacing = intval($conf['spacing']);
528 $wordSpacing = intval($conf['wordSpacing']);
529 $wordSpacing = $wordSpacing?$wordSpacing:$spacing*2;
530 // Position
531 $txtPos = $this->txtPosition($conf,$workArea,$conf['BBOX']);
532 $theText = $this->recodeString($conf['text']);
533
534 if ($conf['imgMap'] && is_array($conf['imgMap.'])) {
535 $this->addToMap($this->calcTextCordsForMap($conf['BBOX'][2],$txtPos, $conf['imgMap.']), $conf['imgMap.']);
536 }
537 if (!$conf['hideButCreateMap']) {
538 // Font Color:
539 $cols=$this->convertColor($conf['fontColor']);
540 // NiceText is calculated
541 if (!$conf['niceText']) {
542 // Font Color is reserved:
543 $this->reduceColors($im,256, 200);
544 $Fcolor = ImageColorAllocate($im, $cols[0],$cols[1],$cols[2]);
545 // antiAliasing is setup:
546 $Fcolor = ($conf['antiAlias']) ? $Fcolor : -$Fcolor;
547
548 for ($a=0; $a<$conf['iterations']; $a++) {
549 if ($spacing || $wordSpacing) { // If any kind of spacing applys, we use this function:
550 $this->SpacedImageTTFText($im, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, t3lib_stdGraphic::prependAbsolutePath($conf['fontFile']), $theText, $spacing, $wordSpacing);
551 } else {
552 ImageTTFText($im, t3lib_div::freetypeDpiComp($conf['fontSize']), $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, t3lib_stdGraphic::prependAbsolutePath($conf['fontFile']), $theText);
553 }
554 }
555 } else { // NICETEXT::
556 // options anti_aliased and iterations is NOT available when doing this!!
557 $w = imagesx($im);
558 $h = imagesy($im);
559 $tmpStr = $this->randomName();
560
561 $fileMenu = $tmpStr.'_menuNT.'.$this->gifExtension;
562 $fileColor = $tmpStr.'_colorNT.'.$this->gifExtension;
563 $fileMask = $tmpStr.'_maskNT.'.$this->gifExtension;
564 // Scalefactor
565 $sF = t3lib_div::intInRange($conf['niceText.']['scaleFactor'],2,5);
566 $newW = ceil($sF*imagesx($im));
567 $newH = ceil($sF*imagesy($im));
568
569 // Make mask
570 $maskImg = imagecreate($newW, $newH);
571 ImageColorAllocate($maskImg, 255,255,255);
572 $Fcolor = ImageColorAllocate($maskImg, 0,0,0);
573 if ($spacing || $wordSpacing) { // If any kind of spacing applys, we use this function:
574 $this->SpacedImageTTFText($maskImg, ceil($sF*$conf['fontSize']), $conf['angle'], ceil($sF*$txtPos[0]), ceil($sF*$txtPos[1]), $Fcolor, t3lib_stdGraphic::prependAbsolutePath($conf['fontFile']), $theText, ceil($sF*$spacing), ceil($sF*$wordSpacing));
575 } else {
576 ImageTTFText($maskImg, t3lib_div::freetypeDpiComp(ceil($sF*$conf['fontSize'])), $conf['angle'], ceil($sF*$txtPos[0]), ceil($sF*$txtPos[1]), $Fcolor, t3lib_stdGraphic::prependAbsolutePath($conf['fontFile']), $theText);
577 }
578 $this->ImageGif($maskImg, $fileMask);
579 ImageDestroy($maskImg);
580
581 // Downscales the mask
582 if ($this->NO_IM_EFFECTS) {
583 if ($this->maskNegate) {
584 $command = trim($this->scalecmd.' '.$w.'x'.$h.'!'); // Negate 2 times makes no negate...
585 } else {
586 $command = trim($this->scalecmd.' '.$w.'x'.$h.'! -negate');
587 }
588 } else {
589 if ($this->maskNegate) {
590 $command = trim($conf['niceText.']['before'].' '.$this->scalecmd.' '.$w.'x'.$h.'! '.$conf['niceText.']['after']);
591 } else {
592 $command = trim($conf['niceText.']['before'].' '.$this->scalecmd.' '.$w.'x'.$h.'! '.$conf['niceText.']['after'].' -negate');
593 }
594 if ($conf['niceText.']['sharpen']) {
595 if ($this->V5_EFFECTS) {
596 $command.=$this->v5_sharpen($conf['niceText.']['sharpen']);
597 } else {
598 $command.=' -sharpen '.t3lib_div::intInRange($conf['niceText.']['sharpen'],1,99);
599 }
600 }
601 }
602 $this->imageMagickExec($fileMask,$fileMask,$command);
603
604 // Make the color-file
605 $colorImg = imagecreate($w,$h);
606 ImageColorAllocate($colorImg, $cols[0],$cols[1],$cols[2]);
607 $this->ImageGif($colorImg, $fileColor);
608 ImageDestroy($colorImg);
609
610 // The mask is applied
611 $this->ImageGif($im, $fileMenu); // The main pictures is saved temporarily
612
613 $this->combineExec($fileMenu,$fileColor,$fileMask,$fileMenu);
614
615 $backIm = $this->imageCreateFromGif($fileMenu); // The main image is loaded again...
616 if ($backIm) { // ... and if nothing went wrong we load it onto the old one.
617 ImageColorTransparent($backIm,-1);
618 $im = $backIm;
619 }
620
621 // Deleting temporary files;
622 if (!$this->dontUnlinkTempFiles) {
623 unlink($fileMenu);
624 unlink($fileColor);
625 unlink($fileMask);
626 }
627 }
628 }
629 }
630
631 /**
632 * Calculates text position for printing the text onto the image based on configuration like alignment and workarea.
633 *
634 * @param array TypoScript array for the TEXT GIFBUILDER object
635 * @param array Workarea definition
636 * @param array Bounding box information, was set in tslib_gifBuilder::start()
637 * @return array [0]=x, [1]=y, [2]=w, [3]=h
638 * @access private
639 * @see makeText()
640 */
641 function txtPosition($conf,$workArea,$BB) {
642 $bbox = $BB[2];
643 $angle=intval($conf['angle'])/180*pi();
644 $conf['angle']=0;
645 $straightBB = $this->calcBBox($conf);
646
647 // offset, align, valign, workarea
648 $result=Array(); // [0]=x, [1]=y, [2]=w, [3]=h
649 $result[2] = $BB[0];
650 $result[3] = $BB[1];
651 $w=$workArea[2];
652 $h=$workArea[3];
653
654 switch($conf['align']) {
655 case 'right':
656 case 'center':
657 $factor=abs(cos($angle));
658 $sign=(cos($angle)<0)?-1:1;
659 $len1 = $sign*$factor*$straightBB[0];
660 $len2= $sign*$BB[0];
661 $result[0] = $w-ceil($len2*$factor+(1-$factor)*$len1);
662
663 $factor=abs(sin($angle));
664 $sign=(sin($angle)<0)?-1:1;
665 $len1= $sign*$factor*$straightBB[0];
666 $len2= $sign*$BB[1];
667 $result[1]=ceil($len2*$factor+(1-$factor)*$len1);
668 break;
669 }
670 switch($conf['align']) {
671 case 'right':
672 break;
673 case 'center':
674 $result[0] = round(($result[0])/2);
675 $result[1] = round(($result[1])/2);
676 break;
677 default:
678 $result[0]=0;
679 $result[1]=0;
680 break;
681 }
682 $result = $this->applyOffset($result,t3lib_div::intExplode(',',$conf['offset']));
683 $result = $this->applyOffset($result,$workArea);
684 return $result;
685 }
686
687 /**
688 * Calculates bounding box information for the TEXT GIFBUILDER object.
689 *
690 * @param array TypoScript array for the TEXT GIFBUILDER object
691 * @return array Array with three keys [0]/[1] being x/y and [2] being the bounding box array
692 * @access private
693 * @see txtPosition(), tslib_gifBuilder::start()
694 */
695 function calcBBox($conf) {
696 if (!$conf['niceText']) {
697 $sF = 1;
698 } else { // NICETEXT::
699 $sF = t3lib_div::intInRange($conf['niceText.']['scaleFactor'],2,5);
700 }
701
702 $spacing = intval($conf['spacing']);
703 $wordSpacing = intval($conf['wordSpacing']);
704 $wordSpacing = $wordSpacing?$wordSpacing:$spacing*2;
705
706 $spacing*=$sF;
707 $wordSpacing*=$sF;
708 $theText = $this->recodeString($conf['text']);
709
710 $charInf = ImageTTFBBox(t3lib_div::freetypeDpiComp($sF*$conf['fontSize']), $conf['angle'], t3lib_stdGraphic::prependAbsolutePath($conf['fontFile']), $theText);
711 $theBBoxInfo = $charInf;
712 if ($conf['angle']) {
713 $xArr = Array($charInf[0],$charInf[2],$charInf[4],$charInf[6]);
714 $yArr = Array($charInf[1],$charInf[3],$charInf[5],$charInf[7]);
715 $x=max($xArr)-min($xArr);
716 $y=max($yArr)-min($yArr);
717 } else {
718 $x = ($charInf[2]-$charInf[0]);
719 $y = ($charInf[1]-$charInf[7]);
720 }
721 if ($spacing || $wordSpacing) { // If any kind of spacing applys, we use this function:
722 $x=0;
723 if (!$spacing && $wordSpacing) {
724 $bits = explode(' ',$theText);
725 while(list(,$word)=each($bits)) {
726 $word.=' ';
727 $wordInf = ImageTTFBBox(t3lib_div::freetypeDpiComp($sF*$conf['fontSize']), $conf['angle'], t3lib_stdGraphic::prependAbsolutePath($conf['fontFile']), $word);
728 $wordW = ($wordInf[2]-$wordInf[0]);
729 $x+=$wordW+$wordSpacing;
730 }
731 } else {
732 $utf8Chars = $this->singleChars($theText);
733 // For each UTF-8 char, do:
734 foreach($utf8Chars as $char) {
735 $charInf = ImageTTFBBox(t3lib_div::freetypeDpiComp($sF*$conf['fontSize']), $conf['angle'], t3lib_stdGraphic::prependAbsolutePath($conf['fontFile']), $char);
736 $charW = ($charInf[2]-$charInf[0]);
737 $x+=$charW+(($char==' ')?$wordSpacing:$spacing);
738 }
739 }
740 }
741
742 if ($sF>1) {
743 $x = ceil($x/$sF);
744 $y = ceil($y/$sF);
745 if (is_array($theBBoxInfo)) {
746 reset($theBBoxInfo);
747 while(list($key,$val)=each($theBBoxInfo)) {
748 $theBBoxInfo[$key]=ceil($theBBoxInfo[$key]/$sF);
749 }
750 }
751 }
752 return Array($x,$y,$theBBoxInfo);
753 }
754
755 /**
756 * Adds an <area> tag to the internal variable $this->map which is used to accumulate the content for an ImageMap
757 *
758 * @param array Coordinates for a polygon image map as created by ->calcTextCordsForMap()
759 * @param array Configuration for "imgMap." property of a TEXT GIFBUILDER object.
760 * @return void
761 * @access private
762 * @see makeText(), calcTextCordsForMap()
763 */
764 function addToMap($cords,$conf) {
765 $JS = $conf['noBlur'] ? '' : ' onfocus="blurLink(this);"';
766 $this->map.='<area shape="poly" coords="'.implode($cords,',').'" href="'.htmlspecialchars($conf['url']).'" target="'.$conf['target'].'"'.$JS.' />';
767 }
768
769 /**
770 * Calculating the coordinates for a TEXT string on an image map. Used in an <area> tag
771 *
772 * @param array Coordinates (from BBOX array)
773 * @param array Offset array
774 * @param array Configuration for "imgMap." property of a TEXT GIFBUILDER object.
775 * @return array
776 * @access private
777 * @see makeText(), calcTextCordsForMap()
778 */
779 function calcTextCordsForMap($cords,$offset, $conf) {
780 $pars = t3lib_div::intExplode(',',$conf['explode'].',');
781
782 $newCords[0] = $cords[0]+$offset[0]-$pars[0];
783 $newCords[1] = $cords[1]+$offset[1]+$pars[1];
784 $newCords[2] = $cords[2]+$offset[0]+$pars[0];
785 $newCords[3] = $cords[3]+$offset[1]+$pars[1];
786 $newCords[4] = $cords[4]+$offset[0]+$pars[0];
787 $newCords[5] = $cords[5]+$offset[1]-$pars[1];
788 $newCords[6] = $cords[6]+$offset[0]-$pars[0];
789 $newCords[7] = $cords[7]+$offset[1]-$pars[1];
790
791 return $newCords;
792 }
793
794 /**
795 * Printing text onto an image like the PHP function imageTTFText does but in addition it offers options for spacing of letters and words.
796 * Spacing is done by printing one char at a time and this means that the spacing is rather uneven and probably not very nice.
797 * See
798 *
799 * @param pointer (See argument for PHP function imageTTFtext())
800 * @param integer (See argument for PHP function imageTTFtext())
801 * @param integer (See argument for PHP function imageTTFtext())
802 * @param integer (See argument for PHP function imageTTFtext())
803 * @param integer (See argument for PHP function imageTTFtext())
804 * @param integer (See argument for PHP function imageTTFtext())
805 * @param string (See argument for PHP function imageTTFtext())
806 * @param string (See argument for PHP function imageTTFtext()). UTF-8 string, possibly with entities in.
807 * @param integer The spacing of letters in pixels
808 * @param integer The spacing of words in pixels
809 * @return void
810 * @access private
811 */
812 function SpacedImageTTFText(&$im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $text, $spacing, $wordSpacing) {
813 if (!$spacing && $wordSpacing) {
814 $bits = explode(' ',$text);
815 reset($bits);
816 while(list(,$word)=each($bits)) {
817 $word.=' ';
818 $word = $word;
819 $wordInf = ImageTTFBBox(t3lib_div::freetypeDpiComp($fontSize), $angle, t3lib_stdGraphic::prependAbsolutePath($fontFile), $word);
820 $wordW = ($wordInf[2]-$wordInf[0]);
821 ImageTTFText($im, t3lib_div::freetypeDpiComp($fontSize), $angle, $x, $y, $Fcolor, t3lib_stdGraphic::prependAbsolutePath($fontFile), $word);
822 $x+=$wordW+$wordSpacing;
823 }
824 } else {
825 $utf8Chars = $this->singleChars($text);
826 // For each UTF-8 char, do:
827 foreach($utf8Chars as $char) {
828 $charInf = ImageTTFBBox(t3lib_div::freetypeDpiComp($fontSize), $angle, t3lib_stdGraphic::prependAbsolutePath($fontFile), $char);
829 $charW = ($charInf[2]-$charInf[0]);
830 ImageTTFText($im, t3lib_div::freetypeDpiComp($fontSize), $angle, $x, $y, $Fcolor, t3lib_stdGraphic::prependAbsolutePath($fontFile), $char);
831 $x+=$charW+(($char==' ')?$wordSpacing:$spacing);
832 }
833 }
834 }
835
836 /**
837 * Function that finds the right fontsize that will render the textstring within a certain width
838 *
839 * @param array The TypoScript properties of the TEXT GIFBUILDER object
840 * @return integer The new fontSize
841 * @access private
842 * @author Rene Fritz <r.fritz@colorcube.de>
843 * @see tslib_gifBuilder::start()
844 */
845 function fontResize($conf) {
846 // you have to use +calc options like [10.h] in 'offset' to get the right position of your text-image, if you use +calc in XY height!!!!
847 $maxWidth = intval($conf['maxWidth']);
848 if ($maxWidth) {
849 if ($spacing || $wordSpacing) { // If any kind of spacing applys, we use this function:
850 return $conf['fontSize'];
851 // ################ no calc for spacing yet !!!!!!
852 } else {
853 $fontSize = $conf['fontSize'];
854 do {
855 $conf['fontSize'] = $fontSize;
856 // determine bounding box.
857 $bounds = ImageTTFBBox(t3lib_div::freetypeDpiComp($conf['fontSize']), $conf['angle'], t3lib_stdGraphic::prependAbsolutePath($conf['fontFile']), $this->recodeString($conf['text']));
858 if ($conf['angle']< 0) {
859 $pixelWidth = abs($bounds[4]-$bounds[0]);
860 } elseif ($conf['angle'] > 0) {
861 $pixelWidth = abs($bounds[2]-$bounds[6]);
862 } else {
863 $pixelWidth = abs($bounds[4]-$bounds[6]);
864 }
865 // This is a very raw calculation but it makes it in one step
866 $fontSize=(int)($maxWidth*$fontSize/$pixelWidth);
867 if ($fontSize >= $conf['fontSize']) {
868 $fontSize = $conf['fontSize']-1;
869 }
870 } while ($pixelWidth > $maxWidth);
871 }//if spacing
872 }
873 return $conf['fontSize'];
874 }
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891 /*********************************************
892 *
893 * Other GIFBUILDER objects related to TEXT
894 *
895 *********************************************/
896
897 /**
898 * Implements the "OUTLINE" GIFBUILDER object / property for the TEXT object
899 *
900 * @param pointer GDlib image pointer
901 * @param array TypoScript array with configuration for the GIFBUILDER object.
902 * @param array The current working area coordinates.
903 * @param array TypoScript array with configuration for the associated TEXT GIFBUILDER object.
904 * @return void
905 * @see tslib_gifBuilder::make(), makeText()
906 */
907 function makeOutline(&$im,$conf,$workArea,$txtConf) {
908 $thickness = intval($conf['thickness']);
909 if ($thickness) {
910 $txtConf['fontColor'] = $conf['color'];
911 $outLineDist = t3lib_div::intInRange($thickness,1,2);
912 for ($b=1;$b<=$outLineDist;$b++) {
913 if ($b==1) {
914 $it = 8;
915 } else {
916 $it = 16;
917 }
918 $outL = $this->circleOffset($b, $it);
919 for ($a=0;$a<$it;$a++) {
920 $this->makeText($im,$txtConf,$this->applyOffset($workArea,$outL[$a]));
921 }
922 }
923 }
924 }
925
926 /**
927 * Creates some offset values in an array used to simulate a circularly applied outline around TEXT
928 *
929 * access private
930 *
931 * @param integer Distance
932 * @param integer Iterations.
933 * @return array
934 * @see makeOutline()
935 */
936 function circleOffset($distance, $iterations) {
937 $res = Array();
938 if ($distance && $iterations) {
939 for ($a=0;$a<$iterations;$a++) {
940 $yOff = round(sin(2*pi()/$iterations*($a+1))*100*$distance);
941 if ($yOff) {$yOff = intval(ceil(abs($yOff/100))*($yOff/abs($yOff)));}
942 $xOff = round(cos(2*pi()/$iterations*($a+1))*100*$distance);
943 if ($xOff) {$xOff = intval(ceil(abs($xOff/100))*($xOff/abs($xOff)));}
944 $res[$a] = Array($xOff,$yOff);
945 }
946 }
947 return $res;
948 }
949
950 /**
951 * Implements the "EMBOSS" GIFBUILDER object / property for the TEXT object
952 *
953 * @param pointer GDlib image pointer
954 * @param array TypoScript array with configuration for the GIFBUILDER object.
955 * @param array The current working area coordinates.
956 * @param array TypoScript array with configuration for the associated TEXT GIFBUILDER object.
957 * @return void
958 * @see tslib_gifBuilder::make(), makeShadow()
959 */
960 function makeEmboss(&$im,$conf,$workArea,$txtConf) {
961 $conf['color']=$conf['highColor'];
962 $this->makeShadow($im,$conf,$workArea,$txtConf);
963 $newOffset = t3lib_div::intExplode(',',$conf['offset']);
964 $newOffset[0]*=-1;
965 $newOffset[1]*=-1;
966 $conf['offset']=implode($newOffset,',');
967 $conf['color']=$conf['lowColor'];
968 $this->makeShadow($im,$conf,$workArea,$txtConf);
969 }
970
971 /**
972 * Implements the "SHADOW" GIFBUILDER object / property for the TEXT object
973 * The operation involves ImageMagick for combining.
974 *
975 * @param pointer GDlib image pointer
976 * @param array TypoScript array with configuration for the GIFBUILDER object.
977 * @param array The current working area coordinates.
978 * @param array TypoScript array with configuration for the associated TEXT GIFBUILDER object.
979 * @return void
980 * @see tslib_gifBuilder::make(), makeText(), makeEmboss()
981 */
982 function makeShadow(&$im,$conf,$workArea,$txtConf) {
983 $workArea = $this->applyOffset($workArea,t3lib_div::intExplode(',',$conf['offset']));
984 $blurRate = t3lib_div::intInRange(intval($conf['blur']),0,99);
985
986 if (!$blurRate || $this->NO_IM_EFFECTS) { // No effects if ImageMagick ver. 5+
987 $txtConf['fontColor'] = $conf['color'];
988 $this->makeText($im,$txtConf,$workArea);
989 } else {
990 $w = imagesx($im);
991 $h = imagesy($im);
992 $blurBorder= 3; // area around the blur used for cropping something
993 $tmpStr = $this->randomName();
994 $fileMenu = $tmpStr.'_menu.'.$this->gifExtension;
995 $fileColor = $tmpStr.'_color.'.$this->gifExtension;
996 $fileMask = $tmpStr.'_mask.'.$this->gifExtension;
997
998 // BlurColor Image laves
999 $blurColImg = imagecreate($w,$h);
1000 $bcols=$this->convertColor($conf['color']);
1001 ImageColorAllocate($blurColImg, $bcols[0],$bcols[1],$bcols[2]);
1002 $this->ImageGif($blurColImg, $fileColor);
1003 ImageDestroy($blurColImg);
1004
1005 // The mask is made: BlurTextImage
1006 $blurTextImg = imagecreate($w+$blurBorder*2,$h+$blurBorder*2);
1007 ImageColorAllocate($blurTextImg, 0,0,0); // black background
1008 $txtConf['fontColor'] = 'white';
1009 $blurBordArr = Array($blurBorder,$blurBorder);
1010 $this->makeText($blurTextImg,$txtConf, $this->applyOffset($workArea,$blurBordArr));
1011 $this->ImageGif($blurTextImg, $fileMask); // dump to temporary file
1012 ImageDestroy($blurTextImg); // destroy
1013
1014
1015 $command='';
1016 $command.=$this->maskNegate;
1017
1018 if ($this->V5_EFFECTS) {
1019 $command.=$this->v5_blur($blurRate+1);
1020 } else {
1021 // Blurring of the mask
1022 $times = ceil($blurRate/10); // How many blur-commands that is executed. Min = 1;
1023 $newBlurRate = $blurRate*4; // Her booster vi blur-rate, så at den allerede ved 25 er på 100. Resten op til 99 går på iterationer af blur-kommandoen
1024 $newBlurRate = t3lib_div::intInRange($newBlurRate,1,99);
1025 for ($a=0;$a<$times;$a++) { // Building blur-command
1026 $command.=' -blur '.$blurRate;
1027 }
1028 }
1029
1030 $this->imageMagickExec($fileMask,$fileMask,$command);
1031
1032 $blurTextImg_tmp = $this->imageCreateFromGif($fileMask); // the mask is loaded again
1033 if ($blurTextImg_tmp) { // if nothing went wrong we continue with the blurred mask
1034
1035 // cropping the border from the mask
1036 $blurTextImg = imagecreate($w,$h);
1037 $this->imagecopyresized($blurTextImg, $blurTextImg_tmp, 0, 0, $blurBorder, $blurBorder, $w, $h, $w, $h);
1038 ImageDestroy($blurTextImg_tmp); // Destroy the temporary mask
1039
1040 // adjust the mask
1041 $intensity = 40;
1042 if ($conf['intensity']) {
1043 $intensity = t3lib_div::intInRange($conf['intensity'],0,100);
1044 }
1045 $intensity = ceil(255-($intensity/100*255));
1046 $this->inputLevels($blurTextImg,0,$intensity,$this->maskNegate);
1047
1048 $opacity = t3lib_div::intInRange(intval($conf['opacity']),0,100);
1049 if ($opacity && $opacity<100) {
1050 $high = ceil(255*$opacity/100);
1051 $this->outputLevels($blurTextImg,0,$high,$this->maskNegate); // reducing levels as the opacity demands
1052 }
1053
1054 $this->ImageGif($blurTextImg, $fileMask); // Dump the mask again
1055 ImageDestroy($blurTextImg); // Destroy the mask
1056
1057 // The pictures are combined
1058 $this->ImageGif($im, $fileMenu); // The main pictures is saved temporarily
1059
1060 $this->combineExec($fileMenu,$fileColor,$fileMask,$fileMenu);
1061
1062 $backIm = $this->imageCreateFromGif($fileMenu); // The main image is loaded again...
1063 if ($backIm) { // ... and if nothing went wrong we load it onto the old one.
1064 ImageColorTransparent($backIm,-1);
1065 $im = $backIm;
1066 }
1067 }
1068 // Deleting temporary files;
1069 if (!$this->dontUnlinkTempFiles) {
1070 unlink($fileMenu);
1071 unlink($fileColor);
1072 unlink($fileMask);
1073 }
1074 }
1075 }
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097 /****************************
1098 *
1099 * Other GIFBUILDER objects
1100 *
1101 ****************************/
1102
1103 /**
1104 * Implements the "BOX" GIFBUILDER object
1105 *
1106 * @param pointer GDlib image pointer
1107 * @param array TypoScript array with configuration for the GIFBUILDER object.
1108 * @param array The current working area coordinates.
1109 * @return void
1110 * @see tslib_gifBuilder::make()
1111 */
1112 function makeBox(&$im,$conf,$workArea) {
1113 $cords = t3lib_div::intExplode(',',$conf['dimensions'].',,,');
1114 $conf['offset']=$cords[0].','.$cords[1];
1115 $cords = $this->objPosition($conf,$workArea,Array($cords[2],$cords[3]));
1116 $cols=$this->convertColor($conf['color']);
1117 $this->reduceColors($im,256, 255);
1118 $tmpColor = ImageColorAllocate($im, $cols[0],$cols[1],$cols[2]);
1119 imagefilledrectangle($im, $cords[0], $cords[1], $cords[0]+$cords[2]-1, $cords[1]+$cords[3]-1, $tmpColor);
1120 }
1121
1122 /**
1123 * Implements the "EFFECT" GIFBUILDER object
1124 * The operation involves ImageMagick for applying effects
1125 *
1126 * @param pointer GDlib image pointer
1127 * @param array TypoScript array with configuration for the GIFBUILDER object.
1128 * @return void
1129 * @see tslib_gifBuilder::make(), applyImageMagickToPHPGif()
1130 */
1131 function makeEffect(&$im, $conf) {
1132 $commands = $this->IMparams($conf['value']);
1133 if ($commands) {
1134 $this->applyImageMagickToPHPGif($im, $commands);
1135 }
1136 }
1137
1138 /**
1139 * Creating ImageMagick paramters from TypoScript property
1140 *
1141 * @param string A string with effect keywords=value pairs separated by "|"
1142 * @return string ImageMagick prepared parameters.
1143 * @access private
1144 * @see makeEffect()
1145 */
1146 function IMparams($setup) {
1147 if (!trim($setup)){return '';}
1148 $effects = explode('|', $setup);
1149 $commands = '';
1150 while(list(,$val)=each($effects)) {
1151 $pairs=explode('=',$val,2);
1152 $value = trim($pairs[1]);
1153 $effect = strtolower(trim($pairs[0]));
1154 switch($effect) {
1155 case 'gamma':
1156 $commands.=' -gamma '.doubleval($value);
1157 break;
1158 case 'blur':
1159 if (!$this->NO_IM_EFFECTS) {
1160 if ($this->V5_EFFECTS) {
1161 $commands.=$this->v5_blur($value);
1162 } else {
1163 $commands.=' -blur '.t3lib_div::intInRange($value,1,99);
1164 }
1165 }
1166 break;
1167 case 'sharpen':
1168 if (!$this->NO_IM_EFFECTS) {
1169 if ($this->V5_EFFECTS) {
1170 $commands.=$this->v5_sharpen($value);
1171 } else {
1172 $commands.=' -sharpen '.t3lib_div::intInRange($value,1,99);
1173 }
1174 }
1175 break;
1176 case 'rotate':
1177 $commands.=' -rotate '.t3lib_div::intInRange($value,0,360);
1178 break;
1179 case 'solarize':
1180 $commands.=' -solarize '.t3lib_div::intInRange($value,0,99);
1181 break;
1182 case 'swirl':
1183 $commands.=' -swirl '.t3lib_div::intInRange($value,0,1000);
1184 break;
1185 case 'wave':
1186 $params = t3lib_div::intExplode(',',$value);
1187 $commands.=' -wave '.t3lib_div::intInRange($params[0],0,99).'x'.t3lib_div::intInRange($params[1],0,99);
1188 break;
1189 case 'charcoal':
1190 $commands.=' -charcoal '.t3lib_div::intInRange($value,0,100);
1191 break;
1192 case 'gray':
1193 $commands.=' -colorspace GRAY';
1194 break;
1195 case 'edge':
1196 $commands.=' -edge '.t3lib_div::intInRange($value,0,99);
1197 break;
1198 case 'emboss':
1199 $commands.=' -emboss';
1200 break;
1201 case 'flip':
1202 $commands.=' -flip';
1203 break;
1204 case 'flop':
1205 $commands.=' -flop';
1206 break;
1207 case 'colors':
1208 $commands.=' -colors '.t3lib_div::intInRange($value,2,255);
1209 break;
1210 case 'shear':
1211 $commands.=' -shear '.t3lib_div::intInRange($value,-90,90);
1212 break;
1213 case 'invert':
1214 $commands.=' -negate';
1215 break;
1216 }
1217 }
1218 return $commands;
1219 }
1220
1221 /**
1222 * Implements the "ADJUST" GIFBUILDER object
1223 *
1224 * @param pointer GDlib image pointer
1225 * @param array TypoScript array with configuration for the GIFBUILDER object.
1226 * @return void
1227 * @see tslib_gifBuilder::make(), autoLevels(), outputLevels(), inputLevels()
1228 */
1229 function adjust(&$im, $conf) {
1230 $setup = $conf['value'];
1231 if (!trim($setup)){return '';}
1232 $effects = explode('|', $setup);
1233 while(list(,$val)=each($effects)) {
1234 $pairs=explode('=',$val,2);
1235 $value = trim($pairs[1]);
1236 $effect = strtolower(trim($pairs[0]));
1237 switch($effect) {
1238 case 'inputlevels': // low,high
1239 $params = t3lib_div::intExplode(',',$value);
1240 $this->inputLevels($im,$params[0],$params[1]);
1241 break;
1242 case 'outputlevels':
1243 $params = t3lib_div::intExplode(',',$value);
1244 $this->outputLevels($im,$params[0],$params[1]);
1245 break;
1246 case 'autolevels':
1247 $this->autoLevels($im);
1248 break;
1249 }
1250 }
1251 }
1252
1253 /**
1254 * Implements the "CROP" GIFBUILDER object
1255 *
1256 * @param pointer GDlib image pointer
1257 * @param array TypoScript array with configuration for the GIFBUILDER object.
1258 * @return void
1259 * @see tslib_gifBuilder::make()
1260 */
1261 function crop(&$im,$conf) {
1262 $this->setWorkArea(''); // clears workArea to total image
1263 $cords = t3lib_div::intExplode(',',$conf['crop'].',,,');
1264 $conf['offset']=$cords[0].','.$cords[1];
1265 $cords = $this->objPosition($conf,$this->workArea,Array($cords[2],$cords[3]));
1266
1267 $newIm = imagecreate($cords[2],$cords[3]);
1268 $cols=$this->convertColor($conf['backColor']?$conf['backColor']:$this->setup['backColor']);
1269 ImageColorAllocate($newIm, $cols[0],$cols[1],$cols[2]);
1270
1271 $newConf = Array();
1272 $workArea = Array(0,0,$cords[2],$cords[3]);
1273 if ($cords[0]<0) {$workArea[0]=abs($cords[0]);} else {$newConf['offset']=-$cords[0];}
1274 if ($cords[1]<0) {$workArea[1]=abs($cords[1]);} else {$newConf['offset'].=','.-$cords[1];}
1275
1276 $this->copyGifOntoGif($newIm,$im,$newConf,$workArea);
1277 $im = $newIm;
1278 $this->w = imagesx($im);
1279 $this->h = imagesy($im);
1280 $this->setWorkArea(''); // clears workArea to total image
1281 }
1282
1283 /**
1284 * Implements the "SCALE" GIFBUILDER object
1285 *
1286 * @param pointer GDlib image pointer
1287 * @param array TypoScript array with configuration for the GIFBUILDER object.
1288 * @return void
1289 * @see tslib_gifBuilder::make()
1290 */
1291 function scale(&$im,$conf) {
1292 if ($conf['width'] || $conf['height'] || $conf['params']) {
1293 $tmpStr = $this->randomName();
1294 $theFile = $tmpStr.'.'.$this->gifExtension;
1295 $this->ImageGif($im, $theFile);
1296 $theNewFile = $this->imageMagickConvert($theFile,$this->gifExtension,$conf['width'],$conf['height'],$conf['params'],'','');
1297 $tmpImg = $this->imageCreateFromGif($theNewFile[3]);
1298 if ($tmpImg) {
1299 ImageDestroy($im);
1300 $im = $tmpImg;
1301 $this->w = imagesx($im);
1302 $this->h = imagesy($im);
1303 $this->setWorkArea(''); // clears workArea to total image
1304 }
1305 if (!$this->dontUnlinkTempFiles) {
1306 unlink($theFile);
1307 if ($theNewFile[3] && $theNewFile[3]!=$theFile) {
1308 unlink($theNewFile[3]);
1309 }
1310 }
1311 }
1312 }
1313
1314 /**
1315 * Implements the "WORKAREA" GIFBUILDER object when setting it
1316 * Setting internal working area boundaries (->workArea)
1317 *
1318 * @param string Working area dimensions, comma separated
1319 * @return void
1320 * @access private
1321 * @see tslib_gifBuilder::make()
1322 */
1323 function setWorkArea($workArea) {
1324 $this->workArea = t3lib_div::intExplode(',',$workArea);
1325 $this->workArea = $this->applyOffset($this->workArea,$this->OFFSET);
1326 if (!$this->workArea[2]) {$this->workArea[2]=$this->w;}
1327 if (!$this->workArea[3]) {$this->workArea[3]=$this->h;}
1328 }
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352 /*************************
1353 *
1354 * Adjustment functions
1355 *
1356 ************************/
1357
1358 /**
1359 * Apply auto-levels to input image pointer
1360 *
1361 * @param integer GDlib Image Pointer
1362 * @return void
1363 */
1364 function autolevels(&$im) {
1365 $totalCols = ImageColorsTotal($im);
1366 $min=255;
1367 $max=0;
1368 for ($c=0; $c<$totalCols; $c++) {
1369 $cols = ImageColorsForIndex($im,$c);
1370 $grayArr[] = round(($cols['red']+$cols['green']+$cols['blue'])/3);
1371 }
1372 $min=min($grayArr);
1373 $max=max($grayArr);
1374 $delta = $max-$min;
1375 if ($delta) {
1376 for ($c=0; $c<$totalCols; $c++) {
1377 $cols = ImageColorsForIndex($im,$c);
1378 $cols['red'] = floor(($cols['red']-$min)/$delta*255);
1379 $cols['green'] = floor(($cols['green']-$min)/$delta*255);
1380 $cols['blue'] = floor(($cols['blue']-$min)/$delta*255);
1381 ImageColorSet($im,$c,$cols['red'],$cols['green'],$cols['blue']);
1382 }
1383 }
1384 }
1385
1386 /**
1387 * Apply output levels to input image pointer (decreasing contrast)
1388 *
1389 * @param integer GDlib Image Pointer
1390 * @param integer The "low" value (close to 0)
1391 * @param integer The "high" value (close to 255)
1392 * @param boolean If swap, then low and high are swapped. (Useful for negated masks...)
1393 * @return void
1394 */
1395 function outputLevels(&$im,$low,$high,$swap='') {
1396 if ($low<$high){
1397 $low = t3lib_div::intInRange($low,0,255);
1398 $high = t3lib_div::intInRange($high,0,255);
1399
1400 if ($swap) {
1401 $temp = $low;
1402 $low = 255-$high;
1403 $high = 255-$temp;
1404 }
1405
1406 $delta = $high-$low;
1407 $totalCols = ImageColorsTotal($im);
1408 for ($c=0; $c<$totalCols; $c++) {
1409 $cols = ImageColorsForIndex($im,$c);
1410 $cols['red'] = $low+floor($cols['red']/255*$delta);
1411 $cols['green'] = $low+floor($cols['green']/255*$delta);
1412 $cols['blue'] = $low+floor($cols['blue']/255*$delta);
1413 ImageColorSet($im,$c,$cols['red'],$cols['green'],$cols['blue']);
1414 }
1415 }
1416 }
1417
1418 /**
1419 * Apply input levels to input image pointer (increasing contrast)
1420 *
1421 * @param integer GDlib Image Pointer
1422 * @param integer The "low" value (close to 0)
1423 * @param integer The "high" value (close to 255)
1424 * @param boolean If swap, then low and high are swapped. (Useful for negated masks...)
1425 * @return void
1426 */
1427 function inputLevels(&$im,$low,$high,$swap='') {
1428 if ($low<$high){
1429 $low = t3lib_div::intInRange($low,0,255);
1430 $high = t3lib_div::intInRange($high,0,255);
1431
1432 if ($swap) {
1433 $temp = $low;
1434 $low = 255-$high;
1435 $high = 255-$temp;
1436 }
1437
1438 $delta = $high-$low;
1439 $totalCols = ImageColorsTotal($im);
1440 for ($c=0; $c<$totalCols; $c++) {
1441 $cols = ImageColorsForIndex($im,$c);
1442 $cols['red'] = t3lib_div::intInRange(($cols['red']-$low)/$delta*255, 0,255);
1443 $cols['green'] = t3lib_div::intInRange(($cols['green']-$low)/$delta*255, 0,255);
1444 $cols['blue'] = t3lib_div::intInRange(($cols['blue']-$low)/$delta*255, 0,255);
1445 ImageColorSet($im,$c,$cols['red'],$cols['green'],$cols['blue']);
1446 }
1447 }
1448 }
1449
1450 /**
1451 * Reduce colors in image
1452 *
1453 * @param integer GDlib Image Pointer
1454 * @param integer The max number of colors in the image before a reduction will happen; basically this means that IF the GD image current has the same amount or more colors than $limit define, THEN a reduction is performed.
1455 * @param integer Number of colors to reduce the image to.
1456 * @return void
1457 */
1458 function reduceColors(&$im,$limit, $cols) {
1459 if (ImageColorsTotal($im)>=$limit) {
1460 $this->makeEffect($im, Array('value'=>'colors='.$cols) );
1461 }
1462 }
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482 /*********************************
1483 *
1484 * GIFBUILDER Helper functions
1485 *
1486 *********************************/
1487
1488 /**
1489 * Checks if the $fontFile is already at an absolute path and if not, prepends the correct path.
1490 * Use PATH_site unless we are in the backend.
1491 * Call it by t3lib_stdGraphic::prependAbsolutePath()
1492 *
1493 * @param string The font file
1494 * @return string The font file with absolute path.
1495 */
1496 function prependAbsolutePath($fontFile) {
1497 $absPath = defined('PATH_typo3') ? dirname(PATH_thisScript).'/' :PATH_site;
1498 $fontFile = t3lib_div::isAbsPath($fontFile) ? $fontFile : $absPath.$fontFile;
1499 return $fontFile;
1500 }
1501
1502 /**
1503 * Returns the IM command for sharpening with ImageMagick 5 (when $this->V5_EFFECTS is set).
1504 * Uses $this->im5fx_sharpenSteps for translation of the factor to an actual command.
1505 *
1506 * @param integer The sharpening factor, 0-100 (effectively in 10 steps)
1507 * @return string The sharpening command, eg. " -sharpen 3x4"
1508 * @see makeText(), IMparams(), v5_blur()
1509 */
1510 function v5_sharpen($factor) {
1511 $factor = t3lib_div::intInRange(ceil($factor/10),0,10);
1512
1513 $sharpenArr=explode(',',','.$this->im5fx_sharpenSteps);
1514 $sharpenF= trim($sharpenArr[$factor]);
1515 if ($sharpenF) {
1516 $cmd = ' -sharpen '.$sharpenF;
1517 return $cmd;
1518 }
1519 }
1520
1521 /**
1522 * Returns the IM command for blurring with ImageMagick 5 (when $this->V5_EFFECTS is set).
1523 * Uses $this->im5fx_blurSteps for translation of the factor to an actual command.
1524 *
1525 * @param integer The blurring factor, 0-100 (effectively in 10 steps)
1526 * @return string The blurring command, eg. " -blur 3x4"
1527 * @see makeText(), IMparams(), v5_sharpen()
1528 */
1529 function v5_blur($factor) {
1530 $factor = t3lib_div::intInRange(ceil($factor/10),0,10);
1531
1532 $blurArr=explode(',',','.$this->im5fx_blurSteps);
1533 $blurF= trim($blurArr[$factor]);
1534 if ($blurF) {
1535 $cmd=' -blur '.$blurF;
1536 return $cmd;
1537 }
1538 }
1539
1540 /**
1541 * Returns a random filename prefixed with "temp_" and then 32 char md5 hash (without extension) from $this->tempPath.
1542 * Used by functions in this class to create truely temporary files for the on-the-fly processing. These files will most likely be deleted right away.
1543 *
1544 * @return string
1545 */
1546 function randomName() {
1547 return $this->tempPath.'temp_'.md5(uniqid(''));
1548 }
1549
1550 /**
1551 * Applies offset value to coordinated in $cords.
1552 * Basically the value of key 0/1 of $OFFSET is added to keys 0/1 of $cords
1553 *
1554 * @param array Integer coordinates in key 0/1
1555 * @param array Offset values in key 0/1
1556 * @return array Modified $cords array
1557 */
1558 function applyOffset($cords,$OFFSET) {
1559 $cords[0] = intval($cords[0])+intval($OFFSET[0]);
1560 $cords[1] = intval($cords[1])+intval($OFFSET[1]);
1561 return $cords;
1562 }
1563
1564 /**
1565 * Converts a "HTML-color" TypoScript datatype to RGB-values.
1566 * Default is 0,0,0
1567 *
1568 * @param string "HTML-color" data type string, eg. 'red', '#ffeedd' or '255,0,255'. You can also add a modifying operator afterwards. There are two options: "255,0,255 : 20" - will add 20 to values, result is "255,20,255". Or "255,0,255 : *1.23" which will multiply all RGB values with 1.23
1569 * @return array RGB values in key 0/1/2 of the array
1570 */
1571 function convertColor($string) {
1572 $col=array();
1573 $cParts = explode(':',$string,2);
1574
1575 // Finding the RGB definitions of the color:
1576 $string=$cParts[0];
1577 if (strstr($string,'#')) {
1578 $string = ereg_replace('[^A-Fa-f0-9]*','',$string);
1579 $col[]=HexDec(substr($string,0,2));
1580 $col[]=HexDec(substr($string,2,2));
1581 $col[]=HexDec(substr($string,4,2));
1582 } elseif (strstr($string,',')) {
1583 $string = ereg_replace('[^,0-9]*','',$string);
1584 $strArr = explode(',',$string);
1585 $col[]=intval($strArr[0]);
1586 $col[]=intval($strArr[1]);
1587 $col[]=intval($strArr[2]);
1588 } else {
1589 $string = strtolower(trim($string));
1590 if ($this->colMap[$string]) {
1591 $col = $this->colMap[$string];
1592 } else {
1593 $col = Array(0,0,0);
1594 }
1595 }
1596 // ... and possibly recalculating the value
1597 if (trim($cParts[1])) {
1598 $cParts[1]=trim($cParts[1]);
1599 if (substr($cParts[1],0,1)=='*') {
1600 $val=doubleval(substr($cParts[1],1));
1601 $col[0]=t3lib_div::intInRange($col[0]*$val,0,255);
1602 $col[1]=t3lib_div::intInRange($col[1]*$val,0,255);
1603 $col[2]=t3lib_div::intInRange($col[2]*$val,0,255);
1604 } else {
1605 $val=intval($cParts[1]);
1606 $col[0]=t3lib_div::intInRange($col[0]+$val,0,255);
1607 $col[1]=t3lib_div::intInRange($col[1]+$val,0,255);
1608 $col[2]=t3lib_div::intInRange($col[2]+$val,0,255);
1609 }
1610 }
1611 return $col;
1612 }
1613
1614 /**
1615 * Recode string
1616 * Used with text strings for fonts when languages has other character sets.
1617 *
1618 * @param string The text to recode
1619 * @return string The recoded string. Should be UTF-8 output. MAY contain entities (eg. &#123; or &#quot; which should render as real chars).
1620 */
1621 function recodeString($string) {
1622 // Recode string to UTF-8 from $this->nativeCharset:
1623 if ($this->nativeCharset && $this->nativeCharset!='utf-8') {
1624 $string = $this->csConvObj->utf8_encode($string,$this->nativeCharset); // Convert to UTF-8
1625 }
1626
1627 // Recode string accoding to TTFLocaleConv. Depreciated.
1628 if ($this->TTFLocaleConv) {
1629 $string = recode_string($this->TTFLocaleConv,$string);
1630 }
1631
1632 return $string;
1633 }
1634
1635 /**
1636 * Split a string into an array of individual characters
1637 * The function will look at $this->nativeCharset and if that is set, the input string is expected to be UTF-8 encoded, possibly with entities in it. Otherwise the string is supposed to be a single-byte charset which is just splitted by a for-loop.
1638 *
1639 * @param string The text string to spli
1640 * @return array Numerical array with a char as each value.
1641 */
1642 function singleChars($theText) {
1643 if ($this->nativeCharset) {
1644 return $this->csConvObj->utf8_to_numberarray($theText,1,1); // Get an array of separated UTF-8 chars
1645 } else {
1646 $output=array();
1647 $c=strlen($theText);
1648 for($a=0;$a<$c;$a++) {
1649 $output[]=substr($theText,$a,1);
1650 }
1651 return $output;
1652 }
1653 }
1654
1655 /**
1656 * Create an array with object position/boundaries based on input TypoScript configuration (such as the "align" property is used), the work area definition and $BB array
1657 *
1658 * @param array TypoScript configuration for a GIFBUILDER object
1659 * @param array Workarea definition
1660 * @param array BB (Bounding box) array. Not just used for TEXT objects but also for others
1661 * @return array [0]=x, [1]=y, [2]=w, [3]=h
1662 * @access private
1663 * @see copyGifOntoGif(), makeBox(), crop()
1664 */
1665 function objPosition($conf,$workArea,$BB) {
1666 // offset, align, valign, workarea
1667 $result=Array();
1668 $result[2] = $BB[0];
1669 $result[3] = $BB[1];
1670 $w=$workArea[2];
1671 $h=$workArea[3];
1672
1673 $align = explode(',',$conf['align']);
1674 $align[0] = strtolower(substr(trim($align[0]),0,1));
1675 $align[1] = strtolower(substr(trim($align[1]),0,1));
1676
1677 switch($align[0]) {
1678 case 'r':
1679 $result[0]=$w-$result[2];
1680 break;
1681 case 'c':
1682 $result[0] = round(($w-$result[2])/2);
1683 break;
1684 default:
1685 $result[0] = 0;
1686 break;
1687 }
1688 switch($align[1]) {
1689 case 'b':
1690 $result[1] = $h-$result[3]; // y pos
1691 break;
1692 case 'c':
1693 $result[1] = round(($h-$result[3])/2);
1694 break;
1695 default:
1696 $result[1]=0;
1697 break;
1698 }
1699 $result = $this->applyOffset($result,t3lib_div::intExplode(',',$conf['offset']));
1700 $result = $this->applyOffset($result,$workArea);
1701 return $result;
1702 }
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724 /***********************************
1725 *
1726 * Scaling, Dimensions of images
1727 *
1728 ***********************************/
1729
1730 /**
1731 * Converts $imagefile to another file in temp-dir of type $newExt (extension).
1732 *
1733 * @param string The image filepath
1734 * @param string New extension, eg. "gif", "png", "jpg", "tif". If $newExt is NOT set, the new imagefile will be of the original format. If newExt = 'WEB' then one of the web-formats is applied.
1735 * @param string Width. $w / $h is optional. If only one is given the image is scaled proportionally. If an 'm' exists in the $w or $h and if both are present the $w and $h is regarded as the Maximum w/h and the proportions will be kept
1736 * @param string Height. See $w
1737 * @param string Additional ImageMagick parameters.
1738 * @param string Refers to which frame-number to select in the image. '' or 0 will select the first frame, 1 will select the next and so on...
1739 * @param array An array with options passed to getImageScale (see this function).
1740 * @param boolean If set, then another image than the input imagefile MUST be returned. Otherwise you can risk that the input image is good enough regarding messures etc and is of course not rendered to a new, temporary file in typo3temp/. But this option will force it to.
1741 * @return array [0]/[1] is w/h, [2] is file extension and [3] is the filename.
1742 * @see getImageScale(), typo3/show_item.php, fileList_ext::renderImage(), tslib_cObj::getImgResource(), SC_tslib_showpic::show(), maskImageOntoImage(), copyImageOntoImage(), scale()
1743 */
1744 function imageMagickConvert($imagefile,$newExt='',$w='',$h='',$params='',$frame='',$options='',$mustCreate=0) {
1745 if ($this->NO_IMAGE_MAGICK) {
1746 // Returning file info right away
1747 return $this->getImageDimensions($imagefile);
1748 }
1749
1750 if($info=$this->getImageDimensions($imagefile)) {
1751 $newExt=strtolower(trim($newExt));
1752 if (!$newExt) { // If no extension is given the original extension is used
1753 $newExt = $info[2];
1754 }
1755 if ($newExt=='web') {
1756 if (t3lib_div::inList($this->webImageExt,$info[2])) {
1757 $newExt = $info[2];
1758 } else {
1759 $newExt = $this->gif_or_jpg($info[2],$info[0],$info[1]);
1760 if (!$params) {
1761 $params = $this->cmds[$newExt];
1762 }
1763 }
1764 }
1765 if (t3lib_div::inList($this->imageFileExt,$newExt)) {
1766 if (strstr($w.$h, 'm')) {$max=1;} else {$max=0;}
1767
1768 $data = $this->getImageScale($info,$w,$h,$options);
1769 $w=$data['origW'];
1770 $h=$data['origH'];
1771
1772 // if no convertion should be performed
1773 $wh_noscale = (!$w && !$h) || ($data[0]==$info[0] && $data[1]==$info[1]); // this flag is true if the width / height does NOT dictate the image to be scaled!! (that is if no w/h is given or if the destination w/h matches the original image-dimensions....
1774
1775 if ($wh_noscale && !$params && !$frame && $newExt==$info[2] && !$mustCreate) {
1776 $info[3] = $imagefile;
1777 return $info;
1778 }
1779 $info[0]=$data[0];
1780 $info[1]=$data[1];
1781
1782 $frame = $this->noFramePrepended ? '' : '['.intval($frame).']';
1783
1784 if (!$params) {
1785 $params = $this->cmds[$newExt];
1786 }
1787
1788 $command = $this->scalecmd.' '.$info[0].'x'.$info[1].'! '.$params.' ';
1789
1790 if ($this->alternativeOutputKey) {
1791 $theOutputName = t3lib_div::shortMD5($command.basename($imagefile).$this->alternativeOutputKey.$frame);
1792 } else {
1793 $theOutputName = t3lib_div::shortMD5($command.$imagefile.filemtime($imagefile).$frame);
1794 }
1795 if ($this->imageMagickConvert_forceFileNameBody) {
1796 $theOutputName = $this->imageMagickConvert_forceFileNameBody;
1797 $this->imageMagickConvert_forceFileNameBody='';
1798 }
1799 $output = $this->tempPath.$this->filenamePrefix.$theOutputName.'.'.$newExt;
1800 $GLOBALS['TEMP_IMAGES_ON_PAGE'][]=$output;
1801
1802 if (!$this->file_exists_typo3temp_file($output,$imagefile) || $this->dontCheckForExistingTempFile) {
1803 $this->imageMagickExec($imagefile.$frame,$output,$command);
1804 }
1805 if (@file_exists($output)) {
1806 $info[3] = $output;
1807 $info[2] = $newExt;
1808 if ($params) { // params could realisticly change som imagedata!
1809 $info=$this->getImageDimensions($info[3]);
1810 }
1811 if ($info[2]==$this->gifExtension && !$this->dontCompress) {
1812 t3lib_div::gif_compress($info[3],''); // Compress with IM (lzw) or GD (rle) (Workaround for the absence of lzw-compression in GD)
1813 }
1814 return $info;
1815 }
1816 }
1817 }
1818 }
1819
1820 /**
1821 * Gets the input image dimensions.
1822 *
1823 * @param string The image filepath
1824 * @return array Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
1825 * @see imageMagickConvert(), tslib_cObj::getImgResource()
1826 */
1827 function getImageDimensions($imagefile) {
1828 ereg('([^\.]*)$',$imagefile,$reg);
1829 if (@file_exists($imagefile) && t3lib_div::inList($this->imageFileExt,strtolower($reg[0]))) {
1830 if ($temp = @getImageSize($imagefile)) {
1831 return Array($temp[0], $temp[1], strtolower($reg[0]), $imagefile);
1832 } else {
1833 return $this->imageMagickIdentify($imagefile);
1834 }
1835 }
1836 }
1837
1838 /**
1839 * Get numbers for scaling the image based on input
1840 *
1841 * @param array Current image information: Width, Height etc.
1842 * @param integer "required" width
1843 * @param integer "required" height
1844 * @param array Options: Keys are like "maxW", "maxH", "minW", "minH"
1845 * @return array
1846 * @access private
1847 * @see imageMagickConvert()
1848 */
1849 function getImageScale($info,$w,$h,$options) {
1850 if (strstr($w.$h, 'm')) {$max=1;} else {$max=0;}
1851 $w=intval($w);
1852 $h=intval($h);
1853 // if there are max-values...
1854 if ($options['maxW']) {
1855 if ($w) { // if width is given...
1856 if ($w>$options['maxW']) {
1857 $w=$options['maxW'];
1858 $max=1; // height should follow
1859 }
1860 } else {
1861 if ($info[0]>$options['maxW']) {
1862 $w=$options['maxW'];
1863 $max=1; // height should follow
1864 }
1865 }
1866 }
1867 if ($options['maxH']) {
1868 if ($h) { // if height is given...
1869 if ($h>$options['maxH']) {
1870 $h=$options['maxH'];
1871 $max=1; // height should follow
1872 }
1873 } else {
1874 if ($info[1]>$options['maxH']) { // Changed [0] to [1] 290801
1875 $h=$options['maxH'];
1876 $max=1; // height should follow
1877 }
1878 }
1879 }
1880 $out['origW']=$w;
1881 $out['origH']=$h;
1882 $out['max'] = $max;
1883
1884 if (!$this->mayScaleUp) {
1885 if ($w>$info[0]){$w=$info[0];}
1886 if ($h>$info[1]){$h=$info[1];}
1887 }
1888 if ($w || $h) { // if scaling should be performed
1889 if ($w && !$h) {
1890 $info[1] = ceil($info[1]*($w/$info[0]));
1891 $info[0] = $w;
1892 }
1893 if (!$w && $h) {
1894 $info[0] = ceil($info[0]*($h/$info[1]));
1895 $info[1] = $h;
1896 }
1897 if ($w && $h) {
1898 if ($max) {
1899 $ratio = $info[0]/$info[1];
1900 if ($h*$ratio > $w) {
1901 $h = round($w/$ratio);
1902 } else {
1903 $w = round($h*$ratio);
1904 }
1905 }
1906 $info[0] = $w;
1907 $info[1] = $h;
1908 }
1909 }
1910 $out[0]=$info[0];
1911 $out[1]=$info[1];
1912 // Set minimum-measures!
1913 if ($options['minW'] && $out[0]<$options['minW']) {
1914 if ($max && $out[0]) {
1915 $out[1]= round($out[1]*$options['minW']/$out[0]);
1916 }
1917 $out[0]=$options['minW'];
1918 }
1919 if ($options['minH'] && $out[1]<$options['minH']) {
1920 if ($max && $out[1]) {
1921 $out[0]= round($out[0]*$options['minH']/$out[1]);
1922 }
1923 $out[1]=$options['minH'];
1924 }
1925
1926 return $out;
1927 }
1928
1929 /**
1930 * Used to check if a certain process of scaling an image is already being carried out (can be logged in the MySQL database)
1931 *
1932 * @param string Output imagefile
1933 * @param string Original basis file
1934 * @return boolean Returns true if the file is already being made; thus "true" means "Don't render the image again"
1935 * @access private
1936 */
1937 function file_exists_typo3temp_file($output,$orig='') {
1938 if ($this->enable_typo3temp_db_tracking) {
1939 if (@file_exists($output)) { // If file exists, then we return immediately
1940 return 1;
1941 } else { // If not, we look up in the cache_typo3temp_log table to see if there is a image being rendered right now.
1942 $md5Hash=md5($output);
1943 $query='SELECT md5hash FROM cache_typo3temp_log WHERE md5hash="'.$md5Hash.'" AND tstamp>'.(time()-30);
1944 $res=mysql(TYPO3_db,$query);
1945 echo mysql_error();
1946 if ($row=mysql_fetch_assoc($res)) { // If there was a record, the image is being generated by another proces (we assume)
1947 if (is_object($GLOBALS['TSFE'])) $GLOBALS['TSFE']->set_no_cache(); // ...so we set no_cache, because we dont want this page (which will NOT display an image...!) to be cached! (Only a page with the correct image on...)
1948 if (is_object($GLOBALS['TT'])) $GLOBALS['TT']->setTSlogMessage('typo3temp_log: Assume this file is being rendered now: '.$output);
1949 return 2; // Return 'success - 2'
1950 } else { // If the current time is more than 30 seconds since this record was written, we clear the record, write a new and render the image.
1951 $query='DELETE FROM cache_typo3temp_log WHERE md5hash="'.$md5Hash.'"';
1952 $res=mysql(TYPO3_db,$query);
1953 $query='INSERT INTO cache_typo3temp_log (md5hash,tstamp,filename,orig_filename) VALUES ("'.$md5Hash.'",'.time().',"'.addslashes($output).'","'.addslashes($orig).'")';
1954 $res=mysql(TYPO3_db,$query);
1955 if (is_object($GLOBALS['TT'])) $GLOBALS['TT']->setTSlogMessage('typo3temp_log: The row did not exist, so a new is written and file is being processed: '.$output);
1956 return 0;
1957 }
1958 }
1959 } else {
1960 return @file_exists($output);
1961 }
1962 }
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981 /***********************************
1982 *
1983 * ImageMagick API functions
1984 *
1985 ***********************************/
1986
1987 /**
1988 * Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
1989 * Using ImageMagick
1990 *
1991 * @param string The absolute image filepath
1992 * @return array
1993 */
1994 function imageMagickIdentify($imagefile) {
1995 if (!$this->NO_IMAGE_MAGICK) {
1996 $cmd = $this->imageMagickPath.'identify '.$this->wrapFileName($imagefile);
1997 exec($cmd, $returnVal);
1998 $splitstring=$returnVal[0];
1999 $this->IM_commands[] = Array ('identify',$cmd,$returnVal[0]);
2000 if ($splitstring) {
2001 ereg('([^\.]*)$',$imagefile,$reg);
2002 $splitinfo = explode(' ', $splitstring);
2003 while (list($key,$val) = each($splitinfo)) {
2004 $temp = '';
2005 if ($val) {$temp = explode('x', $val);}
2006 if (intval($temp[0]) && intval($temp[1])) {
2007 $dim=$temp;
2008 break;
2009 }
2010 }
2011 if ($dim[0] && $dim[1]) {
2012 return Array($dim[0], $dim[1], strtolower($reg[0]), $imagefile);
2013 }
2014 }
2015 }
2016 }
2017
2018 /**
2019 * Executes a ImageMagick "convert" on two filenames, $input and $output using $params before them.
2020 * Can be used for many things, mostly scaling and effects.
2021 *
2022 * @param string The absolute image filepath, input file (read from)
2023 * @param string The absolute image filepath, output filename (written to)
2024 * @param string ImageMagick parameters
2025 * @return string The result of a call to PHP function "exec()"
2026 */
2027 function imageMagickExec($input,$output,$params) {
2028 if (!$this->NO_IMAGE_MAGICK) {
2029 $cmd = $this->imageMagickPath.'convert '.$params.' '.$this->wrapFileName($input).' '.$this->wrapFileName($output);
2030 $this->IM_commands[] = Array ($output,$cmd);
2031 return exec($cmd);
2032 }
2033 }
2034
2035 /**
2036 * Executes a ImageMagick "combine" (or composite in newer times) on four filenames - $input, $overlay and $mask as input files and $output as the output filename (written to)
2037 * Can be used for many things, mostly scaling and effects.
2038 *
2039 * @param string The absolute image filepath, bottom file
2040 * @param string The absolute image filepath, overlay file (top)
2041 * @param string The absolute image filepath, the mask file (grayscale)
2042 * @param string The absolute image filepath, output filename (written to)
2043 * @return void
2044 */
2045 function combineExec($input,$overlay,$mask,$output) {
2046 if (!$this->NO_IMAGE_MAGICK) {
2047 $cmd = $this->imageMagickPath.$this->combineScript.' -compose over '.$this->wrapFileName($input).' '.$this->wrapFileName($overlay).' '.$this->wrapFileName($mask).' '.$this->wrapFileName($output);
2048 $this->IM_commands[] = Array ($output,$cmd);
2049 exec($cmd);
2050 }
2051 }
2052
2053 /**
2054 * Wrapping the input filename in double-quotes
2055 *
2056 * @param string Input filename
2057 * @return string The outpute wrapped in ""
2058 * @access private
2059 */
2060 function wrapFileName($inputName) {
2061 if (strstr($inputName,' ')) {
2062 $inputName='"'.$inputName.'"';
2063 }
2064 return $inputName;
2065 }
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089 /***********************************
2090 *
2091 * Various IO functions
2092 *
2093 ***********************************/
2094
2095 /**
2096 * Returns true if the input file existed
2097 *
2098 * @param string Input file to check
2099 * @return string Returns the filename if the file existed, otherwise empty.
2100 */
2101 function checkFile($file) {
2102 if (@is_file($file)) {
2103 return $file;
2104 } else {
2105 return '';
2106 }
2107 }
2108
2109 /**
2110 * Applies an ImageMagick parameter to a GDlib image pointer resource by writing the resource to file, performing an IM operation upon it and reading back the result into the ImagePointer.
2111 *
2112 * @param pointer The image pointer (reference)
2113 * @param string The ImageMagick parameters. Like effects, scaling etc.
2114 * @return void
2115 */
2116 function applyImageMagickToPHPGif(&$im, $command) {
2117 $tmpStr = $this->randomName();
2118 $theFile = $tmpStr.'.'.$this->gifExtension;
2119 $this->ImageGif($im, $theFile);
2120 $this->imageMagickExec($theFile,$theFile,$command);
2121 $tmpImg = $this->imageCreateFromGif($theFile);
2122 if ($tmpImg) {
2123 ImageDestroy($im);
2124 $im = $tmpImg;
2125 $this->w = imagesx($im);
2126 $this->h = imagesy($im);
2127 }
2128 if (!$this->dontUnlinkTempFiles) {
2129 unlink($theFile);
2130 }
2131 }
2132
2133 /**
2134 * Returns an image extension for an output image based on the number of pixels of the output and the file extension of the original file.
2135 * For example: If the number of pixels exceeds $this->pixelLimitGif (normally 10000) then it will be a "jpg" string in return.
2136 *
2137 * @param string The file extension, lowercase.
2138 * @param integer The width of the output image.
2139 * @param integer The height of the output image.
2140 * @return string The filename, either "jpg" or "gif"/"png" (whatever $this->gifExtension is set to.)
2141 */
2142 function gif_or_jpg($type,$w,$h) {
2143 if ($type=='ai' || $w*$h < $this->pixelLimitGif) {
2144 return $this->gifExtension;
2145 } else {
2146 return 'jpg';
2147 }
2148 }
2149
2150 /**
2151 * Writing the internal image pointer, $this->im, to file based on the extension of the input filename
2152 * Used in GIFBUILDER
2153 * Uses $this->setup['reduceColors'] for gif/png images and $this->setup['quality'] for jpg images to reduce size/quality if needed.
2154 *
2155 * @param string The filename to write to.
2156 * @return string Returns input filename
2157 * @see tslib_gifBuilder::gifBuild()
2158 */
2159 function output($file) {
2160 if ($file) {
2161 ereg('([^\.]*)$',$file,$reg);
2162 $ext=strtolower($reg[0]);
2163 switch($ext) {
2164 case $this->gifExtension:
2165 if ($this->ImageGif($this->im, $file)) {
2166 // ImageMagick operations
2167 if($this->setup['reduceColors']) {
2168 $this->imageMagickExec($file,$file,' -colors '.t3lib_div::intInRange($this->setup['reduceColors'],2,255));
2169 t3lib_div::gif_compress($file,''); // Compress with IM (lzw) or GD (rle) (Workaround for the absence of lzw-compression in GD)
2170 } else {
2171 t3lib_div::gif_compress($file, 'IM'); // Compress with IM! (adds extra compression, LZW from ImageMagick) (Workaround for the absence of lzw-compression in GD)
2172 }
2173 }
2174 break;
2175 case 'jpg':
2176 case 'jpeg':
2177 $tmpStr = $this->randomName();
2178 $theFile = $tmpStr.'.'.$this->gifExtension;
2179 if ($this->ImageGif($this->im, $theFile)) {
2180 // ImageMagick operations
2181 $operations='';
2182 if($this->setup['quality']) {
2183 $operations.=' -quality '.t3lib_div::intInRange($this->setup['quality'],10,100);
2184 }
2185 $this->imageMagickExec($theFile,$file,$operations);
2186 if (!$this->dontUnlinkTempFiles) {
2187 unlink($theFile);
2188 }
2189 }
2190 break;
2191 }
2192 $GLOBALS['TEMP_IMAGES_ON_PAGE'][]=$file;
2193 }
2194 return $file;
2195 }
2196
2197 /**
2198 * Destroy internal image pointer, $this->im
2199 *
2200 * @return void
2201 * @see tslib_gifBuilder::gifBuild()
2202 */
2203 function destroy() {
2204 ImageDestroy($this->im);
2205 }
2206
2207 /**
2208 * Returns Image Tag for input image information array.
2209 *
2210 * @param array Image information array, key 0/1 is width/height and key 3 is the src value
2211 * @return string Image tag for the input image information array.
2212 */
2213 function imgTag ($imgInfo) {
2214 return '<img src="'.$imgInfo[3].'" width="'.$imgInfo[0].'" height="'.$imgInfo[1].'" border="0" alt="" />';
2215 }
2216
2217 /**
2218 * Writes the input GDlib image pointer to file
2219 *
2220 * @param pointer The GDlib image resource pointer
2221 * @param string The filename to write to
2222 * @return ? The output of either imageGif or imagePng based on whether the $this->gifExtension was set to "gif" or "png"
2223 * @see maskImageOntoImage(), scale(), output()
2224 */
2225 function ImageGif($destImg, $theImage) {
2226 imageinterlace ($destImg,0);
2227 if ($this->gifExtension=='gif') {
2228 return ImageGif($destImg, $theImage);
2229 }
2230 if ($this->gifExtension=='png') {
2231 return ImagePng($destImg, $theImage);
2232 }
2233 }
2234
2235 /**
2236 * Creates a new GDlib image resource based on the input image filename.
2237 * If it fails creating a image from the input file a blank gray image with the dimensions of the input image will be created instead.
2238 *
2239 * @param string Image filename
2240 * @return pointer Image Resource pointer
2241 */
2242 function imageCreateFromGif($sourceImg) {
2243 $imgInf = pathinfo($sourceImg);
2244 $ext = strtolower($imgInf['extension']);
2245
2246 // if ($ext=='gif' && (ImageTypes() & IMG_GIF)) {
2247 if ($ext=='gif' && function_exists('imagecreatefromgif')) {
2248 return imageCreateFromGif($sourceImg);
2249 }
2250 // if ($ext=='png' && (ImageTypes() & IMG_PNG)) {
2251 if ($ext=='png' && function_exists('imagecreatefrompng')) {
2252 return imageCreateFromPng($sourceImg);
2253 }
2254 // if (($ext=='jpg' || $ext=='jpeg') && (ImageTypes() & IMG_JPG)) {
2255 if (($ext=='jpg' || $ext=='jpeg') && function_exists('imagecreatefromjpeg')) {
2256 return imageCreateFromJpeg($sourceImg);
2257 }
2258
2259 // If non of the above:
2260 $i = @getimagesize($sourceImg);
2261 $im = imagecreate($i[0],$i[1]);
2262 ImageColorAllocate($im, 128,128,128);
2263 return $im;
2264 }
2265 }
2266
2267
2268 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_stdgraphic.php']) {
2269 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_stdgraphic.php']);
2270 }
2271 ?>