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