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