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