2 namespace TYPO3\CMS\Core\Imaging
;
5 * This file is part of the TYPO3 CMS project.
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use TYPO3\CMS\Core\Utility\CommandUtility
;
18 use TYPO3\CMS\Core\Utility\GeneralUtility
;
19 use TYPO3\CMS\Core\Utility\MathUtility
;
22 * Standard graphical functions
24 * Class contains a bunch of cool functions for manipulating graphics with GDlib/Freetype and ImageMagick.
25 * VERY OFTEN used with gifbuilder that extends this class and provides a TypoScript API to using these functions
27 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
29 class GraphicalFunctions
{
32 * If set, there is no frame pointer prepended to the filenames.
36 public $noFramePrepended = 0;
39 * This should be changed to 'png' if you want this class to read/make PNG-files instead!
43 public $gifExtension = 'gif';
46 * File formats supported by gdlib. This variable get's filled in "init" method
50 public $gdlibExtensions = '';
53 * Set to TRUE if generated png's should be truecolor by default
57 public $png_truecolor = FALSE;
60 * defines the RGB colorspace to use
64 protected $colorspace = 'RGB';
67 * colorspace names allowed
71 protected $allowedColorSpaceNames = array(
101 * 16777216 Colors is the maximum value for PNG, JPEG truecolor images (24-bit, 8-bit / Channel)
105 public $truecolorColors = 16777215;
108 * Commalist of file extensions perceived as images by TYPO3.
109 * List should be set to 'gif,png,jpeg,jpg' if IM is not available. Lowercase and no spaces between!
113 public $imageFileExt = 'gif,jpg,jpeg,png,tif,bmp,tga,pcx,ai,pdf';
116 * Commalist of web image extensions (can be shown by a webbrowser)
120 public $webImageExt = 'gif,jpg,jpeg,png';
125 public $NO_IM_EFFECTS = '';
130 public $cmds = array(
134 'png' => '-colors 64'
140 public $NO_IMAGE_MAGICK = '';
145 public $V5_EFFECTS = 0;
150 public $mayScaleUp = 1;
153 * Filename prefix for images scaled in imageMagickConvert()
157 public $filenamePrefix = '';
160 * Forcing the output filename of imageMagickConvert() to this value. However after calling imageMagickConvert() it will be set blank again.
164 public $imageMagickConvert_forceFileNameBody = '';
167 * This flag should always be FALSE. If set TRUE, imageMagickConvert will always write a new file to the tempdir! Used for debugging.
171 public $dontCheckForExistingTempFile = 0;
174 * Prevents imageMagickConvert() from compressing the gif-files with self::gifCompress()
178 public $dontCompress = 0;
181 * For debugging ONLY!
185 public $dontUnlinkTempFiles = 0;
188 * For debugging only.
189 * Filenames will not be based on mtime and only filename (not path) will be used.
190 * This key is also included in the hash of the filename...
194 public $alternativeOutputKey = '';
197 * All ImageMagick commands executed is stored in this array for tracking. Used by the Install Tools Image section
201 public $IM_commands = array();
206 public $workArea = array();
209 * Preserve the alpha transparency layer of read PNG images
213 protected $saveAlphaLayer = FALSE;
216 * 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.
220 public $tempPath = 'typo3temp/';
223 * Prefix for relative paths. Used in "show_item.php" script. Is prefixed the output file name IN imageMagickConvert()
227 public $absPrefix = '';
230 * ImageMagick scaling command; "-geometry" eller "-sample". Used in makeText() and imageMagickConvert()
234 public $scalecmd = '-geometry';
237 * Used by v5_blur() to simulate 10 continuous steps of blurring
241 public $im5fx_blurSteps = '1x2,2x2,3x2,4x3,5x3,5x4,6x4,7x5,8x5,9x5';
244 * Used by v5_sharpen() to simulate 10 continuous steps of sharpening.
248 public $im5fx_sharpenSteps = '1x2,2x2,3x2,2x3,3x3,4x3,3x4,4x4,4x5,5x5';
251 * This is the limit for the number of pixels in an image before it will be rendered as JPG instead of GIF/PNG
255 public $pixelLimitGif = 10000;
258 * Array mapping HTML color names to RGB values.
262 public $colMap = array(
263 'aqua' => array(0, 255, 255),
264 'black' => array(0, 0, 0),
265 'blue' => array(0, 0, 255),
266 'fuchsia' => array(255, 0, 255),
267 'gray' => array(128, 128, 128),
268 'green' => array(0, 128, 0),
269 'lime' => array(0, 255, 0),
270 'maroon' => array(128, 0, 0),
271 'navy' => array(0, 0, 128),
272 'olive' => array(128, 128, 0),
273 'purple' => array(128, 0, 128),
274 'red' => array(255, 0, 0),
275 'silver' => array(192, 192, 192),
276 'teal' => array(0, 128, 128),
277 'yellow' => array(255, 255, 0),
278 'white' => array(255, 255, 255)
282 * Charset conversion object:
284 * @var \TYPO3\CMS\Core\Charset\CharsetConverter
289 * Is set to the native character set of the input strings.
293 public $nativeCharset = '';
296 * Init function. Must always call this when using the class.
297 * This function will read the configuration information from $GLOBALS['TYPO3_CONF_VARS']['GFX'] can set some values in internal variables.
301 public function init() {
302 $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX'];
303 if (function_exists('imagecreatefromjpeg') && function_exists('imagejpeg')) {
304 $this->gdlibExtensions
.= ',jpg,jpeg';
306 if (function_exists('imagecreatefrompng') && function_exists('imagepng')) {
307 $this->gdlibExtensions
.= ',png';
309 if (function_exists('imagecreatefromgif') && function_exists('imagegif')) {
310 $this->gdlibExtensions
.= ',gif';
312 if ($gfxConf['png_truecolor']) {
313 $this->png_truecolor
= TRUE;
316 if ($gfxConf['colorspace'] && in_array($gfxConf['colorspace'], $this->allowedColorSpaceNames
, TRUE)) {
317 $this->colorspace
= $gfxConf['colorspace'];
320 if (!$gfxConf['im']) {
321 $this->NO_IMAGE_MAGICK
= 1;
323 if (!$this->NO_IMAGE_MAGICK
&& (!$gfxConf['im_version_5'] ||
$gfxConf['im_version_5'] === 'im4' ||
$gfxConf['im_version_5'] === 'im5')) {
324 throw new \
RuntimeException('Your TYPO3 installation is configured to use an old version of ImageMagick, which is not supported anymore. ' . 'Please upgrade to ImageMagick version 6 or GraphicksMagick and set $TYPO3_CONF_VARS[\'GFX\'][\'im_version_5\'] appropriately.', 1305059666);
326 // When GIFBUILDER gets used in truecolor mode
327 // No colors parameter if we generate truecolor images.
328 if ($this->png_truecolor
) {
329 $this->cmds
['png'] = '';
331 // Setting default JPG parameters:
332 $this->jpegQuality
= MathUtility
::forceIntegerInRange($gfxConf['jpg_quality'], 10, 100, 75);
333 $this->cmds
['jpg'] = ($this->cmds
['jpeg'] = '-colorspace ' . $this->colorspace
. ' -sharpen 50 -quality ' . $this->jpegQuality
);
334 if ($gfxConf['im_noFramePrepended']) {
335 $this->noFramePrepended
= 1;
337 if ($gfxConf['gdlib_png']) {
338 $this->gifExtension
= 'png';
340 $this->imageFileExt
= $gfxConf['imagefile_ext'];
342 // Boolean. This is necessary if using ImageMagick 5+.
343 // Effects in Imagemagick 5+ tends to render very slowly!!
344 // - therefore must be disabled in order not to perform sharpen, blurring and such.
345 $this->NO_IM_EFFECTS
= 1;
346 $this->cmds
['jpg'] = ($this->cmds
['jpeg'] = '-colorspace ' . $this->colorspace
. ' -quality ' . $this->jpegQuality
);
348 // ... but if 'im_v5effects' is set, enable effects
349 if ($gfxConf['im_v5effects']) {
350 $this->NO_IM_EFFECTS
= 0;
351 $this->V5_EFFECTS
= 1;
352 if ($gfxConf['im_v5effects'] > 0) {
353 $this->cmds
['jpg'] = ($this->cmds
['jpeg'] = '-colorspace ' . $this->colorspace
. ' -quality ' . (int)$gfxConf['jpg_quality'] . $this->v5_sharpen(10));
356 // Secures that images are not scaled up.
357 if ($gfxConf['im_noScaleUp']) {
358 $this->mayScaleUp
= 0;
360 if (TYPO3_MODE
== 'FE') {
361 $this->csConvObj
= $GLOBALS['TSFE']->csConvObj
;
362 } elseif (is_object($GLOBALS['LANG'])) {
364 $this->csConvObj
= $GLOBALS['LANG']->csConvObj
;
366 // The object may not exist yet, so we need to create it now. Happens in the Install Tool for example.
367 $this->csConvObj
= GeneralUtility
::makeInstance(\TYPO3\CMS\Core\Charset\CharsetConverter
::class);
369 $this->nativeCharset
= 'utf-8';
372 /*************************************************
374 * Layering images / "IMAGE" GIFBUILDER object
376 *************************************************/
378 * Implements the "IMAGE" GIFBUILDER object, when the "mask" property is TRUE.
379 * 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
380 * The operation involves ImageMagick for combining.
382 * @param resource $im GDlib image pointer
383 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
384 * @param array $workArea The current working area coordinates.
386 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make()
388 public function maskImageOntoImage(&$im, $conf, $workArea) {
389 if ($conf['file'] && $conf['mask']) {
390 $imgInf = pathinfo($conf['file']);
391 $imgExt = strtolower($imgInf['extension']);
392 if (!GeneralUtility
::inList($this->gdlibExtensions
, $imgExt)) {
393 $BBimage = $this->imageMagickConvert($conf['file'], $this->gifExtension
);
395 $BBimage = $this->getImageDimensions($conf['file']);
397 $maskInf = pathinfo($conf['mask']);
398 $maskExt = strtolower($maskInf['extension']);
399 if (!GeneralUtility
::inList($this->gdlibExtensions
, $maskExt)) {
400 $BBmask = $this->imageMagickConvert($conf['mask'], $this->gifExtension
);
402 $BBmask = $this->getImageDimensions($conf['mask']);
404 if ($BBimage && $BBmask) {
407 $tmpStr = $this->randomName();
408 $theImage = $tmpStr . '_img.' . $this->gifExtension
;
409 $theDest = $tmpStr . '_dest.' . $this->gifExtension
;
410 $theMask = $tmpStr . '_mask.' . $this->gifExtension
;
411 // Prepare overlay image
412 $cpImg = $this->imageCreateFromFile($BBimage[3]);
413 $destImg = imagecreatetruecolor($w, $h);
414 // Preserve alpha transparency
415 if ($this->saveAlphaLayer
) {
416 imagesavealpha($destImg, TRUE);
417 $Bcolor = imagecolorallocatealpha($destImg, 0, 0, 0, 127);
418 imagefill($destImg, 0, 0, $Bcolor);
420 $Bcolor = ImageColorAllocate($destImg, 0, 0, 0);
421 ImageFilledRectangle($destImg, 0, 0, $w, $h, $Bcolor);
423 $this->copyGifOntoGif($destImg, $cpImg, $conf, $workArea);
424 $this->ImageWrite($destImg, $theImage);
425 imageDestroy($cpImg);
426 imageDestroy($destImg);
427 // Prepare mask image
428 $cpImg = $this->imageCreateFromFile($BBmask[3]);
429 $destImg = imagecreatetruecolor($w, $h);
430 if ($this->saveAlphaLayer
) {
431 imagesavealpha($destImg, TRUE);
432 $Bcolor = imagecolorallocatealpha($destImg, 0, 0, 0, 127);
433 imagefill($destImg, 0, 0, $Bcolor);
435 $Bcolor = ImageColorAllocate($destImg, 0, 0, 0);
436 ImageFilledRectangle($destImg, 0, 0, $w, $h, $Bcolor);
438 $this->copyGifOntoGif($destImg, $cpImg, $conf, $workArea);
439 $this->ImageWrite($destImg, $theMask);
440 imageDestroy($cpImg);
441 imageDestroy($destImg);
443 $this->ImageWrite($im, $theDest);
444 // Let combineExec handle maskNegation
445 $this->combineExec($theDest, $theImage, $theMask, $theDest, TRUE);
446 // The main image is loaded again...
447 $backIm = $this->imageCreateFromFile($theDest);
448 // ... and if nothing went wrong we load it onto the old one.
450 if (!$this->saveAlphaLayer
) {
451 ImageColorTransparent($backIm, -1);
455 // Unlink files from process
456 if (!$this->dontUnlinkTempFiles
) {
466 * Implements the "IMAGE" GIFBUILDER object, when the "mask" property is FALSE (using only $conf['file'])
468 * @param resource $im GDlib image pointer
469 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
470 * @param array $workArea The current working area coordinates.
472 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make(), maskImageOntoImage()
474 public function copyImageOntoImage(&$im, $conf, $workArea) {
476 if (!GeneralUtility
::inList($this->gdlibExtensions
, $conf['BBOX'][2])) {
477 $conf['BBOX'] = $this->imageMagickConvert($conf['BBOX'][3], $this->gifExtension
);
478 $conf['file'] = $conf['BBOX'][3];
480 $cpImg = $this->imageCreateFromFile($conf['file']);
481 $this->copyGifOntoGif($im, $cpImg, $conf, $workArea);
482 imageDestroy($cpImg);
487 * Copies two GDlib image pointers onto each other, using TypoScript configuration from $conf and the input $workArea definition.
489 * @param resource $im GDlib image pointer, destination (bottom image)
490 * @param resource $cpImg GDlib image pointer, source (top image)
491 * @param array $conf TypoScript array with the properties for the IMAGE GIFBUILDER object. Only used for the "tile" property value.
492 * @param array $workArea Work area
493 * @return void Works on the $im image pointer
496 public function copyGifOntoGif(&$im, $cpImg, $conf, $workArea) {
497 $cpW = imagesx($cpImg);
498 $cpH = imagesy($cpImg);
499 $tile = GeneralUtility
::intExplode(',', $conf['tile']);
500 $tile[0] = MathUtility
::forceIntegerInRange($tile[0], 1, 20);
501 $tile[1] = MathUtility
::forceIntegerInRange($tile[1], 1, 20);
502 $cpOff = $this->objPosition($conf, $workArea, array($cpW * $tile[0], $cpH * $tile[1]));
503 for ($xt = 0; $xt < $tile[0]; $xt++
) {
504 $Xstart = $cpOff[0] +
$cpW * $xt;
505 // If this image is inside of the workArea, then go on
506 if ($Xstart +
$cpW > $workArea[0]) {
508 if ($Xstart < $workArea[0]) {
509 $cpImgCutX = $workArea[0] - $Xstart;
510 $Xstart = $workArea[0];
514 $w = $cpW - $cpImgCutX;
515 if ($Xstart > $workArea[0] +
$workArea[2] - $w) {
516 $w = $workArea[0] +
$workArea[2] - $Xstart;
518 // If this image is inside of the workArea, then go on
519 if ($Xstart < $workArea[0] +
$workArea[2]) {
521 for ($yt = 0; $yt < $tile[1]; $yt++
) {
522 $Ystart = $cpOff[1] +
$cpH * $yt;
523 // If this image is inside of the workArea, then go on
524 if ($Ystart +
$cpH > $workArea[1]) {
525 if ($Ystart < $workArea[1]) {
526 $cpImgCutY = $workArea[1] - $Ystart;
527 $Ystart = $workArea[1];
531 $h = $cpH - $cpImgCutY;
532 if ($Ystart > $workArea[1] +
$workArea[3] - $h) {
533 $h = $workArea[1] +
$workArea[3] - $Ystart;
535 // If this image is inside of the workArea, then go on
536 if ($Ystart < $workArea[1] +
$workArea[3]) {
537 $this->imagecopyresized($im, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h);
547 * Alternative function for using the similar PHP function imagecopyresized(). Used for GD2 only.
549 * OK, the reason for this stupid fix is the following story:
550 * GD1.x was capable of copying two images together and combining their palettes! GD2 is apparently not.
551 * With GD2 only the palette of the dest-image is used which mostly results in totally black images when trying to
552 * copy a color-ful image onto the destination.
554 * 1) Create a blank TRUE-COLOR image
555 * 2) Copy the destination image onto that one
556 * 3) Then do the actual operation; Copying the source (top image) onto that
557 * 4) ... and return the result pointer.
558 * 5) Reduce colors (if we do not, the result may become strange!)
559 * It works, but the resulting images is now a true-color PNG which may be very large.
560 * So, why not use 'imagetruecolortopalette ($im, TRUE, 256)' - well because it does NOT WORK! So simple is that.
562 * @param resource $dstImg Destination image
563 * @param resource $srcImg Source image
564 * @param int $dstX Destination x-coordinate
565 * @param int $dstY Destination y-coordinate
566 * @param int $srcX Source x-coordinate
567 * @param int $srcY Source y-coordinate
568 * @param int $dstWidth Destination width
569 * @param int $dstHeight Destination height
570 * @param int $srcWidth Source width
571 * @param int $srcHeight Source height
574 * @see \TYPO3\CMS\Backend\Utility\IconUtility::imagecopyresized()
576 public function imagecopyresized(&$dstImg, $srcImg, $dstX, $dstY, $srcX, $srcY, $dstWidth, $dstHeight, $srcWidth, $srcHeight) {
577 if (!$this->saveAlphaLayer
) {
578 // Make true color image
579 $tmpImg = imagecreatetruecolor(imagesx($dstImg), imagesy($dstImg));
580 // Copy the source image onto that
581 imagecopyresized($tmpImg, $dstImg, 0, 0, 0, 0, imagesx($dstImg), imagesy($dstImg), imagesx($dstImg), imagesy($dstImg));
582 // Then copy the source image onto that (the actual operation!)
583 imagecopyresized($tmpImg, $srcImg, $dstX, $dstY, $srcX, $srcY, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
584 // Set the destination image
587 imagecopyresized($dstImg, $srcImg, $dstX, $dstY, $srcX, $srcY, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
591 /********************************
593 * Text / "TEXT" GIFBUILDER object
595 ********************************/
597 * Implements the "TEXT" GIFBUILDER object
599 * @param resource $im GDlib image pointer
600 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
601 * @param array $workArea The current working area coordinates.
603 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make()
605 public function makeText(&$im, $conf, $workArea) {
607 list($spacing, $wordSpacing) = $this->calcWordSpacing($conf);
609 $txtPos = $this->txtPosition($conf, $workArea, $conf['BBOX']);
610 $theText = $this->recodeString($conf['text']);
611 if ($conf['imgMap'] && is_array($conf['imgMap.'])) {
612 $this->addToMap($this->calcTextCordsForMap($conf['BBOX'][2], $txtPos, $conf['imgMap.']), $conf['imgMap.']);
614 if (!$conf['hideButCreateMap']) {
616 $cols = $this->convertColor($conf['fontColor']);
617 // NiceText is calculated
618 if (!$conf['niceText']) {
619 $Fcolor = ImageColorAllocate($im, $cols[0], $cols[1], $cols[2]);
620 // antiAliasing is setup:
621 $Fcolor = $conf['antiAlias'] ?
$Fcolor : -$Fcolor;
622 for ($a = 0; $a < $conf['iterations']; $a++
) {
623 // If any kind of spacing applys, we use this function:
624 if ($spacing ||
$wordSpacing) {
625 $this->SpacedImageTTFText($im, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, self
::prependAbsolutePath($conf['fontFile']), $theText, $spacing, $wordSpacing, $conf['splitRendering.']);
627 $this->renderTTFText($im, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, $conf['fontFile'], $theText, $conf['splitRendering.'], $conf);
632 // options anti_aliased and iterations is NOT available when doing this!!
635 $tmpStr = $this->randomName();
636 $fileMenu = $tmpStr . '_menuNT.' . $this->gifExtension
;
637 $fileColor = $tmpStr . '_colorNT.' . $this->gifExtension
;
638 $fileMask = $tmpStr . '_maskNT.' . $this->gifExtension
;
640 $sF = MathUtility
::forceIntegerInRange($conf['niceText.']['scaleFactor'], 2, 5);
641 $newW = ceil($sF * imagesx($im));
642 $newH = ceil($sF * imagesy($im));
644 $maskImg = imagecreatetruecolor($newW, $newH);
645 $Bcolor = ImageColorAllocate($maskImg, 255, 255, 255);
646 ImageFilledRectangle($maskImg, 0, 0, $newW, $newH, $Bcolor);
647 $Fcolor = ImageColorAllocate($maskImg, 0, 0, 0);
648 // If any kind of spacing applies, we use this function:
649 if ($spacing ||
$wordSpacing) {
650 $this->SpacedImageTTFText($maskImg, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, self
::prependAbsolutePath($conf['fontFile']), $theText, $spacing, $wordSpacing, $conf['splitRendering.'], $sF);
652 $this->renderTTFText($maskImg, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, $conf['fontFile'], $theText, $conf['splitRendering.'], $conf, $sF);
654 $this->ImageWrite($maskImg, $fileMask);
655 ImageDestroy($maskImg);
656 // Downscales the mask
657 if ($this->NO_IM_EFFECTS
) {
658 $command = trim($this->scalecmd
. ' ' . $w . 'x' . $h . '! -negate');
660 $command = trim($conf['niceText.']['before'] . ' ' . $this->scalecmd
. ' ' . $w . 'x' . $h . '! ' . $conf['niceText.']['after'] . ' -negate');
661 if ($conf['niceText.']['sharpen']) {
662 if ($this->V5_EFFECTS
) {
663 $command .= $this->v5_sharpen($conf['niceText.']['sharpen']);
665 $command .= ' -sharpen ' . MathUtility
::forceIntegerInRange($conf['niceText.']['sharpen'], 1, 99);
669 $this->imageMagickExec($fileMask, $fileMask, $command);
670 // Make the color-file
671 $colorImg = imagecreatetruecolor($w, $h);
672 $Ccolor = ImageColorAllocate($colorImg, $cols[0], $cols[1], $cols[2]);
673 ImageFilledRectangle($colorImg, 0, 0, $w, $h, $Ccolor);
674 $this->ImageWrite($colorImg, $fileColor);
675 ImageDestroy($colorImg);
676 // The mask is applied
677 // The main pictures is saved temporarily
678 $this->ImageWrite($im, $fileMenu);
679 $this->combineExec($fileMenu, $fileColor, $fileMask, $fileMenu);
680 // The main image is loaded again...
681 $backIm = $this->imageCreateFromFile($fileMenu);
682 // ... and if nothing went wrong we load it onto the old one.
684 if (!$this->saveAlphaLayer
) {
685 ImageColorTransparent($backIm, -1);
689 // Deleting temporary files;
690 if (!$this->dontUnlinkTempFiles
) {
700 * Calculates text position for printing the text onto the image based on configuration like alignment and workarea.
702 * @param array $conf TypoScript array for the TEXT GIFBUILDER object
703 * @param array $workArea Workarea definition
704 * @param array $BB Bounding box information, was set in \TYPO3\CMS\Frontend\Imaging\GifBuilder::start()
705 * @return array [0]=x, [1]=y, [2]=w, [3]=h
709 public function txtPosition($conf, $workArea, $BB) {
710 $angle = (int)$conf['angle'] / 180 * pi();
712 $straightBB = $this->calcBBox($conf);
713 // offset, align, valign, workarea
714 // [0]=x, [1]=y, [2]=w, [3]=h
719 switch ($conf['align']) {
723 $factor = abs(cos($angle));
724 $sign = cos($angle) < 0 ?
-1 : 1;
725 $len1 = $sign * $factor * $straightBB[0];
726 $len2 = $sign * $BB[0];
727 $result[0] = $w - ceil(($len2 * $factor +
(1 - $factor) * $len1));
728 $factor = abs(sin($angle));
729 $sign = sin($angle) < 0 ?
-1 : 1;
730 $len1 = $sign * $factor * $straightBB[0];
731 $len2 = $sign * $BB[1];
732 $result[1] = ceil($len2 * $factor +
(1 - $factor) * $len1);
735 switch ($conf['align']) {
739 $result[0] = round($result[0] / 2);
740 $result[1] = round($result[1] / 2);
746 $result = $this->applyOffset($result, GeneralUtility
::intExplode(',', $conf['offset']));
747 $result = $this->applyOffset($result, $workArea);
752 * Calculates bounding box information for the TEXT GIFBUILDER object.
754 * @param array $conf TypoScript array for the TEXT GIFBUILDER object
755 * @return array Array with three keys [0]/[1] being x/y and [2] being the bounding box array
757 * @see txtPosition(), \TYPO3\CMS\Frontend\Imaging\GifBuilder::start()
759 public function calcBBox($conf) {
760 $sF = $this->getTextScalFactor($conf);
761 list($spacing, $wordSpacing) = $this->calcWordSpacing($conf, $sF);
762 $theText = $this->recodeString($conf['text']);
763 $charInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $theText, $conf['splitRendering.'], $sF);
764 $theBBoxInfo = $charInf;
765 if ($conf['angle']) {
766 $xArr = array($charInf[0], $charInf[2], $charInf[4], $charInf[6]);
767 $yArr = array($charInf[1], $charInf[3], $charInf[5], $charInf[7]);
768 $x = max($xArr) - min($xArr);
769 $y = max($yArr) - min($yArr);
771 $x = $charInf[2] - $charInf[0];
772 $y = $charInf[1] - $charInf[7];
774 // Set original lineHeight (used by line breaks):
775 $theBBoxInfo['lineHeight'] = $y;
776 // If any kind of spacing applys, we use this function:
777 if ($spacing ||
$wordSpacing) {
779 if (!$spacing && $wordSpacing) {
780 $bits = explode(' ', $theText);
781 foreach ($bits as $word) {
783 $wordInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $word, $conf['splitRendering.'], $sF);
784 $wordW = $wordInf[2] - $wordInf[0];
785 $x +
= $wordW +
$wordSpacing;
788 $utf8Chars = $this->singleChars($theText);
789 // For each UTF-8 char, do:
790 foreach ($utf8Chars as $char) {
791 $charInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $char, $conf['splitRendering.'], $sF);
792 $charW = $charInf[2] - $charInf[0];
793 $x +
= $charW +
($char == ' ' ?
$wordSpacing : $spacing);
796 } elseif (isset($conf['breakWidth']) && $conf['breakWidth'] && $this->getRenderedTextWidth($conf['text'], $conf) > $conf['breakWidth']) {
799 $breakWidth = $conf['breakWidth'];
800 $breakSpace = $this->getBreakSpace($conf, $theBBoxInfo);
801 $wordPairs = $this->getWordPairsForLineBreak($conf['text']);
802 // Iterate through all word pairs:
803 foreach ($wordPairs as $index => $wordPair) {
804 $wordWidth = $this->getRenderedTextWidth($wordPair, $conf);
805 if ($index == 0 ||
$currentWidth +
$wordWidth <= $breakWidth) {
806 $currentWidth +
= $wordWidth;
808 $maxWidth = max($maxWidth, $currentWidth);
811 $currentWidth = $wordWidth;
814 $x = max($maxWidth, $currentWidth) * $sF;
819 if (is_array($theBBoxInfo)) {
820 foreach ($theBBoxInfo as &$value) {
821 $value = ceil($value / $sF);
826 return array($x, $y, $theBBoxInfo);
830 * Adds an <area> tag to the internal variable $this->map which is used to accumulate the content for an ImageMap
832 * @param array $cords Coordinates for a polygon image map as created by ->calcTextCordsForMap()
833 * @param array $conf Configuration for "imgMap." property of a TEXT GIFBUILDER object.
836 * @see makeText(), calcTextCordsForMap()
838 public function addToMap($cords, $conf) {
839 $this->map
.= '<area' . ' shape="poly"' . ' coords="' . implode(',', $cords) . '"'
840 . ' href="' . htmlspecialchars($conf['url']) . '"'
841 . ($conf['target'] ?
' target="' . htmlspecialchars($conf['target']) . '"' : '') . $JS
842 . ((string)$conf['titleText'] !== '' ?
' title="' . htmlspecialchars($conf['titleText']) . '"' : '')
843 . ' alt="' . htmlspecialchars($conf['altText']) . '" />';
847 * Calculating the coordinates for a TEXT string on an image map. Used in an <area> tag
849 * @param array $cords Coordinates (from BBOX array)
850 * @param array $offset Offset array
851 * @param array $conf Configuration for "imgMap." property of a TEXT GIFBUILDER object.
854 * @see makeText(), calcTextCordsForMap()
856 public function calcTextCordsForMap($cords, $offset, $conf) {
857 $pars = GeneralUtility
::intExplode(',', $conf['explode'] . ',');
858 $newCords[0] = $cords[0] +
$offset[0] - $pars[0];
859 $newCords[1] = $cords[1] +
$offset[1] +
$pars[1];
860 $newCords[2] = $cords[2] +
$offset[0] +
$pars[0];
861 $newCords[3] = $cords[3] +
$offset[1] +
$pars[1];
862 $newCords[4] = $cords[4] +
$offset[0] +
$pars[0];
863 $newCords[5] = $cords[5] +
$offset[1] - $pars[1];
864 $newCords[6] = $cords[6] +
$offset[0] - $pars[0];
865 $newCords[7] = $cords[7] +
$offset[1] - $pars[1];
870 * Printing text onto an image like the PHP function imageTTFText does but in addition it offers options for spacing of letters and words.
871 * Spacing is done by printing one char at a time and this means that the spacing is rather uneven and probably not very nice.
874 * @param resource $im (See argument for PHP function imageTTFtext())
875 * @param int $fontSize (See argument for PHP function imageTTFtext())
876 * @param int $angle (See argument for PHP function imageTTFtext())
877 * @param int $x (See argument for PHP function imageTTFtext())
878 * @param int $y (See argument for PHP function imageTTFtext())
879 * @param int $Fcolor (See argument for PHP function imageTTFtext())
880 * @param string $fontFile (See argument for PHP function imageTTFtext())
881 * @param string $text (See argument for PHP function imageTTFtext()). UTF-8 string, possibly with entities in.
882 * @param int $spacing The spacing of letters in pixels
883 * @param int $wordSpacing The spacing of words in pixels
884 * @param array $splitRenderingConf Array
885 * @param int $sF Scale factor
889 public function SpacedImageTTFText(&$im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $text, $spacing, $wordSpacing, $splitRenderingConf, $sF = 1) {
892 if (!$spacing && $wordSpacing) {
893 $bits = explode(' ', $text);
894 foreach ($bits as $word) {
896 $wordInf = $this->ImageTTFBBoxWrapper($fontSize, $angle, $fontFile, $word, $splitRenderingConf, $sF);
897 $wordW = $wordInf[2] - $wordInf[0];
898 $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $word, $splitRenderingConf, $sF);
899 $x +
= $wordW +
$wordSpacing;
902 $utf8Chars = $this->singleChars($text);
903 // For each UTF-8 char, do:
904 foreach ($utf8Chars as $char) {
905 $charInf = $this->ImageTTFBBoxWrapper($fontSize, $angle, $fontFile, $char, $splitRenderingConf, $sF);
906 $charW = $charInf[2] - $charInf[0];
907 $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $char, $splitRenderingConf, $sF);
908 $x +
= $charW +
($char == ' ' ?
$wordSpacing : $spacing);
914 * Function that finds the right fontsize that will render the textstring within a certain width
916 * @param array $conf The TypoScript properties of the TEXT GIFBUILDER object
917 * @return int The new fontSize
919 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::start()
921 public function fontResize($conf) {
922 // 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!!!!
923 $maxWidth = (int)$conf['maxWidth'];
924 list($spacing, $wordSpacing) = $this->calcWordSpacing($conf);
926 // If any kind of spacing applys, we use this function:
927 if ($spacing ||
$wordSpacing) {
928 return $conf['fontSize'];
931 // Determine bounding box.
932 $bounds = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $this->recodeString($conf['text']), $conf['splitRendering.']);
933 if ($conf['angle'] < 0) {
934 $pixelWidth = abs($bounds[4] - $bounds[0]);
935 } elseif ($conf['angle'] > 0) {
936 $pixelWidth = abs($bounds[2] - $bounds[6]);
938 $pixelWidth = abs($bounds[4] - $bounds[6]);
940 // Size is fine, exit:
941 if ($pixelWidth <= $maxWidth) {
946 } while ($conf['fontSize'] > 1);
949 return $conf['fontSize'];
953 * Wrapper for ImageTTFBBox
955 * @param int $fontSize (See argument for PHP function ImageTTFBBox())
956 * @param int $angle (See argument for PHP function ImageTTFBBox())
957 * @param string $fontFile (See argument for PHP function ImageTTFBBox())
958 * @param string $string (See argument for PHP function ImageTTFBBox())
959 * @param array $splitRendering Split-rendering configuration
960 * @param int $sF Scale factor
961 * @return array Information array.
963 public function ImageTTFBBoxWrapper($fontSize, $angle, $fontFile, $string, $splitRendering, $sF = 1) {
965 $offsetInfo = array();
966 $stringParts = $this->splitString($string, $splitRendering, $fontSize, $fontFile);
967 // Traverse string parts:
968 foreach ($stringParts as $strCfg) {
969 $fontFile = self
::prependAbsolutePath($strCfg['fontFile']);
970 if (is_readable($fontFile)) {
972 * Calculate Bounding Box for part.
973 * Due to a PHP bug, we must retry if $calc[2] is negative.
975 * @see https://bugs.php.net/bug.php?id=51315
976 * @see https://bugs.php.net/bug.php?id=22513
980 $calc = ImageTTFBBox(GeneralUtility
::freetypeDpiComp($sF * $strCfg['fontSize']), $angle, $fontFile, $strCfg['str']);
981 } while ($calc[2] < 0 && $try++
< 10);
982 // Calculate offsets:
983 if (!count($offsetInfo)) {
984 // First run, just copy over.
987 $offsetInfo[2] +
= $calc[2] - $calc[0] +
(int)$splitRendering['compX'] +
(int)$strCfg['xSpaceBefore'] +
(int)$strCfg['xSpaceAfter'];
988 $offsetInfo[3] +
= $calc[3] - $calc[1] - (int)$splitRendering['compY'] - (int)$strCfg['ySpaceBefore'] - (int)$strCfg['ySpaceAfter'];
989 $offsetInfo[4] +
= $calc[4] - $calc[6] +
(int)$splitRendering['compX'] +
(int)$strCfg['xSpaceBefore'] +
(int)$strCfg['xSpaceAfter'];
990 $offsetInfo[5] +
= $calc[5] - $calc[7] - (int)$splitRendering['compY'] - (int)$strCfg['ySpaceBefore'] - (int)$strCfg['ySpaceAfter'];
993 debug('cannot read file: ' . $fontFile, \TYPO3\CMS\Core\Imaging\GraphicalFunctions
::class . '::ImageTTFBBoxWrapper()');
1000 * Wrapper for ImageTTFText
1002 * @param resource $im (See argument for PHP function imageTTFtext())
1003 * @param int $fontSize (See argument for PHP function imageTTFtext())
1004 * @param int $angle (See argument for PHP function imageTTFtext())
1005 * @param int $x (See argument for PHP function imageTTFtext())
1006 * @param int $y (See argument for PHP function imageTTFtext())
1007 * @param int $color (See argument for PHP function imageTTFtext())
1008 * @param string $fontFile (See argument for PHP function imageTTFtext())
1009 * @param string $string (See argument for PHP function imageTTFtext()). UTF-8 string, possibly with entities in.
1010 * @param array $splitRendering Split-rendering configuration
1011 * @param int $sF Scale factor
1014 public function ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering, $sF = 1) {
1016 $stringParts = $this->splitString($string, $splitRendering, $fontSize, $fontFile);
1017 $x = ceil($sF * $x);
1018 $y = ceil($sF * $y);
1019 // Traverse string parts:
1020 foreach ($stringParts as $i => $strCfg) {
1022 $colorIndex = $color;
1023 // Set custom color if any (only when niceText is off):
1024 if ($strCfg['color'] && $sF == 1) {
1025 $cols = $this->convertColor($strCfg['color']);
1026 $colorIndex = ImageColorAllocate($im, $cols[0], $cols[1], $cols[2]);
1027 $colorIndex = $color >= 0 ?
$colorIndex : -$colorIndex;
1029 // Setting xSpaceBefore
1031 $x +
= (int)$strCfg['xSpaceBefore'];
1032 $y -= (int)$strCfg['ySpaceBefore'];
1034 $fontFile = self
::prependAbsolutePath($strCfg['fontFile']);
1035 if (is_readable($fontFile)) {
1037 ImageTTFText($im, GeneralUtility
::freetypeDpiComp($sF * $strCfg['fontSize']), $angle, $x, $y, $colorIndex, $fontFile, $strCfg['str']);
1038 // Calculate offset to apply:
1039 $wordInf = ImageTTFBBox(GeneralUtility
::freetypeDpiComp($sF * $strCfg['fontSize']), $angle, self
::prependAbsolutePath($strCfg['fontFile']), $strCfg['str']);
1040 $x +
= $wordInf[2] - $wordInf[0] +
(int)$splitRendering['compX'] +
(int)$strCfg['xSpaceAfter'];
1041 $y +
= $wordInf[5] - $wordInf[7] - (int)$splitRendering['compY'] - (int)$strCfg['ySpaceAfter'];
1043 debug('cannot read file: ' . $fontFile, \TYPO3\CMS\Core\Imaging\GraphicalFunctions
::class . '::ImageTTFTextWrapper()');
1049 * Splitting a string for ImageTTFBBox up into an array where each part has its own configuration options.
1051 * @param string $string UTF-8 string
1052 * @param array $splitRendering Split-rendering configuration from GIFBUILDER TEXT object.
1053 * @param int $fontSize Current fontsize
1054 * @param string $fontFile Current font file
1055 * @return array Array with input string splitted according to configuration
1057 public function splitString($string, $splitRendering, $fontSize, $fontFile) {
1058 // Initialize by setting the whole string and default configuration as the first entry.
1062 'fontSize' => $fontSize,
1063 'fontFile' => $fontFile
1065 // Traverse the split-rendering configuration:
1066 // Splitting will create more entries in $result with individual configurations.
1067 if (is_array($splitRendering)) {
1068 $sKeyArray = \TYPO3\CMS\Core\TypoScript\TemplateService
::sortedKeyList($splitRendering);
1069 // Traverse configured options:
1070 foreach ($sKeyArray as $key) {
1071 $cfg = $splitRendering[$key . '.'];
1072 // Process each type of split rendering keyword:
1073 switch ((string)$splitRendering[$key]) {
1074 case 'highlightWord':
1075 if ((string)$cfg['value'] !== '') {
1076 $newResult = array();
1077 // Traverse the current parts of the result array:
1078 foreach ($result as $part) {
1079 // Explode the string value by the word value to highlight:
1080 $explodedParts = explode($cfg['value'], $part['str']);
1081 foreach ($explodedParts as $c => $expValue) {
1082 if ((string)$expValue !== '') {
1083 $newResult[] = array_merge($part, array('str' => $expValue));
1085 if ($c +
1 < count($explodedParts)) {
1086 $newResult[] = array(
1087 'str' => $cfg['value'],
1088 'fontSize' => $cfg['fontSize'] ?
$cfg['fontSize'] : $part['fontSize'],
1089 'fontFile' => $cfg['fontFile'] ?
$cfg['fontFile'] : $part['fontFile'],
1090 'color' => $cfg['color'],
1091 'xSpaceBefore' => $cfg['xSpaceBefore'],
1092 'xSpaceAfter' => $cfg['xSpaceAfter'],
1093 'ySpaceBefore' => $cfg['ySpaceBefore'],
1094 'ySpaceAfter' => $cfg['ySpaceAfter']
1099 // Set the new result as result array:
1100 if (count($newResult)) {
1101 $result = $newResult;
1106 if ((string)$cfg['value'] !== '') {
1107 // Initialize range:
1108 $ranges = GeneralUtility
::trimExplode(',', $cfg['value'], TRUE);
1109 foreach ($ranges as $i => $rangeDef) {
1110 $ranges[$i] = GeneralUtility
::intExplode('-', $ranges[$i]);
1111 if (!isset($ranges[$i][1])) {
1112 $ranges[$i][1] = $ranges[$i][0];
1115 $newResult = array();
1116 // Traverse the current parts of the result array:
1117 foreach ($result as $part) {
1121 // Explode the string value by the word value to highlight:
1122 $utf8Chars = $this->singleChars($part['str']);
1123 foreach ($utf8Chars as $utfChar) {
1124 // Find number and evaluate position:
1125 $uNumber = $this->csConvObj
->utf8CharToUnumber($utfChar);
1127 foreach ($ranges as $rangeDef) {
1128 if ($uNumber >= $rangeDef[0] && (!$rangeDef[1] ||
$uNumber <= $rangeDef[1])) {
1133 if ($currentState == -1) {
1134 $currentState = $inRange;
1136 // Initialize first char
1138 if ($inRange != $currentState && !GeneralUtility
::inList('32,10,13,9', $uNumber)) {
1140 if ($bankAccum !== '') {
1141 $newResult[] = array(
1142 'str' => $bankAccum,
1143 'fontSize' => $currentState && $cfg['fontSize'] ?
$cfg['fontSize'] : $part['fontSize'],
1144 'fontFile' => $currentState && $cfg['fontFile'] ?
$cfg['fontFile'] : $part['fontFile'],
1145 'color' => $currentState ?
$cfg['color'] : '',
1146 'xSpaceBefore' => $currentState ?
$cfg['xSpaceBefore'] : '',
1147 'xSpaceAfter' => $currentState ?
$cfg['xSpaceAfter'] : '',
1148 'ySpaceBefore' => $currentState ?
$cfg['ySpaceBefore'] : '',
1149 'ySpaceAfter' => $currentState ?
$cfg['ySpaceAfter'] : ''
1152 // Initialize new settings:
1153 $currentState = $inRange;
1156 // Add char to bank:
1157 $bankAccum .= $utfChar;
1159 // Set result for FINAL part:
1160 if ($bankAccum !== '') {
1161 $newResult[] = array(
1162 'str' => $bankAccum,
1163 'fontSize' => $currentState && $cfg['fontSize'] ?
$cfg['fontSize'] : $part['fontSize'],
1164 'fontFile' => $currentState && $cfg['fontFile'] ?
$cfg['fontFile'] : $part['fontFile'],
1165 'color' => $currentState ?
$cfg['color'] : '',
1166 'xSpaceBefore' => $currentState ?
$cfg['xSpaceBefore'] : '',
1167 'xSpaceAfter' => $currentState ?
$cfg['xSpaceAfter'] : '',
1168 'ySpaceBefore' => $currentState ?
$cfg['ySpaceBefore'] : '',
1169 'ySpaceAfter' => $currentState ?
$cfg['ySpaceAfter'] : ''
1173 // Set the new result as result array:
1174 if (count($newResult)) {
1175 $result = $newResult;
1186 * Calculates the spacing and wordSpacing values
1188 * @param array $conf TypoScript array for the TEXT GIFBUILDER object
1189 * @param int $scaleFactor TypoScript value from eg $conf['niceText.']['scaleFactor']
1190 * @return array Array with two keys [0]/[1] being array($spacing,$wordSpacing)
1194 public function calcWordSpacing($conf, $scaleFactor = 1) {
1195 $spacing = (int)$conf['spacing'];
1196 $wordSpacing = (int)$conf['wordSpacing'];
1197 $wordSpacing = $wordSpacing ?
: $spacing * 2;
1198 $spacing *= $scaleFactor;
1199 $wordSpacing *= $scaleFactor;
1200 return array($spacing, $wordSpacing);
1204 * Calculates and returns the niceText.scaleFactor
1206 * @param array $conf TypoScript array for the TEXT GIFBUILDER object
1207 * @return int TypoScript value from eg $conf['niceText.']['scaleFactor']
1210 public function getTextScalFactor($conf) {
1211 if (!$conf['niceText']) {
1215 $sF = MathUtility
::forceIntegerInRange($conf['niceText.']['scaleFactor'], 2, 5);
1221 * Renders a regular text and takes care of a possible line break automatically.
1223 * @param resource $im (See argument for PHP function imageTTFtext())
1224 * @param int $fontSize (See argument for PHP function imageTTFtext())
1225 * @param int $angle (See argument for PHP function imageTTFtext())
1226 * @param int $x (See argument for PHP function imageTTFtext())
1227 * @param int $y (See argument for PHP function imageTTFtext())
1228 * @param int $color (See argument for PHP function imageTTFtext())
1229 * @param string $fontFile (See argument for PHP function imageTTFtext())
1230 * @param string $string (See argument for PHP function imageTTFtext()). UTF-8 string, possibly with entities in.
1231 * @param array $splitRendering Split-rendering configuration
1232 * @param array $conf The configuration
1233 * @param int $sF Scale factor
1236 protected function renderTTFText(&$im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering, $conf, $sF = 1) {
1237 if (isset($conf['breakWidth']) && $conf['breakWidth'] && $this->getRenderedTextWidth($string, $conf) > $conf['breakWidth']) {
1240 $breakWidth = $conf['breakWidth'];
1241 $breakSpace = $this->getBreakSpace($conf);
1242 $wordPairs = $this->getWordPairsForLineBreak($string);
1243 // Iterate through all word pairs:
1244 foreach ($wordPairs as $index => $wordPair) {
1245 $wordWidth = $this->getRenderedTextWidth($wordPair, $conf);
1246 if ($index == 0 ||
$currentWidth +
$wordWidth <= $breakWidth) {
1247 $currentWidth +
= $wordWidth;
1248 $phrase .= $wordPair;
1250 // Render the current phrase that is below breakWidth:
1251 $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $phrase, $splitRendering, $sF);
1252 // Calculate the news height offset:
1254 // Restart the phrase:
1255 $currentWidth = $wordWidth;
1256 $phrase = $wordPair;
1259 // Render the remaining phrase:
1260 if ($currentWidth) {
1261 $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $phrase, $splitRendering, $sF);
1264 $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering, $sF);
1269 * Gets the word pairs used for automatic line breaks.
1271 * @param string $string
1274 protected function getWordPairsForLineBreak($string) {
1275 $wordPairs = array();
1276 $wordsArray = preg_split('#([- .,!:]+)#', $string, -1, PREG_SPLIT_DELIM_CAPTURE
);
1277 $wordsCount = count($wordsArray);
1278 for ($index = 0; $index < $wordsCount; $index +
= 2) {
1279 $wordPairs[] = $wordsArray[$index] . $wordsArray[($index +
1)];
1285 * Gets the rendered text width.
1287 * @param string $text
1288 * @param array $conf
1291 protected function getRenderedTextWidth($text, $conf) {
1292 $bounds = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $this->recodeString($text), $conf['splitRendering.']);
1293 if ($conf['angle'] < 0) {
1294 $pixelWidth = abs($bounds[4] - $bounds[0]);
1295 } elseif ($conf['angle'] > 0) {
1296 $pixelWidth = abs($bounds[2] - $bounds[6]);
1298 $pixelWidth = abs($bounds[4] - $bounds[6]);
1304 * Gets the break space for each new line.
1306 * @param array $conf TypoScript configuration for the currently rendered object
1307 * @param array $boundingBox The bounding box the the currently rendered object
1308 * @return int The break space
1310 protected function getBreakSpace($conf, array $boundingBox = NULL) {
1311 if (!isset($boundingBox)) {
1312 $boundingBox = $this->calcBBox($conf);
1313 $boundingBox = $boundingBox[2];
1315 if (isset($conf['breakSpace']) && $conf['breakSpace']) {
1316 $breakSpace = $boundingBox['lineHeight'] * $conf['breakSpace'];
1318 $breakSpace = $boundingBox['lineHeight'];
1323 /*********************************************
1325 * Other GIFBUILDER objects related to TEXT
1327 *********************************************/
1329 * Implements the "OUTLINE" GIFBUILDER object / property for the TEXT object
1331 * @param resource $im GDlib image pointer
1332 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1333 * @param array $workArea The current working area coordinates.
1334 * @param array $txtConf TypoScript array with configuration for the associated TEXT GIFBUILDER object.
1336 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make(), makeText()
1338 public function makeOutline(&$im, $conf, $workArea, $txtConf) {
1339 $thickness = (int)$conf['thickness'];
1341 $txtConf['fontColor'] = $conf['color'];
1342 $outLineDist = MathUtility
::forceIntegerInRange($thickness, 1, 2);
1343 for ($b = 1; $b <= $outLineDist; $b++
) {
1349 $outL = $this->circleOffset($b, $it);
1350 for ($a = 0; $a < $it; $a++
) {
1351 $this->makeText($im, $txtConf, $this->applyOffset($workArea, $outL[$a]));
1358 * Creates some offset values in an array used to simulate a circularly applied outline around TEXT
1362 * @param int $distance Distance
1363 * @param int $iterations Iterations.
1365 * @see makeOutline()
1367 public function circleOffset($distance, $iterations) {
1369 if ($distance && $iterations) {
1370 for ($a = 0; $a < $iterations; $a++
) {
1371 $yOff = round(sin((2 * pi() / $iterations * ($a +
1))) * 100 * $distance);
1373 $yOff = (int)(ceil(abs(($yOff / 100))) * ($yOff / abs($yOff)));
1375 $xOff = round(cos((2 * pi() / $iterations * ($a +
1))) * 100 * $distance);
1377 $xOff = (int)(ceil(abs(($xOff / 100))) * ($xOff / abs($xOff)));
1379 $res[$a] = array($xOff, $yOff);
1386 * Implements the "EMBOSS" GIFBUILDER object / property for the TEXT object
1388 * @param resource $im GDlib image pointer
1389 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1390 * @param array $workArea The current working area coordinates.
1391 * @param array $txtConf TypoScript array with configuration for the associated TEXT GIFBUILDER object.
1393 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make(), makeShadow()
1395 public function makeEmboss(&$im, $conf, $workArea, $txtConf) {
1396 $conf['color'] = $conf['highColor'];
1397 $this->makeShadow($im, $conf, $workArea, $txtConf);
1398 $newOffset = GeneralUtility
::intExplode(',', $conf['offset']);
1399 $newOffset[0] *= -1;
1400 $newOffset[1] *= -1;
1401 $conf['offset'] = implode(',', $newOffset);
1402 $conf['color'] = $conf['lowColor'];
1403 $this->makeShadow($im, $conf, $workArea, $txtConf);
1407 * Implements the "SHADOW" GIFBUILDER object / property for the TEXT object
1408 * The operation involves ImageMagick for combining.
1410 * @param resource $im GDlib image pointer
1411 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1412 * @param array $workArea The current working area coordinates.
1413 * @param array $txtConf TypoScript array with configuration for the associated TEXT GIFBUILDER object.
1415 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make(), makeText(), makeEmboss()
1417 public function makeShadow(&$im, $conf, $workArea, $txtConf) {
1418 $workArea = $this->applyOffset($workArea, GeneralUtility
::intExplode(',', $conf['offset']));
1419 $blurRate = MathUtility
::forceIntegerInRange((int)$conf['blur'], 0, 99);
1420 // No effects if ImageMagick ver. 5+
1421 if (!$blurRate ||
$this->NO_IM_EFFECTS
) {
1422 $txtConf['fontColor'] = $conf['color'];
1423 $this->makeText($im, $txtConf, $workArea);
1427 // Area around the blur used for cropping something
1429 $tmpStr = $this->randomName();
1430 $fileMenu = $tmpStr . '_menu.' . $this->gifExtension
;
1431 $fileColor = $tmpStr . '_color.' . $this->gifExtension
;
1432 $fileMask = $tmpStr . '_mask.' . $this->gifExtension
;
1433 // BlurColor Image laves
1434 $blurColImg = imagecreatetruecolor($w, $h);
1435 $bcols = $this->convertColor($conf['color']);
1436 $Bcolor = ImageColorAllocate($blurColImg, $bcols[0], $bcols[1], $bcols[2]);
1437 ImageFilledRectangle($blurColImg, 0, 0, $w, $h, $Bcolor);
1438 $this->ImageWrite($blurColImg, $fileColor);
1439 ImageDestroy($blurColImg);
1440 // The mask is made: BlurTextImage
1441 $blurTextImg = imagecreatetruecolor($w +
$blurBorder * 2, $h +
$blurBorder * 2);
1443 $Bcolor = ImageColorAllocate($blurTextImg, 0, 0, 0);
1444 ImageFilledRectangle($blurTextImg, 0, 0, $w +
$blurBorder * 2, $h +
$blurBorder * 2, $Bcolor);
1445 $txtConf['fontColor'] = 'white';
1446 $blurBordArr = array($blurBorder, $blurBorder);
1447 $this->makeText($blurTextImg, $txtConf, $this->applyOffset($workArea, $blurBordArr));
1448 // Dump to temporary file
1449 $this->ImageWrite($blurTextImg, $fileMask);
1451 ImageDestroy($blurTextImg);
1453 if ($this->V5_EFFECTS
) {
1454 $command .= $this->v5_blur($blurRate +
1);
1456 // Blurring of the mask
1457 // How many blur-commands that is executed. Min = 1;
1458 $times = ceil($blurRate / 10);
1459 // 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.
1460 $newBlurRate = $blurRate * 4;
1461 $newBlurRate = MathUtility
::forceIntegerInRange($newBlurRate, 1, 99);
1462 // Building blur-command
1463 for ($a = 0; $a < $times; $a++
) {
1464 $command .= ' -blur ' . $blurRate;
1467 $this->imageMagickExec($fileMask, $fileMask, $command . ' +matte');
1468 // The mask is loaded again
1469 $blurTextImg_tmp = $this->imageCreateFromFile($fileMask);
1470 // If nothing went wrong we continue with the blurred mask
1471 if ($blurTextImg_tmp) {
1472 // Cropping the border from the mask
1473 $blurTextImg = imagecreatetruecolor($w, $h);
1474 $this->imagecopyresized($blurTextImg, $blurTextImg_tmp, 0, 0, $blurBorder, $blurBorder, $w, $h, $w, $h);
1475 // Destroy the temporary mask
1476 ImageDestroy($blurTextImg_tmp);
1479 if ($conf['intensity']) {
1480 $intensity = MathUtility
::forceIntegerInRange($conf['intensity'], 0, 100);
1482 $intensity = ceil(255 - $intensity / 100 * 255);
1483 $this->inputLevels($blurTextImg, 0, $intensity);
1484 $opacity = MathUtility
::forceIntegerInRange((int)$conf['opacity'], 0, 100);
1485 if ($opacity && $opacity < 100) {
1486 $high = ceil(255 * $opacity / 100);
1487 // Reducing levels as the opacity demands
1488 $this->outputLevels($blurTextImg, 0, $high);
1490 // Dump the mask again
1491 $this->ImageWrite($blurTextImg, $fileMask);
1493 ImageDestroy($blurTextImg);
1494 // The pictures are combined
1495 // The main pictures is saved temporarily
1496 $this->ImageWrite($im, $fileMenu);
1497 $this->combineExec($fileMenu, $fileColor, $fileMask, $fileMenu);
1498 // The main image is loaded again...
1499 $backIm = $this->imageCreateFromFile($fileMenu);
1500 // ... and if nothing went wrong we load it onto the old one.
1502 if (!$this->saveAlphaLayer
) {
1503 ImageColorTransparent($backIm, -1);
1508 // Deleting temporary files;
1509 if (!$this->dontUnlinkTempFiles
) {
1517 /****************************
1519 * Other GIFBUILDER objects
1521 ****************************/
1523 * Implements the "BOX" GIFBUILDER object
1525 * @param resource $im GDlib image pointer
1526 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1527 * @param array $workArea The current working area coordinates.
1529 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make()
1531 public function makeBox(&$im, $conf, $workArea) {
1532 $cords = GeneralUtility
::intExplode(',', $conf['dimensions'] . ',,,');
1533 $conf['offset'] = $cords[0] . ',' . $cords[1];
1534 $cords = $this->objPosition($conf, $workArea, array($cords[2], $cords[3]));
1535 $cols = $this->convertColor($conf['color']);
1537 if (isset($conf['opacity'])) {
1539 // PHP 0 = opaque, 127 = transparent
1540 // TYPO3 100 = opaque, 0 = transparent
1541 $opacity = MathUtility
::forceIntegerInRange((int)$conf['opacity'], 1, 100, 1);
1542 $opacity = abs($opacity - 100);
1543 $opacity = round(127 * $opacity / 100);
1545 $tmpColor = ImageColorAllocateAlpha($im, $cols[0], $cols[1], $cols[2], $opacity);
1546 imagefilledrectangle($im, $cords[0], $cords[1], $cords[0] +
$cords[2] - 1, $cords[1] +
$cords[3] - 1, $tmpColor);
1550 * Implements the "Ellipse" GIFBUILDER object
1551 * Example Typoscript:
1558 * 10.dimensions = 100,100,50,50
1562 * $conf['dimensions'] = offset x, offset y, width of ellipse, height of ellipse
1564 * @param resource $im GDlib image pointer
1565 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1566 * @param array $workArea The current working area coordinates.
1568 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make()
1570 public function makeEllipse(&$im, array $conf, array $workArea) {
1571 $ellipseConfiguration = GeneralUtility
::intExplode(',', $conf['dimensions'] . ',,,');
1572 // Ellipse offset inside workArea (x/y)
1573 $conf['offset'] = $ellipseConfiguration[0] . ',' . $ellipseConfiguration[1];
1575 $imageCoordinates = $this->objPosition($conf, $workArea, array($ellipseConfiguration[2], $ellipseConfiguration[3]));
1576 $color = $this->convertColor($conf['color']);
1577 $fillingColor = imagecolorallocate($im, $color[0], $color[1], $color[2]);
1578 imagefilledellipse($im, $imageCoordinates[0], $imageCoordinates[1], $imageCoordinates[2], $imageCoordinates[3], $fillingColor);
1582 * Implements the "EFFECT" GIFBUILDER object
1583 * The operation involves ImageMagick for applying effects
1585 * @param resource $im GDlib image pointer
1586 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1588 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make(), applyImageMagickToPHPGif()
1590 public function makeEffect(&$im, $conf) {
1591 $commands = $this->IMparams($conf['value']);
1593 $this->applyImageMagickToPHPGif($im, $commands);
1598 * Creating ImageMagick parameters from TypoScript property
1600 * @param string $setup A string with effect keywords=value pairs separated by "|
1601 * @return string ImageMagick prepared parameters.
1605 public function IMparams($setup) {
1606 if (!trim($setup)) {
1609 $effects = explode('|', $setup);
1611 foreach ($effects as $val) {
1612 $pairs = explode('=', $val, 2);
1613 $value = trim($pairs[1]);
1614 $effect = strtolower(trim($pairs[0]));
1617 $commands .= ' -gamma ' . doubleval($value);
1620 if (!$this->NO_IM_EFFECTS
) {
1621 if ($this->V5_EFFECTS
) {
1622 $commands .= $this->v5_blur($value);
1624 $commands .= ' -blur ' . MathUtility
::forceIntegerInRange($value, 1, 99);
1629 if (!$this->NO_IM_EFFECTS
) {
1630 if ($this->V5_EFFECTS
) {
1631 $commands .= $this->v5_sharpen($value);
1633 $commands .= ' -sharpen ' . MathUtility
::forceIntegerInRange($value, 1, 99);
1638 $commands .= ' -rotate ' . MathUtility
::forceIntegerInRange($value, 0, 360);
1641 $commands .= ' -solarize ' . MathUtility
::forceIntegerInRange($value, 0, 99);
1644 $commands .= ' -swirl ' . MathUtility
::forceIntegerInRange($value, 0, 1000);
1647 $params = GeneralUtility
::intExplode(',', $value);
1648 $commands .= ' -wave ' . MathUtility
::forceIntegerInRange($params[0], 0, 99) . 'x' . MathUtility
::forceIntegerInRange($params[1], 0, 99);
1651 $commands .= ' -charcoal ' . MathUtility
::forceIntegerInRange($value, 0, 100);
1654 $commands .= ' -colorspace GRAY';
1657 $commands .= ' -edge ' . MathUtility
::forceIntegerInRange($value, 0, 99);
1660 $commands .= ' -emboss';
1663 $commands .= ' -flip';
1666 $commands .= ' -flop';
1669 $commands .= ' -colors ' . MathUtility
::forceIntegerInRange($value, 2, 255);
1672 $commands .= ' -shear ' . MathUtility
::forceIntegerInRange($value, -90, 90);
1675 $commands .= ' -negate';
1683 * Implements the "ADJUST" GIFBUILDER object
1685 * @param resource $im GDlib image pointer
1686 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1688 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make(), autoLevels(), outputLevels(), inputLevels()
1690 public function adjust(&$im, $conf) {
1691 $setup = $conf['value'];
1692 if (!trim($setup)) {
1695 $effects = explode('|', $setup);
1696 foreach ($effects as $val) {
1697 $pairs = explode('=', $val, 2);
1698 $value = trim($pairs[1]);
1699 $effect = strtolower(trim($pairs[0]));
1703 $params = GeneralUtility
::intExplode(',', $value);
1704 $this->inputLevels($im, $params[0], $params[1]);
1706 case 'outputlevels':
1707 $params = GeneralUtility
::intExplode(',', $value);
1708 $this->outputLevels($im, $params[0], $params[1]);
1711 $this->autoLevels($im);
1718 * Implements the "CROP" GIFBUILDER object
1720 * @param resource $im GDlib image pointer
1721 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1723 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make()
1725 public function crop(&$im, $conf) {
1726 // Clears workArea to total image
1727 $this->setWorkArea('');
1728 $cords = GeneralUtility
::intExplode(',', $conf['crop'] . ',,,');
1729 $conf['offset'] = $cords[0] . ',' . $cords[1];
1730 $cords = $this->objPosition($conf, $this->workArea
, array($cords[2], $cords[3]));
1731 $newIm = imagecreatetruecolor($cords[2], $cords[3]);
1732 $cols = $this->convertColor($conf['backColor'] ?
$conf['backColor'] : $this->setup
['backColor']);
1733 $Bcolor = ImageColorAllocate($newIm, $cols[0], $cols[1], $cols[2]);
1734 ImageFilledRectangle($newIm, 0, 0, $cords[2], $cords[3], $Bcolor);
1736 $workArea = array(0, 0, $cords[2], $cords[3]);
1737 if ($cords[0] < 0) {
1738 $workArea[0] = abs($cords[0]);
1740 $newConf['offset'] = -$cords[0];
1742 if ($cords[1] < 0) {
1743 $workArea[1] = abs($cords[1]);
1745 $newConf['offset'] .= ',' . -$cords[1];
1747 $this->copyGifOntoGif($newIm, $im, $newConf, $workArea);
1749 $this->w
= imagesx($im);
1750 $this->h
= imagesy($im);
1751 // Clears workArea to total image
1752 $this->setWorkArea('');
1756 * Implements the "SCALE" GIFBUILDER object
1758 * @param resource $im GDlib image pointer
1759 * @param array $conf TypoScript array with configuration for the GIFBUILDER object.
1761 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make()
1763 public function scale(&$im, $conf) {
1764 if ($conf['width'] ||
$conf['height'] ||
$conf['params']) {
1765 $tmpStr = $this->randomName();
1766 $theFile = $tmpStr . '.' . $this->gifExtension
;
1767 $this->ImageWrite($im, $theFile);
1768 $theNewFile = $this->imageMagickConvert($theFile, $this->gifExtension
, $conf['width'], $conf['height'], $conf['params']);
1769 $tmpImg = $this->imageCreateFromFile($theNewFile[3]);
1773 $this->w
= imagesx($im);
1774 $this->h
= imagesy($im);
1775 // Clears workArea to total image
1776 $this->setWorkArea('');
1778 if (!$this->dontUnlinkTempFiles
) {
1780 if ($theNewFile[3] && $theNewFile[3] != $theFile) {
1781 unlink($theNewFile[3]);
1788 * Implements the "WORKAREA" GIFBUILDER object when setting it
1789 * Setting internal working area boundaries (->workArea)
1791 * @param string $workArea Working area dimensions, comma separated
1794 * @see \TYPO3\CMS\Frontend\Imaging\GifBuilder::make()
1796 public function setWorkArea($workArea) {
1797 $this->workArea
= GeneralUtility
::intExplode(',', $workArea);
1798 $this->workArea
= $this->applyOffset($this->workArea
, $this->OFFSET
);
1799 if (!$this->workArea
[2]) {
1800 $this->workArea
[2] = $this->w
;
1802 if (!$this->workArea
[3]) {
1803 $this->workArea
[3] = $this->h
;
1807 /*************************
1809 * Adjustment functions
1811 ************************/
1813 * Apply auto-levels to input image pointer
1815 * @param int $im GDlib Image Pointer
1818 public function autolevels(&$im) {
1819 $totalCols = ImageColorsTotal($im);
1822 for ($c = 0; $c < $totalCols; $c++
) {
1823 $cols = ImageColorsForIndex($im, $c);
1824 $grayArr[] = round(($cols['red'] +
$cols['green'] +
$cols['blue']) / 3);
1826 $min = min($grayArr);
1827 $max = max($grayArr);
1828 $delta = $max - $min;
1830 for ($c = 0; $c < $totalCols; $c++
) {
1831 $cols = ImageColorsForIndex($im, $c);
1832 $cols['red'] = floor(($cols['red'] - $min) / $delta * 255);
1833 $cols['green'] = floor(($cols['green'] - $min) / $delta * 255);
1834 $cols['blue'] = floor(($cols['blue'] - $min) / $delta * 255);
1835 ImageColorSet($im, $c, $cols['red'], $cols['green'], $cols['blue']);
1841 * Apply output levels to input image pointer (decreasing contrast)
1843 * @param int $im GDlib Image Pointer
1844 * @param int $low The "low" value (close to 0)
1845 * @param int $high The "high" value (close to 255)
1846 * @param bool $swap If swap, then low and high are swapped. (Useful for negated masks...)
1849 public function outputLevels(&$im, $low, $high, $swap = '') {
1851 $low = MathUtility
::forceIntegerInRange($low, 0, 255);
1852 $high = MathUtility
::forceIntegerInRange($high, 0, 255);
1856 $high = 255 - $temp;
1858 $delta = $high - $low;
1859 $totalCols = ImageColorsTotal($im);
1860 for ($c = 0; $c < $totalCols; $c++
) {
1861 $cols = ImageColorsForIndex($im, $c);
1862 $cols['red'] = $low +
floor($cols['red'] / 255 * $delta);
1863 $cols['green'] = $low +
floor($cols['green'] / 255 * $delta);
1864 $cols['blue'] = $low +
floor($cols['blue'] / 255 * $delta);
1865 ImageColorSet($im, $c, $cols['red'], $cols['green'], $cols['blue']);
1871 * Apply input levels to input image pointer (increasing contrast)
1873 * @param int $im GDlib Image Pointer
1874 * @param int $low The "low" value (close to 0)
1875 * @param int $high The "high" value (close to 255)
1878 public function inputLevels(&$im, $low, $high) {
1880 $low = MathUtility
::forceIntegerInRange($low, 0, 255);
1881 $high = MathUtility
::forceIntegerInRange($high, 0, 255);
1882 $delta = $high - $low;
1883 $totalCols = ImageColorsTotal($im);
1884 for ($c = 0; $c < $totalCols; $c++
) {
1885 $cols = ImageColorsForIndex($im, $c);
1886 $cols['red'] = MathUtility
::forceIntegerInRange(($cols['red'] - $low) / $delta * 255, 0, 255);
1887 $cols['green'] = MathUtility
::forceIntegerInRange(($cols['green'] - $low) / $delta * 255, 0, 255);
1888 $cols['blue'] = MathUtility
::forceIntegerInRange(($cols['blue'] - $low) / $delta * 255, 0, 255);
1889 ImageColorSet($im, $c, $cols['red'], $cols['green'], $cols['blue']);
1895 * Reduce colors in image using IM and create a palette based image if possible (<=256 colors)
1897 * @param string $file Image file to reduce
1898 * @param int $cols Number of colors to reduce the image to.
1899 * @return string Reduced file
1901 public function IMreduceColors($file, $cols) {
1902 $fI = GeneralUtility
::split_fileref($file);
1903 $ext = strtolower($fI['fileext']);
1904 $result = $this->randomName() . '.' . $ext;
1905 if (($reduce = MathUtility
::forceIntegerInRange($cols, 0, $ext == 'gif' ?
256 : $this->truecolorColors
, 0)) > 0) {
1906 $params = ' -colors ' . $reduce;
1907 if ($reduce <= 256) {
1908 $params .= ' -type Palette';
1910 if ($ext == 'png' && $reduce <= 256) {
1913 $this->imageMagickExec($file, $prefix . $result, $params);
1921 /*********************************
1923 * GIFBUILDER Helper functions
1925 *********************************/
1927 * Checks if the $fontFile is already at an absolute path and if not, prepends the correct path.
1928 * Use PATH_site unless we are in the backend.
1929 * Call it by \TYPO3\CMS\Core\Imaging\GraphicalFunctions::prependAbsolutePath()
1931 * @param string $fontFile The font file
1932 * @return string The font file with absolute path.
1934 public function prependAbsolutePath($fontFile) {
1935 $absPath = defined('PATH_typo3') ?
dirname(PATH_thisScript
) . '/' : PATH_site
;
1936 $fontFile = GeneralUtility
::isAbsPath($fontFile) ?
$fontFile : GeneralUtility
::resolveBackPath($absPath . $fontFile);
1941 * Returns the IM command for sharpening with ImageMagick 5 (when $this->V5_EFFECTS is set).
1942 * Uses $this->im5fx_sharpenSteps for translation of the factor to an actual command.
1944 * @param int $factor The sharpening factor, 0-100 (effectively in 10 steps)
1945 * @return string The sharpening command, eg. " -sharpen 3x4
1946 * @see makeText(), IMparams(), v5_blur()
1948 public function v5_sharpen($factor) {
1949 $factor = MathUtility
::forceIntegerInRange(ceil($factor / 10), 0, 10);
1950 $sharpenArr = explode(',', ',' . $this->im5fx_sharpenSteps
);
1951 $sharpenF = trim($sharpenArr[$factor]);
1953 $cmd = ' -sharpen ' . $sharpenF;
1959 * Returns the IM command for blurring with ImageMagick 5 (when $this->V5_EFFECTS is set).
1960 * Uses $this->im5fx_blurSteps for translation of the factor to an actual command.
1962 * @param int $factor The blurring factor, 0-100 (effectively in 10 steps)
1963 * @return string The blurring command, eg. " -blur 3x4
1964 * @see makeText(), IMparams(), v5_sharpen()
1966 public function v5_blur($factor) {
1967 $factor = MathUtility
::forceIntegerInRange(ceil($factor / 10), 0, 10);
1968 $blurArr = explode(',', ',' . $this->im5fx_blurSteps
);
1969 $blurF = trim($blurArr[$factor]);
1971 $cmd = ' -blur ' . $blurF;
1977 * Returns a random filename prefixed with "temp_" and then 32 char md5 hash (without extension) from $this->tempPath.
1978 * 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.
1982 public function randomName() {
1983 $this->createTempSubDir('temp/');
1984 return $this->tempPath
. 'temp/' . md5(uniqid('', TRUE));
1988 * Applies offset value to coordinated in $cords.
1989 * Basically the value of key 0/1 of $OFFSET is added to keys 0/1 of $cords
1991 * @param array $cords Integer coordinates in key 0/1
1992 * @param array $OFFSET Offset values in key 0/1
1993 * @return array Modified $cords array
1995 public function applyOffset($cords, $OFFSET) {
1996 $cords[0] = (int)$cords[0] +
(int)$OFFSET[0];
1997 $cords[1] = (int)$cords[1] +
(int)$OFFSET[1];
2002 * Converts a "HTML-color" TypoScript datatype to RGB-values.
2005 * @param string $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
2006 * @return array RGB values in key 0/1/2 of the array
2008 public function convertColor($string) {
2010 $cParts = explode(':', $string, 2);
2011 // Finding the RGB definitions of the color:
2012 $string = $cParts[0];
2013 if (strstr($string, '#')) {
2014 $string = preg_replace('/[^A-Fa-f0-9]*/', '', $string);
2015 $col[] = HexDec(substr($string, 0, 2));
2016 $col[] = HexDec(substr($string, 2, 2));
2017 $col[] = HexDec(substr($string, 4, 2));
2018 } elseif (strstr($string, ',')) {
2019 $string = preg_replace('/[^,0-9]*/', '', $string);
2020 $strArr = explode(',', $string);
2021 $col[] = (int)$strArr[0];
2022 $col[] = (int)$strArr[1];
2023 $col[] = (int)$strArr[2];
2025 $string = strtolower(trim($string));
2026 if ($this->colMap
[$string]) {
2027 $col = $this->colMap
[$string];
2029 $col = array(0, 0, 0);
2032 // ... and possibly recalculating the value
2033 if (trim($cParts[1])) {
2034 $cParts[1] = trim($cParts[1]);
2035 if ($cParts[1][0] === '*') {
2036 $val = doubleval(substr($cParts[1], 1));
2037 $col[0] = MathUtility
::forceIntegerInRange($col[0] * $val, 0, 255);
2038 $col[1] = MathUtility
::forceIntegerInRange($col[1] * $val, 0, 255);
2039 $col[2] = MathUtility
::forceIntegerInRange($col[2] * $val, 0, 255);
2041 $val = (int)$cParts[1];
2042 $col[0] = MathUtility
::forceIntegerInRange($col[0] +
$val, 0, 255);
2043 $col[1] = MathUtility
::forceIntegerInRange($col[1] +
$val, 0, 255);
2044 $col[2] = MathUtility
::forceIntegerInRange($col[2] +
$val, 0, 255);
2052 * Used with text strings for fonts when languages has other character sets.
2054 * @param string The text to recode
2055 * @return string The recoded string. Should be UTF-8 output. MAY contain entities (eg. { or &#quot; which should render as real chars).
2057 public function recodeString($string) {
2058 // Recode string to UTF-8 from $this->nativeCharset:
2059 if ($this->nativeCharset
&& $this->nativeCharset
!= 'utf-8') {
2061 $string = $this->csConvObj
->utf8_encode($string, $this->nativeCharset
);
2067 * Split a string into an array of individual characters
2068 * 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.
2070 * @param string $theText The text string to split
2071 * @param bool $returnUnicodeNumber Return Unicode numbers instead of chars.
2072 * @return array Numerical array with a char as each value.
2074 public function singleChars($theText, $returnUnicodeNumber = FALSE) {
2075 if ($this->nativeCharset
) {
2076 // Get an array of separated UTF-8 chars
2077 return $this->csConvObj
->utf8_to_numberarray($theText, 1, $returnUnicodeNumber ?
0 : 1);
2080 $c = strlen($theText);
2081 for ($a = 0; $a < $c; $a++
) {
2082 $output[] = substr($theText, $a, 1);
2089 * 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
2091 * @param array $conf TypoScript configuration for a GIFBUILDER object
2092 * @param array makeBox Workarea definition
2093 * @param array $BB BB (Bounding box) array. Not just used for TEXT objects but also for others
2094 * @return array [0]=x, [1]=y, [2]=w, [3]=h
2096 * @see copyGifOntoGif(), makeBox(), crop()
2098 public function objPosition($conf, $workArea, $BB) {
2099 // offset, align, valign, workarea
2101 $result[2] = $BB[0];
2102 $result[3] = $BB[1];
2105 $align = explode(',', $conf['align']);
2106 $align[0] = strtolower(substr(trim($align[0]), 0, 1));
2107 $align[1] = strtolower(substr(trim($align[1]), 0, 1));
2108 switch ($align[0]) {
2110 $result[0] = $w - $result[2];
2113 $result[0] = round(($w - $result[2]) / 2);
2118 switch ($align[1]) {
2121 $result[1] = $h - $result[3];
2124 $result[1] = round(($h - $result[3]) / 2);
2129 $result = $this->applyOffset($result, GeneralUtility
::intExplode(',', $conf['offset']));
2130 $result = $this->applyOffset($result, $workArea);
2134 /***********************************
2136 * Scaling, Dimensions of images
2138 ***********************************/
2140 * Converts $imagefile to another file in temp-dir of type $newExt (extension).
2142 * @param string $imagefile The image filepath
2143 * @param string $newExt 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.
2144 * @param string $w 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
2145 * @param string $h Height. See $w
2146 * @param string $params Additional ImageMagick parameters.
2147 * @param string $frame 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...
2148 * @param array $options An array with options passed to getImageScale (see this function).
2149 * @param bool $mustCreate 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.
2150 * @return array [0]/[1] is w/h, [2] is file extension and [3] is the filename.
2151 * @see getImageScale(), typo3/show_item.php, fileList_ext::renderImage(), \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::getImgResource(), SC_tslib_showpic::show(), maskImageOntoImage(), copyImageOntoImage(), scale()
2153 public function imageMagickConvert($imagefile, $newExt = '', $w = '', $h = '', $params = '', $frame = '', $options = array(), $mustCreate = FALSE) {
2154 if ($this->NO_IMAGE_MAGICK
) {
2155 // Returning file info right away
2156 return $this->getImageDimensions($imagefile);
2158 if ($info = $this->getImageDimensions($imagefile)) {
2159 $newExt = strtolower(trim($newExt));
2160 // If no extension is given the original extension is used
2164 if ($newExt == 'web') {
2165 if (GeneralUtility
::inList($this->webImageExt
, $info[2])) {
2168 $newExt = $this->gif_or_jpg($info[2], $info[0], $info[1]);
2170 $params = $this->cmds
[$newExt];
2174 if (GeneralUtility
::inList($this->imageFileExt
, $newExt)) {
2175 if (strstr($w . $h, 'm')) {
2180 $data = $this->getImageScale($info, $w, $h, $options);
2181 $w = $data['origW'];
2182 $h = $data['origH'];
2183 // If no conversion should be performed
2184 // this flag is TRUE if the width / height does NOT dictate
2185 // the image to be scaled!! (that is if no width / height is
2186 // given or if the destination w/h matches the original image
2187 // dimensions or if the option to not scale the image is set)
2188 $noScale = !$w && !$h ||
$data[0] == $info[0] && $data[1] == $info[1] ||
!empty($options['noScale']);
2189 if ($noScale && !$data['crs'] && !$params && !$frame && $newExt == $info[2] && !$mustCreate) {
2190 // Set the new width and height before returning,
2191 // if the noScale option is set
2192 if (!empty($options['noScale'])) {
2193 $info[0] = $data[0];
2194 $info[1] = $data[1];
2196 $info[3] = $imagefile;
2199 $info[0] = $data[0];
2200 $info[1] = $data[1];
2201 $frame = $this->noFramePrepended ?
'' : (int)$frame;
2203 $params = $this->cmds
[$newExt];
2207 if (!$data['origW']) {
2208 $data['origW'] = $data[0];
2210 if (!$data['origH']) {
2211 $data['origH'] = $data[1];
2213 $offsetX = (int)(($data[0] - $data['origW']) * ($data['cropH'] +
100) / 200);
2214 $offsetY = (int)(($data[1] - $data['origH']) * ($data['cropV'] +
100) / 200);
2215 $params .= ' -crop ' . $data['origW'] . 'x' . $data['origH'] . '+' . $offsetX . '+' . $offsetY . '! ';
2217 $command = $this->scalecmd
. ' ' . $info[0] . 'x' . $info[1] . '! ' . $params . ' ';
2218 $cropscale = $data['crs'] ?
'crs-V' . $data['cropV'] . 'H' . $data['cropH'] : '';
2219 if ($this->alternativeOutputKey
) {
2220 $theOutputName = GeneralUtility
::shortMD5($command . $cropscale . basename($imagefile) . $this->alternativeOutputKey
. '[' . $frame . ']');
2222 $theOutputName = GeneralUtility
::shortMD5($command . $cropscale . $imagefile . filemtime($imagefile) . '[' . $frame . ']');
2224 if ($this->imageMagickConvert_forceFileNameBody
) {
2225 $theOutputName = $this->imageMagickConvert_forceFileNameBody
;
2226 $this->imageMagickConvert_forceFileNameBody
= '';
2228 // Making the temporary filename:
2229 $this->createTempSubDir('pics/');
2230 $output = $this->absPrefix
. $this->tempPath
. 'pics/' . $this->filenamePrefix
. $theOutputName . '.' . $newExt;
2231 if ($this->dontCheckForExistingTempFile ||
!file_exists($output)) {
2232 $this->imageMagickExec($imagefile, $output, $command, $frame);
2234 if (file_exists($output)) {
2237 // params could realisticly change some imagedata!
2239 $info = $this->getImageDimensions($info[3]);
2241 if ($info[2] == $this->gifExtension
&& !$this->dontCompress
) {
2242 // Compress with IM (lzw) or GD (rle) (Workaround for the absence of lzw-compression in GD)
2243 self
::gifCompress($info[3], '');
2252 * Gets the input image dimensions.
2254 * @param string $imageFile The image filepath
2255 * @return array|NULL Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
2256 * @see imageMagickConvert(), \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::getImgResource()
2258 public function getImageDimensions($imageFile) {
2259 preg_match('/([^\\.]*)$/', $imageFile, $reg);
2260 if (file_exists($imageFile) && GeneralUtility
::inList($this->imageFileExt
, strtolower($reg[0]))) {
2261 if ($returnArr = $this->getCachedImageDimensions($imageFile)) {
2264 if ($temp = @getImageSize
($imageFile)) {
2265 $returnArr = array($temp[0], $temp[1], strtolower($reg[0]), $imageFile);
2267 $returnArr = $this->imageMagickIdentify($imageFile);
2270 $this->cacheImageDimensions($returnArr);
2279 * Cache the result of the getImageDimensions function into the database. Does not check if the
2282 * @param array $identifyResult Result of the getImageDimensions function
2283 * @return bool TRUE if operation was successful
2285 public function cacheImageDimensions($identifyResult) {
2286 // Create md5 hash of filemtime and filesize
2287 $fileStatus = stat($identifyResult[3]);
2288 $md5Hash = md5($fileStatus['mtime'] . $fileStatus['size']);
2291 $fieldArray = array(
2292 'md5hash' => $md5Hash,
2293 'md5filename' => md5($identifyResult[3]),
2294 'tstamp' => $GLOBALS['EXEC_TIME'],
2295 'filename' => $identifyResult[3],
2296 'imagewidth' => $identifyResult[0],
2297 'imageheight' => $identifyResult[1]
2299 $GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_imagesizes', $fieldArray);
2300 if (!($err = $GLOBALS['TYPO3_DB']->sql_error())) {
2308 * Fetch the cached imageDimensions from the MySQL database. Does not check if the image file exists!
2310 * @param string $imageFile The image filepath
2311 * @return array Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
2313 public function getCachedImageDimensions($imageFile) {
2314 // Create md5 hash of filemtime and filesize
2315 $fileStatus = stat($imageFile);
2316 $md5Hash = md5($fileStatus['mtime'] . $fileStatus['size']);
2317 $cachedImageDimensions = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('md5hash, md5filename, imagewidth, imageheight', 'cache_imagesizes', 'md5filename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr(md5($imageFile), 'cache_imagesizes'));
2319 if (is_array($cachedImageDimensions)) {
2320 if ($cachedImageDimensions['md5hash'] != $md5Hash) {
2321 // File has changed, delete the row
2322 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_imagesizes', 'md5filename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cachedImageDimensions['md5filename'], 'cache_imagesizes'));
2324 preg_match('/([^\\.]*)$/', $imageFile, $imageExtension);
2326 (int)$cachedImageDimensions['imagewidth'],
2327 (int)$cachedImageDimensions['imageheight'],
2328 strtolower($imageExtension[0]),
2337 * Get numbers for scaling the image based on input
2339 * @param array $info Current image information: Width, Height etc.
2340 * @param int $w "required" width
2341 * @param int $h "required" height
2342 * @param array $options Options: Keys are like "maxW", "maxH", "minW", "minH
2345 * @see imageMagickConvert()
2347 public function getImageScale($info, $w, $h, $options) {
2348 if (strstr($w . $h, 'm')) {
2353 if (strstr($w . $h, 'c')) {
2354 $out['cropH'] = (int)substr(strstr($w, 'c'), 1);
2355 $out['cropV'] = (int)substr(strstr($h, 'c'), 1);
2363 // If there are max-values...
2364 if (!empty($options['maxW'])) {
2365 // If width is given...
2367 if ($w > $options['maxW']) {
2368 $w = $options['maxW'];
2369 // Height should follow
2373 if ($info[0] > $options['maxW']) {
2374 $w = $options['maxW'];
2375 // Height should follow
2380 if (!empty($options['maxH'])) {
2381 // If height is given...
2383 if ($h > $options['maxH']) {
2384 $h = $options['maxH'];
2385 // Height should follow
2389 // Changed [0] to [1] 290801
2390 if ($info[1] > $options['maxH']) {
2391 $h = $options['maxH'];
2392 // Height should follow
2400 if (!$this->mayScaleUp
) {
2401 if ($w > $info[0]) {
2404 if ($h > $info[1]) {
2408 // If scaling should be performed
2411 $info[1] = ceil($info[1] * ($w / $info[0]));
2415 $info[0] = ceil($info[0] * ($h / $info[1]));
2420 $ratio = $info[0] / $info[1];
2421 if ($h * $ratio > $w) {
2422 $h = round($w / $ratio);
2424 $w = round($h * $ratio);
2428 $ratio = $info[0] / $info[1];
2429 if ($h * $ratio < $w) {
2430 $h = round($w / $ratio);
2432 $w = round($h * $ratio);
2441 // Set minimum-measures!
2442 if (isset($options['minW']) && $out[0] < $options['minW']) {
2443 if (($max ||
$crs) && $out[0]) {
2444 $out[1] = round($out[1] * $options['minW'] / $out[0]);
2446 $out[0] = $options['minW'];
2448 if (isset($options['minH']) && $out[1] < $options['minH']) {
2449 if (($max ||
$crs) && $out[1]) {
2450 $out[0] = round($out[0] * $options['minH'] / $out[1]);
2452 $out[1] = $options['minH'];
2457 /***********************************
2459 * ImageMagick API functions
2461 ***********************************/
2463 * Returns an array where [0]/[1] is w/h, [2] is extension and [3] is the filename.
2466 * @param string $imagefile The relative (to PATH_site) image filepath
2469 public function imageMagickIdentify($imagefile) {
2470 if (!$this->NO_IMAGE_MAGICK
) {
2471 $frame = $this->noFramePrepended ?
'' : '[0]';
2472 $cmd = GeneralUtility
::imageMagickCommand('identify', CommandUtility
::escapeShellArgument($imagefile) . $frame);
2473 $returnVal = array();
2474 \TYPO3\CMS\Core\Utility\CommandUtility
::exec($cmd, $returnVal);
2475 $splitstring = array_pop($returnVal);
2476 $this->IM_commands
[] = array('identify', $cmd, $splitstring);
2478 preg_match('/([^\\.]*)$/', $imagefile, $reg);
2479 $splitinfo = explode(' ', $splitstring);
2480 foreach ($splitinfo as $key => $val) {
2483 $temp = explode('x', $val);
2485 if ((int)$temp[0] && (int)$temp[1]) {
2490 if ($dim[0] && $dim[1]) {
2491 return array($dim[0], $dim[1], strtolower($reg[0]), $imagefile);
2498 * Executes a ImageMagick "convert" on two filenames, $input and $output using $params before them.
2499 * Can be used for many things, mostly scaling and effects.
2501 * @param string $input The relative (to PATH_site) image filepath, input file (read from)
2502 * @param string $output The relative (to PATH_site) image filepath, output filename (written to)
2503 * @param string $params ImageMagick parameters
2504 * @param int $frame Optional, refers to which frame-number to select in the image. '' or 0
2505 * @return string The result of a call to PHP function "exec()
2507 public function imageMagickExec($input, $output, $params, $frame = 0) {
2508 if (!$this->NO_IMAGE_MAGICK
) {
2509 // Unless noFramePrepended is set in the Install Tool, a frame number is added to
2510 // select a specific page of the image (by default this will be the first page)
2511 if (!$this->noFramePrepended
) {
2512 $frame = '[' . (int)$frame . ']';
2516 $cmd = GeneralUtility
::imageMagickCommand('convert', $params . ' ' . CommandUtility
::escapeShellArgument($input . $frame) . ' ' . CommandUtility
::escapeShellArgument($output));
2517 $this->IM_commands
[] = array($output, $cmd);
2518 $ret = \TYPO3\CMS\Core\Utility\CommandUtility
::exec($cmd);
2519 // Change the permissions of the file
2520 GeneralUtility
::fixPermissions($output);
2526 * 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)
2527 * Can be used for many things, mostly scaling and effects.
2529 * @param string $input The relative (to PATH_site) image filepath, bottom file
2530 * @param string $overlay The relative (to PATH_site) image filepath, overlay file (top)
2531 * @param string $mask The relative (to PATH_site) image filepath, the mask file (grayscale)
2532 * @param string $output The relative (to PATH_site) image filepath, output filename (written to)
2533 * @param bool $handleNegation
2536 public function combineExec($input, $overlay, $mask, $output, $handleNegation = FALSE) {
2537 if (!$this->NO_IMAGE_MAGICK
) {
2538 $params = '-colorspace GRAY +matte';
2539 $theMask = $this->randomName() . '.' . $this->gifExtension
;
2540 $this->imageMagickExec($mask, $theMask, $params);
2541 $cmd = GeneralUtility
::imageMagickCommand('combine', '-compose over +matte ' . CommandUtility
::escapeShellArgument($input) . ' ' . CommandUtility
::escapeShellArgument($overlay) . ' ' . CommandUtility
::escapeShellArgument($theMask) . ' ' . CommandUtility
::escapeShellArgument($output));
2542 // +matte = no alpha layer in output
2543 $this->IM_commands
[] = array($output, $cmd);
2544 $ret = \TYPO3\CMS\Core\Utility\CommandUtility
::exec($cmd);
2545 // Change the permissions of the file
2546 GeneralUtility
::fixPermissions($output);
2547 if (is_file($theMask)) {
2555 * Compressing a GIF file if not already LZW compressed.
2556 * This function is a workaround for the fact that ImageMagick and/or GD does not compress GIF-files to their minimun size (that is RLE or no compression used)
2558 * The function takes a file-reference, $theFile, and saves it again through GD or ImageMagick in order to compress the file
2560 * If $type is not set, the compression is done with ImageMagick (provided that $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw'] is pointing to the path of a lzw-enabled version of 'convert') else with GD (should be RLE-enabled!)
2561 * If $type is set to either 'IM' or 'GD' the compression is done with ImageMagick and GD respectively
2565 * $theFile is expected to be a valid GIF-file!
2566 * The function returns a code for the operation.
2568 * @param string $theFile Filepath
2569 * @param string $type See description of function
2570 * @return string Returns "GD" if GD was used, otherwise "IM" if ImageMagick was used. If nothing done at all, it returns empty string.
2572 static public function gifCompress($theFile, $type) {
2573 $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX'];
2574 if (!$gfxConf['gif_compress'] ||
strtolower(substr($theFile, -4, 4)) !== '.gif') {
2578 if (($type === 'IM' ||
!$type) && $gfxConf['im'] && $gfxConf['im_path_lzw']) {
2579 // Use temporary file to prevent problems with read and write lock on same file on network file systems
2580 $temporaryName = dirname($theFile) . '/' . md5(uniqid('', TRUE)) . '.gif';
2581 // Rename could fail, if a simultaneous thread is currently working on the same thing
2582 if (@rename
($theFile, $temporaryName)) {
2583 $cmd = GeneralUtility
::imageMagickCommand('convert', '"' . $temporaryName . '" "' . $theFile . '"', $gfxConf['im_path_lzw']);
2584 CommandUtility
::exec($cmd);
2585 unlink($temporaryName);
2588 if (@is_file
($theFile)) {
2589 GeneralUtility
::fixPermissions($theFile);
2591 } elseif (($type === 'GD' ||
!$type) && $gfxConf['gdlib'] && !$gfxConf['gdlib_png']) {
2592 $tempImage = imageCreateFromGif($theFile);
2593 imageGif($tempImage, $theFile);
2594 imageDestroy($tempImage);
2596 if (@is_file
($theFile)) {
2597 GeneralUtility
::fixPermissions($theFile);
2607 * Converts a png file to gif.
2608 * This converts a png file to gif IF the FLAG $GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif'] is set TRUE.
2610 * @param string $theFile The filename with path
2611 * @return string|NULL New filename
2613 static public function pngToGifByImagemagick($theFile) {
2614 if (!$GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif']
2615 ||
!$GLOBALS['TYPO3_CONF_VARS']['GFX']['im']
2616 ||
!$GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw']
2617 ||
strtolower(substr($theFile, -4, 4)) !== '.png'
2618 ||
!@is_file
($theFile)
2623 $newFile = substr($theFile, 0, -4) . '.gif';
2624 $cmd = GeneralUtility
::imageMagickCommand(
2625 'convert', '"' . $theFile . '" "' . $newFile . '"', $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw']
2627 CommandUtility
::exec($cmd);
2628 $theFile = $newFile;
2629 if (@is_file
($newFile)) {
2630 GeneralUtility
::fixPermissions($newFile);
2637 * Returns filename of the png/gif version of the input file (which can be png or gif).
2638 * If input file type does not match the wanted output type a conversion is made and temp-filename returned.
2640 * @param string $theFile Filepath of image file
2641 * @param bool $output_png If TRUE, then input file is converted to PNG, otherwise to GIF
2642 * @return string|NULL If the new image file exists, its filepath is returned
2644 static public function readPngGif($theFile, $output_png = FALSE) {
2645 if (!$GLOBALS['TYPO3_CONF_VARS']['GFX']['im'] ||
!@is_file
($theFile)) {
2649 $ext = strtolower(substr($theFile, -4, 4));
2650 if ((string)$ext == '.png' && $output_png ||
(string)$ext == '.gif' && !$output_png) {
2654 $newFile = PATH_site
. 'typo3temp/readPG_' . md5(($theFile . '|' . filemtime($theFile))) . ($output_png ?
'.png' : '.gif');
2655 $cmd = GeneralUtility
::imageMagickCommand(
2656 'convert', '"' . $theFile . '" "' . $newFile . '"', $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path']
2658 CommandUtility
::exec($cmd);
2659 if (@is_file
($newFile)) {
2660 GeneralUtility
::fixPermissions($newFile);
2665 /***********************************
2667 * Various IO functions
2669 ***********************************