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