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