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