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