[BUGFIX] Moving files in filelist renames file to "1"
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_stdgraphic.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Standard graphical functions
29 *
30 * Revised for TYPO3 3.6 July/2003 by Kasper Skårhøj
31 *
32 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
33 */
34
35 /**
36 * Class contains a bunch of cool functions for manipulating graphics with GDlib/Freetype and ImageMagick
37 * VERY OFTEN used with gifbuilder that extends this class and provides a TypoScript API to using these functions
38 *
39 * With TYPO3 4.4 GDlib 1.x support was dropped, also an option from $TYPO3_CONF_VARS
40 * $TYPO3_CONF_VARS['GFX']['gdlib_2'] = 0, // String/Boolean. Set this if you are using the new GDlib 2.0.1+. If you don't set this flag and still use GDlib2, you might encounter strange behaviours like black images etc. This feature might take effect only if ImageMagick is installed and working as well! You can also use the value "no_imagecopyresized_fix" - in that case it will NOT try to fix a known issue where "imagecopyresized" does not work correctly.
41 *
42 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
43 * @package TYPO3
44 * @subpackage t3lib
45 * @see tslib_gifBuilder
46 */
47 class t3lib_stdGraphic {
48
49 // Internal configuration, set in init()
50 // The ImageMagick filename used for combining two images. This name changed during the versions.
51 var $combineScript = 'combine';
52 // If set, there is no frame pointer prepended to the filenames.
53 var $noFramePrepended = 0;
54 // If set, imagecopyresized will not be called directly. For GD2 (some PHP installs?)
55 var $imagecopyresized_fix = 0;
56 // This should be changed to 'png' if you want this class to read/make PNG-files instead!
57 var $gifExtension = 'gif';
58 // File formats supported by gdlib. This variable get's filled in "init" method
59 var $gdlibExtensions = '';
60 // Set to TRUE if generated png's should be truecolor by default
61 var $png_truecolor = FALSE;
62 // 16777216 Colors is the maximum value for PNG, JPEG truecolor images (24-bit, 8-bit / Channel)
63 var $truecolorColors = 0xffffff;
64 // 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.
65 var $enable_typo3temp_db_tracking = 0;
66 // 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!
67 var $imageFileExt = 'gif,jpg,jpeg,png,tif,bmp,tga,pcx,ai,pdf';
68 // Commalist of web image extensions (can be shown by a webbrowser)
69 var $webImageExt = 'gif,jpg,jpeg,png';
70 // Will be ' -negate' if ImageMagick ver 5.2+. See init();
71 var $maskNegate = '';
72 var $NO_IM_EFFECTS = '';
73 var $cmds = array(
74 'jpg' => '',
75 'jpeg' => '',
76 'gif' => '',
77 'png' => '-colors 64'
78 );
79 var $NO_IMAGE_MAGICK = '';
80 var $V5_EFFECTS = 0;
81 var $mayScaleUp = 1;
82
83 // Variables for testing, alternative usage etc.
84 // Filename prefix for images scaled in imageMagickConvert()
85 var $filenamePrefix = '';
86 // Forcing the output filename of imageMagickConvert() to this value. However after calling imageMagickConvert() it will be set blank again.
87 var $imageMagickConvert_forceFileNameBody = '';
88 // This flag should always be FALSE. If set TRUE, imageMagickConvert will always write a new file to the tempdir! Used for debugging.
89 var $dontCheckForExistingTempFile = 0;
90 // Prevents imageMagickConvert() from compressing the gif-files with t3lib_div::gif_compress()
91 var $dontCompress = 0;
92 // For debugging ONLY!
93 var $dontUnlinkTempFiles = 0;
94 // 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...
95 var $alternativeOutputKey = '';
96
97 // Internal:
98 // All ImageMagick commands executed is stored in this array for tracking. Used by the Install Tools Image section
99 var $IM_commands = array();
100 var $workArea = array();
101
102 /**
103 * Preserve the alpha transparency layer of read PNG images
104 *
105 * @var boolean
106 */
107 protected $saveAlphaLayer = FALSE;
108
109 // Constants:
110 // The temp-directory where to store the files. Normally relative to PATH_site but is allowed to be the absolute path AS LONG AS it is a subdir to PATH_site.
111 var $tempPath = 'typo3temp/';
112 // Prefix for relative paths. Used in "show_item.php" script. Is prefixed the output file name IN imageMagickConvert()
113 var $absPrefix = '';
114 // ImageMagick scaling command; "-geometry" eller "-sample". Used in makeText() and imageMagickConvert()
115 var $scalecmd = '-geometry';
116 // Used by v5_blur() to simulate 10 continuous steps of blurring
117 var $im5fx_blurSteps = '1x2,2x2,3x2,4x3,5x3,5x4,6x4,7x5,8x5,9x5';
118 // Used by v5_sharpen() to simulate 10 continuous steps of sharpening.
119 var $im5fx_sharpenSteps = '1x2,2x2,3x2,2x3,3x3,4x3,3x4,4x4,4x5,5x5';
120 // This is the limit for the number of pixels in an image before it will be rendered as JPG instead of GIF/PNG
121 var $pixelLimitGif = 10000;
122 // Array mapping HTML color names to RGB values.
123 var $colMap = array(
124 'aqua' => array(0, 255, 255),
125 'black' => array(0, 0, 0),
126 'blue' => array(0, 0, 255),
127 'fuchsia' => array(255, 0, 255),
128 'gray' => array(128, 128, 128),
129 'green' => array(0, 128, 0),
130 'lime' => array(0, 255, 0),
131 'maroon' => array(128, 0, 0),
132 'navy' => array(0, 0, 128),
133 'olive' => array(128, 128, 0),
134 'purple' => array(128, 0, 128),
135 'red' => array(255, 0, 0),
136 'silver' => array(192, 192, 192),
137 'teal' => array(0, 128, 128),
138 'yellow' => array(255, 255, 0),
139 'white' => array(255, 255, 255)
140 );
141
142 /**
143 * Charset conversion object:
144 *
145 * @var t3lib_cs
146 */
147 var $csConvObj;
148 // Is set to the native character set of the input strings.
149 var $nativeCharset = '';
150
151 /**
152 * Init function. Must always call this when using the class.
153 * This function will read the configuration information from $GLOBALS['TYPO3_CONF_VARS']['GFX'] can set some values in internal variables.
154 *
155 * @return void
156 */
157 function init() {
158 $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX'];
159
160 if (function_exists('imagecreatefromjpeg') && function_exists('imagejpeg')) {
161 $this->gdlibExtensions .= ',jpg,jpeg';
162 }
163 if (function_exists('imagecreatefrompng') && function_exists('imagepng')) {
164 $this->gdlibExtensions .= ',png';
165 }
166 if (function_exists('imagecreatefromgif') && function_exists('imagegif')) {
167 $this->gdlibExtensions .= ',gif';
168 }
169 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['png_truecolor']) {
170 $this->png_truecolor = TRUE;
171 }
172 if (!$gfxConf['im']) {
173 $this->NO_IMAGE_MAGICK = 1;
174 }
175 if (!$this->NO_IMAGE_MAGICK && (!$gfxConf['im_version_5'] || $gfxConf['im_version_5'] === 'im4' || $gfxConf['im_version_5'] === 'im5')) {
176 throw new RuntimeException(
177 'Your TYPO3 installation is configured to use an old version of ImageMagick, which is not supported anymore. ' .
178 'Please upgrade to ImageMagick version 6 or GraphicksMagick and set $TYPO3_CONF_VARS[\'GFX\'][\'im_version_5\'] appropriately.',
179 1305059666
180 );
181 }
182
183 // When GIFBUILDER gets used in truecolor mode
184 // No colors parameter if we generate truecolor images.
185 if ($this->png_truecolor) {
186 $this->cmds['png'] = '';
187 }
188
189 // Setting default JPG parameters:
190 $this->jpegQuality = t3lib_utility_Math::forceIntegerInRange($gfxConf['jpg_quality'], 10, 100, 75);
191 $this->cmds['jpg'] = $this->cmds['jpeg'] = '-colorspace RGB -sharpen 50 -quality ' . $this->jpegQuality;
192
193 if ($gfxConf['im_combine_filename']) {
194 $this->combineScript = $gfxConf['im_combine_filename'];
195 }
196 if ($gfxConf['im_noFramePrepended']) {
197 $this->noFramePrepended = 1;
198 }
199
200 // Kept for backwards compatibility, can be turned on manually through localconf.php,
201 // but not through the installer anymore
202 $this->imagecopyresized_fix = ($gfxConf['gdlib_2'] === 'no_imagecopyresized_fix' ? 0 : 1);
203
204 if ($gfxConf['gdlib_png']) {
205 $this->gifExtension = 'png';
206 }
207 if ($gfxConf['enable_typo3temp_db_tracking']) {
208 $this->enable_typo3temp_db_tracking = $gfxConf['enable_typo3temp_db_tracking'];
209 }
210
211 $this->imageFileExt = $gfxConf['imagefile_ext'];
212
213 // This should be set if ImageMagick ver. 5+ is used.
214 if ($gfxConf['im_negate_mask']) {
215 // Boolean. Indicates if the mask images should be inverted first.
216 // This depends of the ImageMagick version. Below ver. 5.1 this should be FALSE.
217 // Above ImageMagick version 5.2+ it should be TRUE.
218 // Just set the flag if the masks works opposite the intension!
219 $this->maskNegate = ' -negate';
220 }
221 if ($gfxConf['im_no_effects']) {
222 // Boolean. This is necessary if using ImageMagick 5+.
223 // Effects in Imagemagick 5+ tends to render very slowly!!
224 // - therefore must be disabled in order not to perform sharpen, blurring and such.
225 $this->NO_IM_EFFECTS = 1;
226
227 $this->cmds['jpg'] = $this->cmds['jpeg'] = '-colorspace RGB -quality ' . $this->jpegQuality;
228 }
229 // ... but if 'im_v5effects' is set, don't care about 'im_no_effects'
230 if ($gfxConf['im_v5effects']) {
231 $this->NO_IM_EFFECTS = 0;
232 $this->V5_EFFECTS = 1;
233
234 if ($gfxConf['im_v5effects'] > 0) {
235 $this->cmds['jpg'] = $this->cmds['jpeg'] = '-colorspace RGB -quality ' . intval($gfxConf['jpg_quality']) . $this->v5_sharpen(10);
236 }
237 }
238
239 // Secures that images are not scaled up.
240 if ($gfxConf['im_noScaleUp']) {
241 $this->mayScaleUp = 0;
242 }
243
244 if (TYPO3_MODE == 'FE') {
245 $this->csConvObj = $GLOBALS['TSFE']->csConvObj;
246 } elseif (is_object($GLOBALS['LANG'])) { // BE assumed:
247 $this->csConvObj = $GLOBALS['LANG']->csConvObj;
248 } else { // The object may not exist yet, so we need to create it now. Happens in the Install Tool for example.
249 $this->csConvObj = t3lib_div::makeInstance('t3lib_cs');
250 }
251 $this->nativeCharset = 'utf-8';
252 }
253
254 /*************************************************
255 *
256 * Layering images / "IMAGE" GIFBUILDER object
257 *
258 *************************************************/
259
260 /**
261 * Implements the "IMAGE" GIFBUILDER object, when the "mask" property is TRUE.
262 * 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
263 * The operation involves ImageMagick for combining.
264 *
265 * @param pointer $im GDlib image pointer
266 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
267 * @param array $workArea The current working area coordinates.
268 * @return void
269 * @see tslib_gifBuilder::make()
270 */
271 function maskImageOntoImage(&$im, $conf, $workArea) {
272 if ($conf['file'] && $conf['mask']) {
273 $imgInf = pathinfo($conf['file']);
274 $imgExt = strtolower($imgInf['extension']);
275 if (!t3lib_div::inList($this->gdlibExtensions, $imgExt)) {
276 $BBimage = $this->imageMagickConvert($conf['file'], $this->gifExtension, '', '', '', '', '');
277 } else {
278 $BBimage = $this->getImageDimensions($conf['file']);
279 }
280 $maskInf = pathinfo($conf['mask']);
281 $maskExt = strtolower($maskInf['extension']);
282 if (!t3lib_div::inList($this->gdlibExtensions, $maskExt)) {
283 $BBmask = $this->imageMagickConvert($conf['mask'], $this->gifExtension, '', '', '', '', '');
284 } else {
285 $BBmask = $this->getImageDimensions($conf['mask']);
286 }
287 if ($BBimage && $BBmask) {
288 $w = imagesx($im);
289 $h = imagesy($im);
290 $tmpStr = $this->randomName();
291 $theImage = $tmpStr . '_img.' . $this->gifExtension;
292 $theDest = $tmpStr . '_dest.' . $this->gifExtension;
293 $theMask = $tmpStr . '_mask.' . $this->gifExtension;
294 // Prepare overlay image
295 $cpImg = $this->imageCreateFromFile($BBimage[3]);
296 $destImg = imagecreatetruecolor($w, $h);
297
298 // Preserve alpha transparency
299 if ($this->saveAlphaLayer) {
300 imagesavealpha($destImg, TRUE);
301 $Bcolor = imagecolorallocatealpha($destImg, 0, 0, 0, 127);
302 imagefill($destImg, 0, 0, $Bcolor);
303 } else {
304 $Bcolor = ImageColorAllocate($destImg, 0, 0, 0);
305 ImageFilledRectangle($destImg, 0, 0, $w, $h, $Bcolor);
306 }
307
308 $this->copyGifOntoGif($destImg, $cpImg, $conf, $workArea);
309 $this->ImageWrite($destImg, $theImage);
310 imageDestroy($cpImg);
311 imageDestroy($destImg);
312
313 // Prepare mask image
314 $cpImg = $this->imageCreateFromFile($BBmask[3]);
315 $destImg = imagecreatetruecolor($w, $h);
316 if ($this->saveAlphaLayer) {
317 imagesavealpha($destImg, TRUE);
318 $Bcolor = imagecolorallocatealpha($destImg, 0, 0, 0, 127);
319 imagefill($destImg, 0, 0, $Bcolor);
320 } else {
321 $Bcolor = ImageColorAllocate($destImg, 0, 0, 0);
322 ImageFilledRectangle($destImg, 0, 0, $w, $h, $Bcolor);
323 }
324 $this->copyGifOntoGif($destImg, $cpImg, $conf, $workArea);
325 $this->ImageWrite($destImg, $theMask);
326 imageDestroy($cpImg);
327 imageDestroy($destImg);
328 // Mask the images
329 $this->ImageWrite($im, $theDest);
330
331 // Let combineExec handle maskNegation
332 $this->combineExec($theDest, $theImage, $theMask, $theDest, TRUE);
333
334 // The main image is loaded again...
335 $backIm = $this->imageCreateFromFile($theDest);
336 // ... and if nothing went wrong we load it onto the old one.
337 if ($backIm) {
338 if (!$this->saveAlphaLayer) {
339 ImageColorTransparent($backIm, -1);
340 }
341 $im = $backIm;
342 }
343 // Unlink files from process
344 if (!$this->dontUnlinkTempFiles) {
345 unlink($theDest);
346 unlink($theImage);
347 unlink($theMask);
348 }
349 }
350 }
351 }
352
353 /**
354 * Implements the "IMAGE" GIFBUILDER object, when the "mask" property is FALSE (using only $conf['file'])
355 *
356 * @param pointer $im GDlib image pointer
357 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
358 * @param array $workArea The current working area coordinates.
359 * @return void
360 * @see tslib_gifBuilder::make(), maskImageOntoImage()
361 */
362 function copyImageOntoImage(&$im, $conf, $workArea) {
363 if ($conf['file']) {
364 if (!t3lib_div::inList($this->gdlibExtensions, $conf['BBOX'][2])) {
365 $conf['BBOX'] = $this->imageMagickConvert($conf['BBOX'][3], $this->gifExtension, '', '', '', '', '');
366 $conf['file'] = $conf['BBOX'][3];
367 }
368 $cpImg = $this->imageCreateFromFile($conf['file']);
369 $this->copyGifOntoGif($im, $cpImg, $conf, $workArea);
370 imageDestroy($cpImg);
371 }
372 }
373
374 /**
375 * Copies two GDlib image pointers onto each other, using TypoScript configuration from $conf and the input $workArea definition.
376 *
377 * @param pointer $im GDlib image pointer, destination (bottom image)
378 * @param pointer $cpImg GDlib image pointer, source (top image)
379 * @param array $conf TypoScript array with the properties for the IMAGE GIFBUILDER object. Only used for the "tile" property value.
380 * @param array $workArea Work area
381 * @return void Works on the $im image pointer
382 * @access private
383 */
384 function copyGifOntoGif(&$im, $cpImg, $conf, $workArea) {
385 $cpW = imagesx($cpImg);
386 $cpH = imagesy($cpImg);
387 $tile = t3lib_div::intExplode(',', $conf['tile']);
388 $tile[0] = t3lib_utility_Math::forceIntegerInRange($tile[0], 1, 20);
389 $tile[1] = t3lib_utility_Math::forceIntegerInRange($tile[1], 1, 20);
390 $cpOff = $this->objPosition($conf, $workArea, array($cpW * $tile[0], $cpH * $tile[1]));
391
392 for ($xt = 0; $xt < $tile[0]; $xt++) {
393 $Xstart = $cpOff[0] + $cpW * $xt;
394 // If this image is inside of the workArea, then go on
395 if ($Xstart + $cpW > $workArea[0]) {
396 // X:
397 if ($Xstart < $workArea[0]) {
398 $cpImgCutX = $workArea[0] - $Xstart;
399 $Xstart = $workArea[0];
400 } else {
401 $cpImgCutX = 0;
402 }
403 $w = $cpW - $cpImgCutX;
404 if ($Xstart > $workArea[0] + $workArea[2] - $w) {
405 $w = $workArea[0] + $workArea[2] - $Xstart;
406 }
407 // If this image is inside of the workArea, then go on
408 if ($Xstart < $workArea[0] + $workArea[2]) {
409 // Y:
410 for ($yt = 0; $yt < $tile[1]; $yt++) {
411 $Ystart = $cpOff[1] + $cpH * $yt;
412 // If this image is inside of the workArea, then go on
413 if ($Ystart + $cpH > $workArea[1]) {
414 if ($Ystart < $workArea[1]) {
415 $cpImgCutY = $workArea[1] - $Ystart;
416 $Ystart = $workArea[1];
417 } else {
418 $cpImgCutY = 0;
419 }
420 $h = $cpH - $cpImgCutY;
421 if ($Ystart > $workArea[1] + $workArea[3] - $h) {
422 $h = $workArea[1] + $workArea[3] - $Ystart;
423 }
424 // If this image is inside of the workArea, then go on
425 if ($Ystart < $workArea[1] + $workArea[3]) {
426 $this->imagecopyresized($im, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h);
427 }
428 }
429 } // Y:
430 }
431 }
432 }
433 }
434
435 /**
436 * Alternative function for using the similar PHP function imagecopyresized(). Used for GD2 only.
437 *
438 * OK, the reason for this stupid fix is the following story:
439 * GD1.x was capable of copying two images together and combining their palettes! GD2 is apparently not.
440 * With GD2 only the palette of the dest-image is used which mostly results in totally black images when trying to
441 * copy a color-ful image onto the destination.
442 * The GD2-fix is to
443 * 1) Create a blank TRUE-COLOR image
444 * 2) Copy the destination image onto that one
445 * 3) Then do the actual operation; Copying the source (top image) onto that
446 * 4) ... and return the result pointer.
447 * 5) Reduce colors (if we do not, the result may become strange!)
448 * It works, but the resulting images is now a true-color PNG which may be very large.
449 * So, why not use 'imagetruecolortopalette ($im, TRUE, 256)' - well because it does NOT WORK! So simple is that.
450 *
451 * @param resource $dstImg Destination image
452 * @param resource $srcImg Source image
453 * @param integer $dstX Destination x-coordinate
454 * @param integer $dstY Destination y-coordinate
455 * @param integer $srcX Source x-coordinate
456 * @param integer $srcY Source y-coordinate
457 * @param integer $dstWidth Destination width
458 * @param integer $dstHeight Destination height
459 * @param integer $srcWidth Source width
460 * @param integer $srcHeight Source height
461 * @return void
462 * @access private
463 * @see t3lib_iconWorks::imagecopyresized()
464 */
465 function imagecopyresized(&$dstImg, $srcImg, $dstX, $dstY, $srcX, $srcY, $dstWidth, $dstHeight, $srcWidth, $srcHeight) {
466 if ($this->imagecopyresized_fix && !$this->saveAlphaLayer) {
467 // Make true color image
468 $tmpImg = imagecreatetruecolor(imagesx($dstImg), imagesy($dstImg));
469 // Copy the source image onto that
470 imagecopyresized($tmpImg, $dstImg, 0, 0, 0, 0, imagesx($dstImg), imagesy($dstImg), imagesx($dstImg), imagesy($dstImg));
471 // Then copy the source image onto that (the actual operation!)
472 imagecopyresized($tmpImg, $srcImg, $dstX, $dstY, $srcX, $srcY, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
473 // Set the destination image
474 $dstImg = $tmpImg;
475 } else {
476 imagecopyresized($dstImg, $srcImg, $dstX, $dstY, $srcX, $srcY, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
477 }
478 }
479
480 /********************************
481 *
482 * Text / "TEXT" GIFBUILDER object
483 *
484 ********************************/
485
486 /**
487 * Implements the "TEXT" GIFBUILDER object
488 *
489 * @param pointer $im GDlib image pointer
490 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
491 * @param array $workArea The current working area coordinates.
492 * @return void
493 * @see tslib_gifBuilder::make()
494 */
495 function makeText(&$im, $conf, $workArea) {
496 // Spacing
497 list($spacing, $wordSpacing) = $this->calcWordSpacing($conf);
498 // Position
499 $txtPos = $this->txtPosition($conf, $workArea, $conf['BBOX']);
500 $theText = $this->recodeString($conf['text']);
501
502 if ($conf['imgMap'] && is_array($conf['imgMap.'])) {
503 $this->addToMap($this->calcTextCordsForMap($conf['BBOX'][2], $txtPos, $conf['imgMap.']), $conf['imgMap.']);
504 }
505 if (!$conf['hideButCreateMap']) {
506 // Font Color:
507 $cols = $this->convertColor($conf['fontColor']);
508 // NiceText is calculated
509 if (!$conf['niceText']) {
510 $Fcolor = ImageColorAllocate($im, $cols[0], $cols[1], $cols[2]);
511 // antiAliasing is setup:
512 $Fcolor = ($conf['antiAlias']) ? $Fcolor : -$Fcolor;
513
514 for ($a = 0; $a < $conf['iterations']; $a++) {
515 // If any kind of spacing applys, we use this function:
516 if ($spacing || $wordSpacing) {
517 $this->SpacedImageTTFText($im, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, self::prependAbsolutePath($conf['fontFile']), $theText, $spacing, $wordSpacing, $conf['splitRendering.']);
518 } else {
519 $this->renderTTFText($im, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, $conf['fontFile'], $theText, $conf['splitRendering.'], $conf);
520 }
521 }
522 } else { // NICETEXT::
523 // options anti_aliased and iterations is NOT available when doing this!!
524 $w = imagesx($im);
525 $h = imagesy($im);
526 $tmpStr = $this->randomName();
527
528 $fileMenu = $tmpStr . '_menuNT.' . $this->gifExtension;
529 $fileColor = $tmpStr . '_colorNT.' . $this->gifExtension;
530 $fileMask = $tmpStr . '_maskNT.' . $this->gifExtension;
531 // Scalefactor
532 $sF = t3lib_utility_Math::forceIntegerInRange($conf['niceText.']['scaleFactor'], 2, 5);
533 $newW = ceil($sF * imagesx($im));
534 $newH = ceil($sF * imagesy($im));
535
536 // Make mask
537 $maskImg = imagecreatetruecolor($newW, $newH);
538 $Bcolor = ImageColorAllocate($maskImg, 255, 255, 255);
539 ImageFilledRectangle($maskImg, 0, 0, $newW, $newH, $Bcolor);
540 $Fcolor = ImageColorAllocate($maskImg, 0, 0, 0);
541 // If any kind of spacing applies, we use this function:
542 if ($spacing || $wordSpacing) {
543 $this->SpacedImageTTFText($maskImg, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, self::prependAbsolutePath($conf['fontFile']), $theText, $spacing, $wordSpacing, $conf['splitRendering.'], $sF);
544 } else {
545 $this->renderTTFText($maskImg, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, $conf['fontFile'], $theText, $conf['splitRendering.'], $conf, $sF);
546 }
547 $this->ImageWrite($maskImg, $fileMask);
548 ImageDestroy($maskImg);
549
550 // Downscales the mask
551 if ($this->NO_IM_EFFECTS) {
552 if ($this->maskNegate) {
553 // Negate 2 times makes no negate...
554 $command = trim($this->scalecmd . ' ' . $w . 'x' . $h . '!');
555 } else {
556 $command = trim($this->scalecmd . ' ' . $w . 'x' . $h . '! -negate');
557 }
558 } else {
559 if ($this->maskNegate) {
560 $command = trim($conf['niceText.']['before'] . ' ' . $this->scalecmd . ' ' . $w . 'x' . $h . '! ' . $conf['niceText.']['after']);
561 } else {
562 $command = trim($conf['niceText.']['before'] . ' ' . $this->scalecmd . ' ' . $w . 'x' . $h . '! ' . $conf['niceText.']['after'] . ' -negate');
563 }
564 if ($conf['niceText.']['sharpen']) {
565 if ($this->V5_EFFECTS) {
566 $command .= $this->v5_sharpen($conf['niceText.']['sharpen']);
567 } else {
568 $command .= ' -sharpen ' . t3lib_utility_Math::forceIntegerInRange($conf['niceText.']['sharpen'], 1, 99);
569 }
570 }
571 }
572
573 $this->imageMagickExec($fileMask, $fileMask, $command);
574
575 // Make the color-file
576 $colorImg = imagecreatetruecolor($w, $h);
577 $Ccolor = ImageColorAllocate($colorImg, $cols[0], $cols[1], $cols[2]);
578 ImageFilledRectangle($colorImg, 0, 0, $w, $h, $Ccolor);
579 $this->ImageWrite($colorImg, $fileColor);
580 ImageDestroy($colorImg);
581
582 // The mask is applied
583 // The main pictures is saved temporarily
584 $this->ImageWrite($im, $fileMenu);
585
586 $this->combineExec($fileMenu, $fileColor, $fileMask, $fileMenu);
587
588 // The main image is loaded again...
589 $backIm = $this->imageCreateFromFile($fileMenu);
590 // ... and if nothing went wrong we load it onto the old one.
591 if ($backIm) {
592 if (!$this->saveAlphaLayer) {
593 ImageColorTransparent($backIm, -1);
594 }
595 $im = $backIm;
596 }
597
598 // Deleting temporary files;
599 if (!$this->dontUnlinkTempFiles) {
600 unlink($fileMenu);
601 unlink($fileColor);
602 unlink($fileMask);
603 }
604 }
605 }
606 }
607
608 /**
609 * Calculates text position for printing the text onto the image based on configuration like alignment and workarea.
610 *
611 * @param array $conf TypoScript array for the TEXT GIFBUILDER object
612 * @param array $workArea Workarea definition
613 * @param array $BB Bounding box information, was set in tslib_gifBuilder::start()
614 * @return array [0]=x, [1]=y, [2]=w, [3]=h
615 * @access private
616 * @see makeText()
617 */
618 function txtPosition($conf, $workArea, $BB) {
619 $bbox = $BB[2];
620 $angle = intval($conf['angle']) / 180 * pi();
621 $conf['angle'] = 0;
622 $straightBB = $this->calcBBox($conf);
623
624 // offset, align, valign, workarea
625 // [0]=x, [1]=y, [2]=w, [3]=h
626 $result = array();
627 $result[2] = $BB[0];
628 $result[3] = $BB[1];
629 $w = $workArea[2];
630 $h = $workArea[3];
631
632 switch ($conf['align']) {
633 case 'right':
634 case 'center':
635 $factor = abs(cos($angle));
636 $sign = (cos($angle) < 0) ? -1 : 1;
637 $len1 = $sign * $factor * $straightBB[0];
638 $len2 = $sign * $BB[0];
639 $result[0] = $w - ceil($len2 * $factor + (1 - $factor) * $len1);
640
641 $factor = abs(sin($angle));
642 $sign = (sin($angle) < 0) ? -1 : 1;
643 $len1 = $sign * $factor * $straightBB[0];
644 $len2 = $sign * $BB[1];
645 $result[1] = ceil($len2 * $factor + (1 - $factor) * $len1);
646 break;
647 }
648 switch ($conf['align']) {
649 case 'right':
650 break;
651 case 'center':
652 $result[0] = round(($result[0]) / 2);
653 $result[1] = round(($result[1]) / 2);
654 break;
655 default:
656 $result[0] = 0;
657 $result[1] = 0;
658 break;
659 }
660 $result = $this->applyOffset($result, t3lib_div::intExplode(',', $conf['offset']));
661 $result = $this->applyOffset($result, $workArea);
662 return $result;
663 }
664
665 /**
666 * Calculates bounding box information for the TEXT GIFBUILDER object.
667 *
668 * @param array $conf TypoScript array for the TEXT GIFBUILDER object
669 * @return array Array with three keys [0]/[1] being x/y and [2] being the bounding box array
670 * @access private
671 * @see txtPosition(), tslib_gifBuilder::start()
672 */
673 function calcBBox($conf) {
674 $sF = $this->getTextScalFactor($conf);
675 list($spacing, $wordSpacing) = $this->calcWordSpacing($conf, $sF);
676 $theText = $this->recodeString($conf['text']);
677
678 $charInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $theText, $conf['splitRendering.'], $sF);
679 $theBBoxInfo = $charInf;
680 if ($conf['angle']) {
681 $xArr = array($charInf[0], $charInf[2], $charInf[4], $charInf[6]);
682 $yArr = array($charInf[1], $charInf[3], $charInf[5], $charInf[7]);
683 $x = max($xArr) - min($xArr);
684 $y = max($yArr) - min($yArr);
685 } else {
686 $x = ($charInf[2] - $charInf[0]);
687 $y = ($charInf[1] - $charInf[7]);
688 }
689 // Set original lineHeight (used by line breaks):
690 $theBBoxInfo['lineHeight'] = $y;
691
692 // If any kind of spacing applys, we use this function:
693 if ($spacing || $wordSpacing) {
694 $x = 0;
695 if (!$spacing && $wordSpacing) {
696 $bits = explode(' ', $theText);
697 foreach ($bits as $word) {
698 $word .= ' ';
699 $wordInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $word, $conf['splitRendering.'], $sF);
700 $wordW = ($wordInf[2] - $wordInf[0]);
701 $x += $wordW + $wordSpacing;
702 }
703 } else {
704 $utf8Chars = $this->singleChars($theText);
705 // For each UTF-8 char, do:
706 foreach ($utf8Chars as $char) {
707 $charInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $char, $conf['splitRendering.'], $sF);
708 $charW = ($charInf[2] - $charInf[0]);
709 $x += $charW + (($char == ' ') ? $wordSpacing : $spacing);
710 }
711 }
712 } elseif (isset($conf['breakWidth']) && $conf['breakWidth'] && $this->getRenderedTextWidth($conf['text'], $conf) > $conf['breakWidth']) {
713 $maxWidth = 0;
714 $currentWidth = 0;
715 $breakWidth = $conf['breakWidth'];
716 $breakSpace = $this->getBreakSpace($conf, $theBBoxInfo);
717
718 $wordPairs = $this->getWordPairsForLineBreak($conf['text']);
719 // Iterate through all word pairs:
720 foreach ($wordPairs as $index => $wordPair) {
721 $wordWidth = $this->getRenderedTextWidth($wordPair, $conf);
722 if ($index == 0 || $currentWidth + $wordWidth <= $breakWidth) {
723 $currentWidth += $wordWidth;
724 } else {
725 $maxWidth = max($maxWidth, $currentWidth);
726 $y += $breakSpace;
727 // Restart:
728 $currentWidth = $wordWidth;
729 }
730 }
731 $x = max($maxWidth, $currentWidth) * $sF;
732 }
733
734 if ($sF > 1) {
735 $x = ceil($x / $sF);
736 $y = ceil($y / $sF);
737 if (is_array($theBBoxInfo)) {
738 foreach ($theBBoxInfo as &$value) {
739 $value = ceil($value / $sF);
740 }
741 unset($value);
742 }
743 }
744 return array($x, $y, $theBBoxInfo);
745 }
746
747 /**
748 * Adds an <area> tag to the internal variable $this->map which is used to accumulate the content for an ImageMap
749 *
750 * @param array $cords Coordinates for a polygon image map as created by ->calcTextCordsForMap()
751 * @param array $conf Configuration for "imgMap." property of a TEXT GIFBUILDER object.
752 * @return void
753 * @access private
754 * @see makeText(), calcTextCordsForMap()
755 */
756 function addToMap($cords, $conf) {
757 $this->map .= '<area' .
758 ' shape="poly"' .
759 ' coords="' . implode(',', $cords) . '"' .
760 ' href="' . htmlspecialchars($conf['url']) . '"' .
761 ($conf['target'] ? ' target="' . htmlspecialchars($conf['target']) . '"' : '') .
762 $JS .
763 (strlen($conf['titleText']) ? ' title="' . htmlspecialchars($conf['titleText']) . '"' : '') .
764 ' alt="' . htmlspecialchars($conf['altText']) . '" />';
765 }
766
767 /**
768 * Calculating the coordinates for a TEXT string on an image map. Used in an <area> tag
769 *
770 * @param array $cords Coordinates (from BBOX array)
771 * @param array $offset Offset array
772 * @param array $conf Configuration for "imgMap." property of a TEXT GIFBUILDER object.
773 * @return array
774 * @access private
775 * @see makeText(), calcTextCordsForMap()
776 */
777 function calcTextCordsForMap($cords, $offset, $conf) {
778 $pars = t3lib_div::intExplode(',', $conf['explode'] . ',');
779
780 $newCords[0] = $cords[0] + $offset[0] - $pars[0];
781 $newCords[1] = $cords[1] + $offset[1] + $pars[1];
782 $newCords[2] = $cords[2] + $offset[0] + $pars[0];
783 $newCords[3] = $cords[3] + $offset[1] + $pars[1];
784 $newCords[4] = $cords[4] + $offset[0] + $pars[0];
785 $newCords[5] = $cords[5] + $offset[1] - $pars[1];
786 $newCords[6] = $cords[6] + $offset[0] - $pars[0];
787 $newCords[7] = $cords[7] + $offset[1] - $pars[1];
788
789 return $newCords;
790 }
791
792 /**
793 * Printing text onto an image like the PHP function imageTTFText does but in addition it offers options for spacing of letters and words.
794 * Spacing is done by printing one char at a time and this means that the spacing is rather uneven and probably not very nice.
795 * See
796 *
797 * @param pointer $im (See argument for PHP function imageTTFtext())
798 * @param integer $fontSize (See argument for PHP function imageTTFtext())
799 * @param integer $angle (See argument for PHP function imageTTFtext())
800 * @param integer $x (See argument for PHP function imageTTFtext())
801 * @param integer $y (See argument for PHP function imageTTFtext())
802 * @param integer $Fcolor (See argument for PHP function imageTTFtext())
803 * @param string $fontFile (See argument for PHP function imageTTFtext())
804 * @param string $text (See argument for PHP function imageTTFtext()). UTF-8 string, possibly with entities in.
805 * @param integer $spacing The spacing of letters in pixels
806 * @param integer $wordSpacing The spacing of words in pixels
807 * @param array $splitRenderingConf Array
808 * @param integer $sF Scale factor
809 * @return void
810 * @access private
811 */
812 function SpacedImageTTFText(&$im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $text, $spacing, $wordSpacing, $splitRenderingConf, $sF = 1) {
813
814 $spacing *= $sF;
815 $wordSpacing *= $sF;
816
817 if (!$spacing && $wordSpacing) {
818 $bits = explode(' ', $text);
819 foreach ($bits as $word) {
820 $word .= ' ';
821 $wordInf = $this->ImageTTFBBoxWrapper($fontSize, $angle, $fontFile, $word, $splitRenderingConf, $sF);
822 $wordW = ($wordInf[2] - $wordInf[0]);
823 $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $word, $splitRenderingConf, $sF);
824 $x += $wordW + $wordSpacing;
825 }
826 } else {
827 $utf8Chars = $this->singleChars($text);
828 // For each UTF-8 char, do:
829 foreach ($utf8Chars as $char) {
830 $charInf = $this->ImageTTFBBoxWrapper($fontSize, $angle, $fontFile, $char, $splitRenderingConf, $sF);
831 $charW = ($charInf[2] - $charInf[0]);
832 $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $char, $splitRenderingConf, $sF);
833 $x += $charW + (($char == ' ') ? $wordSpacing : $spacing);
834 }
835 }
836 }
837
838 /**
839 * Function that finds the right fontsize that will render the textstring within a certain width
840 *
841 * @param array $conf The TypoScript properties of the TEXT GIFBUILDER object
842 * @return integer The new fontSize
843 * @access private
844 * @see tslib_gifBuilder::start()
845 */
846 function fontResize($conf) {
847 // 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!!!!
848 $maxWidth = intval($conf['maxWidth']);
849 list($spacing, $wordSpacing) = $this->calcWordSpacing($conf);
850 if ($maxWidth) {
851 // If any kind of spacing applys, we use this function:
852 if ($spacing || $wordSpacing) {
853 return $conf['fontSize'];
854 // ################ no calc for spacing yet !!!!!!
855 } else {
856 do {
857 // Determine bounding box.
858 $bounds = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $this->recodeString($conf['text']), $conf['splitRendering.']);
859 if ($conf['angle'] < 0) {
860 $pixelWidth = abs($bounds[4] - $bounds[0]);
861 } elseif ($conf['angle'] > 0) {
862 $pixelWidth = abs($bounds[2] - $bounds[6]);
863 } else {
864 $pixelWidth = abs($bounds[4] - $bounds[6]);
865 }
866
867 // Size is fine, exit:
868 if ($pixelWidth <= $maxWidth) {
869 break;
870 } else {
871 $conf['fontSize']--;
872 }
873 } while ($conf['fontSize'] > 1);
874 }
875 // If spacing
876 }
877 return $conf['fontSize'];
878 }
879
880 /**
881 * Wrapper for ImageTTFBBox
882 *
883 * @param integer $fontSize (See argument for PHP function ImageTTFBBox())
884 * @param integer $angle (See argument for PHP function ImageTTFBBox())
885 * @param string $fontFile (See argument for PHP function ImageTTFBBox())
886 * @param string $string (See argument for PHP function ImageTTFBBox())
887 * @param array $splitRendering Split-rendering configuration
888 * @param integer $sF Scale factor
889 * @return array Information array.
890 */
891 function ImageTTFBBoxWrapper($fontSize, $angle, $fontFile, $string, $splitRendering, $sF = 1) {
892
893 // Initialize:
894 $offsetInfo = array();
895 $stringParts = $this->splitString($string, $splitRendering, $fontSize, $fontFile);
896
897 // Traverse string parts:
898 foreach ($stringParts as $strCfg) {
899 $fontFile = self::prependAbsolutePath($strCfg['fontFile']);
900 if (is_readable($fontFile)) {
901
902 /**
903 * Calculate Bounding Box for part.
904 * Due to a PHP bug, we must retry if $calc[2] is negative.
905 * @see https://bugs.php.net/bug.php?id=51315
906 * @see https://bugs.php.net/bug.php?id=22513
907 */
908 $try = 0;
909 do {
910 $calc = ImageTTFBBox(
911 t3lib_div::freetypeDpiComp($sF * $strCfg['fontSize']),
912 $angle,
913 $fontFile,
914 $strCfg['str']
915 );
916 } while ($calc[2] < 0 && $try++ < 10);
917
918 // Calculate offsets:
919 if (!count($offsetInfo)) {
920 // First run, just copy over.
921 $offsetInfo = $calc;
922 } else {
923 $offsetInfo[2] += $calc[2] - $calc[0] + intval($splitRendering['compX']) + intval($strCfg['xSpaceBefore']) + intval($strCfg['xSpaceAfter']);
924 $offsetInfo[3] += $calc[3] - $calc[1] - intval($splitRendering['compY']) - intval($strCfg['ySpaceBefore']) - intval($strCfg['ySpaceAfter']);
925 $offsetInfo[4] += $calc[4] - $calc[6] + intval($splitRendering['compX']) + intval($strCfg['xSpaceBefore']) + intval($strCfg['xSpaceAfter']);
926 $offsetInfo[5] += $calc[5] - $calc[7] - intval($splitRendering['compY']) - intval($strCfg['ySpaceBefore']) - intval($strCfg['ySpaceAfter']);
927 }
928
929 } else {
930 debug('cannot read file: ' . $fontFile, 't3lib_stdGraphic::ImageTTFBBoxWrapper()');
931 }
932 }
933
934 return $offsetInfo;
935 }
936
937 /**
938 * Wrapper for ImageTTFText
939 *
940 * @param pointer $im (See argument for PHP function imageTTFtext())
941 * @param integer $fontSize (See argument for PHP function imageTTFtext())
942 * @param integer $angle (See argument for PHP function imageTTFtext())
943 * @param integer $x (See argument for PHP function imageTTFtext())
944 * @param integer $y (See argument for PHP function imageTTFtext())
945 * @param integer $color (See argument for PHP function imageTTFtext())
946 * @param string $fontFile (See argument for PHP function imageTTFtext())
947 * @param string $string (See argument for PHP function imageTTFtext()). UTF-8 string, possibly with entities in.
948 * @param array $splitRendering Split-rendering configuration
949 * @param integer $sF Scale factor
950 * @return void
951 */
952 function ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering, $sF = 1) {
953
954 // Initialize:
955 $stringParts = $this->splitString($string, $splitRendering, $fontSize, $fontFile);
956 $x = ceil($sF * $x);
957 $y = ceil($sF * $y);
958
959 // Traverse string parts:
960 foreach ($stringParts as $i => $strCfg) {
961
962 // Initialize:
963 $colorIndex = $color;
964
965 // Set custom color if any (only when niceText is off):
966 if ($strCfg['color'] && $sF == 1) {
967 $cols = $this->convertColor($strCfg['color']);
968 $colorIndex = ImageColorAllocate($im, $cols[0], $cols[1], $cols[2]);
969 $colorIndex = $color >= 0 ? $colorIndex : -$colorIndex;
970 }
971
972 // Setting xSpaceBefore
973 if ($i) {
974 $x += intval($strCfg['xSpaceBefore']);
975 $y -= intval($strCfg['ySpaceBefore']);
976 }
977
978 $fontFile = self::prependAbsolutePath($strCfg['fontFile']);
979 if (is_readable($fontFile)) {
980
981 // Render part:
982 ImageTTFText($im, t3lib_div::freetypeDpiComp($sF * $strCfg['fontSize']), $angle, $x, $y, $colorIndex, $fontFile, $strCfg['str']);
983
984 // Calculate offset to apply:
985 $wordInf = ImageTTFBBox(t3lib_div::freetypeDpiComp($sF * $strCfg['fontSize']), $angle, self::prependAbsolutePath($strCfg['fontFile']), $strCfg['str']);
986 $x += $wordInf[2] - $wordInf[0] + intval($splitRendering['compX']) + intval($strCfg['xSpaceAfter']);
987 $y += $wordInf[5] - $wordInf[7] - intval($splitRendering['compY']) - intval($strCfg['ySpaceAfter']);
988
989 } else {
990 debug('cannot read file: ' . $fontFile, 't3lib_stdGraphic::ImageTTFTextWrapper()');
991 }
992
993 }
994 }
995
996 /**
997 * Splitting a string for ImageTTFBBox up into an array where each part has its own configuration options.
998 *
999 * @param string $string UTF-8 string
1000 * @param array $splitRendering Split-rendering configuration from GIFBUILDER TEXT object.
1001 * @param integer $fontSize Current fontsize
1002 * @param string $fontFile Current font file
1003 * @return array Array with input string splitted according to configuration
1004 */
1005 function splitString($string, $splitRendering, $fontSize, $fontFile) {
1006
1007 // Initialize by setting the whole string and default configuration as the first entry.
1008 $result = array();
1009 $result[] = array(
1010 'str' => $string,
1011 'fontSize' => $fontSize,
1012 'fontFile' => $fontFile
1013 );
1014
1015 // Traverse the split-rendering configuration:
1016 // Splitting will create more entries in $result with individual configurations.
1017 if (is_array($splitRendering)) {
1018 $sKeyArray = t3lib_TStemplate::sortedKeyList($splitRendering);
1019
1020 // Traverse configured options:
1021 foreach ($sKeyArray as $key) {
1022 $cfg = $splitRendering[$key . '.'];
1023
1024 // Process each type of split rendering keyword:
1025 switch ((string) $splitRendering[$key]) {
1026 case 'highlightWord':
1027 if (strlen($cfg['value'])) {
1028 $newResult = array();
1029
1030 // Traverse the current parts of the result array:
1031 foreach ($result as $part) {
1032 // Explode the string value by the word value to highlight:
1033 $explodedParts = explode($cfg['value'], $part['str']);
1034 foreach ($explodedParts as $c => $expValue) {
1035 if (strlen($expValue)) {
1036 $newResult[] = array_merge($part, array('str' => $expValue));
1037 }
1038 if ($c + 1 < count($explodedParts)) {
1039 $newResult[] = array(
1040 'str' => $cfg['value'],
1041 'fontSize' => $cfg['fontSize'] ? $cfg['fontSize'] : $part['fontSize'],
1042 'fontFile' => $cfg['fontFile'] ? $cfg['fontFile'] : $part['fontFile'],
1043 'color' => $cfg['color'],
1044 'xSpaceBefore' => $cfg['xSpaceBefore'],
1045 'xSpaceAfter' => $cfg['xSpaceAfter'],
1046 'ySpaceBefore' => $cfg['ySpaceBefore'],
1047 'ySpaceAfter' => $cfg['ySpaceAfter'],
1048 );
1049 }
1050 }
1051 }
1052
1053 // Set the new result as result array:
1054 if (count($newResult)) {
1055 $result = $newResult;
1056 }
1057 }
1058 break;
1059 case 'charRange':
1060 if (strlen($cfg['value'])) {
1061
1062 // Initialize range:
1063 $ranges = t3lib_div::trimExplode(',', $cfg['value'], 1);
1064 foreach ($ranges as $i => $rangeDef) {
1065 $ranges[$i] = t3lib_div::intExplode('-', $ranges[$i]);
1066 if (!isset($ranges[$i][1])) {
1067 $ranges[$i][1] = $ranges[$i][0];
1068 }
1069 }
1070 $newResult = array();
1071
1072 // Traverse the current parts of the result array:
1073 foreach ($result as $part) {
1074
1075 // Initialize:
1076 $currentState = -1;
1077 $bankAccum = '';
1078
1079 // Explode the string value by the word value to highlight:
1080 $utf8Chars = $this->singleChars($part['str']);
1081 foreach ($utf8Chars as $utfChar) {
1082
1083 // Find number and evaluate position:
1084 $uNumber = $this->csConvObj->utf8CharToUnumber($utfChar);
1085 $inRange = 0;
1086 foreach ($ranges as $rangeDef) {
1087 if ($uNumber >= $rangeDef[0] && (!$rangeDef[1] || $uNumber <= $rangeDef[1])) {
1088 $inRange = 1;
1089 break;
1090 }
1091 }
1092 if ($currentState == -1) {
1093 $currentState = $inRange;
1094 } // Initialize first char
1095
1096 // Switch bank:
1097 if ($inRange != $currentState && !t3lib_div::inList('32,10,13,9', $uNumber)) {
1098
1099 // Set result:
1100 if (strlen($bankAccum)) {
1101 $newResult[] = array(
1102 'str' => $bankAccum,
1103 'fontSize' => $currentState && $cfg['fontSize'] ? $cfg['fontSize'] : $part['fontSize'],
1104 'fontFile' => $currentState && $cfg['fontFile'] ? $cfg['fontFile'] : $part['fontFile'],
1105 'color' => $currentState ? $cfg['color'] : '',
1106 'xSpaceBefore' => $currentState ? $cfg['xSpaceBefore'] : '',
1107 'xSpaceAfter' => $currentState ? $cfg['xSpaceAfter'] : '',
1108 'ySpaceBefore' => $currentState ? $cfg['ySpaceBefore'] : '',
1109 'ySpaceAfter' => $currentState ? $cfg['ySpaceAfter'] : '',
1110 );
1111 }
1112
1113 // Initialize new settings:
1114 $currentState = $inRange;
1115 $bankAccum = '';
1116 }
1117
1118 // Add char to bank:
1119 $bankAccum .= $utfChar;
1120 }
1121
1122 // Set result for FINAL part:
1123 if (strlen($bankAccum)) {
1124 $newResult[] = array(
1125 'str' => $bankAccum,
1126 'fontSize' => $currentState && $cfg['fontSize'] ? $cfg['fontSize'] : $part['fontSize'],
1127 'fontFile' => $currentState && $cfg['fontFile'] ? $cfg['fontFile'] : $part['fontFile'],
1128 'color' => $currentState ? $cfg['color'] : '',
1129 'xSpaceBefore' => $currentState ? $cfg['xSpaceBefore'] : '',
1130 'xSpaceAfter' => $currentState ? $cfg['xSpaceAfter'] : '',
1131 'ySpaceBefore' => $currentState ? $cfg['ySpaceBefore'] : '',
1132 'ySpaceAfter' => $currentState ? $cfg['ySpaceAfter'] : '',
1133 );
1134 }
1135 }
1136
1137 // Set the new result as result array:
1138 if (count($newResult)) {
1139 $result = $newResult;
1140 }
1141 }
1142 break;
1143 }
1144 }
1145 }
1146
1147 return $result;
1148 }
1149
1150 /**
1151 * Calculates the spacing and wordSpacing values
1152 *
1153 * @param array $conf TypoScript array for the TEXT GIFBUILDER object
1154 * @param integer $scaleFactor TypoScript value from eg $conf['niceText.']['scaleFactor']
1155 * @return array Array with two keys [0]/[1] being array($spacing,$wordSpacing)
1156 * @access private
1157 * @see calcBBox()
1158 */
1159 function calcWordSpacing($conf, $scaleFactor = 1) {
1160
1161 $spacing = intval($conf['spacing']);
1162 $wordSpacing = intval($conf['wordSpacing']);
1163 $wordSpacing = $wordSpacing ? $wordSpacing : $spacing * 2;
1164
1165 $spacing *= $scaleFactor;
1166 $wordSpacing *= $scaleFactor;
1167
1168 return array($spacing, $wordSpacing);
1169 }
1170
1171 /**
1172 * Calculates and returns the niceText.scaleFactor
1173 *
1174 * @param array $conf TypoScript array for the TEXT GIFBUILDER object
1175 * @return integer TypoScript value from eg $conf['niceText.']['scaleFactor']
1176 * @access private
1177 */
1178 function getTextScalFactor($conf) {
1179 if (!$conf['niceText']) {
1180 $sF = 1;
1181 } else { // NICETEXT::
1182 $sF = t3lib_utility_Math::forceIntegerInRange($conf['niceText.']['scaleFactor'], 2, 5);
1183 }
1184 return $sF;
1185 }
1186
1187 /**
1188 * Renders a regular text and takes care of a possible line break automatically.
1189 *
1190 * @param pointer $im (See argument for PHP function imageTTFtext())
1191 * @param integer $fontSize (See argument for PHP function imageTTFtext())
1192 * @param integer $angle (See argument for PHP function imageTTFtext())
1193 * @param integer $x (See argument for PHP function imageTTFtext())
1194 * @param integer $y (See argument for PHP function imageTTFtext())
1195 * @param integer $color (See argument for PHP function imageTTFtext())
1196 * @param string $fontFile (See argument for PHP function imageTTFtext())
1197 * @param string $string (See argument for PHP function imageTTFtext()). UTF-8 string, possibly with entities in.
1198 * @param array $splitRendering Split-rendering configuration
1199 * @param array $conf The configuration
1200 * @param integer $sF Scale factor
1201 * @return void
1202 */
1203 protected function renderTTFText(&$im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering, $conf, $sF = 1) {
1204 if (isset($conf['breakWidth']) && $conf['breakWidth'] && $this->getRenderedTextWidth($string, $conf) > $conf['breakWidth']) {
1205 $phrase = '';
1206 $currentWidth = 0;
1207
1208 $breakWidth = $conf['breakWidth'];
1209 $breakSpace = $this->getBreakSpace($conf);
1210
1211 $wordPairs = $this->getWordPairsForLineBreak($string);
1212 // Iterate through all word pairs:
1213 foreach ($wordPairs as $index => $wordPair) {
1214 $wordWidth = $this->getRenderedTextWidth($wordPair, $conf);
1215 if ($index == 0 || $currentWidth + $wordWidth <= $breakWidth) {
1216 $currentWidth += $wordWidth;
1217 $phrase .= $wordPair;
1218 } else {
1219 // Render the current phrase that is below breakWidth:
1220 $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $phrase, $splitRendering, $sF);
1221 // Calculate the news height offset:
1222 $y += $breakSpace;
1223 // Restart the phrase:
1224 $currentWidth = $wordWidth;
1225 $phrase = $wordPair;
1226 }
1227 }
1228 // Render the remaining phrase:
1229 if ($currentWidth) {
1230 $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $phrase, $splitRendering, $sF);
1231 }
1232 } else {
1233 $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering, $sF);
1234 }
1235 }
1236
1237 /**
1238 * Gets the word pairs used for automatic line breaks.
1239 *
1240 * @param string $string
1241 * @return array
1242 */
1243 protected function getWordPairsForLineBreak($string) {
1244 $wordPairs = array();
1245
1246 $wordsArray = preg_split('#([- .,!:]+)#', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
1247 $wordsCount = count($wordsArray);
1248 for ($index = 0; $index < $wordsCount; $index += 2) {
1249 $wordPairs[] = $wordsArray[$index] . $wordsArray[$index + 1];
1250 }
1251
1252 return $wordPairs;
1253 }
1254
1255 /**
1256 * Gets the rendered text width.
1257 *
1258 * @param string $text
1259 * @param array $conf
1260 * @param integer
1261 */
1262 protected function getRenderedTextWidth($text, $conf) {
1263 $bounds = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $this->recodeString($text), $conf['splitRendering.']);
1264 if ($conf['angle'] < 0) {
1265 $pixelWidth = abs($bounds[4] - $bounds[0]);
1266 } elseif ($conf['angle'] > 0) {
1267 $pixelWidth = abs($bounds[2] - $bounds[6]);
1268 } else {
1269 $pixelWidth = abs($bounds[4] - $bounds[6]);
1270 }
1271 return $pixelWidth;
1272 }
1273
1274 /**
1275 * Gets the break space for each new line.
1276 *
1277 * @param array $conf TypoScript configuration for the currently rendered object
1278 * @param array $boundingBox The bounding box the the currently rendered object
1279 * @return integer The break space
1280 */
1281 protected function getBreakSpace($conf, array $boundingBox = NULL) {
1282 if (!isset($boundingBox)) {
1283 $boundingBox = $this->calcBBox($conf);
1284 $boundingBox = $boundingBox[2];
1285 }
1286
1287 if (isset($conf['breakSpace']) && $conf['breakSpace']) {
1288 $breakSpace = $boundingBox['lineHeight'] * $conf['breakSpace'];
1289 } else {
1290 $breakSpace = $boundingBox['lineHeight'];
1291 }
1292
1293 return $breakSpace;
1294 }
1295
1296 /*********************************************
1297 *
1298 * Other GIFBUILDER objects related to TEXT
1299 *
1300 *********************************************/
1301
1302 /**
1303 * Implements the "OUTLINE" GIFBUILDER object / property for the TEXT object
1304 *
1305 * @param pointer $im GDlib image pointer
1306 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1307 * @param array $workArea The current working area coordinates.
1308 * @param array $txtConf TypoScript array with configuration for the associated TEXT GIFBUILDER object.
1309 * @return void
1310 * @see tslib_gifBuilder::make(), makeText()
1311 */
1312 function makeOutline(&$im, $conf, $workArea, $txtConf) {
1313 $thickness = intval($conf['thickness']);
1314 if ($thickness) {
1315 $txtConf['fontColor'] = $conf['color'];
1316 $outLineDist = t3lib_utility_Math::forceIntegerInRange($thickness, 1, 2);
1317 for ($b = 1; $b <= $outLineDist; $b++) {
1318 if ($b == 1) {
1319 $it = 8;
1320 } else {
1321 $it = 16;
1322 }
1323 $outL = $this->circleOffset($b, $it);
1324 for ($a = 0; $a < $it; $a++) {
1325 $this->makeText($im, $txtConf, $this->applyOffset($workArea, $outL[$a]));
1326 }
1327 }
1328 }
1329 }
1330
1331 /**
1332 * Creates some offset values in an array used to simulate a circularly applied outline around TEXT
1333 *
1334 * access private
1335 *
1336 * @param integer $distance Distance
1337 * @param integer $iterations Iterations.
1338 * @return array
1339 * @see makeOutline()
1340 */
1341 function circleOffset($distance, $iterations) {
1342 $res = array();
1343 if ($distance && $iterations) {
1344 for ($a = 0; $a < $iterations; $a++) {
1345 $yOff = round(sin(2 * pi() / $iterations * ($a + 1)) * 100 * $distance);
1346 if ($yOff) {
1347 $yOff = intval(ceil(abs($yOff / 100)) * ($yOff / abs($yOff)));
1348 }
1349 $xOff = round(cos(2 * pi() / $iterations * ($a + 1)) * 100 * $distance);
1350 if ($xOff) {
1351 $xOff = intval(ceil(abs($xOff / 100)) * ($xOff / abs($xOff)));
1352 }
1353 $res[$a] = array($xOff, $yOff);
1354 }
1355 }
1356 return $res;
1357 }
1358
1359 /**
1360 * Implements the "EMBOSS" GIFBUILDER object / property for the TEXT object
1361 *
1362 * @param pointer $im GDlib image pointer
1363 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1364 * @param array $workArea The current working area coordinates.
1365 * @param array $txtConf TypoScript array with configuration for the associated TEXT GIFBUILDER object.
1366 * @return void
1367 * @see tslib_gifBuilder::make(), makeShadow()
1368 */
1369 function makeEmboss(&$im, $conf, $workArea, $txtConf) {
1370 $conf['color'] = $conf['highColor'];
1371 $this->makeShadow($im, $conf, $workArea, $txtConf);
1372 $newOffset = t3lib_div::intExplode(',', $conf['offset']);
1373 $newOffset[0] *= -1;
1374 $newOffset[1] *= -1;
1375 $conf['offset'] = implode(',', $newOffset);
1376 $conf['color'] = $conf['lowColor'];
1377 $this->makeShadow($im, $conf, $workArea, $txtConf);
1378 }
1379
1380 /**
1381 * Implements the "SHADOW" GIFBUILDER object / property for the TEXT object
1382 * The operation involves ImageMagick for combining.
1383 *
1384 * @param pointer $im GDlib image pointer
1385 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1386 * @param array $workArea The current working area coordinates.
1387 * @param array $txtConf TypoScript array with configuration for the associated TEXT GIFBUILDER object.
1388 * @retur void
1389 * @see tslib_gifBuilder::make(), makeText(), makeEmboss()
1390 */
1391 function makeShadow(&$im, $conf, $workArea, $txtConf) {
1392 $workArea = $this->applyOffset($workArea, t3lib_div::intExplode(',', $conf['offset']));
1393 $blurRate = t3lib_utility_Math::forceIntegerInRange(intval($conf['blur']), 0, 99);
1394
1395 // No effects if ImageMagick ver. 5+
1396 if (!$blurRate || $this->NO_IM_EFFECTS) {
1397 $txtConf['fontColor'] = $conf['color'];
1398 $this->makeText($im, $txtConf, $workArea);
1399 } else {
1400 $w = imagesx($im);
1401 $h = imagesy($im);
1402 // Area around the blur used for cropping something
1403 $blurBorder = 3;
1404 $tmpStr = $this->randomName();
1405 $fileMenu = $tmpStr . '_menu.' . $this->gifExtension;
1406 $fileColor = $tmpStr . '_color.' . $this->gifExtension;
1407 $fileMask = $tmpStr . '_mask.' . $this->gifExtension;
1408
1409 // BlurColor Image laves
1410 $blurColImg = imagecreatetruecolor($w, $h);
1411 $bcols = $this->convertColor($conf['color']);
1412 $Bcolor = ImageColorAllocate($blurColImg, $bcols[0], $bcols[1], $bcols[2]);
1413 ImageFilledRectangle($blurColImg, 0, 0, $w, $h, $Bcolor);
1414 $this->ImageWrite($blurColImg, $fileColor);
1415 ImageDestroy($blurColImg);
1416
1417 // The mask is made: BlurTextImage
1418 $blurTextImg = imagecreatetruecolor($w + $blurBorder * 2, $h + $blurBorder * 2);
1419 // Black background
1420 $Bcolor = ImageColorAllocate($blurTextImg, 0, 0, 0);
1421 ImageFilledRectangle($blurTextImg, 0, 0, $w + $blurBorder * 2, $h + $blurBorder * 2, $Bcolor);
1422 $txtConf['fontColor'] = 'white';
1423 $blurBordArr = array($blurBorder, $blurBorder);
1424 $this->makeText($blurTextImg, $txtConf, $this->applyOffset($workArea, $blurBordArr));
1425 // Dump to temporary file
1426 $this->ImageWrite($blurTextImg, $fileMask);
1427 // Destroy
1428 ImageDestroy($blurTextImg);
1429
1430 $command = '';
1431 $command .= $this->maskNegate;
1432
1433 if ($this->V5_EFFECTS) {
1434 $command .= $this->v5_blur($blurRate + 1);
1435 } else {
1436 // Blurring of the mask
1437 // How many blur-commands that is executed. Min = 1;
1438 $times = ceil($blurRate / 10);
1439 // Here I boost the blur-rate so that it is 100 already at 25. The rest is done by up to 99 iterations of the blur-command.
1440 $newBlurRate = $blurRate * 4;
1441 $newBlurRate = t3lib_utility_Math::forceIntegerInRange($newBlurRate, 1, 99);
1442 // Building blur-command
1443 for ($a = 0; $a < $times; $a++) {
1444 $command .= ' -blur ' . $blurRate;
1445 }
1446 }
1447
1448 $this->imageMagickExec($fileMask, $fileMask, $command . ' +matte');
1449
1450 // The mask is loaded again
1451 $blurTextImg_tmp = $this->imageCreateFromFile($fileMask);
1452 // If nothing went wrong we continue with the blurred mask
1453 if ($blurTextImg_tmp) {
1454
1455 // Cropping the border from the mask
1456 $blurTextImg = imagecreatetruecolor($w, $h);
1457 $this->imagecopyresized($blurTextImg, $blurTextImg_tmp, 0, 0, $blurBorder, $blurBorder, $w, $h, $w, $h);
1458 // Destroy the temporary mask
1459 ImageDestroy($blurTextImg_tmp);
1460
1461 // Adjust the mask
1462 $intensity = 40;
1463 if ($conf['intensity']) {
1464 $intensity = t3lib_utility_Math::forceIntegerInRange($conf['intensity'], 0, 100);
1465 }
1466 $intensity = ceil(255 - ($intensity / 100 * 255));
1467 $this->inputLevels($blurTextImg, 0, $intensity, $this->maskNegate);
1468
1469 $opacity = t3lib_utility_Math::forceIntegerInRange(intval($conf['opacity']), 0, 100);
1470 if ($opacity && $opacity < 100) {
1471 $high = ceil(255 * $opacity / 100);
1472 // Reducing levels as the opacity demands
1473 $this->outputLevels($blurTextImg, 0, $high, $this->maskNegate);
1474 }
1475
1476 // Dump the mask again
1477 $this->ImageWrite($blurTextImg, $fileMask);
1478 // Destroy the mask
1479 ImageDestroy($blurTextImg);
1480
1481 // The pictures are combined
1482 // The main pictures is saved temporarily
1483 $this->ImageWrite($im, $fileMenu);
1484
1485 $this->combineExec($fileMenu, $fileColor, $fileMask, $fileMenu);
1486
1487 // The main image is loaded again...
1488 $backIm = $this->imageCreateFromFile($fileMenu);
1489 // ... and if nothing went wrong we load it onto the old one.
1490 if ($backIm) {
1491 if (!$this->saveAlphaLayer) {
1492 ImageColorTransparent($backIm, -1);
1493 }
1494 $im = $backIm;
1495 }
1496 }
1497 // Deleting temporary files;
1498 if (!$this->dontUnlinkTempFiles) {
1499 unlink($fileMenu);
1500 unlink($fileColor);
1501 unlink($fileMask);
1502 }
1503 }
1504 }
1505
1506 /****************************
1507 *
1508 * Other GIFBUILDER objects
1509 *
1510 ****************************/
1511
1512 /**
1513 * Implements the "BOX" GIFBUILDER object
1514 *
1515 * @param pointer $im GDlib image pointer
1516 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1517 * @param array $workArea The current working area coordinates.
1518 * @return void
1519 * @see tslib_gifBuilder::make()
1520 */
1521 function makeBox(&$im, $conf, $workArea) {
1522 $cords = t3lib_div::intExplode(',', $conf['dimensions'] . ',,,');
1523 $conf['offset'] = $cords[0] . ',' . $cords[1];
1524 $cords = $this->objPosition($conf, $workArea, array($cords[2], $cords[3]));
1525 $cols = $this->convertColor($conf['color']);
1526
1527 $opacity = 0;
1528 if (isset($conf['opacity'])) {
1529 // conversion:
1530 // PHP 0 = opaque, 127 = transparent
1531 // TYPO3 100 = opaque, 0 = transparent
1532 $opacity = t3lib_utility_Math::forceIntegerInRange(intval($conf['opacity']), 1, 100, 1);
1533 $opacity = abs($opacity - 100);
1534 $opacity = round((127 * $opacity) / 100);
1535 }
1536
1537 $tmpColor = ImageColorAllocateAlpha($im, $cols[0], $cols[1], $cols[2], $opacity);
1538 imagefilledrectangle($im, $cords[0], $cords[1], $cords[0] + $cords[2] - 1, $cords[1] + $cords[3] - 1, $tmpColor);
1539 }
1540
1541 /**
1542 * Implements the "Ellipse" GIFBUILDER object
1543 * Example Typoscript:
1544 * file = GIFBUILDER
1545 * file {
1546 * XY = 200,200
1547 * format = jpg
1548 * quality = 100
1549 * 10 = ELLIPSE
1550 * 10.dimensions = 100,100,50,50
1551 * 10.color = red
1552 *
1553 * $workArea = X,Y
1554 * $conf['dimensions'] = offset x, offset y, width of ellipse, height of ellipse
1555 *
1556 * @param pointer $im GDlib image pointer
1557 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1558 * @param array $workArea The current working area coordinates.
1559 * @return void
1560 * @see tslib_gifBuilder::make()
1561 */
1562 public function makeEllipse(&$im, array $conf, array $workArea) {
1563 $ellipseConfiguration = t3lib_div::intExplode(',', $conf['dimensions'] . ',,,');
1564 // Ellipse offset inside workArea (x/y)
1565 $conf['offset'] = $ellipseConfiguration[0] . ',' . $ellipseConfiguration[1];
1566
1567 // @see objPosition
1568 $imageCoordinates = $this->objPosition($conf, $workArea, array($ellipseConfiguration[2], $ellipseConfiguration[3]));
1569
1570 $color = $this->convertColor($conf['color']);
1571 $fillingColor = imagecolorallocate($im, $color[0], $color[1], $color[2]);
1572 imagefilledellipse($im, $imageCoordinates[0], $imageCoordinates[1], $imageCoordinates[2], $imageCoordinates[3], $fillingColor);
1573 }
1574
1575 /**
1576 * Implements the "EFFECT" GIFBUILDER object
1577 * The operation involves ImageMagick for applying effects
1578 *
1579 * @param pointer $im GDlib image pointer
1580 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1581 * @return void
1582 * @see tslib_gifBuilder::make(), applyImageMagickToPHPGif()
1583 */
1584 function makeEffect(&$im, $conf) {
1585 $commands = $this->IMparams($conf['value']);
1586 if ($commands) {
1587 $this->applyImageMagickToPHPGif($im, $commands);
1588 }
1589 }
1590
1591 /**
1592 * Creating ImageMagick paramters from TypoScript property
1593 *
1594 * @param string $setup A string with effect keywords=value pairs separated by "|"
1595 * @return string ImageMagick prepared parameters.
1596 * @access private
1597 * @see makeEffect()
1598 */
1599 function IMparams($setup) {
1600 if (!trim($setup)) {
1601 return '';
1602 }
1603 $effects = explode('|', $setup);
1604 $commands = '';
1605 foreach ($effects as $val) {
1606 $pairs = explode('=', $val, 2);
1607 $value = trim($pairs[1]);
1608 $effect = strtolower(trim($pairs[0]));
1609 switch ($effect) {
1610 case 'gamma':
1611 $commands .= ' -gamma ' . doubleval($value);
1612 break;
1613 case 'blur':
1614 if (!$this->NO_IM_EFFECTS) {
1615 if ($this->V5_EFFECTS) {
1616 $commands .= $this->v5_blur($value);
1617 } else {
1618 $commands .= ' -blur ' . t3lib_utility_Math::forceIntegerInRange($value, 1, 99);
1619 }
1620 }
1621 break;
1622 case 'sharpen':
1623 if (!$this->NO_IM_EFFECTS) {
1624 if ($this->V5_EFFECTS) {
1625 $commands .= $this->v5_sharpen($value);
1626 } else {
1627 $commands .= ' -sharpen ' . t3lib_utility_Math::forceIntegerInRange($value, 1, 99);
1628 }
1629 }
1630 break;
1631 case 'rotate':
1632 $commands .= ' -rotate ' . t3lib_utility_Math::forceIntegerInRange($value, 0, 360);
1633 break;
1634 case 'solarize':
1635 $commands .= ' -solarize ' . t3lib_utility_Math::forceIntegerInRange($value, 0, 99);
1636 break;
1637 case 'swirl':
1638 $commands .= ' -swirl ' . t3lib_utility_Math::forceIntegerInRange($value, 0, 1000);
1639 break;
1640 case 'wave':
1641 $params = t3lib_div::intExplode(',', $value);
1642 $commands .= ' -wave ' . t3lib_utility_Math::forceIntegerInRange($params[0], 0, 99) . 'x' . t3lib_utility_Math::forceIntegerInRange($params[1], 0, 99);
1643 break;
1644 case 'charcoal':
1645 $commands .= ' -charcoal ' . t3lib_utility_Math::forceIntegerInRange($value, 0, 100);
1646 break;
1647 case 'gray':
1648 $commands .= ' -colorspace GRAY';
1649 break;
1650 case 'edge':
1651 $commands .= ' -edge ' . t3lib_utility_Math::forceIntegerInRange($value, 0, 99);
1652 break;
1653 case 'emboss':
1654 $commands .= ' -emboss';
1655 break;
1656 case 'flip':
1657 $commands .= ' -flip';
1658 break;
1659 case 'flop':
1660 $commands .= ' -flop';
1661 break;
1662 case 'colors':
1663 $commands .= ' -colors ' . t3lib_utility_Math::forceIntegerInRange($value, 2, 255);
1664 break;
1665 case 'shear':
1666 $commands .= ' -shear ' . t3lib_utility_Math::forceIntegerInRange($value, -90, 90);
1667 break;
1668 case 'invert':
1669 $commands .= ' -negate';
1670 break;
1671 }
1672 }
1673 return $commands;
1674 }
1675
1676 /**
1677 * Implements the "ADJUST" GIFBUILDER object
1678 *
1679 * @param pointer $im GDlib image pointer
1680 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1681 * @return void
1682 * @see tslib_gifBuilder::make(), autoLevels(), outputLevels(), inputLevels()
1683 */
1684 function adjust(&$im, $conf) {
1685 $setup = $conf['value'];
1686 if (!trim($setup)) {
1687 return '';
1688 }
1689 $effects = explode('|', $setup);
1690 foreach ($effects as $val) {
1691 $pairs = explode('=', $val, 2);
1692 $value = trim($pairs[1]);
1693 $effect = strtolower(trim($pairs[0]));
1694 switch ($effect) {
1695 case 'inputlevels': // low,high
1696 $params = t3lib_div::intExplode(',', $value);
1697 $this->inputLevels($im, $params[0], $params[1]);
1698 break;
1699 case 'outputlevels':
1700 $params = t3lib_div::intExplode(',', $value);
1701 $this->outputLevels($im, $params[0], $params[1]);
1702 break;
1703 case 'autolevels':
1704 $this->autoLevels($im);
1705 break;
1706 }
1707 }
1708 }
1709
1710 /**
1711 * Implements the "CROP" GIFBUILDER object
1712 *
1713 * @param pointer $im GDlib image pointer
1714 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1715 * @return void
1716 * @see tslib_gifBuilder::make()
1717 */
1718 function crop(&$im, $conf) {
1719 // Clears workArea to total image
1720 $this->setWorkArea('');
1721 $cords = t3lib_div::intExplode(',', $conf['crop'] . ',,,');
1722 $conf['offset'] = $cords[0] . ',' . $cords[1];
1723 $cords = $this->objPosition($conf, $this->workArea, array($cords[2], $cords[3]));
1724
1725 $newIm = imagecreatetruecolor($cords[2], $cords[3]);
1726 $cols = $this->convertColor($conf['backColor'] ? $conf['backColor'] : $this->setup['backColor']);
1727 $Bcolor = ImageColorAllocate($newIm, $cols[0], $cols[1], $cols[2]);
1728 ImageFilledRectangle($newIm, 0, 0, $cords[2], $cords[3], $Bcolor);
1729
1730 $newConf = array();
1731 $workArea = array(0, 0, $cords[2], $cords[3]);
1732 if ($cords[0] < 0) {
1733 $workArea[0] = abs($cords[0]);
1734 } else {
1735 $newConf['offset'] = -$cords[0];
1736 }
1737 if ($cords[1] < 0) {
1738 $workArea[1] = abs($cords[1]);
1739 } else {
1740 $newConf['offset'] .= ',' . -$cords[1];
1741 }
1742
1743 $this->copyGifOntoGif($newIm, $im, $newConf, $workArea);
1744 $im = $newIm;
1745 $this->w = imagesx($im);
1746 $this->h = imagesy($im);
1747 // Clears workArea to total image
1748 $this->setWorkArea('');
1749 }
1750
1751 /**
1752 * Implements the "SCALE" GIFBUILDER object
1753 *
1754 * @param pointer $im GDlib image pointer
1755 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1756 * @return void
1757 * @see tslib_gifBuilder::make()
1758 */
1759 function scale(&$im, $conf) {
1760 if ($conf['width'] || $conf['height'] || $conf['params']) {
1761 $tmpStr = $this->randomName();
1762 $theFile = $tmpStr . '.' . $this->gifExtension;
1763 $this->ImageWrite($im, $theFile);
1764 $theNewFile = $this->imageMagickConvert($theFile, $this->gifExtension, $conf['width'], $conf['height'], $conf['params'], '', '');
1765 $tmpImg = $this->imageCreateFromFile($theNewFile[3]);
1766 if ($tmpImg) {
1767 ImageDestroy($im);
1768 $im = $tmpImg;
1769 $this->w = imagesx($im);
1770 $this->h = imagesy($im);
1771 // Clears workArea to total image
1772 $this->setWorkArea('');
1773 }
1774 if (!$this->dontUnlinkTempFiles) {
1775 unlink($theFile);
1776 if ($theNewFile[3] && $theNewFile[3] != $theFile) {
1777 unlink($theNewFile[3]);
1778 }
1779 }
1780 }
1781 }
1782
1783 /**
1784 * Implements the "WORKAREA" GIFBUILDER object when setting it
1785 * Setting internal working area boundaries (->workArea)
1786 *
1787 * @param string $workArea Working area dimensions, comma separated
1788 * @return void
1789 * @access private
1790 * @see tslib_gifBuilder::make()
1791 */
1792 function setWorkArea($workArea) {
1793 $this->workArea = t3lib_div::intExplode(',', $workArea);
1794 $this->workArea = $this->applyOffset($this->workArea, $this->OFFSET);
1795 if (!$this->workArea[2]) {
1796 $this->workArea[2] = $this->w;
1797 }
1798 if (!$this->workArea[3]) {
1799 $this->workArea[3] = $this->h;
1800 }
1801 }
1802
1803 /*************************
1804 *
1805 * Adjustment functions
1806 *
1807 ************************/
1808
1809 /**
1810 * Apply auto-levels to input image pointer
1811 *
1812 * @param integer $im GDlib Image Pointer
1813 * @return void
1814 */
1815 function autolevels(&$im) {
1816 $totalCols = ImageColorsTotal($im);
1817 $min = 255;
1818 $max = 0;
1819 for ($c = 0; $c < $totalCols; $c++) {
1820 $cols = ImageColorsForIndex($im, $c);
1821 $grayArr[] = round(($cols['red'] + $cols['green'] + $cols['blue']) / 3);
1822 }
1823 $min = min($grayArr);
1824 $max = max($grayArr);
1825 $delta = $max - $min;
1826 if ($delta) {
1827 for ($c = 0; $c < $totalCols; $c++) {
1828 $cols = ImageColorsForIndex($im, $c);
1829 $cols['red'] = floor(($cols['red'] - $min) / $delta * 255);
1830 $cols['green'] = floor(($cols['green'] - $min) / $delta * 255);
1831 $cols['blue'] = floor(($cols['blue'] - $min) / $delta * 255);
1832 ImageColorSet($im, $c, $cols['red'], $cols['green'], $cols['blue']);
1833 }
1834 }
1835 }
1836
1837 /**
1838 * Apply output levels to input image pointer (decreasing contrast)
1839 *
1840 * @param integer $im GDlib Image Pointer
1841 * @param integer $low The "low" value (close to 0)
1842 * @param integer $high The "high" value (close to 255)
1843 * @param boolean $swap If swap, then low and high are swapped. (Useful for negated masks...)
1844 * @return void
1845 */
1846 function outputLevels(&$im, $low, $high, $swap = '') {
1847 if ($low < $high) {
1848 $low = t3lib_utility_Math::forceIntegerInRange($low, 0, 255);
1849 $high = t3lib_utility_Math::forceIntegerInRange($high, 0, 255);
1850
1851 if ($swap) {
1852 $temp = $low;
1853 $low = 255 - $high;
1854 $high = 255 - $temp;
1855 }
1856
1857 $delta = $high - $low;
1858 $totalCols = ImageColorsTotal($im);
1859 for ($c = 0; $c < $totalCols; $c++) {
1860 $cols = ImageColorsForIndex($im, $c);
1861 $cols['red'] = $low + floor($cols['red'] / 255 * $delta);
1862 $cols['green'] = $low + floor($cols['green'] / 255 * $delta);
1863 $cols['blue'] = $low + floor($cols['blue'] / 255 * $delta);
1864 ImageColorSet($im, $c, $cols['red'], $cols['green'], $cols['blue']);
1865 }
1866 }
1867 }
1868
1869 /**
1870 * Apply input levels to input image pointer (increasing contrast)
1871 *
1872 * @param integer $im GDlib Image Pointer
1873 * @param integer $low The "low" value (close to 0)
1874 * @param integer $high The "high" value (close to 255)
1875 * @param boolean $swap If swap, then low and high are swapped. (Useful for negated masks...)
1876 * @return void
1877 */
1878 function inputLevels(&$im, $low, $high, $swap = '') {
1879 if ($low < $high) {
1880 $low = t3lib_utility_Math::forceIntegerInRange($low, 0, 255);
1881 $high = t3lib_utility_Math::forceIntegerInRange($high, 0, 255);
1882
1883 if ($swap) {
1884 $temp = $low;
1885 $low = 255 - $high;
1886 $high = 255 - $temp;
1887 }
1888
1889 $delta = $high - $low;
1890 $totalCols = ImageColorsTotal($im);
1891 for ($c = 0; $c < $totalCols; $c++) {
1892 $cols = ImageColorsForIndex($im, $c);
1893 $cols['red'] = t3lib_utility_Math::forceIntegerInRange(($cols['red'] - $low) / $delta * 255, 0, 255);
1894 $cols['green'] = t3lib_utility_Math::forceIntegerInRange(($cols['green'] - $low) / $delta * 255, 0, 255);
1895 $cols['blue'] = t3lib_utility_Math::forceIntegerInRange(($cols['blue'] - $low) / $delta * 255, 0, 255);
1896 ImageColorSet($im, $c, $cols['red'], $cols['green'], $cols['blue']);
1897 }
1898 }
1899 }
1900
1901 /**
1902 * Reduce colors in image using IM and create a palette based image if possible (<=256 colors)
1903 *
1904 * @param string $file Image file to reduce
1905 * @param integer $cols Number of colors to reduce the image to.
1906 * @return string Reduced file
1907 */
1908 function IMreduceColors($file, $cols) {
1909 $fI = t3lib_div::split_fileref($file);
1910 $ext = strtolower($fI['fileext']);
1911 $result = $this->randomName() . '.' . $ext;
1912 if (($reduce = t3lib_utility_Math::forceIntegerInRange($cols, 0, ($ext == 'gif' ? 256 : $this->truecolorColors), 0)) > 0) {
1913 $params = ' -colors ' . $reduce;
1914 if ($reduce <= 256) {
1915 $params .= ' -type Palette';
1916 }
1917 if ($ext == 'png' && $reduce <= 256) {
1918 $prefix = 'png8:';
1919 }
1920 $this->imageMagickExec($file, $prefix . $result, $params);
1921 if ($result) {
1922 return $result;
1923 }
1924 }
1925 return '';
1926 }
1927
1928 /*********************************
1929 *
1930 * GIFBUILDER Helper functions
1931 *
1932 *********************************/
1933
1934 /**
1935 * Checks if the $fontFile is already at an absolute path and if not, prepends the correct path.
1936 * Use PATH_site unless we are in the backend.
1937 * Call it by t3lib_stdGraphic::prependAbsolutePath()
1938 *
1939 * @param string $fontFile The font file
1940 * @return string The font file with absolute path.
1941 */
1942 function prependAbsolutePath($fontFile) {
1943 $absPath = defined('PATH_typo3') ? dirname(PATH_thisScript) . '/' : PATH_site;
1944 $fontFile = t3lib_div::isAbsPath($fontFile) ? $fontFile : t3lib_div::resolveBackPath($absPath . $fontFile);
1945 return $fontFile;
1946 }
1947
1948 /**
1949 * Returns the IM command for sharpening with ImageMagick 5 (when $this->V5_EFFECTS is set).
1950 * Uses $this->im5fx_sharpenSteps for translation of the factor to an actual command.
1951 *
1952 * @param integer $factor The sharpening factor, 0-100 (effectively in 10 steps)
1953 * @return string The sharpening command, eg. " -sharpen 3x4"
1954 * @see makeText(), IMparams(), v5_blur()
1955 */
1956 function v5_sharpen($factor) {
1957 $factor = t3lib_utility_Math::forceIntegerInRange(ceil($factor / 10), 0, 10);
1958
1959 $sharpenArr = explode(',', ',' . $this->im5fx_sharpenSteps);
1960 $sharpenF = trim($sharpenArr[$factor]);
1961 if ($sharpenF) {
1962 $cmd = ' -sharpen ' . $sharpenF;
1963 return $cmd;
1964 }
1965 }
1966
1967 /**
1968 * Returns the IM command for blurring with ImageMagick 5 (when $this->V5_EFFECTS is set).
1969 * Uses $this->im5fx_blurSteps for translation of the factor to an actual command.
1970 *
1971 * @param integer $factor The blurring factor, 0-100 (effectively in 10 steps)
1972 * @return string The blurring command, eg. " -blur 3x4"
1973 * @see makeText(), IMparams(), v5_sharpen()
1974 */
1975 function v5_blur($factor) {
1976 $factor = t3lib_utility_Math::forceIntegerInRange(ceil($factor / 10), 0, 10);
1977
1978 $blurArr = explode(',', ',' . $this->im5fx_blurSteps);
1979 $blurF = trim($blurArr[$factor]);
1980 if ($blurF) {
1981 $cmd = ' -blur ' . $blurF;
1982 return $cmd;
1983 }
1984 }
1985
1986 /**
1987 * Returns a random filename prefixed with "temp_" and then 32 char md5 hash (without extension) from $this->tempPath.
1988 * 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.
1989 *
1990 * @return string
1991 */
1992 function randomName() {
1993 $this->createTempSubDir('temp/');
1994 return $this->tempPath . 'temp/' . md5(uniqid(''));
1995 }
1996
1997 /**
1998 * Applies offset value to coordinated in $cords.
1999 * Basically the value of key 0/1 of $OFFSET is added to keys 0/1 of $cords
2000 *
2001 * @param array $cords Integer coordinates in key 0/1
2002 * @param array $OFFSET Offset values in key 0/1
2003 * @return array Modified $cords array
2004 */
2005 function applyOffset($cords, $OFFSET) {
2006 $cords[0] = intval($cords[0]) + intval($OFFSET[0]);
2007 $cords[1] = intval($cords[1]) + intval($OFFSET[1]);
2008 return $cords;
2009 }
2010
2011 /**
2012 * Converts a "HTML-color" TypoScript datatype to RGB-values.
2013 * Default is 0,0,0
2014 *
2015 * @param string $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
2016 * @return array RGB values in key 0/1/2 of the array
2017 */
2018 function convertColor($string) {
2019 $col = array();
2020 $cParts = explode(':', $string, 2);
2021
2022 // Finding the RGB definitions of the color:
2023 $string = $cParts[0];
2024 if (strstr($string, '#')) {
2025 $string = preg_replace('/[^A-Fa-f0-9]*/', '', $string);
2026 $col[] = HexDec(substr($string, 0, 2));
2027 $col[] = HexDec(substr($string, 2, 2));
2028 $col[] = HexDec(substr($string, 4, 2));
2029 } elseif (strstr($string, ',')) {
2030 $string = preg_replace('/[^,0-9]*/', '', $string);
2031 $strArr = explode(',', $string);
2032 $col[] = intval($strArr[0]);
2033 $col[] = intval($strArr[1]);
2034 $col[] = intval($strArr[2]);
2035 } else {
2036 $string = strtolower(trim($string));
2037 if ($this->colMap[$string]) {
2038 $col = $this->colMap[$string];
2039 } else {
2040 $col = array(0, 0, 0);
2041 }
2042 }
2043 // ... and possibly recalculating the value
2044 if (trim($cParts[1])) {
2045 $cParts[1] = trim($cParts[1]);
2046 if (substr($cParts[1], 0, 1) == '*') {
2047 $val = doubleval(substr($cParts[1], 1));
2048 $col[0] = t3lib_utility_Math::forceIntegerInRange($col[0] * $val, 0, 255);
2049 $col[1] = t3lib_utility_Math::forceIntegerInRange($col[1] * $val, 0, 255);
2050 $col[2] = t3lib_utility_Math::forceIntegerInRange($col[2] * $val, 0, 255);
2051 } else {
2052 $val = intval($cParts[1]);
2053 $col[0] = t3lib_utility_Math::forceIntegerInRange($col[0] + $val, 0, 255);
2054 $col[1] = t3lib_utility_Math::forceIntegerInRange($col[1] + $val, 0, 255);
2055 $col[2] = t3lib_utility_Math::forceIntegerInRange($col[2] + $val, 0, 255);
2056 }
2057 }
2058 return $col;
2059 }
2060
2061 /**
2062 * Recode string
2063 * Used with text strings for fonts when languages has other character sets.
2064 *
2065 * @param string The text to recode
2066 * @return string The recoded string. Should be UTF-8 output. MAY contain entities (eg. &#123; or &#quot; which should render as real chars).
2067 */
2068 function recodeString($string) {
2069 // Recode string to UTF-8 from $this->nativeCharset:
2070 if ($this->nativeCharset && $this->nativeCharset != 'utf-8') {
2071 // Convert to UTF-8
2072 $string = $this->csConvObj->utf8_encode($string, $this->nativeCharset);
2073 }
2074
2075 return $string;
2076 }
2077
2078 /**
2079 * Split a string into an array of individual characters
2080 * 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.
2081 *
2082 * @param string $theText The text string to split
2083 * @param boolean $returnUnicodeNumber Return Unicode numbers instead of chars.
2084 * @return array Numerical array with a char as each value.
2085 */
2086 function singleChars($theText, $returnUnicodeNumber = FALSE) {
2087 if ($this->nativeCharset) {
2088 // Get an array of separated UTF-8 chars
2089 return $this->csConvObj->utf8_to_numberarray($theText, 1, $returnUnicodeNumber ? 0 : 1);
2090 } else {
2091 $output = array();
2092 $c = strlen($theText);
2093 for ($a = 0; $a < $c; $a++) {
2094 $output[] = substr($theText, $a, 1);
2095 }
2096 return $output;
2097 }
2098 }
2099
2100 /**
2101 * 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
2102 *
2103 * @param array $conf TypoScript configuration for a GIFBUILDER object
2104 * @param array makeBox Workarea definition
2105 * @param array $BB BB (Bounding box) array. Not just used for TEXT objects but also for others
2106 * @return array [0]=x, [1]=y, [2]=w, [3]=h
2107 * @access private
2108 * @see copyGifOntoGif(), makeBox(), crop()
2109 */
2110 function objPosition($conf, $workArea, $BB) {
2111 // offset, align, valign, workarea
2112 $result = array();
2113 $result[2] = $BB[0];
2114 $result[3] = $BB[1];
2115 $w = $workArea[2];
2116 $h = $workArea[3];
2117
2118 $align = explode(',', $conf['align']);
2119 $align[0] = strtolower(substr(trim($align[0]), 0, 1));
2120 $align[1] = strtolower(substr(trim($align[1]), 0, 1));
2121
2122 switch ($align[0]) {
2123 case 'r':
2124 $result[0] = $w - $result[2];
2125 break;
2126 case 'c':
2127 $result[0] = round(($w - $result[2]) / 2);
2128 break;
2129 default:
2130 $result[0] = 0;
2131 break;
2132 }
2133 switch ($align[1]) {
2134 case 'b':
2135 // y pos
2136 $result[1] = $h - $result[3];
2137 break;
2138 case 'c':
2139 $result[1] = round(($h - $result[3]) / 2);
2140 break;
2141 default:
2142 $result[1] = 0;
2143 break;
2144 }
2145 $result = $this->applyOffset($result, t3lib_div::intExplode(',', $conf['offset']));
2146 $result = $this->applyOffset($result, $workArea);
2147 return $result;
2148 }
2149
2150 /***********************************
2151 *
2152 * Scaling, Dimensions of images
2153 *
2154 ***********************************/
2155
2156 /**
2157 * Converts $imagefile to another file in temp-dir of type $newExt (extension).
2158 *
2159 * @param string $imagefile The image filepath
2160 * @param string $newExt 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.
2161 * @param string $w 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
2162 * @param string $h Height. See $w
2163 * @param string $params Additional ImageMagick parameters.
2164 * @param string $frame 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...
2165 * @param array $options An array with options passed to getImageScale (see this function).
2166 * @param boolean $mustCreate 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.
2167 * @return array [0]/[1] is w/h, [2] is file extension and [3] is the filename.
2168 * @see getImageScale(), typo3/show_item.php, fileList_ext::renderImage(), tslib_cObj::getImgResource(), SC_tslib_showpic::show(), maskImageOntoImage(), copyImageOntoImage(), scale()
2169 */
2170 function imageMagickConvert($imagefile, $newExt = '', $w = '', $h = '', $params = '', $frame = '', $options = '', $mustCreate = 0) {
2171 if ($this->NO_IMAGE_MAGICK) {
2172 // Returning file info right away
2173 return $this->getImageDimensions($imagefile);
2174 }
2175
2176 if ($info = $this->getImageDimensions($imagefile)) {
2177 $newExt = strtolower(trim($newExt));
2178 // If no extension is given the original extension is used
2179 if (!$newExt) {
2180 $newExt = $info[2];
2181 }
2182 if ($newExt == 'web') {
2183 if (t3lib_div::inList($this->webImageExt, $info[2])) {
2184 $newExt = $info[2];
2185 } else {
2186 $newExt = $this->gif_or_jpg($info[2], $info[0], $info[1]);
2187 if (!$params) {
2188 $params = $this->cmds[$newExt];
2189 }
2190 }
2191 }
2192 if (t3lib_div::inList($this->imageFileExt, $newExt)) {
2193 if (strstr($w . $h, 'm')) {
2194 $max = 1;
2195 } else {
2196 $max = 0;
2197 }
2198
2199 $data = $this->getImageScale($info, $w, $h, $options);
2200 $w = $data['origW'];
2201 $h = $data['origH'];
2202
2203 // If no conversion should be performed
2204 // this flag is TRUE if the width / height does NOT dictate
2205 // the image to be scaled!! (that is if no width / height is
2206 // given or if the destination w/h matches the original image
2207 // dimensions or if the option to not scale the image is set)
2208 $noScale = (!$w && !$h) || ($data[0] == $info[0] && $data[1] == $info[1]) || $options['noScale'];
2209
2210 if ($noScale && !$data['crs'] && !$params && !$frame && $newExt == $info[2] && !$mustCreate) {
2211 // Set the new width and height before returning,
2212 // if the noScale option is set
2213 if ($options['noScale']) {
2214 $info[0] = $data[0];
2215 $info[1] = $data[1];
2216 }
2217 $info[3] = $imagefile;
2218 return $info;
2219 }
2220 $info[0] = $data[0];
2221 $info[1] = $data[1];
2222
2223 $frame = $this->noFramePrepended ? '' : intval($frame);
2224
2225 if (!$params) {
2226 $params = $this->cmds[$newExt];
2227 }
2228
2229 // Cropscaling:
2230 if ($data['crs']) {
2231 if (!$data['origW']) {
2232 $data['origW'] = $data[0];
2233 }
2234 if (!$data['origH']) {
2235 $data['origH'] = $data[1];
2236 }
2237 $offsetX = intval(($data[0] - $data['origW']) * ($data['cropH'] + 100) / 200);
2238 $offsetY = intval(($data[1] - $data['origH']) * ($data['cropV'] + 100) / 200);
2239 $params .= ' -crop ' . $data['origW'] . 'x' . $data['origH'] . '+' . $offsetX . '+' . $offsetY . ' ';
2240 }
2241
2242 $command = $this->scalecmd . ' ' . $info[0] . 'x' . $info[1] . '! ' . $params . ' ';
2243 $cropscale = ($data['crs'] ? 'crs-V' . $data['cropV'] . 'H' . $data['cropH'] : '');
2244
2245 if ($this->alternativeOutputKey) {
2246 $theOutputName = t3lib_div::shortMD5($command . $cropscale . basename($imagefile) . $this->alternativeOutputKey . '[' . $frame . ']');
2247 } else {
2248 $theOutputName = t3lib_div::shortMD5($command . $cropscale . $imagefile . filemtime($imagefile) . '[' . $frame . ']');
2249 }
2250 if ($this->imageMagickConvert_forceFileNameBody) {
2251 $theOutputName = $this->imageMagickConvert_forceFileNameBody;
2252 $this->imageMagickConvert_forceFileNameBody = '';
2253 }
2254
2255 // Making the temporary filename:
2256 $this->createTempSubDir('pics/');
2257 $output = $this->absPrefix . $this->tempPath . 'pics/' . $this->filenamePrefix . $theOutputName . '.' . $newExt;
2258
2259 // Register temporary filename:
2260 $GLOBALS['TEMP_IMAGES_ON_PAGE'][] = $output;
2261
2262 if ($this->dontCheckForExistingTempFile || !$this->file_exists_typo3temp_file($output, $imagefile)) {
2263 $this->imageMagickExec($imagefile, $output, $command, $frame);
2264 }
2265 if (file_exists($output)) {
2266 $info[3] = $output;
2267 $info[2] = $newExt;
2268 // params could realisticly change some imagedata!
2269 if ($params) {
2270 $info = $this->getImageDimensions($info[3]);
2271 }
2272 if ($info[2] == $this->gifExtension && !$this->dontCompress) {
2273 // Compress with IM (lzw) or GD (rle) (Workaround for the absence of lzw-compression in GD)
2274 t3lib_div::gif_compress($info[3], '');
2275 }
2276 return $info;
2277 }
2278 }
2279 }
2280 }
2281
2282 /**
2283 * Gets the input image dimensions.
2284 *
2285 * @param string $imageFile The image filepath
2286 * @return array Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
2287 * @see imageMagickConvert(), tslib_cObj::getImgResource()
2288 */
2289 function getImageDimensions($imageFile) {
2290 preg_match('/([^\.]*)$/', $imageFile, $reg);
2291 if (file_exists($imageFile) && t3lib_div::inList($this->imageFileExt, strtolower($reg[0]))) {
2292 if ($returnArr = $this->getCachedImageDimensions($imageFile)) {
2293 return $returnArr;
2294 } else {
2295 if ($temp = @getImageSize($imageFile)) {
2296 $returnArr = array($temp[0], $temp[1], strtolower($reg[0]), $imageFile);
2297 } else {
2298 $returnArr = $this->imageMagickIdentify($imageFile);
2299 }
2300 if ($returnArr) {
2301 $this->cacheImageDimensions($returnArr);
2302 return $returnArr;
2303 }
2304 }
2305 }
2306 return FALSE;
2307 }
2308
2309 /**
2310 * Cache the result of the getImageDimensions function into the database. Does not check if the
2311 * file exists!
2312 *
2313 * @param array $identifyResult Result of the getImageDimensions function
2314 * @return boolean TRUE if operation was successful
2315 */
2316 function cacheImageDimensions($identifyResult) {
2317 // Create md5 hash of filemtime and filesize
2318 $md5Hash = md5(filemtime($identifyResult[3]) . filesize($identifyResult[3]));
2319
2320 $result = FALSE;
2321 if ($md5Hash) {
2322 $fieldArray = array(
2323 'md5hash' => $md5Hash,
2324 'md5filename' => md5($identifyResult[3]),
2325 'tstamp' => $GLOBALS['EXEC_TIME'],
2326 'filename' => $identifyResult[3],
2327 'imagewidth' => $identifyResult[0],
2328 'imageheight' => $identifyResult[1],
2329 );
2330
2331 $GLOBALS['TYPO3_DB']->exec_INSERTquery(
2332 'cache_imagesizes',
2333 $fieldArray
2334 );
2335
2336 if (!$err = $GLOBALS['TYPO3_DB']->sql_error()) {
2337 $result = TRUE;
2338 }
2339 }
2340
2341 return $result;
2342 }
2343
2344 /**
2345 * Fetch the cached imageDimensions from the MySQL database. Does not check if the image file exists!
2346 *
2347 * @param string $imageFile The image filepath
2348 * @return array Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
2349 */
2350 function getCachedImageDimensions($imageFile) {
2351 // Create md5 hash of filemtime and filesize
2352 $md5Hash = md5(filemtime($imageFile) . filesize($imageFile));
2353
2354 $cachedImageDimensions = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
2355 'md5hash, md5filename, imagewidth, imageheight',
2356 'cache_imagesizes',
2357 'md5filename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr(md5($imageFile), 'cache_imagesizes')
2358 );
2359
2360 $result = FALSE;
2361 if (is_array($cachedImageDimensions)) {
2362 if ($cachedImageDimensions['md5hash'] != $md5Hash) {
2363 // File has changed, delete the row
2364 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
2365 'cache_imagesizes',
2366 'md5filename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cachedImageDimensions['md5filename'], 'cache_imagesizes')
2367 );
2368 } else {
2369 preg_match('/([^\.]*)$/', $imageFile, $imageExtension);
2370 $result = array(
2371 (int)$cachedImageDimensions['imagewidth'],
2372 (int)$cachedImageDimensions['imageheight'],
2373 strtolower($imageExtension[0]),
2374 $imageFile,
2375 );
2376 }
2377 }
2378
2379 return $result;
2380 }
2381
2382 /**
2383 * Get numbers for scaling the image based on input
2384 *
2385 * @param array $info Current image information: Width, Height etc.
2386 * @param integer $w "required" width
2387 * @param integer $h "required" height
2388 * @param array $options Options: Keys are like "maxW", "maxH", "minW", "minH"
2389 * @return array
2390 * @access private
2391 * @see imageMagickConvert()
2392 */
2393 function getImageScale($info, $w, $h, $options) {
2394 if (strstr($w . $h, 'm')) {
2395 $max = 1;
2396 } else {
2397 $max = 0;
2398 }
2399
2400 if (strstr($w . $h, 'c')) {
2401 $out['cropH'] = intval(substr(strstr($w, 'c'), 1));
2402 $out['cropV'] = intval(substr(strstr($h, 'c'), 1));
2403 $crs = TRUE;
2404 } else {
2405 $crs = FALSE;
2406 }
2407 $out['crs'] = $crs;
2408
2409 $w = intval($w);
2410 $h = intval($h);
2411 // If there are max-values...
2412 if ($options['maxW']) {
2413 // If width is given...
2414 if ($w) {
2415 if ($w > $options['maxW']) {
2416 $w = $options['maxW'];
2417 // Height should follow
2418 $max = 1;
2419 }
2420 } else {
2421 if ($info[0] > $options['maxW']) {
2422 $w = $options['maxW'];
2423 // Height should follow
2424 $max = 1;
2425 }
2426 }
2427 }
2428 if ($options['maxH']) {
2429 // If height is given...
2430 if ($h) {
2431 if ($h > $options['maxH']) {
2432 $h = $options['maxH'];
2433 // Height should follow
2434 $max = 1;
2435 }
2436 } else {
2437 // Changed [0] to [1] 290801
2438 if ($info[1] > $options['maxH']) {
2439 $h = $options['maxH'];
2440 // Height should follow
2441 $max = 1;
2442 }
2443 }
2444 }
2445 $out['origW'] = $w;
2446 $out['origH'] = $h;
2447 $out['max'] = $max;
2448
2449 if (!$this->mayScaleUp) {
2450 if ($w > $info[0]) {
2451 $w = $info[0];
2452 }
2453 if ($h > $info[1]) {
2454 $h = $info[1];
2455 }
2456 }
2457 // If scaling should be performed
2458 if ($w || $h) {
2459 if ($w && !$h) {
2460 $info[1] = ceil($info[1] * ($w / $info[0]));
2461 $info[0] = $w;
2462 }
2463 if (!$w && $h) {
2464 $info[0] = ceil($info[0] * ($h / $info[1]));
2465 $info[1] = $h;
2466 }
2467 if ($w && $h) {
2468 if ($max) {
2469 $ratio = $info[0] / $info[1];
2470 if ($h * $ratio > $w) {
2471 $h = round($w / $ratio);
2472 } else {
2473 $w = round($h * $ratio);
2474 }
2475 }
2476 if ($crs) {
2477 $ratio = $info[0] / $info[1];
2478 if ($h * $ratio < $w) {
2479 $h = round($w / $ratio);
2480 } else {
2481 $w = round($h * $ratio);
2482 }
2483 }
2484 $info[0] = $w;
2485 $info[1] = $h;
2486 }
2487 }
2488 $out[0] = $info[0];
2489 $out[1] = $info[1];
2490 // Set minimum-measures!
2491 if ($options['minW'] && $out[0] < $options['minW']) {
2492 if (($max || $crs) && $out[0]) {
2493 $out[1] = round($out[1] * $options['minW'] / $out[0]);
2494 }
2495 $out[0] = $options['minW'];
2496 }
2497 if ($options['minH'] && $out[1] < $options['minH']) {
2498 if (($max || $crs) && $out[1]) {
2499 $out[0] = round($out[0] * $options['minH'] / $out[1]);
2500 }
2501 $out[1] = $options['minH'];
2502 }
2503
2504 return $out;
2505 }
2506
2507 /**
2508 * Used to check if a certain process of scaling an image is already being carried out (can be logged in the SQL database)
2509 *
2510 * @param string $output Output imagefile
2511 * @param string $orig Original basis file
2512 * @return boolean Returns TRUE if the file is already being made; thus "TRUE" means "Don't render the image again"
2513 * @access private
2514 */
2515 function file_exists_typo3temp_file($output, $orig = '') {
2516 if ($this->enable_typo3temp_db_tracking) {
2517 // If file exists, then we return immediately
2518 if (file_exists($output)) {
2519 return 1;
2520 } else { // If not, we look up in the cache_typo3temp_log table to see if there is a image being rendered right now.
2521 $md5Hash = md5($output);
2522 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
2523 'md5hash',
2524 'cache_typo3temp_log',
2525 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5Hash, 'cache_typo3temp_log') . ' AND tstamp>' . ($GLOBALS['EXEC_TIME'] - 30)
2526 );
2527 // If there was a record, the image is being generated by another proces (we assume)
2528 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
2529 $GLOBALS['TYPO3_DB']->sql_free_result($res);
2530 if (is_object($GLOBALS['TSFE'])) {
2531 $GLOBALS['TSFE']->set_no_cache();
2532 } // ...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...)
2533 if (is_object($GLOBALS['TT'])) {
2534 $GLOBALS['TT']->setTSlogMessage('typo3temp_log: Assume this file is being rendered now: ' . $output);
2535 }
2536 // Return 'success - 2'
2537 return 2;
2538 } 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.
2539
2540 $insertFields = array(
2541 'md5hash' => $md5Hash,
2542 'tstamp' => $GLOBALS['EXEC_TIME'],
2543 'filename' => $output,
2544 'orig_filename' => $orig
2545 );
2546 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_typo3temp_log', 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5Hash, 'cache_typo3temp_log'));
2547 $GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_typo3temp_log', $insertFields);
2548
2549 if (is_object($GLOBALS['TT'])) {
2550 $GLOBALS['TT']->setTSlogMessage('typo3temp_log: The row did not exist, so a new is written and file is being processed: ' . $output);
2551 }
2552 return 0;
2553 }
2554 }
2555 } else {
2556 return file_exists($output);
2557 }
2558 }
2559
2560 /***********************************
2561 *
2562 * ImageMagick API functions
2563 *
2564 ***********************************/
2565
2566 /**
2567 * Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
2568 * Using ImageMagick
2569 *
2570 * @param string $imagefile The relative (to PATH_site) image filepath
2571 * @return array
2572 */
2573 function imageMagickIdentify($imagefile) {
2574 if (!$this->NO_IMAGE_MAGICK) {
2575 $frame = $this->noFramePrepended ? '' : '[0]';
2576 $cmd = t3lib_div::imageMagickCommand('identify', $this->wrapFileName($imagefile) . $frame);
2577 $returnVal = array();
2578 t3lib_utility_Command::exec($cmd, $returnVal);
2579 $splitstring = $returnVal[0];
2580 $this->IM_commands[] = array('identify', $cmd, $returnVal[0]);
2581 if ($splitstring) {
2582 preg_match('/([^\.]*)$/', $imagefile, $reg);
2583 $splitinfo = explode(' ', $splitstring);
2584 foreach ($splitinfo as $key => $val) {
2585 $temp = '';
2586 if ($val) {
2587 $temp = explode('x', $val);
2588 }
2589 if (intval($temp[0]) && intval($temp[1])) {
2590 $dim = $temp;
2591 break;
2592 }
2593 }
2594 if ($dim[0] && $dim[1]) {
2595 return array($dim[0], $dim[1], strtolower($reg[0]), $imagefile);
2596 }
2597 }
2598 }
2599 }
2600
2601 /**
2602 * Executes a ImageMagick "convert" on two filenames, $input and $output using $params before them.
2603 * Can be used for many things, mostly scaling and effects.
2604 *
2605 * @param string $input The relative (to PATH_site) image filepath, input file (read from)
2606 * @param string $output The relative (to PATH_site) image filepath, output filename (written to)
2607 * @param string $params ImageMagick parameters
2608 * @param integer $frame Optional, refers to which frame-number to select in the image. '' or 0
2609 * will select the first frame, 1 will select the next and so on...
2610 * @return string The result of a call to PHP function "exec()"
2611 */
2612 function imageMagickExec($input, $output, $params, $frame = 0) {
2613 if (!$this->NO_IMAGE_MAGICK) {
2614
2615 // Unless noFramePrepended is set in the Install Tool, a frame number is added to
2616 // select a specific page of the image (by default this will be the first page)
2617 if (!$this->noFramePrepended) {
2618 $frame = '[' . intval($frame) . ']';
2619 } else {
2620 $frame = '';
2621 }
2622
2623 $cmd = t3lib_div::imageMagickCommand('convert', $params . ' ' . $this->wrapFileName($input) . $frame . ' ' . $this->wrapFileName($output));
2624 $this->IM_commands[] = array($output, $cmd);
2625
2626 $ret = t3lib_utility_Command::exec($cmd);
2627 // Change the permissions of the file
2628 t3lib_div::fixPermissions($output);
2629
2630 return $ret;
2631 }
2632 }
2633
2634 /**
2635 * 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)
2636 * Can be used for many things, mostly scaling and effects.
2637 *
2638 * @param string $input The relative (to PATH_site) image filepath, bottom file
2639 * @param string $overlay The relative (to PATH_site) image filepath, overlay file (top)
2640 * @param string $mask The relative (to PATH_site) image filepath, the mask file (grayscale)
2641 * @param string $output The relative (to PATH_site) image filepath, output filename (written to)
2642 * @param boolean $handleNegation
2643 * @return void
2644 */
2645 function combineExec($input, $overlay, $mask, $output, $handleNegation = FALSE) {
2646 if (!$this->NO_IMAGE_MAGICK) {
2647 $params = '-colorspace GRAY +matte';
2648 if ($handleNegation) {
2649 if ($this->maskNegate) {
2650 $params .= ' ' . $this->maskNegate;
2651 }
2652 }
2653 $theMask = $this->randomName() . '.' . $this->gifExtension;
2654 $this->imageMagickExec($mask, $theMask, $params);
2655 $cmd = t3lib_div::imageMagickCommand('combine', '-compose over +matte ' . $this->wrapFileName($input) . ' ' . $this->wrapFileName($overlay) . ' ' . $this->wrapFileName($theMask) . ' ' . $this->wrapFileName($output)); // +matte = no alpha layer in output
2656 $this->IM_commands[] = array($output, $cmd);
2657
2658 $ret = t3lib_utility_Command::exec($cmd);
2659 // Change the permissions of the file
2660 t3lib_div::fixPermissions($output);
2661
2662 if (is_file($theMask)) {
2663 @unlink($theMask);
2664 }
2665
2666 return $ret;
2667 }
2668 }
2669
2670 /**
2671 * Escapes a file name so it can safely be used on the command line.
2672 *
2673 * @param string $inputName filename to safeguard, must not be empty
2674 *
2675 * @return string $inputName escaped as needed
2676 */
2677 protected function wrapFileName($inputName) {
2678 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
2679 $currentLocale = setlocale(LC_CTYPE, 0);
2680 setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
2681 }
2682 $escapedInputName = escapeshellarg($inputName);
2683 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
2684 setlocale(LC_CTYPE, $currentLocale);
2685 }
2686 return $escapedInputName;
2687 }
2688
2689 /***********************************
2690 *
2691 * Various IO functions
2692 *
2693 ***********************************/
2694
2695 /**
2696 * Returns TRUE if the input file existed
2697 *
2698 * @param string $file Input file to check
2699 * @return string Returns the filename if the file existed, otherwise empty.
2700 */
2701 function checkFile($file) {
2702 if (@is_file($file)) {
2703 return $file;
2704 } else {
2705 return '';
2706 }
2707 }
2708
2709 /**
2710 * Creates subdirectory in typo3temp/ if not already found.
2711 *
2712 * @param string $dirName Name of sub directory
2713 * @return boolean Result of t3lib_div::mkdir(), TRUE if it went well.
2714 */
2715 function createTempSubDir($dirName) {
2716
2717 // Checking if the this->tempPath is already prefixed with PATH_site and if not, prefix it with that constant.
2718 if (t3lib_div::isFirstPartOfStr($this->tempPath, PATH_site)) {
2719 $tmpPath = $this->tempPath;
2720 } else {
2721 $tmpPath = PATH_site . $this->tempPath;
2722 }
2723
2724 // Making the temporary filename:
2725 if (!@is_dir($tmpPath . $dirName)) {
2726 return t3lib_div::mkdir($tmpPath . $dirName);
2727 }
2728 }
2729
2730 /**
2731 * 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.
2732 *
2733 * @param pointer $im The image pointer (reference)
2734 * @param string $command The ImageMagick parameters. Like effects, scaling etc.
2735 * @return void
2736 */
2737 function applyImageMagickToPHPGif(&$im, $command) {
2738 $tmpStr = $this->randomName();
2739 $theFile = $tmpStr . '.' . $this->gifExtension;
2740 $this->ImageWrite($im, $theFile);
2741 $this->imageMagickExec($theFile, $theFile, $command);
2742 $tmpImg = $this->imageCreateFromFile($theFile);
2743 if ($tmpImg) {
2744 ImageDestroy($im);
2745 $im = $tmpImg;
2746 $this->w = imagesx($im);
2747 $this->h = imagesy($im);
2748 }
2749 if (!$this->dontUnlinkTempFiles) {
2750 unlink($theFile);
2751 }
2752 }
2753
2754 /**
2755 * 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.
2756 * For example: If the number of pixels exceeds $this->pixelLimitGif (normally 10000) then it will be a "jpg" string in return.
2757 *
2758 * @param string $type The file extension, lowercase.
2759 * @param integer $w The width of the output image.
2760 * @param integer $h The height of the output image.
2761 * @return string The filename, either "jpg" or "gif"/"png" (whatever $this->gifExtension is set to.)
2762 */
2763 function gif_or_jpg($type, $w, $h) {
2764 if ($type == 'ai' || $w * $h < $this->pixelLimitGif) {
2765 return $this->gifExtension;
2766 } else {
2767 return 'jpg';
2768 }
2769 }
2770
2771 /**
2772 * Writing the internal image pointer, $this->im, to file based on the extension of the input filename
2773 * Used in GIFBUILDER
2774 * Uses $this->setup['reduceColors'] for gif/png images and $this->setup['quality'] for jpg images to reduce size/quality if needed.
2775 *
2776 * @param string $file The filename to write to.
2777 * @return string Returns input filename
2778 * @see tslib_gifBuilder::gifBuild()
2779 */
2780 function output($file) {
2781 if ($file) {
2782 $reg = array();
2783 preg_match('/([^\.]*)$/', $file, $reg);
2784 $ext = strtolower($reg[0]);
2785 switch ($ext) {
2786 case 'gif':
2787 case 'png':
2788 if ($this->ImageWrite($this->im, $file)) {
2789 // ImageMagick operations
2790 if ($this->setup['reduceColors'] || !$this->png_truecolor) {
2791 $reduced = $this->IMreduceColors($file, t3lib_utility_Math::forceIntegerInRange($this->setup['reduceColors'], 256, $this->truecolorColors, 256));
2792 if ($reduced) {
2793 @copy($reduced, $file);
2794 @unlink($reduced);
2795 }
2796 }
2797 // Compress with IM! (adds extra compression, LZW from ImageMagick)
2798 // (Workaround for the absence of lzw-compression in GD)
2799 t3lib_div::gif_compress($file, 'IM');
2800 }
2801 break;
2802 case 'jpg':
2803 case 'jpeg':
2804 // Use the default
2805 $quality = 0;
2806 if ($this->setup['quality']) {
2807 $quality = t3lib_utility_Math::forceIntegerInRange($this->setup['quality'], 10, 100);
2808 }
2809 if ($this->ImageWrite($this->im, $file, $quality)) {
2810 }
2811 break;
2812 }
2813 $GLOBALS['TEMP_IMAGES_ON_PAGE'][] = $file;
2814 }
2815 return $file;
2816 }
2817
2818 /**
2819 * Destroy internal image pointer, $this->im
2820 *
2821 * @return void
2822 * @see tslib_gifBuilder::gifBuild()
2823 */
2824 function destroy() {
2825 ImageDestroy($this->im);
2826 }
2827
2828 /**
2829 * Returns Image Tag for input image information array.
2830 *
2831 * @param array $imgInfo Image information array, key 0/1 is width/height and key 3 is the src value
2832 * @return string Image tag for the input image information array.
2833 */
2834 function imgTag($imgInfo) {
2835 return '<img src="' . $imgInfo[3] . '" width="' . $imgInfo[0] . '" height="' . $imgInfo[1] . '" border="0" alt="" />';
2836 }
2837
2838 /**
2839 * Writes the input GDlib image pointer to file
2840 *
2841 * @param pointer $destImg The GDlib image resource pointer
2842 * @param string $theImage The filename to write to
2843 * @param integer $quality The image quality (for JPEGs)
2844 * @return boolean The output of either imageGif, imagePng or imageJpeg based on the filename to write
2845 * @see maskImageOntoImage(), scale(), output()
2846 */
2847 function ImageWrite($destImg, $theImage, $quality = 0) {
2848 imageinterlace($destImg, 0);
2849 $ext = strtolower(substr($theImage, strrpos($theImage, '.') + 1));
2850 $result = FALSE;
2851 switch ($ext) {
2852 case 'jpg':
2853 case 'jpeg':
2854 if (function_exists('imageJpeg')) {
2855 if ($quality == 0) {
2856 $quality = $this->jpegQuality;
2857 }
2858 $result = imageJpeg($destImg, $theImage, $quality);
2859 }
2860 break;
2861 case 'gif':
2862 if (function_exists('imageGif')) {
2863 imagetruecolortopalette($destImg, TRUE, 256);
2864 $result = imageGif($destImg, $theImage);
2865 }
2866 break;
2867 case 'png':
2868 if (function_exists('imagePng')) {
2869 $result = ImagePng($destImg, $theImage);
2870 }
2871 break;
2872 }
2873 if ($result) {
2874 t3lib_div::fixPermissions($theImage);
2875 }
2876 return $result;
2877 }
2878
2879 /**
2880 * Creates a new GDlib image resource based on the input image filename.
2881 * 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.
2882 *
2883 * @param string $sourceImg Image filename
2884 * @return pointer Image Resource pointer
2885 */
2886 function imageCreateFromFile($sourceImg) {
2887 $imgInf = pathinfo($sourceImg);
2888 $ext = strtolower($imgInf['extension']);
2889
2890 switch ($ext) {
2891 case 'gif':
2892 if (function_exists('imagecreatefromgif')) {
2893 return imageCreateFromGif($sourceImg);
2894 }
2895 break;
2896 case 'png':
2897 if (function_exists('imagecreatefrompng')) {
2898 $imageHandle = imageCreateFromPng($sourceImg);
2899 if ($this->saveAlphaLayer) {
2900 imagesavealpha($imageHandle, TRUE);
2901 }
2902 return $imageHandle;
2903 }
2904 break;
2905 case 'jpg':
2906 case 'jpeg':
2907 if (function_exists('imagecreatefromjpeg')) {
2908 return imageCreateFromJpeg($sourceImg);
2909 }
2910 break;
2911 }
2912
2913 // If non of the above:
2914 $i = @getimagesize($sourceImg);
2915 $im = imagecreatetruecolor($i[0], $i[1]);
2916 $Bcolor = ImageColorAllocate($im, 128, 128, 128);
2917 ImageFilledRectang