Fixed bug #17663: Remove deprecated reduceColors() from t3lib_stdGraphic
[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 * $Id$
31 * Revised for TYPO3 3.6 July/2003 by Kasper Skårhøj
32 *
33 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
34 */
35 /**
36 * [CLASS/FUNCTION INDEX of SCRIPT]
37 *
38 *
39 *
40 * 155: class t3lib_stdGraphic
41 * 236: function init()
42 *
43 * SECTION: Layering images / "IMAGE" GIFBUILDER object
44 * 366: function maskImageOntoImage(&$im,$conf,$workArea)
45 * 436: function copyImageOntoImage(&$im,$conf,$workArea)
46 * 458: function copyGifOntoGif(&$im,$cpImg,$conf,$workArea)
47 * 537: function imagecopyresized(&$im, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h)
48 *
49 * SECTION: Text / "TEXT" GIFBUILDER object
50 * 587: function makeText(&$im,$conf,$workArea)
51 * 707: function txtPosition($conf,$workArea,$BB)
52 * 761: function calcBBox($conf)
53 * 820: function addToMap($cords,$conf)
54 * 843: function calcTextCordsForMap($cords,$offset, $conf)
55 * 878: function SpacedImageTTFText(&$im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $text, $spacing, $wordSpacing, $splitRenderingConf, $sF=1)
56 * 915: function fontResize($conf)
57 * 958: function ImageTTFBBoxWrapper($fontSize, $angle, $fontFile, $string, $splitRendering, $sF=1)
58 * 1005: function ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering,$sF=1)
59 * 1058: function splitString($string,$splitRendering,$fontSize,$fontFile)
60 * 1208: function calcWordSpacing($conf, $scaleFactor=1)
61 * 1227: function getTextScalFactor($conf)
62 *
63 * SECTION: Other GIFBUILDER objects related to TEXT
64 * 1262: function makeOutline(&$im,$conf,$workArea,$txtConf)
65 * 1291: function circleOffset($distance, $iterations)
66 * 1315: function makeEmboss(&$im,$conf,$workArea,$txtConf)
67 * 1337: function makeShadow(&$im,$conf,$workArea,$txtConf)
68 *
69 * SECTION: Other GIFBUILDER objects
70 * 1469: function makeBox(&$im,$conf,$workArea)
71 * 1491: function makeEffect(&$im, $conf)
72 * 1506: function IMparams($setup)
73 * 1589: function adjust(&$im, $conf)
74 * 1621: function crop(&$im,$conf)
75 * 1652: function scale(&$im,$conf)
76 * 1684: function setWorkArea($workArea)
77 *
78 * SECTION: Adjustment functions
79 * 1725: function autolevels(&$im)
80 * 1756: function outputLevels(&$im,$low,$high,$swap='')
81 * 1788: function inputLevels(&$im,$low,$high,$swap='')
82 * 1819: function reduceColors(&$im,$limit, $cols)
83 * 1832: function IMreduceColors($file, $cols)
84 *
85 * SECTION: GIFBUILDER Helper functions
86 * 1875: function prependAbsolutePath($fontFile)
87 * 1889: function v5_sharpen($factor)
88 * 1908: function v5_blur($factor)
89 * 1925: function randomName()
90 * 1938: function applyOffset($cords,$OFFSET)
91 * 1951: function convertColor($string)
92 * 2001: function recodeString($string)
93 * 2023: function singleChars($theText,$returnUnicodeNumber=FALSE)
94 * 2046: function objPosition($conf,$workArea,$BB)
95 *
96 * SECTION: Scaling, Dimensions of images
97 * 2125: function imageMagickConvert($imagefile,$newExt='',$w='',$h='',$params='',$frame='',$options='',$mustCreate=0)
98 * 2238: function getImageDimensions($imageFile)
99 * 2266: function cacheImageDimensions($identifyResult)
100 * 2298: function getCachedImageDimensions($imageFile)
101 * 2332: function getImageScale($info,$w,$h,$options)
102 * 2438: function file_exists_typo3temp_file($output,$orig='')
103 *
104 * SECTION: ImageMagick API functions
105 * 2499: function imageMagickIdentify($imagefile)
106 * 2534: function imageMagickExec($input,$output,$params)
107 * 2557: function combineExec($input,$overlay,$mask,$output, $handleNegation = false)
108 * 2588: function wrapFileName($inputName)
109 *
110 * SECTION: Various IO functions
111 * 2629: function checkFile($file)
112 * 2643: function createTempSubDir($dirName)
113 * 2665: function applyImageMagickToPHPGif(&$im, $command)
114 * 2691: function gif_or_jpg($type,$w,$h)
115 * 2708: function output($file)
116 * 2748: function destroy()
117 * 2758: function imgTag ($imgInfo)
118 * 2770: function ImageWrite($destImg, $theImage)
119 * 2808: function imageGif($destImg, $theImage)
120 * 2820: function imageCreateFromGif($sourceImg)
121 * 2831: function imageCreateFromFile($sourceImg)
122 * 2870: function imagecreate($w, $h)
123 * 2885: function hexColor($col)
124 * 2903: function unifyColors(&$img, $colArr, $closest = false)
125 *
126 * TOTAL FUNCTIONS: 66
127 * (This index is automatically created/updated by the extension "extdeveval")
128 *
129 */
130
131
132 /**
133 * Class contains a bunch of cool functions for manipulating graphics with GDlib/Freetype and ImageMagick
134 * VERY OFTEN used with gifbuilder that extends this class and provides a TypoScript API to using these functions
135 *
136 * With TYPO3 4.4 GDlib 1.x support was dropped, also an option from config_default.php:
137 * $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.
138 *
139 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
140 * @package TYPO3
141 * @subpackage t3lib
142 * @see tslib_gifBuilder
143 */
144 class t3lib_stdGraphic {
145
146 // Internal configuration, set in init()
147 var $combineScript = 'combine'; // The ImageMagick filename used for combining two images. This name changed during the versions.
148 var $noFramePrepended = 0; // If set, there is no frame pointer prepended to the filenames.
149 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())
150 var $imagecopyresized_fix = 0; // If set, imagecopyresized will not be called directly. For GD2 (some PHP installs?)
151 var $gifExtension = 'gif'; // This should be changed to 'png' if you want this class to read/make PNG-files instead!
152 var $gdlibExtensions = ''; // File formats supported by gdlib. This variable get's filled in "init" method
153 var $png_truecolor = FALSE; // Set to true if generated png's should be truecolor by default
154 var $truecolorColors = 0xffffff; // 16777216 Colors is the maximum value for PNG, JPEG truecolor images (24-bit, 8-bit / Channel)
155 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.
156 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!
157 var $webImageExt = 'gif,jpg,jpeg,png'; // Commalist of web image extensions (can be shown by a webbrowser)
158 var $maskNegate = ''; // Will be ' -negate' if ImageMagick ver 5.2+. See init();
159 var $NO_IM_EFFECTS = '';
160 var $cmds = array(
161 'jpg' => '',
162 'jpeg' => '',
163 'gif' => '',
164 'png' => '-colors 64'
165 );
166 var $NO_IMAGE_MAGICK = '';
167 var $V5_EFFECTS = 0;
168 var $im_version_4 = 0;
169 var $mayScaleUp = 1;
170
171 // Variables for testing, alternative usage etc.
172 var $filenamePrefix = ''; // Filename prefix for images scaled in imageMagickConvert()
173 var $imageMagickConvert_forceFileNameBody = ''; // Forcing the output filename of imageMagickConvert() to this value. However after calling imageMagickConvert() it will be set blank again.
174 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.
175 var $dontCompress = 0; // Prevents imageMagickConvert() from compressing the gif-files with t3lib_div::gif_compress()
176 var $dontUnlinkTempFiles = 0; // For debugging ONLY!
177 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...
178
179 // Internal:
180 var $IM_commands = array(); // All ImageMagick commands executed is stored in this array for tracking. Used by the Install Tools Image section
181 var $workArea = array();
182
183 // Constants:
184 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.
185 var $absPrefix = ''; // Prefix for relative paths. Used in "show_item.php" script. Is prefixed the output file name IN imageMagickConvert()
186 var $scalecmd = '-geometry'; // ImageMagick scaling command; "-geometry" eller "-sample". Used in makeText() and imageMagickConvert()
187 var $im5fx_blurSteps = '1x2,2x2,3x2,4x3,5x3,5x4,6x4,7x5,8x5,9x5'; // Used by v5_blur() to simulate 10 continuous steps of blurring
188 var $im5fx_sharpenSteps = '1x2,2x2,3x2,2x3,3x3,4x3,3x4,4x4,4x5,5x5'; // Used by v5_sharpen() to simulate 10 continuous steps of sharpening.
189 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
190 var $colMap = array( // Array mapping HTML color names to RGB values.
191 'aqua' => array(0, 255, 255),
192 'black' => array(0, 0, 0),
193 'blue' => array(0, 0, 255),
194 'fuchsia' => array(255, 0, 255),
195 'gray' => array(128, 128, 128),
196 'green' => array(0, 128, 0),
197 'lime' => array(0, 255, 0),
198 'maroon' => array(128, 0, 0),
199 'navy' => array(0, 0, 128),
200 'olive' => array(128, 128, 0),
201 'purple' => array(128, 0, 128),
202 'red' => array(255, 0, 0),
203 'silver' => array(192, 192, 192),
204 'teal' => array(0, 128, 128),
205 'yellow' => array(255, 255, 0),
206 'white' => array(255, 255, 255)
207 );
208
209 /**
210 * Charset conversion object:
211 *
212 * @var t3lib_cs
213 */
214 var $csConvObj;
215 var $nativeCharset = ''; // Is set to the native character set of the input strings.
216
217
218 /**
219 * Init function. Must always call this when using the class.
220 * This function will read the configuration information from $GLOBALS['TYPO3_CONF_VARS']['GFX'] can set some values in internal variables.
221 *
222 * @return void
223 */
224 function init() {
225 $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX'];
226
227 if (function_exists('imagecreatefromjpeg') && function_exists('imagejpeg')) {
228 $this->gdlibExtensions .= ',jpg,jpeg';
229 }
230 if (function_exists('imagecreatefrompng') && function_exists('imagepng')) {
231 $this->gdlibExtensions .= ',png';
232 }
233 if (function_exists('imagecreatefromgif') && function_exists('imagegif')) {
234 $this->gdlibExtensions .= ',gif';
235 }
236 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['png_truecolor']) {
237 $this->png_truecolor = true;
238 }
239 if (!$gfxConf['im_version_5']) {
240 t3lib_div::deprecationLog('The option $TYPO3_CONF_VARS[\'GFX\'][\'im_version_5\'] is not set, ImageMagic 4 is assumed. This is deprecated since TYPO3 4.5, support will be removed in TYPO3 4.6. Make sure to upgrade to ImageMagick version 6 or GraphichsMagick.');
241 $this->im_version_4 = true;
242 } elseif ($gfxConf['im_version_5'] === 'im5') {
243 t3lib_div::deprecationLog('The option $TYPO3_CONF_VARS[\'GFX\'][\'im_version_5\'] is set to \'im5\'. This is deprecated since TYPO3 4.5, support will be removed in TYPO3 4.6. Make sure to upgrade to ImageMagick version 6 or GraphichsMagick.');
244 }
245
246 // When GIFBUILDER gets used in truecolor mode
247 // No colors parameter if we generate truecolor images.
248 if ($this->png_truecolor) {
249 $this->cmds['png'] = '';
250 }
251
252 // Setting default JPG parameters:
253 $this->jpegQuality = t3lib_div::intInRange($gfxConf['jpg_quality'], 10, 100, 75);
254 $this->cmds['jpg'] = $this->cmds['jpeg'] = '-colorspace RGB -sharpen 50 -quality ' . $this->jpegQuality;
255
256 if ($gfxConf['im_combine_filename']) {
257 $this->combineScript = $gfxConf['im_combine_filename'];
258 }
259 if ($gfxConf['im_noFramePrepended']) {
260 $this->noFramePrepended = 1;
261 }
262
263 // kept for backwards compatibility, can be turned on manually through localconf.php,
264 // but not through the installer anymore
265 $this->imagecopyresized_fix = ($gfxConf['gdlib_2'] === 'no_imagecopyresized_fix' ? 0 : 1);
266
267 if ($gfxConf['gdlib_png']) {
268 $this->gifExtension = 'png';
269 }
270 if ($gfxConf['enable_typo3temp_db_tracking']) {
271 $this->enable_typo3temp_db_tracking = $gfxConf['enable_typo3temp_db_tracking'];
272 }
273
274 $this->imageFileExt = $gfxConf['imagefile_ext'];
275
276 // This should be set if ImageMagick ver. 5+ is used.
277 if ($gfxConf['im_negate_mask']) {
278 // Boolean. Indicates if the mask images should be inverted first.
279 // This depends of the ImageMagick version. Below ver. 5.1 this should be false.
280 // Above ImageMagick version 5.2+ it should be true.
281 // Just set the flag if the masks works opposite the intension!
282 $this->maskNegate = ' -negate';
283 }
284 if ($gfxConf['im_no_effects']) {
285 // Boolean. This is necessary if using ImageMagick 5+.
286 // Effects in Imagemagick 5+ tends to render very slowly!!
287 // - therefore must be disabled in order not to perform sharpen, blurring and such.
288 $this->NO_IM_EFFECTS = 1;
289
290 $this->cmds['jpg'] = $this->cmds['jpeg'] = '-colorspace RGB -quality ' . $this->jpegQuality;
291 }
292 // ... but if 'im_v5effects' is set, don't care about 'im_no_effects'
293 if ($gfxConf['im_v5effects']) {
294 $this->NO_IM_EFFECTS = 0;
295 $this->V5_EFFECTS = 1;
296
297 if ($gfxConf['im_v5effects'] > 0) {
298 $this->cmds['jpg'] = $this->cmds['jpeg'] = '-colorspace RGB -quality ' . intval($gfxConf['jpg_quality']) . $this->v5_sharpen(10);
299 }
300 }
301
302 if (!$gfxConf['im']) {
303 $this->NO_IMAGE_MAGICK = 1;
304 }
305 // Secures that images are not scaled up.
306 if ($gfxConf['im_noScaleUp']) {
307 $this->mayScaleUp = 0;
308 }
309
310 if (TYPO3_MODE == 'FE') {
311 $this->csConvObj = $GLOBALS['TSFE']->csConvObj;
312 } elseif (is_object($GLOBALS['LANG'])) { // BE assumed:
313 $this->csConvObj = $GLOBALS['LANG']->csConvObj;
314 } else { // The object may not exist yet, so we need to create it now. Happens in the Install Tool for example.
315 $this->csConvObj = t3lib_div::makeInstance('t3lib_cs');
316 }
317 $this->nativeCharset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'];
318 }
319
320
321 /*************************************************
322 *
323 * Layering images / "IMAGE" GIFBUILDER object
324 *
325 *************************************************/
326
327 /**
328 * Implements the "IMAGE" GIFBUILDER object, when the "mask" property is true.
329 * 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
330 * The operation involves ImageMagick for combining.
331 *
332 * @param pointer GDlib image pointer
333 * @param array TypoScript array with configuration for the GIFBUILDER object.
334 * @param array The current working area coordinates.
335 * @return void
336 * @see tslib_gifBuilder::make()
337 */
338 function maskImageOntoImage(&$im, $conf, $workArea) {
339 if ($conf['file'] && $conf['mask']) {
340 $imgInf = pathinfo($conf['file']);
341 $imgExt = strtolower($imgInf['extension']);
342 if (!t3lib_div::inList($this->gdlibExtensions, $imgExt)) {
343 $BBimage = $this->imageMagickConvert($conf['file'], $this->gifExtension, '', '', '', '', '');
344 } else {
345 $BBimage = $this->getImageDimensions($conf['file']);
346 }
347 $maskInf = pathinfo($conf['mask']);
348 $maskExt = strtolower($maskInf['extension']);
349 if (!t3lib_div::inList($this->gdlibExtensions, $maskExt)) {
350 $BBmask = $this->imageMagickConvert($conf['mask'], $this->gifExtension, '', '', '', '', '');
351 } else {
352 $BBmask = $this->getImageDimensions($conf['mask']);
353 }
354 if ($BBimage && $BBmask) {
355 $w = imagesx($im);
356 $h = imagesy($im);
357 $tmpStr = $this->randomName();
358 $theImage = $tmpStr . '_img.' . $this->gifExtension;
359 $theDest = $tmpStr . '_dest.' . $this->gifExtension;
360 $theMask = $tmpStr . '_mask.' . $this->gifExtension;
361 // prepare overlay image
362 $cpImg = $this->imageCreateFromFile($BBimage[3]);
363 $destImg = imagecreatetruecolor($w, $h);
364 $Bcolor = ImageColorAllocate($destImg, 0, 0, 0);
365 ImageFilledRectangle($destImg, 0, 0, $w, $h, $Bcolor);
366 $this->copyGifOntoGif($destImg, $cpImg, $conf, $workArea);
367 $this->ImageWrite($destImg, $theImage);
368 imageDestroy($cpImg);
369 imageDestroy($destImg);
370 // prepare mask image
371 $cpImg = $this->imageCreateFromFile($BBmask[3]);
372 $destImg = imagecreatetruecolor($w, $h);
373 $Bcolor = ImageColorAllocate($destImg, 0, 0, 0);
374 ImageFilledRectangle($destImg, 0, 0, $w, $h, $Bcolor);
375 $this->copyGifOntoGif($destImg, $cpImg, $conf, $workArea);
376 $this->ImageWrite($destImg, $theMask);
377 imageDestroy($cpImg);
378 imageDestroy($destImg);
379 // mask the images
380 $this->ImageWrite($im, $theDest);
381
382 $this->combineExec($theDest, $theImage, $theMask, $theDest, TRUE); // Let combineExec handle maskNegation
383
384 $backIm = $this->imageCreateFromFile($theDest); // The main image is loaded again...
385 if ($backIm) { // ... and if nothing went wrong we load it onto the old one.
386 ImageColorTransparent($backIm, -1);
387 $im = $backIm;
388 }
389 // unlink files from process
390 if (!$this->dontUnlinkTempFiles) {
391 unlink($theDest);
392 unlink($theImage);
393 unlink($theMask);
394 }
395 }
396 }
397 }
398
399 /**
400 * Implements the "IMAGE" GIFBUILDER object, when the "mask" property is false (using only $conf['file'])
401 *
402 * @param pointer GDlib image pointer
403 * @param array TypoScript array with configuration for the GIFBUILDER object.
404 * @param array The current working area coordinates.
405 * @return void
406 * @see tslib_gifBuilder::make(), maskImageOntoImage()
407 */
408 function copyImageOntoImage(&$im, $conf, $workArea) {
409 if ($conf['file']) {
410 if (!t3lib_div::inList($this->gdlibExtensions, $conf['BBOX'][2])) {
411 $conf['BBOX'] = $this->imageMagickConvert($conf['BBOX'][3], $this->gifExtension, '', '', '', '', '');
412 $conf['file'] = $conf['BBOX'][3];
413 }
414 $cpImg = $this->imageCreateFromFile($conf['file']);
415 $this->copyGifOntoGif($im, $cpImg, $conf, $workArea);
416 imageDestroy($cpImg);
417 }
418 }
419
420 /**
421 * Copies two GDlib image pointers onto each other, using TypoScript configuration from $conf and the input $workArea definition.
422 *
423 * @param pointer GDlib image pointer, destination (bottom image)
424 * @param pointer GDlib image pointer, source (top image)
425 * @param array TypoScript array with the properties for the IMAGE GIFBUILDER object. Only used for the "tile" property value.
426 * @param array Work area
427 * @return void Works on the $im image pointer
428 * @access private
429 */
430 function copyGifOntoGif(&$im, $cpImg, $conf, $workArea) {
431 $cpW = imagesx($cpImg);
432 $cpH = imagesy($cpImg);
433 $tile = t3lib_div::intExplode(',', $conf['tile']);
434 $tile[0] = t3lib_div::intInRange($tile[0], 1, 20);
435 $tile[1] = t3lib_div::intInRange($tile[1], 1, 20);
436 $cpOff = $this->objPosition($conf, $workArea, array($cpW * $tile[0], $cpH * $tile[1]));
437
438 for ($xt = 0; $xt < $tile[0]; $xt++) {
439 $Xstart = $cpOff[0] + $cpW * $xt;
440 if ($Xstart + $cpW > $workArea[0]) { // if this image is inside of the workArea, then go on
441 // X:
442 if ($Xstart < $workArea[0]) {
443 $cpImgCutX = $workArea[0] - $Xstart;
444 $Xstart = $workArea[0];
445 } else {
446 $cpImgCutX = 0;
447 }
448 $w = $cpW - $cpImgCutX;
449 if ($Xstart > $workArea[0] + $workArea[2] - $w) {
450 $w = $workArea[0] + $workArea[2] - $Xstart;
451 }
452 if ($Xstart < $workArea[0] + $workArea[2]) { // if this image is inside of the workArea, then go on
453 // Y:
454 for ($yt = 0; $yt < $tile[1]; $yt++) {
455 $Ystart = $cpOff[1] + $cpH * $yt;
456 if ($Ystart + $cpH > $workArea[1]) { // if this image is inside of the workArea, then go on
457 if ($Ystart < $workArea[1]) {
458 $cpImgCutY = $workArea[1] - $Ystart;
459 $Ystart = $workArea[1];
460 } else {
461 $cpImgCutY = 0;
462 }
463 $h = $cpH - $cpImgCutY;
464 if ($Ystart > $workArea[1] + $workArea[3] - $h) {
465 $h = $workArea[1] + $workArea[3] - $Ystart;
466 }
467 if ($Ystart < $workArea[1] + $workArea[3]) { // if this image is inside of the workArea, then go on
468 $this->imagecopyresized($im, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h);
469 }
470 }
471 } // Y:
472 }
473 }
474 }
475 }
476
477 /**
478 * Alternative function for using the similar PHP function imagecopyresized(). Used for GD2 only.
479 *
480 * OK, the reason for this stupid fix is the following story:
481 * GD1.x was capable of copying two images together and combining their palettes! GD2 is apparently not.
482 * With GD2 only the palette of the dest-image is used which mostly results in totally black images when trying to
483 * copy a color-ful image onto the destination.
484 * The GD2-fix is to
485 * 1) Create a blank TRUE-COLOR image
486 * 2) Copy the destination image onto that one
487 * 3) Then do the actual operation; Copying the source (top image) onto that
488 * 4) ... and return the result pointer.
489 * 5) Reduce colors (if we do not, the result may become strange!)
490 * It works, but the resulting images is now a true-color PNG which may be very large.
491 * So, why not use 'imagetruecolortopalette ($im, TRUE, 256)' - well because it does NOT WORK! So simple is that.
492 *
493 * For parameters, see PHP function "imagecopyresized()"
494 *
495 * @param pointer see PHP function "imagecopyresized()"
496 * @param pointer 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 * @param integer see PHP function "imagecopyresized()"
505 * @return void
506 * @access private
507 * @see t3lib_iconWorks::imagecopyresized()
508 */
509 function imagecopyresized(&$im, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h) {
510 if ($this->imagecopyresized_fix) {
511 $im_base = imagecreatetruecolor(imagesx($im), imagesy($im)); // Make true color image
512 imagecopyresized($im_base, $im, 0, 0, 0, 0, imagesx($im), imagesy($im), imagesx($im), imagesy($im)); // Copy the source image onto that
513 imagecopyresized($im_base, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h); // Then copy the $cpImg onto that (the actual operation!)
514 $im = $im_base; // Set pointer
515 } else {
516 imagecopyresized($im, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h);
517 }
518 }
519
520
521 /********************************
522 *
523 * Text / "TEXT" GIFBUILDER object
524 *
525 ********************************/
526
527 /**
528 * Implements the "TEXT" GIFBUILDER object
529 *
530 * @param pointer GDlib image pointer
531 * @param array TypoScript array with configuration for the GIFBUILDER object.
532 * @param array The current working area coordinates.
533 * @return void
534 * @see tslib_gifBuilder::make()
535 */
536 function makeText(&$im, $conf, $workArea) {
537 // Spacing
538 list($spacing, $wordSpacing) = $this->calcWordSpacing($conf);
539 // Position
540 $txtPos = $this->txtPosition($conf, $workArea, $conf['BBOX']);
541 $theText = $this->recodeString($conf['text']);
542
543 if ($conf['imgMap'] && is_array($conf['imgMap.'])) {
544 $this->addToMap($this->calcTextCordsForMap($conf['BBOX'][2], $txtPos, $conf['imgMap.']), $conf['imgMap.']);
545 }
546 if (!$conf['hideButCreateMap']) {
547 // Font Color:
548 $cols = $this->convertColor($conf['fontColor']);
549 // NiceText is calculated
550 if (!$conf['niceText']) {
551 $Fcolor = ImageColorAllocate($im, $cols[0], $cols[1], $cols[2]);
552 // antiAliasing is setup:
553 $Fcolor = ($conf['antiAlias']) ? $Fcolor : -$Fcolor;
554
555 for ($a = 0; $a < $conf['iterations']; $a++) {
556 if ($spacing || $wordSpacing) { // If any kind of spacing applys, we use this function:
557 $this->SpacedImageTTFText($im, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, t3lib_stdGraphic::prependAbsolutePath($conf['fontFile']), $theText, $spacing, $wordSpacing, $conf['splitRendering.']);
558 } else {
559 $this->renderTTFText($im, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, $conf['fontFile'], $theText, $conf['splitRendering.'], $conf);
560 }
561 }
562 } else { // NICETEXT::
563 // options anti_aliased and iterations is NOT available when doing this!!
564 $w = imagesx($im);
565 $h = imagesy($im);
566 $tmpStr = $this->randomName();
567
568 $fileMenu = $tmpStr . '_menuNT.' . $this->gifExtension;
569 $fileColor = $tmpStr . '_colorNT.' . $this->gifExtension;
570 $fileMask = $tmpStr . '_maskNT.' . $this->gifExtension;
571 // Scalefactor
572 $sF = t3lib_div::intInRange($conf['niceText.']['scaleFactor'], 2, 5);
573 $newW = ceil($sF * imagesx($im));
574 $newH = ceil($sF * imagesy($im));
575
576 // Make mask
577 $maskImg = imagecreatetruecolor($newW, $newH);
578 $Bcolor = ImageColorAllocate($maskImg, 255, 255, 255);
579 ImageFilledRectangle($maskImg, 0, 0, $newW, $newH, $Bcolor);
580 $Fcolor = ImageColorAllocate($maskImg, 0, 0, 0);
581 if ($spacing || $wordSpacing) { // If any kind of spacing applys, we use this function:
582 $this->SpacedImageTTFText($maskImg, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, t3lib_stdGraphic::prependAbsolutePath($conf['fontFile']), $theText, $spacing, $wordSpacing, $conf['splitRendering.'], $sF);
583 } else {
584 $this->renderTTFText($maskImg, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, $conf['fontFile'], $theText, $conf['splitRendering.'], $conf, $sF);
585 }
586 $this->ImageWrite($maskImg, $fileMask);
587 ImageDestroy($maskImg);
588
589 // Downscales the mask
590 if ($this->NO_IM_EFFECTS) {
591 if ($this->maskNegate) {
592 $command = trim($this->scalecmd . ' ' . $w . 'x' . $h . '!'); // Negate 2 times makes no negate...
593 } else {
594 $command = trim($this->scalecmd . ' ' . $w . 'x' . $h . '! -negate');
595 }
596 } else {
597 if ($this->maskNegate) {
598 $command = trim($conf['niceText.']['before'] . ' ' . $this->scalecmd . ' ' . $w . 'x' . $h . '! ' . $conf['niceText.']['after']);
599 } else {
600 $command = trim($conf['niceText.']['before'] . ' ' . $this->scalecmd . ' ' . $w . 'x' . $h . '! ' . $conf['niceText.']['after'] . ' -negate');
601 }
602 if ($conf['niceText.']['sharpen']) {
603 if ($this->V5_EFFECTS) {
604 $command .= $this->v5_sharpen($conf['niceText.']['sharpen']);
605 } else {
606 $command .= ' -sharpen ' . t3lib_div::intInRange($conf['niceText.']['sharpen'], 1, 99);
607 }
608 }
609 }
610
611 $this->imageMagickExec($fileMask, $fileMask, $command);
612
613 // Make the color-file
614 $colorImg = imagecreatetruecolor($w, $h);
615 $Ccolor = ImageColorAllocate($colorImg, $cols[0], $cols[1], $cols[2]);
616 ImageFilledRectangle($colorImg, 0, 0, $w, $h, $Ccolor);
617 $this->ImageWrite($colorImg, $fileColor);
618 ImageDestroy($colorImg);
619
620 // The mask is applied
621 $this->ImageWrite($im, $fileMenu); // The main pictures is saved temporarily
622
623 $this->combineExec($fileMenu, $fileColor, $fileMask, $fileMenu);
624
625 $backIm = $this->imageCreateFromFile($fileMenu); // The main image is loaded again...
626 if ($backIm) { // ... and if nothing went wrong we load it onto the old one.
627 ImageColorTransparent($backIm, -1);
628 $im = $backIm;
629 }
630
631 // Deleting temporary files;
632 if (!$this->dontUnlinkTempFiles) {
633 unlink($fileMenu);
634 unlink($fileColor);
635 unlink($fileMask);
636 }
637 }
638 }
639 }
640
641 /**
642 * Calculates text position for printing the text onto the image based on configuration like alignment and workarea.
643 *
644 * @param array TypoScript array for the TEXT GIFBUILDER object
645 * @param array Workarea definition
646 * @param array Bounding box information, was set in tslib_gifBuilder::start()
647 * @return array [0]=x, [1]=y, [2]=w, [3]=h
648 * @access private
649 * @see makeText()
650 */
651 function txtPosition($conf, $workArea, $BB) {
652 $bbox = $BB[2];
653 $angle = intval($conf['angle']) / 180 * pi();
654 $conf['angle'] = 0;
655 $straightBB = $this->calcBBox($conf);
656
657 // offset, align, valign, workarea
658 $result = array(); // [0]=x, [1]=y, [2]=w, [3]=h
659 $result[2] = $BB[0];
660 $result[3] = $BB[1];
661 $w = $workArea[2];
662 $h = $workArea[3];
663
664 switch ($conf['align']) {
665 case 'right':
666 case 'center':
667 $factor = abs(cos($angle));
668 $sign = (cos($angle) < 0) ? -1 : 1;
669 $len1 = $sign * $factor * $straightBB[0];
670 $len2 = $sign * $BB[0];
671 $result[0] = $w - ceil($len2 * $factor + (1 - $factor) * $len1);
672
673 $factor = abs(sin($angle));
674 $sign = (sin($angle) < 0) ? -1 : 1;
675 $len1 = $sign * $factor * $straightBB[0];
676 $len2 = $sign * $BB[1];
677 $result[1] = ceil($len2 * $factor + (1 - $factor) * $len1);
678 break;
679 }
680 switch ($conf['align']) {
681 case 'right':
682 break;
683 case 'center':
684 $result[0] = round(($result[0]) / 2);
685 $result[1] = round(($result[1]) / 2);
686 break;
687 default:
688 $result[0] = 0;
689 $result[1] = 0;
690 break;
691 }
692 $result = $this->applyOffset($result, t3lib_div::intExplode(',', $conf['offset']));
693 $result = $this->applyOffset($result, $workArea);
694 return $result;
695 }
696
697 /**
698 * Calculates bounding box information for the TEXT GIFBUILDER object.
699 *
700 * @param array TypoScript array for the TEXT GIFBUILDER object
701 * @return array Array with three keys [0]/[1] being x/y and [2] being the bounding box array
702 * @access private
703 * @see txtPosition(), tslib_gifBuilder::start()
704 */
705 function calcBBox($conf) {
706 $sF = $this->getTextScalFactor($conf);
707 list($spacing, $wordSpacing) = $this->calcWordSpacing($conf, $sF);
708 $theText = $this->recodeString($conf['text']);
709
710 $charInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $theText, $conf['splitRendering.'], $sF);
711 $theBBoxInfo = $charInf;
712 if ($conf['angle']) {
713 $xArr = array($charInf[0], $charInf[2], $charInf[4], $charInf[6]);
714 $yArr = array($charInf[1], $charInf[3], $charInf[5], $charInf[7]);
715 $x = max($xArr) - min($xArr);
716 $y = max($yArr) - min($yArr);
717 } else {
718 $x = ($charInf[2] - $charInf[0]);
719 $y = ($charInf[1] - $charInf[7]);
720 }
721 // Set original lineHeight (used by line breaks):
722 $theBBoxInfo['lineHeight'] = $y;
723
724 if ($spacing || $wordSpacing) { // If any kind of spacing applys, we use this function:
725 $x = 0;
726 if (!$spacing && $wordSpacing) {
727 $bits = explode(' ', $theText);
728 foreach ($bits as $word) {
729 $word .= ' ';
730 $wordInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $word, $conf['splitRendering.'], $sF);
731 $wordW = ($wordInf[2] - $wordInf[0]);
732 $x += $wordW + $wordSpacing;
733 }
734 } else {
735 $utf8Chars = $this->singleChars($theText);
736 // For each UTF-8 char, do:
737 foreach ($utf8Chars as $char) {
738 $charInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $char, $conf['splitRendering.'], $sF);
739 $charW = ($charInf[2] - $charInf[0]);
740 $x += $charW + (($char == ' ') ? $wordSpacing : $spacing);
741 }
742 }
743 } elseif (isset($conf['breakWidth']) && $conf['breakWidth'] && $this->getRenderedTextWidth($conf['text'], $conf) > $conf['breakWidth']) {
744 $maxWidth = 0;
745 $currentWidth = 0;
746 $breakWidth = $conf['breakWidth'];
747 $breakSpace = $this->getBreakSpace($conf, $theBBoxInfo);
748
749 $wordPairs = $this->getWordPairsForLineBreak($conf['text']);
750 // Iterate through all word pairs:
751 foreach ($wordPairs as $index => $wordPair) {
752 $wordWidth = $this->getRenderedTextWidth($wordPair, $conf);
753 if ($index == 0 || $currentWidth + $wordWidth <= $breakWidth) {
754 $currentWidth += $wordWidth;
755 } else {
756 $maxWidth = max($maxWidth, $currentWidth);
757 $y += $breakSpace;
758 // Restart:
759 $currentWidth = $wordWidth;
760 }
761 }
762 $x = max($maxWidth, $currentWidth) * $sF;
763 }
764
765 if ($sF > 1) {
766 $x = ceil($x / $sF);
767 $y = ceil($y / $sF);
768 if (is_array($theBBoxInfo)) {
769 foreach ($theBBoxInfo as &$value) {
770 $value = ceil($value / $sF);
771 }
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 (!$this->im_version_4) {
1914 // IM4 doesn't have this options but forces them automatically if applicaple (<256 colors in image)
1915 if ($reduce <= 256) {
1916 $params .= ' -type Palette';
1917 }
1918 if ($ext == 'png' && $reduce <= 256) {
1919 $prefix = 'png8:';
1920 }
1921 }
1922 $this->imageMagickExec($file, $prefix . $result, $params);
1923 if ($result) {
1924 return $result;
1925 }
1926 }
1927 return '';
1928 }
1929
1930
1931 /*********************************
1932 *
1933 * GIFBUILDER Helper functions
1934 *
1935 *********************************/
1936
1937 /**
1938 * Checks if the $fontFile is already at an absolute path and if not, prepends the correct path.
1939 * Use PATH_site unless we are in the backend.
1940 * Call it by t3lib_stdGraphic::prependAbsolutePath()
1941 *
1942 * @param string The font file
1943 * @return string The font file with absolute path.
1944 */
1945 function prependAbsolutePath($fontFile) {
1946 $absPath = defined('PATH_typo3') ? dirname(PATH_thisScript) . '/' : PATH_site;
1947 $fontFile = t3lib_div::isAbsPath($fontFile) ? $fontFile : t3lib_div::resolveBackPath($absPath . $fontFile);
1948 return $fontFile;
1949 }
1950
1951 /**
1952 * Returns the IM command for sharpening with ImageMagick 5 (when $this->V5_EFFECTS is set).
1953 * Uses $this->im5fx_sharpenSteps for translation of the factor to an actual command.
1954 *
1955 * @param integer The sharpening factor, 0-100 (effectively in 10 steps)
1956 * @return string The sharpening command, eg. " -sharpen 3x4"
1957 * @see makeText(), IMparams(), v5_blur()
1958 */
1959 function v5_sharpen($factor) {
1960 $factor = t3lib_div::intInRange(ceil($factor / 10), 0, 10);
1961
1962 $sharpenArr = explode(',', ',' . $this->im5fx_sharpenSteps);
1963 $sharpenF = trim($sharpenArr[$factor]);
1964 if ($sharpenF) {
1965 $cmd = ' -sharpen ' . $sharpenF;
1966 return $cmd;
1967 }
1968 }
1969
1970 /**
1971 * Returns the IM command for blurring with ImageMagick 5 (when $this->V5_EFFECTS is set).
1972 * Uses $this->im5fx_blurSteps for translation of the factor to an actual command.
1973 *
1974 * @param integer The blurring factor, 0-100 (effectively in 10 steps)
1975 * @return string The blurring command, eg. " -blur 3x4"
1976 * @see makeText(), IMparams(), v5_sharpen()
1977 */
1978 function v5_blur($factor) {
1979 $factor = t3lib_div::intInRange(ceil($factor / 10), 0, 10);
1980
1981 $blurArr = explode(',', ',' . $this->im5fx_blurSteps);
1982 $blurF = trim($blurArr[$factor]);
1983 if ($blurF) {
1984 $cmd = ' -blur ' . $blurF;
1985 return $cmd;
1986 }
1987 }
1988
1989 /**
1990 * Returns a random filename prefixed with "temp_" and then 32 char md5 hash (without extension) from $this->tempPath.
1991 * 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.
1992 *
1993 * @return string
1994 */
1995 function randomName() {
1996 $this->createTempSubDir('temp/');
1997 return $this->tempPath . 'temp/' . md5(uniqid(''));
1998 }
1999
2000 /**
2001 * Applies offset value to coordinated in $cords.
2002 * Basically the value of key 0/1 of $OFFSET is added to keys 0/1 of $cords
2003 *
2004 * @param array Integer coordinates in key 0/1
2005 * @param array Offset values in key 0/1
2006 * @return array Modified $cords array
2007 */
2008 function applyOffset($cords, $OFFSET) {
2009 $cords[0] = intval($cords[0]) + intval($OFFSET[0]);
2010 $cords[1] = intval($cords[1]) + intval($OFFSET[1]);
2011 return $cords;
2012 }
2013
2014 /**
2015 * Converts a "HTML-color" TypoScript datatype to RGB-values.
2016 * Default is 0,0,0
2017 *
2018 * @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
2019 * @return array RGB values in key 0/1/2 of the array
2020 */
2021 function convertColor($string) {
2022 $col = array();
2023 $cParts = explode(':', $string, 2);
2024
2025 // Finding the RGB definitions of the color:
2026 $string = $cParts[0];
2027 if (strstr($string, '#')) {
2028 $string = preg_replace('/[^A-Fa-f0-9]*/', '', $string);
2029 $col[] = HexDec(substr($string, 0, 2));
2030 $col[] = HexDec(substr($string, 2, 2));
2031 $col[] = HexDec(substr($string, 4, 2));
2032 } elseif (strstr($string, ',')) {
2033 $string = preg_replace('/[^,0-9]*/', '', $string);
2034 $strArr = explode(',', $string);
2035 $col[] = intval($strArr[0]);
2036 $col[] = intval($strArr[1]);
2037 $col[] = intval($strArr[2]);
2038 } else {
2039 $string = strtolower(trim($string));
2040 if ($this->colMap[$string]) {
2041 $col = $this->colMap[$string];
2042 } else {
2043 $col = array(0, 0, 0);
2044 }
2045 }
2046 // ... and possibly recalculating the value
2047 if (trim($cParts[1])) {
2048 $cParts[1] = trim($cParts[1]);
2049 if (substr($cParts[1], 0, 1) == '*') {
2050 $val = doubleval(substr($cParts[1], 1));
2051 $col[0] = t3lib_div::intInRange($col[0] * $val, 0, 255);
2052 $col[1] = t3lib_div::intInRange($col[1] * $val, 0, 255);
2053 $col[2] = t3lib_div::intInRange($col[2] * $val, 0, 255);
2054 } else {
2055 $val = intval($cParts[1]);
2056 $col[0] = t3lib_div::intInRange($col[0] + $val, 0, 255);
2057 $col[1] = t3lib_div::intInRange($col[1] + $val, 0, 255);
2058 $col[2] = t3lib_div::intInRange($col[2] + $val, 0, 255);
2059 }
2060 }
2061 return $col;
2062 }
2063
2064 /**
2065 * Recode string
2066 * Used with text strings for fonts when languages has other character sets.
2067 *
2068 * @param string The text to recode
2069 * @return string The recoded string. Should be UTF-8 output. MAY contain entities (eg. &#123; or &#quot; which should render as real chars).
2070 */
2071 function recodeString($string) {
2072 // Recode string to UTF-8 from $this->nativeCharset:
2073 if ($this->nativeCharset && $this->nativeCharset != 'utf-8') {
2074 $string = $this->csConvObj->utf8_encode($string, $this->nativeCharset); // Convert to UTF-8
2075 }
2076
2077 return $string;
2078 }
2079
2080 /**
2081 * Split a string into an array of individual characters
2082 * 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.
2083 *
2084 * @param string The text string to split
2085 * @param boolean Return Unicode numbers instead of chars.
2086 * @return array Numerical array with a char as each value.
2087 */
2088 function singleChars($theText, $returnUnicodeNumber = FALSE) {
2089 if ($this->nativeCharset) {
2090 return $this->csConvObj->utf8_to_numberarray($theText, 1, $returnUnicodeNumber ? 0 : 1); // Get an array of separated UTF-8 chars
2091 } else {
2092 $output = array();
2093 $c = strlen($theText);
2094 for ($a = 0; $a < $c; $a++) {
2095 $output[] = substr($theText, $a, 1);
2096 }
2097 return $output;
2098 }
2099 }
2100
2101 /**
2102 * 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
2103 *
2104 * @param array TypoScript configuration for a GIFBUILDER object
2105 * @param array Workarea definition
2106 * @param array BB (Bounding box) array. Not just used for TEXT objects but also for others
2107 * @return array [0]=x, [1]=y, [2]=w, [3]=h
2108 * @access private
2109 * @see copyGifOntoGif(), makeBox(), crop()
2110 */
2111 function objPosition($conf, $workArea, $BB) {
2112 // offset, align, valign, workarea
2113 $result = array();
2114 $result[2] = $BB[0];
2115 $result[3] = $BB[1];
2116 $w = $workArea[2];
2117 $h = $workArea[3];
2118
2119 $align = explode(',', $conf['align']);
2120 $align[0] = strtolower(substr(trim($align[0]), 0, 1));
2121 $align[1] = strtolower(substr(trim($align[1]), 0, 1));
2122
2123 switch ($align[0]) {
2124 case 'r':
2125 $result[0] = $w - $result[2];
2126 break;
2127 case 'c':
2128 $result[0] = round(($w - $result[2]) / 2);
2129 break;
2130 default:
2131 $result[0] = 0;
2132 break;
2133 }
2134 switch ($align[1]) {
2135 case 'b':
2136 $result[1] = $h - $result[3]; // y pos
2137 break;
2138 case 'c':
2139 $result[1] = round(($h - $result[3]) / 2);
2140 break;
2141 default:
2142 $result[1] = 0;
2143 break;
2144 }
2145 $result = $this->applyOffset($result, t3lib_div::intExplode(',', $conf['offset']));
2146 $result = $this->applyOffset($result, $workArea);
2147 return $result;
2148 }
2149
2150
2151 /***********************************
2152 *
2153 * Scaling, Dimensions of images
2154 *
2155 ***********************************/
2156
2157 /**
2158 * Converts $imagefile to another file in temp-dir of type $newExt (extension).
2159 *
2160 * @param string The image filepath
2161 * @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.
2162 * @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
2163 * @param string Height. See $w
2164 * @param string Additional ImageMagick parameters.
2165 * @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...
2166 * @param array An array with options passed to getImageScale (see this function).
2167 * @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.
2168 * @return array [0]/[1] is w/h, [2] is file extension and [3] is the filename.
2169 * @see getImageScale(), typo3/show_item.php, fileList_ext::renderImage(), tslib_cObj::getImgResource(), SC_tslib_showpic::show(), maskImageOntoImage(), copyImageOntoImage(), scale()
2170 */
2171 function imageMagickConvert($imagefile, $newExt = '', $w = '', $h = '', $params = '', $frame = '', $options = '', $mustCreate = 0) {
2172 if ($this->NO_IMAGE_MAGICK) {
2173 // Returning file info right away
2174 return $this->getImageDimensions($imagefile);
2175 }
2176
2177 if ($info = $this->getImageDimensions($imagefile)) {
2178 $newExt = strtolower(trim($newExt));
2179 if (!$newExt) { // If no extension is given the original extension is used
2180 $newExt = $info[2];
2181 }
2182 if ($newExt == 'web') {
2183 if (t3lib_div::inList($this->webImageExt, $info[2])) {
2184 $newExt = $info[2];
2185 } else {
2186 $newExt = $this->gif_or_jpg($info[2], $info[0], $info[1]);
2187 if (!$params) {
2188 $params = $this->cmds[$newExt];
2189 }
2190 }
2191 }
2192 if (t3lib_div::inList($this->imageFileExt, $newExt)) {
2193 if (strstr($w . $h, 'm')) {
2194 $max = 1;
2195 } else {
2196 $max = 0;
2197 }
2198
2199 $data = $this->getImageScale($info, $w, $h, $options);
2200 $w = $data['origW'];
2201 $h = $data['origH'];
2202
2203 // if no conversion should be performed
2204 // this flag is true if the width / height does NOT dictate
2205 // the image to be scaled!! (that is if no width / height is
2206 // given or if the destination w/h matches the original image
2207 // dimensions or if the option to not scale the image is set)
2208 $noScale = (!$w && !$h) || ($data[0] == $info[0] && $data[1] == $info[1]) || $options['noScale'];
2209
2210 if ($noScale && !$data['crs'] && !$params && !$frame && $newExt == $info[2] && !$mustCreate) {
2211 // set the new width and height before returning,
2212 // if the noScale option is set
2213 if ($options['noScale']) {
2214 $info[0] = $data[0];
2215 $info[1] = $data[1];
2216 }
2217 $info[3] = $imagefile;
2218 return $info;
2219 }
2220 $info[0] = $data[0];
2221 $info[1] = $data[1];
2222
2223 $frame = $this->noFramePrepended ? '' : intval($frame);
2224
2225 if (!$params) {
2226 $params = $this->cmds[$newExt];
2227 }
2228
2229 // Cropscaling:
2230 if ($data['crs']) {
2231 if (!$data['origW']) {
2232 $data['origW'] = $data[0];
2233 }
2234 if (!$data['origH']) {
2235 $data['origH'] = $data[1];
2236 }
2237 $offsetX = intval(($data[0] - $data['origW']) * ($data['cropH'] + 100) / 200);
2238 $offsetY = intval(($data[1] - $data['origH']) * ($data['cropV'] + 100) / 200);
2239 $params .= ' -crop ' . $data['origW'] . 'x' . $data['origH'] . '+' . $offsetX . '+' . $offsetY . ' ';
2240 }
2241
2242 $command = $this->scalecmd . ' ' . $info[0] . 'x' . $info[1] . '! ' . $params . ' ';
2243 $cropscale = ($data['crs'] ? 'crs-V' . $data['cropV'] . 'H' . $data['cropH'] : '');
2244
2245 if ($this->alternativeOutputKey) {
2246 $theOutputName = t3lib_div::shortMD5($command . $cropscale . basename($imagefile) . $this->alternativeOutputKey . '[' . $frame . ']');
2247 } else {
2248 $theOutputName = t3lib_div::shortMD5($command . $cropscale . $imagefile . filemtime($imagefile) . '[' . $frame . ']');
2249 }
2250 if ($this->imageMagickConvert_forceFileNameBody) {
2251 $theOutputName = $this->imageMagickConvert_forceFileNameBody;
2252 $this->imageMagickConvert_forceFileNameBody = '';
2253 }
2254
2255 // Making the temporary filename:
2256 $this->createTempSubDir('pics/');
2257 $output = $this->absPrefix . $this->tempPath . 'pics/' . $this->filenamePrefix . $theOutputName . '.' . $newExt;
2258
2259 // Register temporary filename:
2260 $GLOBALS['TEMP_IMAGES_ON_PAGE'][] = $output;
2261
2262 if ($this->dontCheckForExistingTempFile || !$this->file_exists_typo3temp_file($output, $imagefile)) {
2263 $this->imageMagickExec($imagefile, $output, $command, $frame);
2264 }
2265 if (file_exists($output)) {
2266 $info[3] = $output;
2267 $info[2] = $newExt;
2268 if ($params) { // params could realisticly change some imagedata!
2269 $info = $this->getImageDimensions($info[3]);
2270 }
2271 if ($info[2] == $this->gifExtension && !$this->dontCompress) {
2272 t3lib_div::gif_compress($info[3], ''); // Compress with IM (lzw) or GD (rle) (Workaround for the absence of lzw-compression in GD)
2273 }
2274 return $info;
2275 }
2276 }
2277 }
2278 }
2279
2280 /**
2281 * Gets the input image dimensions.
2282 *
2283 * @param string The image filepath
2284 * @return array Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
2285 * @see imageMagickConvert(), tslib_cObj::getImgResource()
2286 */
2287 function getImageDimensions($imageFile) {
2288 preg_match('/([^\.]*)$/', $imageFile, $reg);
2289 if (file_exists($imageFile) && t3lib_div::inList($this->imageFileExt, strtolower($reg[0]))) {
2290 if ($returnArr = $this->getCachedImageDimensions($imageFile)) {
2291 return $returnArr;
2292 } else {
2293 if ($temp = @getImageSize($imageFile)) {
2294 $returnArr = array($temp[0], $temp[1], strtolower($reg[0]), $imageFile);
2295 } else {
2296 $returnArr = $this->imageMagickIdentify($imageFile);
2297 }
2298 if ($returnArr) {
2299 $this->cacheImageDimensions($returnArr);
2300 return $returnArr;
2301 }
2302 }
2303 }
2304 return FALSE;
2305 }
2306
2307 /**
2308 * Cache the result of the getImageDimensions function into the database. Does not check if the
2309 * file exists!
2310 *
2311 * @param array $identifyResult: Result of the getImageDimensions function
2312 * @return boolean True if operation was successful
2313 * @author Michael Stucki <michael@typo3.org> / Robert Lemke <rl@robertlemke.de>
2314 */
2315 function cacheImageDimensions($identifyResult) {
2316 // Create md5 hash of filemtime and filesize
2317 $md5Hash = md5(filemtime($identifyResult[3]) . filesize($identifyResult[3]));
2318
2319 $result = FALSE;
2320 if ($md5Hash) {
2321 $fieldArray = array(
2322 'md5hash' => $md5Hash,
2323 'md5filename' => md5($identifyResult[3]),
2324 'tstamp' => $GLOBALS['EXEC_TIME'],
2325 'filename' => $identifyResult[3],
2326 'imagewidth' => $identifyResult[0],
2327 'imageheight' => $identifyResult[1],
2328 );
2329
2330 $GLOBALS['TYPO3_DB']->exec_INSERTquery(
2331 'cache_imagesizes',
2332 $fieldArray
2333 );
2334
2335 if (!$err = $GLOBALS['TYPO3_DB']->sql_error()) {
2336 $result = TRUE;
2337 }
2338 }
2339
2340 return $result;
2341 }
2342
2343 /**
2344 * Fetch the cached imageDimensions from the MySQL database. Does not check if the image file exists!
2345 *
2346 * @param string The image filepath
2347 * @return array Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
2348 * @author Michael Stucki <michael@typo3.org>
2349 * @author Robert Lemke <rl@robertlemke.de>
2350 */
2351 function getCachedImageDimensions($imageFile) {
2352 // Create md5 hash of filemtime and filesize
2353 $md5Hash = md5(filemtime($imageFile) . filesize($imageFile));
2354
2355 $cachedImageDimensions = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
2356 'md5hash, md5filename, imagewidth, imageheight',
2357 'cache_imagesizes',
2358 'md5filename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr(md5($imageFile), 'cache_imagesizes')
2359 );
2360
2361 $result = FALSE;
2362 if (is_array($cachedImageDimensions)) {
2363 if ($cachedImageDimensions['md5hash'] != $md5Hash) {
2364 // File has changed, delete the row
2365 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
2366 'cache_imagesizes',
2367 'md5filename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cachedImageDimensions['md5filename'], 'cache_imagesizes')
2368 );
2369 } else {
2370 preg_match('/([^\.]*)$/', $imageFile, $imageExtension);
2371 $result = array(
2372 (int)$cachedImageDimensions['imagewidth'],
2373 (int)$cachedImageDimensions['imageheight'],
2374 strtolower($imageExtension[0]),
2375 $imageFile,
2376 );
2377 }
2378 }
2379
2380 return $result;
2381 }
2382
2383 /**
2384 * Get numbers for scaling the image based on input
2385 *
2386 * @param array Current image information: Width, Height etc.
2387 * @param integer "required" width
2388 * @param integer "required" height
2389 * @param array Options: Keys are like "maxW", "maxH", "minW", "minH"
2390 * @return array
2391 * @access private
2392 * @see imageMagickConvert()
2393 */
2394 function getImageScale($info, $w, $h, $options) {
2395 if (strstr($w . $h, 'm')) {
2396 $max = 1;
2397 } else {
2398 $max = 0;
2399 }
2400
2401 if (strstr($w . $h, 'c')) {
2402 $out['cropH'] = intval(substr(strstr($w, 'c'), 1));
2403 $out['cropV'] = intval(substr(strstr($h, 'c'), 1));
2404 $crs = TRUE;
2405 } else {
2406 $crs = FALSE;
2407 }
2408 $out['crs'] = $crs;
2409
2410 $w = intval($w);
2411 $h = intval($h);
2412 // if there are max-values...
2413 if ($options['maxW']) {
2414 if ($w) { // if width is given...
2415 if ($w > $options['maxW']) {
2416 $w = $options['maxW'];
2417 $max = 1; // height should follow
2418 }
2419 } else {
2420 if ($info[0] > $options['maxW']) {
2421 $w = $options['maxW'];
2422 $max = 1; // height should follow
2423 }
2424 }
2425 }
2426 if ($options['maxH']) {
2427 if ($h) { // if height is given...
2428 if ($h > $options['maxH']) {
2429 $h = $options['maxH'];
2430 $max = 1; // height should follow
2431 }
2432 } else {
2433 if ($info[1] > $options['maxH']) { // Changed [0] to [1] 290801
2434 $h = $options['maxH'];
2435 $max = 1; // height should follow
2436 }
2437 }
2438 }
2439 $out['origW'] = $w;
2440 $out['origH'] = $h;
2441 $out['max'] = $max;
2442
2443 if (!$this->mayScaleUp) {
2444 if ($w > $info[0]) {
2445 $w = $info[0];
2446 }
2447 if ($h > $info[1]) {
2448 $h = $info[1];
2449 }
2450 }
2451 if ($w || $h) { // if scaling should be performed
2452 if ($w && !$h) {
2453 $info[1] = ceil($info[1] * ($w / $info[0]));
2454 $info[0] = $w;
2455 }
2456 if (!$w && $h) {
2457 $info[0] = ceil($info[0] * ($h / $info[1]));
2458 $info[1] = $h;
2459 }
2460 if ($w && $h) {
2461 if ($max) {
2462 $ratio = $info[0] / $info[1];
2463 if ($h * $ratio > $w) {
2464 $h = round($w / $ratio);
2465 } else {
2466 $w = round($h * $ratio);
2467 }
2468 }
2469 if ($crs) {
2470 $ratio = $info[0] / $info[1];
2471 if ($h * $ratio < $w) {
2472 $h = round($w / $ratio);
2473 } else {
2474 $w = round($h * $ratio);
2475 }
2476 }
2477 $info[0] = $w;
2478 $info[1] = $h;
2479 }
2480 }
2481 $out[0] = $info[0];
2482 $out[1] = $info[1];
2483 // Set minimum-measures!
2484 if ($options['minW'] && $out[0] < $options['minW']) {
2485 if (($max || $crs) && $out[0]) {
2486 $out[1] = round($out[1] * $options['minW'] / $out[0]);
2487 }
2488 $out[0] = $options['minW'];
2489 }
2490 if ($options['minH'] && $out[1] < $options['minH']) {
2491 if (($max || $crs) && $out[1]) {
2492 $out[0] = round($out[0] * $options['minH'] / $out[1]);
2493 }
2494 $out[1] = $options['minH'];
2495 }
2496
2497 return $out;
2498 }
2499
2500 /**
2501 * Used to check if a certain process of scaling an image is already being carried out (can be logged in the SQL database)
2502 *
2503 * @param string Output imagefile
2504 * @param string Original basis file
2505 * @return boolean Returns true if the file is already being made; thus "true" means "Don't render the image again"
2506 * @access private
2507 */
2508 function file_exists_typo3temp_file($output, $orig = '') {
2509 if ($this->enable_typo3temp_db_tracking) {
2510 if (file_exists($output)) { // If file exists, then we return immediately
2511 return 1;
2512 } else { // If not, we look up in the cache_typo3temp_log table to see if there is a image being rendered right now.
2513 $md5Hash = md5($output);
2514 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
2515 'md5hash',
2516 'cache_typo3temp_log',
2517 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5Hash, 'cache_typo3temp_log') . ' AND tstamp>' . ($GLOBALS['EXEC_TIME'] - 30)
2518 );
2519 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { // If there was a record, the image is being generated by another proces (we assume)
2520 if (is_object($GLOBALS['TSFE'])) {
2521 $GLOBALS['TSFE']->set_no_cache();
2522 } // ...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...)
2523 if (is_object($GLOBALS['TT'])) {
2524 $GLOBALS['TT']->setTSlogMessage('typo3temp_log: Assume this file is being rendered now: ' . $output);
2525 }
2526 return 2; // Return 'success - 2'
2527 } 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.
2528
2529 $insertFields = array(
2530 'md5hash' => $md5Hash,
2531 'tstamp' => $GLOBALS['EXEC_TIME'],
2532 'filename' => $output,
2533 'orig_filename' => $orig
2534 );
2535 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_typo3temp_log', 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5Hash, 'cache_typo3temp_log'));
2536 $GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_typo3temp_log', $insertFields);
2537
2538 if (is_object($GLOBALS['TT'])) {
2539 $GLOBALS['TT']->setTSlogMessage('typo3temp_log: The row did not exist, so a new is written and file is being processed: ' . $output);
2540 }
2541 return 0;
2542 }
2543 }
2544 } else {
2545 return file_exists($output);
2546 }
2547 }
2548
2549
2550 /***********************************
2551 *
2552 * ImageMagick API functions
2553 *
2554 ***********************************/
2555
2556 /**
2557 * Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
2558 * Using ImageMagick
2559 *
2560 * @param string The relative (to PATH_site) image filepath
2561 * @return array
2562 */
2563 function imageMagickIdentify($imagefile) {
2564 if (!$this->NO_IMAGE_MAGICK) {
2565 $frame = $this->noFramePrepended ? '' : '[0]';
2566 $cmd = t3lib_div::imageMagickCommand('identify', $this->wrapFileName($imagefile) . $frame);
2567 $returnVal = array();
2568 t3lib_utility_Command::exec($cmd, $returnVal);
2569 $splitstring = $returnVal[0];
2570 $this->IM_commands[] = array('identify', $cmd, $returnVal[0]);
2571 if ($splitstring) {
2572 preg_match('/([^\.]*)$/', $imagefile, $reg);
2573 $splitinfo = explode(' ', $splitstring);
2574 foreach ($splitinfo as $key => $val) {
2575 $temp = '';
2576 if ($val) {
2577 $temp = explode('x', $val);
2578 }
2579 if (intval($temp[0]) && intval($temp[1])) {
2580 $dim = $temp;
2581 break;
2582 }
2583 }
2584 if ($dim[0] && $dim[1]) {
2585 return array($dim[0], $dim[1], strtolower($reg[0]), $imagefile);
2586 }
2587 }
2588 }
2589 }
2590
2591 /**
2592 * Executes a ImageMagick "convert" on two filenames, $input and $output using $params before them.
2593 * Can be used for many things, mostly scaling and effects.
2594 *
2595 * @param string The relative (to PATH_site) image filepath, input file (read from)
2596 * @param string The relative (to PATH_site) image filepath, output filename (written to)
2597 * @param string ImageMagick parameters
2598 * @param integer Optional, refers to which frame-number to select in the image. '' or 0
2599 * will select the first frame, 1 will select the next and so on...
2600 * @return string The result of a call to PHP function "exec()"
2601 */
2602 function imageMagickExec($input, $output, $params, $frame = 0) {
2603 if (!$this->NO_IMAGE_MAGICK) {
2604
2605 // Unless noFramePrepended is set in the Install Tool, a frame number is added to
2606 // select a specific page of the image (by default this will be the first page)
2607 if (!$this->noFramePrepended) {
2608 $frame = '[' . intval($frame) . ']';
2609 } else {
2610 $frame = '';
2611 }
2612
2613 $cmd = t3lib_div::imageMagickCommand('convert', $params . ' ' . $this->wrapFileName($input) . $frame . ' ' . $this->wrapFileName($output));
2614 $this->IM_commands[] = array($output, $cmd);
2615
2616 $ret = t3lib_utility_Command::exec($cmd);
2617 t3lib_div::fixPermissions($output); // Change the permissions of the file
2618
2619 return $ret;
2620 }
2621 }
2622
2623 /**
2624 * 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)
2625 * Can be used for many things, mostly scaling and effects.
2626 *
2627 * @param string The relative (to PATH_site) image filepath, bottom file
2628 * @param string The relative (to PATH_site) image filepath, overlay file (top)
2629 * @param string The relative (to PATH_site) image filepath, the mask file (grayscale)
2630 * @param string The relative (to PATH_site) image filepath, output filename (written to)
2631 * @param [type] $handleNegation: ...
2632 * @return void
2633 */
2634 function combineExec($input, $overlay, $mask, $output, $handleNegation = FALSE) {
2635 if (!$this->NO_IMAGE_MAGICK) {
2636 $params = '-colorspace GRAY +matte';
2637 if ($handleNegation) {
2638 if ($this->maskNegate) {
2639 $params .= ' ' . $this->maskNegate;
2640 }
2641 }
2642 $theMask = $this->randomName() . '.' . $this->gifExtension;
2643 $this->imageMagickExec($mask, $theMask, $params);
2644 $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
2645 $this->IM_commands[] = array($output, $cmd);
2646
2647 $ret = t3lib_utility_Command::exec($cmd);
2648 t3lib_div::fixPermissions($output); // Change the permissions of the file
2649
2650 if (is_file($theMask)) {
2651 @unlink($theMask);
2652 }
2653
2654 return $ret;
2655 }
2656 }
2657
2658 /**
2659 * Escapes a file name so it can safely be used on the command line.
2660 *
2661 * @param string $inputName filename to safeguard, must not be empty
2662 *
2663 * @return string $inputName escaped as needed
2664 */
2665 protected function wrapFileName($inputName) {
2666 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
2667 $currentLocale = setlocale(LC_CTYPE, 0);
2668 setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
2669 }
2670 $escapedInputName = escapeshellarg($inputName);
2671 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
2672 setlocale(LC_CTYPE, $currentLocale);
2673 }
2674 return $escapedInputName;
2675 }
2676
2677
2678 /***********************************
2679 *
2680 * Various IO functions
2681 *
2682 ***********************************/
2683
2684 /**
2685 * Returns true if the input file existed
2686 *
2687 * @param string Input file to check
2688 * @return string Returns the filename if the file existed, otherwise empty.
2689 */
2690 function checkFile($file) {
2691 if (@is_file($file)) {
2692 return $file;
2693 } else {
2694 return '';
2695 }
2696 }
2697
2698 /**
2699 * Creates subdirectory in typo3temp/ if not already found.
2700 *
2701 * @param string Name of sub directory
2702 * @return boolean Result of t3lib_div::mkdir(), true if it went well.
2703 */
2704 function createTempSubDir($dirName) {
2705
2706 // Checking if the this->tempPath is already prefixed with PATH_site and if not, prefix it with that constant.
2707 if (t3lib_div::isFirstPartOfStr($this->tempPath, PATH_site)) {
2708 $tmpPath = $this->tempPath;
2709 } else {
2710 $tmpPath = PATH_site . $this->tempPath;
2711 }
2712
2713 // Making the temporary filename:
2714 if (!@is_dir($tmpPath . $dirName)) {
2715 return t3lib_div::mkdir($tmpPath . $dirName);
2716 }
2717 }
2718
2719 /**
2720 * 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.
2721 *
2722 * @param pointer The image pointer (reference)
2723 * @param string The ImageMagick parameters. Like effects, scaling etc.
2724 * @return void
2725 */
2726 function applyImageMagickToPHPGif(&$im, $command) {
2727 $tmpStr = $this->randomName();
2728 $theFile = $tmpStr . '.' . $this->gifExtension;
2729 $this->ImageWrite($im, $theFile);
2730 $this->imageMagickExec($theFile, $theFile, $command);
2731 $tmpImg = $this->imageCreateFromFile($theFile);
2732 if ($tmpImg) {
2733 ImageDestroy($im);
2734 $im = $tmpImg;
2735 $this->w = imagesx($im);
2736 $this->h = imagesy($im);
2737 }
2738 if (!$this->dontUnlinkTempFiles) {
2739 unlink($theFile);
2740 }
2741 }
2742
2743 /**
2744 * 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.
2745 * For example: If the number of pixels exceeds $this->pixelLimitGif (normally 10000) then it will be a "jpg" string in return.
2746 *
2747 * @param string The file extension, lowercase.
2748 * @param integer The width of the output image.
2749 * @param integer The height of the output image.
2750 * @return string The filename, either "jpg" or "gif"/"png" (whatever $this->gifExtension is set to.)
2751 */
2752 function gif_or_jpg($type, $w, $h) {
2753 if ($type == 'ai' || $w * $h < $this->pixelLimitGif) {
2754 return $this->gifExtension;
2755 } else {
2756 return 'jpg';
2757 }
2758 }
2759
2760 /**
2761 * Writing the internal image pointer, $this->im, to file based on the extension of the input filename
2762 * Used in GIFBUILDER
2763 * Uses $this->setup['reduceColors'] for gif/png images and $this->setup['quality'] for jpg images to reduce size/quality if needed.
2764 *
2765 * @param string The filename to write to.
2766 * @return string Returns input filename
2767 * @see tslib_gifBuilder::gifBuild()
2768 */
2769 function output($file) {
2770 if ($file) {
2771 $reg = array();
2772 preg_match('/([^\.]*)$/', $file, $reg);
2773 $ext = strtolower($reg[0]);
2774 switch ($ext) {
2775 case 'gif':
2776 case 'png':
2777 if ($this->ImageWrite($this->im, $file)) {
2778 // ImageMagick operations
2779 if ($this->setup['reduceColors'] || !$this->png_truecolor) {
2780 $reduced = $this->IMreduceColors($file, t3lib_div::intInRange($this->setup['reduceColors'], 256, $this->truecolorColors, 256));
2781 if ($reduced) {
2782 @copy($reduced, $file);
2783 @unlink($reduced);
2784 }
2785 }
2786 t3lib_div::gif_compress($file, 'IM'); // Compress with IM! (adds extra compression, LZW from ImageMagick) (Workaround for the absence of lzw-compression in GD)
2787 }
2788 break;
2789 case 'jpg':
2790 case 'jpeg':
2791 $quality = 0; // Use the default
2792 if ($this->setup['quality']) {
2793 $quality = t3lib_div::intInRange($this->setup['quality'], 10, 100);
2794 }
2795 if ($this->ImageWrite($this->im, $file, $quality)) {
2796 ;
2797 }
2798 break;
2799 }
2800 $GLOBALS['TEMP_IMAGES_ON_PAGE'][] = $file;
2801 }
2802 return $file;
2803 }
2804
2805 /**
2806 * Destroy internal image pointer, $this->im
2807 *
2808 * @return void
2809 * @see tslib_gifBuilder::gifBuild()
2810 */
2811 function destroy() {
2812 ImageDestroy($this->im);
2813 }
2814
2815 /**
2816 * Returns Image Tag for input image information array.
2817 *
2818 * @param array Image information array, key 0/1 is width/height and key 3 is the src value
2819 * @return string Image tag for the input image information array.
2820 */
2821 function imgTag($imgInfo) {
2822 return '<img src="' . $imgInfo[3] . '" width="' . $imgInfo[0] . '" height="' . $imgInfo[1] . '" border="0" alt="" />';
2823 }
2824
2825 /**
2826 * Writes the input GDlib image pointer to file
2827 *
2828 * @param pointer The GDlib image resource pointer
2829 * @param string The filename to write to
2830 * @param integer The image quality (for JPEGs)
2831 * @return boolean The output of either imageGif, imagePng or imageJpeg based on the filename to write
2832 * @see maskImageOntoImage(), scale(), output()
2833 */
2834 function ImageWrite($destImg, $theImage, $quality = 0) {
2835 imageinterlace($destImg, 0);
2836 $ext = strtolower(substr($theImage, strrpos($theImage, '.') + 1));
2837 $result = FALSE;
2838 switch ($ext) {
2839 case 'jpg':
2840 case 'jpeg':
2841 if (function_exists('imageJpeg')) {
2842 if ($quality == 0) {
2843 $quality = $this->jpegQuality;
2844 }
2845 $result = imageJpeg($destImg, $theImage, $quality);
2846 }
2847 break;
2848 case 'gif':
2849 if (function_exists('imageGif')) {
2850 imagetruecolortopalette($destImg, TRUE, 256);
2851 $result = imageGif($destImg, $theImage);
2852 }
2853 break;
2854 case 'png':
2855 if (function_exists('imagePng')) {
2856 $result = ImagePng($destImg, $theImage);
2857 }
2858 break;
2859 }
2860 if ($result) {
2861 t3lib_div::fixPermissions($theImage);
2862 }
2863 return $result;
2864 }
2865
2866 /**
2867 * Creates a new GDlib image resource based on the input image filename.
2868 * 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.
2869 *
2870 * @param string Image filename
2871 * @return pointer Image Resource pointer
2872 */
2873 function imageCreateFromFile($sourceImg) {
2874 $imgInf = pathinfo($sourceImg);
2875 $ext = strtolower($imgInf['extension']);
2876
2877 switch ($ext) {
2878 case 'gif':
2879 if (function_exists('imagecreatefromgif')) {
2880 return imageCreateFromGif($sourceImg);
2881 }
2882 break;
2883 case 'png':
2884 if (function_exists('imagecreatefrompng')) {
2885 return imageCreateFromPng($sourceImg);
2886 }
2887 break;
2888 case 'jpg':
2889 case 'jpeg':
2890 if (function_exists('imagecreatefromjpeg')) {
2891 return imageCreateFromJpeg($sourceImg);
2892 }
2893 break;
2894 }
2895
2896 // If non of the above:
2897 $i = @getimagesize($sourceImg);
2898 $im = imagecreatetruecolor($i[0], $i[1]);
2899 $Bcolor = ImageColorAllocate($im, 128, 128, 128);
2900 ImageFilledRectangle($im, 0, 0, $i[0], $i[1], $Bcolor);
2901 return $im;