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