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