TYPO3 CMS  TYPO3_6-2
GraphicalFunctions.php
Go to the documentation of this file.
1 <?php
3 
19 
29 
30  // Internal configuration, set in init()
31 
32  // If set, there is no frame pointer prepended to the filenames.
36  public $noFramePrepended = 0;
37 
38  // This should be changed to 'png' if you want this class to read/make PNG-files instead!
42  public $gifExtension = 'gif';
43 
44  // File formats supported by gdlib. This variable get's filled in "init" method
48  public $gdlibExtensions = '';
49 
50  // Set to TRUE if generated png's should be truecolor by default
54  public $png_truecolor = FALSE;
55 
61  protected $colorspace = 'RGB';
62 
68  protected $allowedColorSpaceNames = array(
69  'CMY',
70  'CMYK',
71  'Gray',
72  'HCL',
73  'HSB',
74  'HSL',
75  'HWB',
76  'Lab',
77  'LCH',
78  'LMS',
79  'Log',
80  'Luv',
81  'OHTA',
82  'Rec601Luma',
83  'Rec601YCbCr',
84  'Rec709Luma',
85  'Rec709YCbCr',
86  'RGB',
87  'sRGB',
88  'Transparent',
89  'XYZ',
90  'YCbCr',
91  'YCC',
92  'YIQ',
93  'YCbCr',
94  'YUV'
95  );
96 
97  // 16777216 Colors is the maximum value for PNG, JPEG truecolor images (24-bit, 8-bit / Channel)
101  public $truecolorColors = 16777215;
102 
103  // 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.
108 
109  // 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!
113  public $imageFileExt = 'gif,jpg,jpeg,png,tif,bmp,tga,pcx,ai,pdf';
114 
115  // Commalist of web image extensions (can be shown by a webbrowser)
119  public $webImageExt = 'gif,jpg,jpeg,png';
120 
124  public $NO_IM_EFFECTS = '';
125 
129  public $cmds = array(
130  'jpg' => '',
131  'jpeg' => '',
132  'gif' => '',
133  'png' => '-colors 64'
134  );
135 
139  public $NO_IMAGE_MAGICK = '';
140 
144  public $V5_EFFECTS = 0;
145 
149  public $mayScaleUp = 1;
150 
151  // Variables for testing, alternative usage etc.
152  // Filename prefix for images scaled in imageMagickConvert()
156  public $filenamePrefix = '';
157 
158  // Forcing the output filename of imageMagickConvert() to this value. However after calling imageMagickConvert() it will be set blank again.
163 
164  // This flag should always be FALSE. If set TRUE, imageMagickConvert will always write a new file to the tempdir! Used for debugging.
169 
170  // Prevents imageMagickConvert() from compressing the gif-files with \TYPO3\CMS\Core\Utility\GeneralUtility::gif_compress()
174  public $dontCompress = 0;
175 
176  // For debugging ONLY!
181 
182  // 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...
187 
188  // Internal:
189  // All ImageMagick commands executed is stored in this array for tracking. Used by the Install Tools Image section
193  public $IM_commands = array();
194 
198  public $workArea = array();
199 
205  protected $saveAlphaLayer = FALSE;
206 
207  // Constants:
208  // 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.
212  public $tempPath = 'typo3temp/';
213 
214  // Prefix for relative paths. Used in "show_item.php" script. Is prefixed the output file name IN imageMagickConvert()
218  public $absPrefix = '';
219 
220  // ImageMagick scaling command; "-geometry" eller "-sample". Used in makeText() and imageMagickConvert()
224  public $scalecmd = '-geometry';
225 
226  // Used by v5_blur() to simulate 10 continuous steps of blurring
230  public $im5fx_blurSteps = '1x2,2x2,3x2,4x3,5x3,5x4,6x4,7x5,8x5,9x5';
231 
232  // Used by v5_sharpen() to simulate 10 continuous steps of sharpening.
236  public $im5fx_sharpenSteps = '1x2,2x2,3x2,2x3,3x3,4x3,3x4,4x4,4x5,5x5';
237 
238  // This is the limit for the number of pixels in an image before it will be rendered as JPG instead of GIF/PNG
242  public $pixelLimitGif = 10000;
243 
244  // Array mapping HTML color names to RGB values.
248  public $colMap = array(
249  'aqua' => array(0, 255, 255),
250  'black' => array(0, 0, 0),
251  'blue' => array(0, 0, 255),
252  'fuchsia' => array(255, 0, 255),
253  'gray' => array(128, 128, 128),
254  'green' => array(0, 128, 0),
255  'lime' => array(0, 255, 0),
256  'maroon' => array(128, 0, 0),
257  'navy' => array(0, 0, 128),
258  'olive' => array(128, 128, 0),
259  'purple' => array(128, 0, 128),
260  'red' => array(255, 0, 0),
261  'silver' => array(192, 192, 192),
262  'teal' => array(0, 128, 128),
263  'yellow' => array(255, 255, 0),
264  'white' => array(255, 255, 255)
265  );
266 
273  public $csConvObj;
274 
275  // Is set to the native character set of the input strings.
279  public $nativeCharset = '';
280 
288  public function init() {
289  $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX'];
290  if (function_exists('imagecreatefromjpeg') && function_exists('imagejpeg')) {
291  $this->gdlibExtensions .= ',jpg,jpeg';
292  }
293  if (function_exists('imagecreatefrompng') && function_exists('imagepng')) {
294  $this->gdlibExtensions .= ',png';
295  }
296  if (function_exists('imagecreatefromgif') && function_exists('imagegif')) {
297  $this->gdlibExtensions .= ',gif';
298  }
299  if ($gfxConf['png_truecolor']) {
300  $this->png_truecolor = TRUE;
301  }
302 
303  if ($gfxConf['colorspace'] && in_array($gfxConf['colorspace'], $this->allowedColorSpaceNames, TRUE)) {
304  $this->colorspace = $gfxConf['colorspace'];
305  }
306 
307  if (!$gfxConf['im']) {
308  $this->NO_IMAGE_MAGICK = 1;
309  }
310  if (!$this->NO_IMAGE_MAGICK && (!$gfxConf['im_version_5'] || $gfxConf['im_version_5'] === 'im4' || $gfxConf['im_version_5'] === 'im5')) {
311  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);
312  }
313  // When GIFBUILDER gets used in truecolor mode
314  // No colors parameter if we generate truecolor images.
315  if ($this->png_truecolor) {
316  $this->cmds['png'] = '';
317  }
318  // Setting default JPG parameters:
319  $this->jpegQuality = MathUtility::forceIntegerInRange($gfxConf['jpg_quality'], 10, 100, 75);
320  $this->cmds['jpg'] = ($this->cmds['jpeg'] = '-colorspace ' . $this->colorspace . ' -sharpen 50 -quality ' . $this->jpegQuality);
321  if ($gfxConf['im_noFramePrepended']) {
322  $this->noFramePrepended = 1;
323  }
324  if ($gfxConf['gdlib_png']) {
325  $this->gifExtension = 'png';
326  }
327  if ($gfxConf['enable_typo3temp_db_tracking']) {
328  $this->enable_typo3temp_db_tracking = $gfxConf['enable_typo3temp_db_tracking'];
329  }
330  $this->imageFileExt = $gfxConf['imagefile_ext'];
331 
332  // Boolean. This is necessary if using ImageMagick 5+.
333  // Effects in Imagemagick 5+ tends to render very slowly!!
334  // - therefore must be disabled in order not to perform sharpen, blurring and such.
335  $this->NO_IM_EFFECTS = 1;
336  $this->cmds['jpg'] = ($this->cmds['jpeg'] = '-colorspace ' . $this->colorspace . ' -quality ' . $this->jpegQuality);
337 
338  // ... but if 'im_v5effects' is set, enable effects
339  if ($gfxConf['im_v5effects']) {
340  $this->NO_IM_EFFECTS = 0;
341  $this->V5_EFFECTS = 1;
342  if ($gfxConf['im_v5effects'] > 0) {
343  $this->cmds['jpg'] = ($this->cmds['jpeg'] = '-colorspace ' . $this->colorspace . ' -quality ' . (int)$gfxConf['jpg_quality'] . $this->v5_sharpen(10));
344  }
345  }
346  // Secures that images are not scaled up.
347  if ($gfxConf['im_noScaleUp']) {
348  $this->mayScaleUp = 0;
349  }
350  if (TYPO3_MODE == 'FE') {
351  $this->csConvObj = $GLOBALS['TSFE']->csConvObj;
352  } elseif (is_object($GLOBALS['LANG'])) {
353  // BE assumed:
354  $this->csConvObj = $GLOBALS['LANG']->csConvObj;
355  } else {
356  // The object may not exist yet, so we need to create it now. Happens in the Install Tool for example.
357  $this->csConvObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Charset\\CharsetConverter');
358  }
359  $this->nativeCharset = 'utf-8';
360  }
361 
362  /*************************************************
363  *
364  * Layering images / "IMAGE" GIFBUILDER object
365  *
366  *************************************************/
379  public function maskImageOntoImage(&$im, $conf, $workArea) {
380  if ($conf['file'] && $conf['mask']) {
381  $imgInf = pathinfo($conf['file']);
382  $imgExt = strtolower($imgInf['extension']);
383  if (!GeneralUtility::inList($this->gdlibExtensions, $imgExt)) {
384  $BBimage = $this->imageMagickConvert($conf['file'], $this->gifExtension);
385  } else {
386  $BBimage = $this->getImageDimensions($conf['file']);
387  }
388  $maskInf = pathinfo($conf['mask']);
389  $maskExt = strtolower($maskInf['extension']);
390  if (!GeneralUtility::inList($this->gdlibExtensions, $maskExt)) {
391  $BBmask = $this->imageMagickConvert($conf['mask'], $this->gifExtension);
392  } else {
393  $BBmask = $this->getImageDimensions($conf['mask']);
394  }
395  if ($BBimage && $BBmask) {
396  $w = imagesx($im);
397  $h = imagesy($im);
398  $tmpStr = $this->randomName();
399  $theImage = $tmpStr . '_img.' . $this->gifExtension;
400  $theDest = $tmpStr . '_dest.' . $this->gifExtension;
401  $theMask = $tmpStr . '_mask.' . $this->gifExtension;
402  // Prepare overlay image
403  $cpImg = $this->imageCreateFromFile($BBimage[3]);
404  $destImg = imagecreatetruecolor($w, $h);
405  // Preserve alpha transparency
406  if ($this->saveAlphaLayer) {
407  imagesavealpha($destImg, TRUE);
408  $Bcolor = imagecolorallocatealpha($destImg, 0, 0, 0, 127);
409  imagefill($destImg, 0, 0, $Bcolor);
410  } else {
411  $Bcolor = ImageColorAllocate($destImg, 0, 0, 0);
412  ImageFilledRectangle($destImg, 0, 0, $w, $h, $Bcolor);
413  }
414  $this->copyGifOntoGif($destImg, $cpImg, $conf, $workArea);
415  $this->ImageWrite($destImg, $theImage);
416  imageDestroy($cpImg);
417  imageDestroy($destImg);
418  // Prepare mask image
419  $cpImg = $this->imageCreateFromFile($BBmask[3]);
420  $destImg = imagecreatetruecolor($w, $h);
421  if ($this->saveAlphaLayer) {
422  imagesavealpha($destImg, TRUE);
423  $Bcolor = imagecolorallocatealpha($destImg, 0, 0, 0, 127);
424  imagefill($destImg, 0, 0, $Bcolor);
425  } else {
426  $Bcolor = ImageColorAllocate($destImg, 0, 0, 0);
427  ImageFilledRectangle($destImg, 0, 0, $w, $h, $Bcolor);
428  }
429  $this->copyGifOntoGif($destImg, $cpImg, $conf, $workArea);
430  $this->ImageWrite($destImg, $theMask);
431  imageDestroy($cpImg);
432  imageDestroy($destImg);
433  // Mask the images
434  $this->ImageWrite($im, $theDest);
435  // Let combineExec handle maskNegation
436  $this->combineExec($theDest, $theImage, $theMask, $theDest, TRUE);
437  // The main image is loaded again...
438  $backIm = $this->imageCreateFromFile($theDest);
439  // ... and if nothing went wrong we load it onto the old one.
440  if ($backIm) {
441  if (!$this->saveAlphaLayer) {
442  ImageColorTransparent($backIm, -1);
443  }
444  $im = $backIm;
445  }
446  // Unlink files from process
447  if (!$this->dontUnlinkTempFiles) {
448  unlink($theDest);
449  unlink($theImage);
450  unlink($theMask);
451  }
452  }
453  }
454  }
455 
466  public function copyImageOntoImage(&$im, $conf, $workArea) {
467  if ($conf['file']) {
468  if (!GeneralUtility::inList($this->gdlibExtensions, $conf['BBOX'][2])) {
469  $conf['BBOX'] = $this->imageMagickConvert($conf['BBOX'][3], $this->gifExtension);
470  $conf['file'] = $conf['BBOX'][3];
471  }
472  $cpImg = $this->imageCreateFromFile($conf['file']);
473  $this->copyGifOntoGif($im, $cpImg, $conf, $workArea);
474  imageDestroy($cpImg);
475  }
476  }
477 
489  public function copyGifOntoGif(&$im, $cpImg, $conf, $workArea) {
490  $cpW = imagesx($cpImg);
491  $cpH = imagesy($cpImg);
492  $tile = GeneralUtility::intExplode(',', $conf['tile']);
493  $tile[0] = MathUtility::forceIntegerInRange($tile[0], 1, 20);
494  $tile[1] = MathUtility::forceIntegerInRange($tile[1], 1, 20);
495  $cpOff = $this->objPosition($conf, $workArea, array($cpW * $tile[0], $cpH * $tile[1]));
496  for ($xt = 0; $xt < $tile[0]; $xt++) {
497  $Xstart = $cpOff[0] + $cpW * $xt;
498  // If this image is inside of the workArea, then go on
499  if ($Xstart + $cpW > $workArea[0]) {
500  // X:
501  if ($Xstart < $workArea[0]) {
502  $cpImgCutX = $workArea[0] - $Xstart;
503  $Xstart = $workArea[0];
504  } else {
505  $cpImgCutX = 0;
506  }
507  $w = $cpW - $cpImgCutX;
508  if ($Xstart > $workArea[0] + $workArea[2] - $w) {
509  $w = $workArea[0] + $workArea[2] - $Xstart;
510  }
511  // If this image is inside of the workArea, then go on
512  if ($Xstart < $workArea[0] + $workArea[2]) {
513  // Y:
514  for ($yt = 0; $yt < $tile[1]; $yt++) {
515  $Ystart = $cpOff[1] + $cpH * $yt;
516  // If this image is inside of the workArea, then go on
517  if ($Ystart + $cpH > $workArea[1]) {
518  if ($Ystart < $workArea[1]) {
519  $cpImgCutY = $workArea[1] - $Ystart;
520  $Ystart = $workArea[1];
521  } else {
522  $cpImgCutY = 0;
523  }
524  $h = $cpH - $cpImgCutY;
525  if ($Ystart > $workArea[1] + $workArea[3] - $h) {
526  $h = $workArea[1] + $workArea[3] - $Ystart;
527  }
528  // If this image is inside of the workArea, then go on
529  if ($Ystart < $workArea[1] + $workArea[3]) {
530  $this->imagecopyresized($im, $cpImg, $Xstart, $Ystart, $cpImgCutX, $cpImgCutY, $w, $h, $w, $h);
531  }
532  }
533  }
534  }
535  }
536  }
537  }
538 
570  public function imagecopyresized(&$dstImg, $srcImg, $dstX, $dstY, $srcX, $srcY, $dstWidth, $dstHeight, $srcWidth, $srcHeight) {
571  if (!$this->saveAlphaLayer) {
572  // Make true color image
573  $tmpImg = imagecreatetruecolor(imagesx($dstImg), imagesy($dstImg));
574  // Copy the source image onto that
575  imagecopyresized($tmpImg, $dstImg, 0, 0, 0, 0, imagesx($dstImg), imagesy($dstImg), imagesx($dstImg), imagesy($dstImg));
576  // Then copy the source image onto that (the actual operation!)
577  imagecopyresized($tmpImg, $srcImg, $dstX, $dstY, $srcX, $srcY, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
578  // Set the destination image
579  $dstImg = $tmpImg;
580  } else {
581  imagecopyresized($dstImg, $srcImg, $dstX, $dstY, $srcX, $srcY, $dstWidth, $dstHeight, $srcWidth, $srcHeight);
582  }
583  }
584 
585  /********************************
586  *
587  * Text / "TEXT" GIFBUILDER object
588  *
589  ********************************/
600  public function makeText(&$im, $conf, $workArea) {
601  // Spacing
602  list($spacing, $wordSpacing) = $this->calcWordSpacing($conf);
603  // Position
604  $txtPos = $this->txtPosition($conf, $workArea, $conf['BBOX']);
605  $theText = $this->recodeString($conf['text']);
606  if ($conf['imgMap'] && is_array($conf['imgMap.'])) {
607  $this->addToMap($this->calcTextCordsForMap($conf['BBOX'][2], $txtPos, $conf['imgMap.']), $conf['imgMap.']);
608  }
609  if (!$conf['hideButCreateMap']) {
610  // Font Color:
611  $cols = $this->convertColor($conf['fontColor']);
612  // NiceText is calculated
613  if (!$conf['niceText']) {
614  $Fcolor = ImageColorAllocate($im, $cols[0], $cols[1], $cols[2]);
615  // antiAliasing is setup:
616  $Fcolor = $conf['antiAlias'] ? $Fcolor : -$Fcolor;
617  for ($a = 0; $a < $conf['iterations']; $a++) {
618  // If any kind of spacing applys, we use this function:
619  if ($spacing || $wordSpacing) {
620  $this->SpacedImageTTFText($im, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, self::prependAbsolutePath($conf['fontFile']), $theText, $spacing, $wordSpacing, $conf['splitRendering.']);
621  } else {
622  $this->renderTTFText($im, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, $conf['fontFile'], $theText, $conf['splitRendering.'], $conf);
623  }
624  }
625  } else {
626  // NICETEXT::
627  // options anti_aliased and iterations is NOT available when doing this!!
628  $w = imagesx($im);
629  $h = imagesy($im);
630  $tmpStr = $this->randomName();
631  $fileMenu = $tmpStr . '_menuNT.' . $this->gifExtension;
632  $fileColor = $tmpStr . '_colorNT.' . $this->gifExtension;
633  $fileMask = $tmpStr . '_maskNT.' . $this->gifExtension;
634  // Scalefactor
635  $sF = MathUtility::forceIntegerInRange($conf['niceText.']['scaleFactor'], 2, 5);
636  $newW = ceil($sF * imagesx($im));
637  $newH = ceil($sF * imagesy($im));
638  // Make mask
639  $maskImg = imagecreatetruecolor($newW, $newH);
640  $Bcolor = ImageColorAllocate($maskImg, 255, 255, 255);
641  ImageFilledRectangle($maskImg, 0, 0, $newW, $newH, $Bcolor);
642  $Fcolor = ImageColorAllocate($maskImg, 0, 0, 0);
643  // If any kind of spacing applies, we use this function:
644  if ($spacing || $wordSpacing) {
645  $this->SpacedImageTTFText($maskImg, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, self::prependAbsolutePath($conf['fontFile']), $theText, $spacing, $wordSpacing, $conf['splitRendering.'], $sF);
646  } else {
647  $this->renderTTFText($maskImg, $conf['fontSize'], $conf['angle'], $txtPos[0], $txtPos[1], $Fcolor, $conf['fontFile'], $theText, $conf['splitRendering.'], $conf, $sF);
648  }
649  $this->ImageWrite($maskImg, $fileMask);
650  ImageDestroy($maskImg);
651  // Downscales the mask
652  if ($this->NO_IM_EFFECTS) {
653  $command = trim($this->scalecmd . ' ' . $w . 'x' . $h . '! -negate');
654  } else {
655  $command = trim($conf['niceText.']['before'] . ' ' . $this->scalecmd . ' ' . $w . 'x' . $h . '! ' . $conf['niceText.']['after'] . ' -negate');
656  if ($conf['niceText.']['sharpen']) {
657  if ($this->V5_EFFECTS) {
658  $command .= $this->v5_sharpen($conf['niceText.']['sharpen']);
659  } else {
660  $command .= ' -sharpen ' . MathUtility::forceIntegerInRange($conf['niceText.']['sharpen'], 1, 99);
661  }
662  }
663  }
664  $this->imageMagickExec($fileMask, $fileMask, $command);
665  // Make the color-file
666  $colorImg = imagecreatetruecolor($w, $h);
667  $Ccolor = ImageColorAllocate($colorImg, $cols[0], $cols[1], $cols[2]);
668  ImageFilledRectangle($colorImg, 0, 0, $w, $h, $Ccolor);
669  $this->ImageWrite($colorImg, $fileColor);
670  ImageDestroy($colorImg);
671  // The mask is applied
672  // The main pictures is saved temporarily
673  $this->ImageWrite($im, $fileMenu);
674  $this->combineExec($fileMenu, $fileColor, $fileMask, $fileMenu);
675  // The main image is loaded again...
676  $backIm = $this->imageCreateFromFile($fileMenu);
677  // ... and if nothing went wrong we load it onto the old one.
678  if ($backIm) {
679  if (!$this->saveAlphaLayer) {
680  ImageColorTransparent($backIm, -1);
681  }
682  $im = $backIm;
683  }
684  // Deleting temporary files;
685  if (!$this->dontUnlinkTempFiles) {
686  unlink($fileMenu);
687  unlink($fileColor);
688  unlink($fileMask);
689  }
690  }
691  }
692  }
693 
705  public function txtPosition($conf, $workArea, $BB) {
706  $angle = (int)$conf['angle'] / 180 * pi();
707  $conf['angle'] = 0;
708  $straightBB = $this->calcBBox($conf);
709  // offset, align, valign, workarea
710  // [0]=x, [1]=y, [2]=w, [3]=h
711  $result = array();
712  $result[2] = $BB[0];
713  $result[3] = $BB[1];
714  $w = $workArea[2];
715  switch ($conf['align']) {
716  case 'right':
717 
718  case 'center':
719  $factor = abs(cos($angle));
720  $sign = cos($angle) < 0 ? -1 : 1;
721  $len1 = $sign * $factor * $straightBB[0];
722  $len2 = $sign * $BB[0];
723  $result[0] = $w - ceil(($len2 * $factor + (1 - $factor) * $len1));
724  $factor = abs(sin($angle));
725  $sign = sin($angle) < 0 ? -1 : 1;
726  $len1 = $sign * $factor * $straightBB[0];
727  $len2 = $sign * $BB[1];
728  $result[1] = ceil($len2 * $factor + (1 - $factor) * $len1);
729  break;
730  }
731  switch ($conf['align']) {
732  case 'right':
733  break;
734  case 'center':
735  $result[0] = round($result[0] / 2);
736  $result[1] = round($result[1] / 2);
737  break;
738  default:
739  $result[0] = 0;
740  $result[1] = 0;
741  }
742  $result = $this->applyOffset($result, GeneralUtility::intExplode(',', $conf['offset']));
744  return $result;
745  }
746 
756  public function calcBBox($conf) {
757  $sF = $this->getTextScalFactor($conf);
758  list($spacing, $wordSpacing) = $this->calcWordSpacing($conf, $sF);
759  $theText = $this->recodeString($conf['text']);
760  $charInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $theText, $conf['splitRendering.'], $sF);
761  $theBBoxInfo = $charInf;
762  if ($conf['angle']) {
763  $xArr = array($charInf[0], $charInf[2], $charInf[4], $charInf[6]);
764  $yArr = array($charInf[1], $charInf[3], $charInf[5], $charInf[7]);
765  $x = max($xArr) - min($xArr);
766  $y = max($yArr) - min($yArr);
767  } else {
768  $x = $charInf[2] - $charInf[0];
769  $y = $charInf[1] - $charInf[7];
770  }
771  // Set original lineHeight (used by line breaks):
772  $theBBoxInfo['lineHeight'] = $y;
773  // If any kind of spacing applys, we use this function:
774  if ($spacing || $wordSpacing) {
775  $x = 0;
776  if (!$spacing && $wordSpacing) {
777  $bits = explode(' ', $theText);
778  foreach ($bits as $word) {
779  $word .= ' ';
780  $wordInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $word, $conf['splitRendering.'], $sF);
781  $wordW = $wordInf[2] - $wordInf[0];
782  $x += $wordW + $wordSpacing;
783  }
784  } else {
785  $utf8Chars = $this->singleChars($theText);
786  // For each UTF-8 char, do:
787  foreach ($utf8Chars as $char) {
788  $charInf = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $char, $conf['splitRendering.'], $sF);
789  $charW = $charInf[2] - $charInf[0];
790  $x += $charW + ($char == ' ' ? $wordSpacing : $spacing);
791  }
792  }
793  } elseif (isset($conf['breakWidth']) && $conf['breakWidth'] && $this->getRenderedTextWidth($conf['text'], $conf) > $conf['breakWidth']) {
794  $maxWidth = 0;
795  $currentWidth = 0;
796  $breakWidth = $conf['breakWidth'];
797  $breakSpace = $this->getBreakSpace($conf, $theBBoxInfo);
798  $wordPairs = $this->getWordPairsForLineBreak($conf['text']);
799  // Iterate through all word pairs:
800  foreach ($wordPairs as $index => $wordPair) {
801  $wordWidth = $this->getRenderedTextWidth($wordPair, $conf);
802  if ($index == 0 || $currentWidth + $wordWidth <= $breakWidth) {
803  $currentWidth += $wordWidth;
804  } else {
805  $maxWidth = max($maxWidth, $currentWidth);
806  $y += $breakSpace;
807  // Restart:
808  $currentWidth = $wordWidth;
809  }
810  }
811  $x = max($maxWidth, $currentWidth) * $sF;
812  }
813  if ($sF > 1) {
814  $x = ceil($x / $sF);
815  $y = ceil($y / $sF);
816  if (is_array($theBBoxInfo)) {
817  foreach ($theBBoxInfo as &$value) {
818  $value = ceil($value / $sF);
819  }
820  unset($value);
821  }
822  }
823  return array($x, $y, $theBBoxInfo);
824  }
825 
836  public function addToMap($cords, $conf) {
837  $this->map .= '<area' . ' shape="poly"' . ' coords="' . implode(',', $cords) . '"' . ' href="' . htmlspecialchars($conf['url']) . '"' . ($conf['target'] ? ' target="' . htmlspecialchars($conf['target']) . '"' : '') . $JS . (strlen($conf['titleText']) ? ' title="' . htmlspecialchars($conf['titleText']) . '"' : '') . ' alt="' . htmlspecialchars($conf['altText']) . '" />';
838  }
839 
851  public function calcTextCordsForMap($cords, $offset, $conf) {
852  $pars = GeneralUtility::intExplode(',', $conf['explode'] . ',');
853  $newCords[0] = $cords[0] + $offset[0] - $pars[0];
854  $newCords[1] = $cords[1] + $offset[1] + $pars[1];
855  $newCords[2] = $cords[2] + $offset[0] + $pars[0];
856  $newCords[3] = $cords[3] + $offset[1] + $pars[1];
857  $newCords[4] = $cords[4] + $offset[0] + $pars[0];
858  $newCords[5] = $cords[5] + $offset[1] - $pars[1];
859  $newCords[6] = $cords[6] + $offset[0] - $pars[0];
860  $newCords[7] = $cords[7] + $offset[1] - $pars[1];
861  return $newCords;
862  }
863 
885  public function SpacedImageTTFText(&$im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $text, $spacing, $wordSpacing, $splitRenderingConf, $sF = 1) {
886  $spacing *= $sF;
887  $wordSpacing *= $sF;
888  if (!$spacing && $wordSpacing) {
889  $bits = explode(' ', $text);
890  foreach ($bits as $word) {
891  $word .= ' ';
892  $wordInf = $this->ImageTTFBBoxWrapper($fontSize, $angle, $fontFile, $word, $splitRenderingConf, $sF);
893  $wordW = $wordInf[2] - $wordInf[0];
894  $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $word, $splitRenderingConf, $sF);
895  $x += $wordW + $wordSpacing;
896  }
897  } else {
898  $utf8Chars = $this->singleChars($text);
899  // For each UTF-8 char, do:
900  foreach ($utf8Chars as $char) {
901  $charInf = $this->ImageTTFBBoxWrapper($fontSize, $angle, $fontFile, $char, $splitRenderingConf, $sF);
902  $charW = $charInf[2] - $charInf[0];
903  $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $char, $splitRenderingConf, $sF);
904  $x += $charW + ($char == ' ' ? $wordSpacing : $spacing);
905  }
906  }
907  }
908 
918  public function fontResize($conf) {
919  // 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!!!!
920  $maxWidth = (int)$conf['maxWidth'];
921  list($spacing, $wordSpacing) = $this->calcWordSpacing($conf);
922  if ($maxWidth) {
923  // If any kind of spacing applys, we use this function:
924  if ($spacing || $wordSpacing) {
925  return $conf['fontSize'];
926  } else {
927  do {
928  // Determine bounding box.
929  $bounds = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $this->recodeString($conf['text']), $conf['splitRendering.']);
930  if ($conf['angle'] < 0) {
931  $pixelWidth = abs($bounds[4] - $bounds[0]);
932  } elseif ($conf['angle'] > 0) {
933  $pixelWidth = abs($bounds[2] - $bounds[6]);
934  } else {
935  $pixelWidth = abs($bounds[4] - $bounds[6]);
936  }
937  // Size is fine, exit:
938  if ($pixelWidth <= $maxWidth) {
939  break;
940  } else {
941  $conf['fontSize']--;
942  }
943  } while ($conf['fontSize'] > 1);
944  }
945  }
946  return $conf['fontSize'];
947  }
948 
961  public function ImageTTFBBoxWrapper($fontSize, $angle, $fontFile, $string, $splitRendering, $sF = 1) {
962  // Initialize:
963  $offsetInfo = array();
964  $stringParts = $this->splitString($string, $splitRendering, $fontSize, $fontFile);
965  // Traverse string parts:
966  foreach ($stringParts as $strCfg) {
967  $fontFile = self::prependAbsolutePath($strCfg['fontFile']);
968  if (is_readable($fontFile)) {
976  $try = 0;
977  do {
978  $calc = ImageTTFBBox(GeneralUtility::freetypeDpiComp($sF * $strCfg['fontSize']), $angle, $fontFile, $strCfg['str']);
979  } while ($calc[2] < 0 && $try++ < 10);
980  // Calculate offsets:
981  if (!count($offsetInfo)) {
982  // First run, just copy over.
983  $offsetInfo = $calc;
984  } else {
985  $offsetInfo[2] += $calc[2] - $calc[0] + (int)$splitRendering['compX'] + (int)$strCfg['xSpaceBefore'] + (int)$strCfg['xSpaceAfter'];
986  $offsetInfo[3] += $calc[3] - $calc[1] - (int)$splitRendering['compY'] - (int)$strCfg['ySpaceBefore'] - (int)$strCfg['ySpaceAfter'];
987  $offsetInfo[4] += $calc[4] - $calc[6] + (int)$splitRendering['compX'] + (int)$strCfg['xSpaceBefore'] + (int)$strCfg['xSpaceAfter'];
988  $offsetInfo[5] += $calc[5] - $calc[7] - (int)$splitRendering['compY'] - (int)$strCfg['ySpaceBefore'] - (int)$strCfg['ySpaceAfter'];
989  }
990  } else {
991  debug('cannot read file: ' . $fontFile, 'TYPO3\\CMS\\Core\\Imaging\\GraphicalFunctions::ImageTTFBBoxWrapper()');
992  }
993  }
994  return $offsetInfo;
995  }
996 
1013  public function ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering, $sF = 1) {
1014  // Initialize:
1015  $stringParts = $this->splitString($string, $splitRendering, $fontSize, $fontFile);
1016  $x = ceil($sF * $x);
1017  $y = ceil($sF * $y);
1018  // Traverse string parts:
1019  foreach ($stringParts as $i => $strCfg) {
1020  // Initialize:
1021  $colorIndex = $color;
1022  // Set custom color if any (only when niceText is off):
1023  if ($strCfg['color'] && $sF == 1) {
1024  $cols = $this->convertColor($strCfg['color']);
1025  $colorIndex = ImageColorAllocate($im, $cols[0], $cols[1], $cols[2]);
1026  $colorIndex = $color >= 0 ? $colorIndex : -$colorIndex;
1027  }
1028  // Setting xSpaceBefore
1029  if ($i) {
1030  $x += (int)$strCfg['xSpaceBefore'];
1031  $y -= (int)$strCfg['ySpaceBefore'];
1032  }
1033  $fontFile = self::prependAbsolutePath($strCfg['fontFile']);
1034  if (is_readable($fontFile)) {
1035  // Render part:
1036  ImageTTFText($im, GeneralUtility::freetypeDpiComp($sF * $strCfg['fontSize']), $angle, $x, $y, $colorIndex, $fontFile, $strCfg['str']);
1037  // Calculate offset to apply:
1038  $wordInf = ImageTTFBBox(GeneralUtility::freetypeDpiComp($sF * $strCfg['fontSize']), $angle, self::prependAbsolutePath($strCfg['fontFile']), $strCfg['str']);
1039  $x += $wordInf[2] - $wordInf[0] + (int)$splitRendering['compX'] + (int)$strCfg['xSpaceAfter'];
1040  $y += $wordInf[5] - $wordInf[7] - (int)$splitRendering['compY'] - (int)$strCfg['ySpaceAfter'];
1041  } else {
1042  debug('cannot read file: ' . $fontFile, 'TYPO3\\CMS\\Core\\Imaging\\GraphicalFunctions::ImageTTFTextWrapper()');
1043  }
1044  }
1045  }
1046 
1057  public function splitString($string, $splitRendering, $fontSize, $fontFile) {
1058  // Initialize by setting the whole string and default configuration as the first entry.
1059  $result = array();
1060  $result[] = array(
1061  'str' => $string,
1062  'fontSize' => $fontSize,
1063  'fontFile' => $fontFile
1064  );
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 (strlen($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 (strlen($expValue)) {
1083  $newResult[] = array_merge($part, array('str' => $expValue));
1084  }
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']
1095  );
1096  }
1097  }
1098  }
1099  // Set the new result as result array:
1100  if (count($newResult)) {
1101  $result = $newResult;
1102  }
1103  }
1104  break;
1105  case 'charRange':
1106  if (strlen($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];
1113  }
1114  }
1115  $newResult = array();
1116  // Traverse the current parts of the result array:
1117  foreach ($result as $part) {
1118  // Initialize:
1119  $currentState = -1;
1120  $bankAccum = '';
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);
1126  $inRange = 0;
1127  foreach ($ranges as $rangeDef) {
1128  if ($uNumber >= $rangeDef[0] && (!$rangeDef[1] || $uNumber <= $rangeDef[1])) {
1129  $inRange = 1;
1130  break;
1131  }
1132  }
1133  if ($currentState == -1) {
1134  $currentState = $inRange;
1135  }
1136  // Initialize first char
1137  // Switch bank:
1138  if ($inRange != $currentState && !GeneralUtility::inList('32,10,13,9', $uNumber)) {
1139  // Set result:
1140  if (strlen($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'] : ''
1150  );
1151  }
1152  // Initialize new settings:
1153  $currentState = $inRange;
1154  $bankAccum = '';
1155  }
1156  // Add char to bank:
1157  $bankAccum .= $utfChar;
1158  }
1159  // Set result for FINAL part:
1160  if (strlen($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'] : ''
1170  );
1171  }
1172  }
1173  // Set the new result as result array:
1174  if (count($newResult)) {
1175  $result = $newResult;
1176  }
1177  }
1178  break;
1179  }
1180  }
1181  }
1182  return $result;
1183  }
1184 
1195  public function calcWordSpacing($conf, $scaleFactor = 1) {
1196  $spacing = (int)$conf['spacing'];
1197  $wordSpacing = (int)$conf['wordSpacing'];
1198  $wordSpacing = $wordSpacing ?: $spacing * 2;
1199  $spacing *= $scaleFactor;
1200  $wordSpacing *= $scaleFactor;
1201  return array($spacing, $wordSpacing);
1202  }
1203 
1212  public function getTextScalFactor($conf) {
1213  if (!$conf['niceText']) {
1214  $sF = 1;
1215  } else {
1216  // NICETEXT::
1217  $sF = MathUtility::forceIntegerInRange($conf['niceText.']['scaleFactor'], 2, 5);
1218  }
1219  return $sF;
1220  }
1221 
1238  protected function renderTTFText(&$im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering, $conf, $sF = 1) {
1239  if (isset($conf['breakWidth']) && $conf['breakWidth'] && $this->getRenderedTextWidth($string, $conf) > $conf['breakWidth']) {
1240  $phrase = '';
1241  $currentWidth = 0;
1242  $breakWidth = $conf['breakWidth'];
1243  $breakSpace = $this->getBreakSpace($conf);
1244  $wordPairs = $this->getWordPairsForLineBreak($string);
1245  // Iterate through all word pairs:
1246  foreach ($wordPairs as $index => $wordPair) {
1247  $wordWidth = $this->getRenderedTextWidth($wordPair, $conf);
1248  if ($index == 0 || $currentWidth + $wordWidth <= $breakWidth) {
1249  $currentWidth += $wordWidth;
1250  $phrase .= $wordPair;
1251  } else {
1252  // Render the current phrase that is below breakWidth:
1253  $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $phrase, $splitRendering, $sF);
1254  // Calculate the news height offset:
1255  $y += $breakSpace;
1256  // Restart the phrase:
1257  $currentWidth = $wordWidth;
1258  $phrase = $wordPair;
1259  }
1260  }
1261  // Render the remaining phrase:
1262  if ($currentWidth) {
1263  $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $phrase, $splitRendering, $sF);
1264  }
1265  } else {
1266  $this->ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering, $sF);
1267  }
1268  }
1269 
1276  protected function getWordPairsForLineBreak($string) {
1277  $wordPairs = array();
1278  $wordsArray = preg_split('#([- .,!:]+)#', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
1279  $wordsCount = count($wordsArray);
1280  for ($index = 0; $index < $wordsCount; $index += 2) {
1281  $wordPairs[] = $wordsArray[$index] . $wordsArray[($index + 1)];
1282  }
1283  return $wordPairs;
1284  }
1285 
1293  protected function getRenderedTextWidth($text, $conf) {
1294  $bounds = $this->ImageTTFBBoxWrapper($conf['fontSize'], $conf['angle'], $conf['fontFile'], $this->recodeString($text), $conf['splitRendering.']);
1295  if ($conf['angle'] < 0) {
1296  $pixelWidth = abs($bounds[4] - $bounds[0]);
1297  } elseif ($conf['angle'] > 0) {
1298  $pixelWidth = abs($bounds[2] - $bounds[6]);
1299  } else {
1300  $pixelWidth = abs($bounds[4] - $bounds[6]);
1301  }
1302  return $pixelWidth;
1303  }
1304 
1312  protected function getBreakSpace($conf, array $boundingBox = NULL) {
1313  if (!isset($boundingBox)) {
1314  $boundingBox = $this->calcBBox($conf);
1315  $boundingBox = $boundingBox[2];
1316  }
1317  if (isset($conf['breakSpace']) && $conf['breakSpace']) {
1318  $breakSpace = $boundingBox['lineHeight'] * $conf['breakSpace'];
1319  } else {
1320  $breakSpace = $boundingBox['lineHeight'];
1321  }
1322  return $breakSpace;
1323  }
1324 
1325  /*********************************************
1326  *
1327  * Other GIFBUILDER objects related to TEXT
1328  *
1329  *********************************************/
1341  public function makeOutline(&$im, $conf, $workArea, $txtConf) {
1342  $thickness = (int)$conf['thickness'];
1343  if ($thickness) {
1344  $txtConf['fontColor'] = $conf['color'];
1345  $outLineDist = MathUtility::forceIntegerInRange($thickness, 1, 2);
1346  for ($b = 1; $b <= $outLineDist; $b++) {
1347  if ($b == 1) {
1348  $it = 8;
1349  } else {
1350  $it = 16;
1351  }
1352  $outL = $this->circleOffset($b, $it);
1353  for ($a = 0; $a < $it; $a++) {
1354  $this->makeText($im, $txtConf, $this->applyOffset($workArea, $outL[$a]));
1355  }
1356  }
1357  }
1358  }
1359 
1371  public function circleOffset($distance, $iterations) {
1372  $res = array();
1373  if ($distance && $iterations) {
1374  for ($a = 0; $a < $iterations; $a++) {
1375  $yOff = round(sin((2 * pi() / $iterations * ($a + 1))) * 100 * $distance);
1376  if ($yOff) {
1377  $yOff = (int)(ceil(abs(($yOff / 100))) * ($yOff / abs($yOff)));
1378  }
1379  $xOff = round(cos((2 * pi() / $iterations * ($a + 1))) * 100 * $distance);
1380  if ($xOff) {
1381  $xOff = (int)(ceil(abs(($xOff / 100))) * ($xOff / abs($xOff)));
1382  }
1383  $res[$a] = array($xOff, $yOff);
1384  }
1385  }
1386  return $res;
1387  }
1388 
1400  public function makeEmboss(&$im, $conf, $workArea, $txtConf) {
1401  $conf['color'] = $conf['highColor'];
1402  $this->makeShadow($im, $conf, $workArea, $txtConf);
1403  $newOffset = GeneralUtility::intExplode(',', $conf['offset']);
1404  $newOffset[0] *= -1;
1405  $newOffset[1] *= -1;
1406  $conf['offset'] = implode(',', $newOffset);
1407  $conf['color'] = $conf['lowColor'];
1408  $this->makeShadow($im, $conf, $workArea, $txtConf);
1409  }
1410 
1423  public function makeShadow(&$im, $conf, $workArea, $txtConf) {
1424  $workArea = $this->applyOffset($workArea, GeneralUtility::intExplode(',', $conf['offset']));
1425  $blurRate = MathUtility::forceIntegerInRange((int)$conf['blur'], 0, 99);
1426  // No effects if ImageMagick ver. 5+
1427  if (!$blurRate || $this->NO_IM_EFFECTS) {
1428  $txtConf['fontColor'] = $conf['color'];
1429  $this->makeText($im, $txtConf, $workArea);
1430  } else {
1431  $w = imagesx($im);
1432  $h = imagesy($im);
1433  // Area around the blur used for cropping something
1434  $blurBorder = 3;
1435  $tmpStr = $this->randomName();
1436  $fileMenu = $tmpStr . '_menu.' . $this->gifExtension;
1437  $fileColor = $tmpStr . '_color.' . $this->gifExtension;
1438  $fileMask = $tmpStr . '_mask.' . $this->gifExtension;
1439  // BlurColor Image laves
1440  $blurColImg = imagecreatetruecolor($w, $h);
1441  $bcols = $this->convertColor($conf['color']);
1442  $Bcolor = ImageColorAllocate($blurColImg, $bcols[0], $bcols[1], $bcols[2]);
1443  ImageFilledRectangle($blurColImg, 0, 0, $w, $h, $Bcolor);
1444  $this->ImageWrite($blurColImg, $fileColor);
1445  ImageDestroy($blurColImg);
1446  // The mask is made: BlurTextImage
1447  $blurTextImg = imagecreatetruecolor($w + $blurBorder * 2, $h + $blurBorder * 2);
1448  // Black background
1449  $Bcolor = ImageColorAllocate($blurTextImg, 0, 0, 0);
1450  ImageFilledRectangle($blurTextImg, 0, 0, $w + $blurBorder * 2, $h + $blurBorder * 2, $Bcolor);
1451  $txtConf['fontColor'] = 'white';
1452  $blurBordArr = array($blurBorder, $blurBorder);
1453  $this->makeText($blurTextImg, $txtConf, $this->applyOffset($workArea, $blurBordArr));
1454  // Dump to temporary file
1455  $this->ImageWrite($blurTextImg, $fileMask);
1456  // Destroy
1457  ImageDestroy($blurTextImg);
1458  $command = '';
1459  if ($this->V5_EFFECTS) {
1460  $command .= $this->v5_blur($blurRate + 1);
1461  } else {
1462  // Blurring of the mask
1463  // How many blur-commands that is executed. Min = 1;
1464  $times = ceil($blurRate / 10);
1465  // 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.
1466  $newBlurRate = $blurRate * 4;
1467  $newBlurRate = MathUtility::forceIntegerInRange($newBlurRate, 1, 99);
1468  // Building blur-command
1469  for ($a = 0; $a < $times; $a++) {
1470  $command .= ' -blur ' . $blurRate;
1471  }
1472  }
1473  $this->imageMagickExec($fileMask, $fileMask, $command . ' +matte');
1474  // The mask is loaded again
1475  $blurTextImg_tmp = $this->imageCreateFromFile($fileMask);
1476  // If nothing went wrong we continue with the blurred mask
1477  if ($blurTextImg_tmp) {
1478  // Cropping the border from the mask
1479  $blurTextImg = imagecreatetruecolor($w, $h);
1480  $this->imagecopyresized($blurTextImg, $blurTextImg_tmp, 0, 0, $blurBorder, $blurBorder, $w, $h, $w, $h);
1481  // Destroy the temporary mask
1482  ImageDestroy($blurTextImg_tmp);
1483  // Adjust the mask
1484  $intensity = 40;
1485  if ($conf['intensity']) {
1486  $intensity = MathUtility::forceIntegerInRange($conf['intensity'], 0, 100);
1487  }
1488  $intensity = ceil(255 - $intensity / 100 * 255);
1489  $this->inputLevels($blurTextImg, 0, $intensity);
1490  $opacity = MathUtility::forceIntegerInRange((int)$conf['opacity'], 0, 100);
1491  if ($opacity && $opacity < 100) {
1492  $high = ceil(255 * $opacity / 100);
1493  // Reducing levels as the opacity demands
1494  $this->outputLevels($blurTextImg, 0, $high);
1495  }
1496  // Dump the mask again
1497  $this->ImageWrite($blurTextImg, $fileMask);
1498  // Destroy the mask
1499  ImageDestroy($blurTextImg);
1500  // The pictures are combined
1501  // The main pictures is saved temporarily
1502  $this->ImageWrite($im, $fileMenu);
1503  $this->combineExec($fileMenu, $fileColor, $fileMask, $fileMenu);
1504  // The main image is loaded again...
1505  $backIm = $this->imageCreateFromFile($fileMenu);
1506  // ... and if nothing went wrong we load it onto the old one.
1507  if ($backIm) {
1508  if (!$this->saveAlphaLayer) {
1509  ImageColorTransparent($backIm, -1);
1510  }
1511  $im = $backIm;
1512  }
1513  }
1514  // Deleting temporary files;
1515  if (!$this->dontUnlinkTempFiles) {
1516  unlink($fileMenu);
1517  unlink($fileColor);
1518  unlink($fileMask);
1519  }
1520  }
1521  }
1522 
1523  /****************************
1524  *
1525  * Other GIFBUILDER objects
1526  *
1527  ****************************/
1538  public function makeBox(&$im, $conf, $workArea) {
1539  $cords = GeneralUtility::intExplode(',', $conf['dimensions'] . ',,,');
1540  $conf['offset'] = $cords[0] . ',' . $cords[1];
1541  $cords = $this->objPosition($conf, $workArea, array($cords[2], $cords[3]));
1542  $cols = $this->convertColor($conf['color']);
1543  $opacity = 0;
1544  if (isset($conf['opacity'])) {
1545  // conversion:
1546  // PHP 0 = opaque, 127 = transparent
1547  // TYPO3 100 = opaque, 0 = transparent
1548  $opacity = MathUtility::forceIntegerInRange((int)$conf['opacity'], 1, 100, 1);
1549  $opacity = abs($opacity - 100);
1550  $opacity = round(127 * $opacity / 100);
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 
1577  public function makeEllipse(&$im, array $conf, array $workArea) {
1578  $ellipseConfiguration = GeneralUtility::intExplode(',', $conf['dimensions'] . ',,,');
1579  // Ellipse offset inside workArea (x/y)
1580  $conf['offset'] = $ellipseConfiguration[0] . ',' . $ellipseConfiguration[1];
1581  // @see objPosition
1582  $imageCoordinates = $this->objPosition($conf, $workArea, array($ellipseConfiguration[2], $ellipseConfiguration[3]));
1583  $color = $this->convertColor($conf['color']);
1584  $fillingColor = imagecolorallocate($im, $color[0], $color[1], $color[2]);
1585  imagefilledellipse($im, $imageCoordinates[0], $imageCoordinates[1], $imageCoordinates[2], $imageCoordinates[3], $fillingColor);
1586  }
1587 
1598  public function makeEffect(&$im, $conf) {
1599  $commands = $this->IMparams($conf['value']);
1600  if ($commands) {
1601  $this->applyImageMagickToPHPGif($im, $commands);
1602  }
1603  }
1604 
1614  public function IMparams($setup) {
1615  if (!trim($setup)) {
1616  return '';
1617  }
1618  $effects = explode('|', $setup);
1619  $commands = '';
1620  foreach ($effects as $val) {
1621  $pairs = explode('=', $val, 2);
1622  $value = trim($pairs[1]);
1623  $effect = strtolower(trim($pairs[0]));
1624  switch ($effect) {
1625  case 'gamma':
1626  $commands .= ' -gamma ' . doubleval($value);
1627  break;
1628  case 'blur':
1629  if (!$this->NO_IM_EFFECTS) {
1630  if ($this->V5_EFFECTS) {
1631  $commands .= $this->v5_blur($value);
1632  } else {
1633  $commands .= ' -blur ' . MathUtility::forceIntegerInRange($value, 1, 99);
1634  }
1635  }
1636  break;
1637  case 'sharpen':
1638  if (!$this->NO_IM_EFFECTS) {
1639  if ($this->V5_EFFECTS) {
1640  $commands .= $this->v5_sharpen($value);
1641  } else {
1642  $commands .= ' -sharpen ' . MathUtility::forceIntegerInRange($value, 1, 99);
1643  }
1644  }
1645  break;
1646  case 'rotate':
1647  $commands .= ' -rotate ' . MathUtility::forceIntegerInRange($value, 0, 360);
1648  break;
1649  case 'solarize':
1650  $commands .= ' -solarize ' . MathUtility::forceIntegerInRange($value, 0, 99);
1651  break;
1652  case 'swirl':
1653  $commands .= ' -swirl ' . MathUtility::forceIntegerInRange($value, 0, 1000);
1654  break;
1655  case 'wave':
1656  $params = GeneralUtility::intExplode(',', $value);
1657  $commands .= ' -wave ' . MathUtility::forceIntegerInRange($params[0], 0, 99) . 'x' . MathUtility::forceIntegerInRange($params[1], 0, 99);
1658  break;
1659  case 'charcoal':
1660  $commands .= ' -charcoal ' . MathUtility::forceIntegerInRange($value, 0, 100);
1661  break;
1662  case 'gray':
1663  $commands .= ' -colorspace GRAY';
1664  break;
1665  case 'edge':
1666  $commands .= ' -edge ' . MathUtility::forceIntegerInRange($value, 0, 99);
1667  break;
1668  case 'emboss':
1669  $commands .= ' -emboss';
1670  break;
1671  case 'flip':
1672  $commands .= ' -flip';
1673  break;
1674  case 'flop':
1675  $commands .= ' -flop';
1676  break;
1677  case 'colors':
1678  $commands .= ' -colors ' . MathUtility::forceIntegerInRange($value, 2, 255);
1679  break;
1680  case 'shear':
1681  $commands .= ' -shear ' . MathUtility::forceIntegerInRange($value, -90, 90);
1682  break;
1683  case 'invert':
1684  $commands .= ' -negate';
1685  break;
1686  }
1687  }
1688  return $commands;
1689  }
1690 
1700  public function adjust(&$im, $conf) {
1701  $setup = $conf['value'];
1702  if (!trim($setup)) {
1703  return '';
1704  }
1705  $effects = explode('|', $setup);
1706  foreach ($effects as $val) {
1707  $pairs = explode('=', $val, 2);
1708  $value = trim($pairs[1]);
1709  $effect = strtolower(trim($pairs[0]));
1710  switch ($effect) {
1711  case 'inputlevels':
1712  // low,high
1713  $params = GeneralUtility::intExplode(',', $value);
1714  $this->inputLevels($im, $params[0], $params[1]);
1715  break;
1716  case 'outputlevels':
1717  $params = GeneralUtility::intExplode(',', $value);
1718  $this->outputLevels($im, $params[0], $params[1]);
1719  break;
1720  case 'autolevels':
1721  $this->autoLevels($im);
1722  break;
1723  }
1724  }
1725  }
1726 
1736  public function crop(&$im, $conf) {
1737  // Clears workArea to total image
1738  $this->setWorkArea('');
1739  $cords = GeneralUtility::intExplode(',', $conf['crop'] . ',,,');
1740  $conf['offset'] = $cords[0] . ',' . $cords[1];
1741  $cords = $this->objPosition($conf, $this->workArea, array($cords[2], $cords[3]));
1742  $newIm = imagecreatetruecolor($cords[2], $cords[3]);
1743  $cols = $this->convertColor($conf['backColor'] ? $conf['backColor'] : $this->setup['backColor']);
1744  $Bcolor = ImageColorAllocate($newIm, $cols[0], $cols[1], $cols[2]);
1745  ImageFilledRectangle($newIm, 0, 0, $cords[2], $cords[3], $Bcolor);
1746  $newConf = array();
1747  $workArea = array(0, 0, $cords[2], $cords[3]);
1748  if ($cords[0] < 0) {
1749  $workArea[0] = abs($cords[0]);
1750  } else {
1751  $newConf['offset'] = -$cords[0];
1752  }
1753  if ($cords[1] < 0) {
1754  $workArea[1] = abs($cords[1]);
1755  } else {
1756  $newConf['offset'] .= ',' . -$cords[1];
1757  }
1758  $this->copyGifOntoGif($newIm, $im, $newConf, $workArea);
1759  $im = $newIm;
1760  $this->w = imagesx($im);
1761  $this->h = imagesy($im);
1762  // Clears workArea to total image
1763  $this->setWorkArea('');
1764  }
1765 
1775  public function scale(&$im, $conf) {
1776  if ($conf['width'] || $conf['height'] || $conf['params']) {
1777  $tmpStr = $this->randomName();
1778  $theFile = $tmpStr . '.' . $this->gifExtension;
1779  $this->ImageWrite($im, $theFile);
1780  $theNewFile = $this->imageMagickConvert($theFile, $this->gifExtension, $conf['width'], $conf['height'], $conf['params']);
1781  $tmpImg = $this->imageCreateFromFile($theNewFile[3]);
1782  if ($tmpImg) {
1783  ImageDestroy($im);
1784  $im = $tmpImg;
1785  $this->w = imagesx($im);
1786  $this->h = imagesy($im);
1787  // Clears workArea to total image
1788  $this->setWorkArea('');
1789  }
1790  if (!$this->dontUnlinkTempFiles) {
1791  unlink($theFile);
1792  if ($theNewFile[3] && $theNewFile[3] != $theFile) {
1793  unlink($theNewFile[3]);
1794  }
1795  }
1796  }
1797  }
1798 
1809  public function setWorkArea($workArea) {
1810  $this->workArea = GeneralUtility::intExplode(',', $workArea);
1811  $this->workArea = $this->applyOffset($this->workArea, $this->OFFSET);
1812  if (!$this->workArea[2]) {
1813  $this->workArea[2] = $this->w;
1814  }
1815  if (!$this->workArea[3]) {
1816  $this->workArea[3] = $this->h;
1817  }
1818  }
1819 
1820  /*************************
1821  *
1822  * Adjustment functions
1823  *
1824  ************************/
1832  public function autolevels(&$im) {
1833  $totalCols = ImageColorsTotal($im);
1834  $min = 255;
1835  $max = 0;
1836  for ($c = 0; $c < $totalCols; $c++) {
1837  $cols = ImageColorsForIndex($im, $c);
1838  $grayArr[] = round(($cols['red'] + $cols['green'] + $cols['blue']) / 3);
1839  }
1840  $min = min($grayArr);
1841  $max = max($grayArr);
1842  $delta = $max - $min;
1843  if ($delta) {
1844  for ($c = 0; $c < $totalCols; $c++) {
1845  $cols = ImageColorsForIndex($im, $c);
1846  $cols['red'] = floor(($cols['red'] - $min) / $delta * 255);
1847  $cols['green'] = floor(($cols['green'] - $min) / $delta * 255);
1848  $cols['blue'] = floor(($cols['blue'] - $min) / $delta * 255);
1849  ImageColorSet($im, $c, $cols['red'], $cols['green'], $cols['blue']);
1850  }
1851  }
1852  }
1853 
1864  public function outputLevels(&$im, $low, $high, $swap = '') {
1865  if ($low < $high) {
1866  $low = MathUtility::forceIntegerInRange($low, 0, 255);
1867  $high = MathUtility::forceIntegerInRange($high, 0, 255);
1868  if ($swap) {
1869  $temp = $low;
1870  $low = 255 - $high;
1871  $high = 255 - $temp;
1872  }
1873  $delta = $high - $low;
1874  $totalCols = ImageColorsTotal($im);
1875  for ($c = 0; $c < $totalCols; $c++) {
1876  $cols = ImageColorsForIndex($im, $c);
1877  $cols['red'] = $low + floor($cols['red'] / 255 * $delta);
1878  $cols['green'] = $low + floor($cols['green'] / 255 * $delta);
1879  $cols['blue'] = $low + floor($cols['blue'] / 255 * $delta);
1880  ImageColorSet($im, $c, $cols['red'], $cols['green'], $cols['blue']);
1881  }
1882  }
1883  }
1884 
1895  public function inputLevels(&$im, $low, $high, $swap = '') {
1896  if ($low < $high) {
1897  $low = MathUtility::forceIntegerInRange($low, 0, 255);
1898  $high = MathUtility::forceIntegerInRange($high, 0, 255);
1899  $delta = $high - $low;
1900  $totalCols = ImageColorsTotal($im);
1901  for ($c = 0; $c < $totalCols; $c++) {
1902  $cols = ImageColorsForIndex($im, $c);
1903  $cols['red'] = MathUtility::forceIntegerInRange(($cols['red'] - $low) / $delta * 255, 0, 255);
1904  $cols['green'] = MathUtility::forceIntegerInRange(($cols['green'] - $low) / $delta * 255, 0, 255);
1905  $cols['blue'] = MathUtility::forceIntegerInRange(($cols['blue'] - $low) / $delta * 255, 0, 255);
1906  ImageColorSet($im, $c, $cols['red'], $cols['green'], $cols['blue']);
1907  }
1908  }
1909  }
1910 
1919  public function IMreduceColors($file, $cols) {
1920  $fI = GeneralUtility::split_fileref($file);
1921  $ext = strtolower($fI['fileext']);
1922  $result = $this->randomName() . '.' . $ext;
1923  if (($reduce = MathUtility::forceIntegerInRange($cols, 0, $ext == 'gif' ? 256 : $this->truecolorColors, 0)) > 0) {
1924  $params = ' -colors ' . $reduce;
1925  if ($reduce <= 256) {
1926  $params .= ' -type Palette';
1927  }
1928  if ($ext == 'png' && $reduce <= 256) {
1929  $prefix = 'png8:';
1930  }
1931  $this->imageMagickExec($file, $prefix . $result, $params);
1932  if ($result) {
1933  return $result;
1934  }
1935  }
1936  return '';
1937  }
1938 
1939  /*********************************
1940  *
1941  * GIFBUILDER Helper functions
1942  *
1943  *********************************/
1953  public function prependAbsolutePath($fontFile) {
1954  $absPath = defined('PATH_typo3') ? dirname(PATH_thisScript) . '/' : PATH_site;
1955  $fontFile = GeneralUtility::isAbsPath($fontFile) ? $fontFile : GeneralUtility::resolveBackPath($absPath . $fontFile);
1956  return $fontFile;
1957  }
1958 
1968  public function v5_sharpen($factor) {
1969  $factor = MathUtility::forceIntegerInRange(ceil($factor / 10), 0, 10);
1970  $sharpenArr = explode(',', ',' . $this->im5fx_sharpenSteps);
1971  $sharpenF = trim($sharpenArr[$factor]);
1972  if ($sharpenF) {
1973  $cmd = ' -sharpen ' . $sharpenF;
1974  return $cmd;
1975  }
1976  }
1977 
1987  public function v5_blur($factor) {
1988  $factor = MathUtility::forceIntegerInRange(ceil($factor / 10), 0, 10);
1989  $blurArr = explode(',', ',' . $this->im5fx_blurSteps);
1990  $blurF = trim($blurArr[$factor]);
1991  if ($blurF) {
1992  $cmd = ' -blur ' . $blurF;
1993  return $cmd;
1994  }
1995  }
1996 
2004  public function randomName() {
2005  $this->createTempSubDir('temp/');
2006  return $this->tempPath . 'temp/' . md5(uniqid('', TRUE));
2007  }
2008 
2018  public function applyOffset($cords, $OFFSET) {
2019  $cords[0] = (int)$cords[0] + (int)$OFFSET[0];
2020  $cords[1] = (int)$cords[1] + (int)$OFFSET[1];
2021  return $cords;
2022  }
2023 
2032  public function convertColor($string) {
2033  $col = array();
2034  $cParts = explode(':', $string, 2);
2035  // Finding the RGB definitions of the color:
2036  $string = $cParts[0];
2037  if (strstr($string, '#')) {
2038  $string = preg_replace('/[^A-Fa-f0-9]*/', '', $string);
2039  $col[] = HexDec(substr($string, 0, 2));
2040  $col[] = HexDec(substr($string, 2, 2));
2041  $col[] = HexDec(substr($string, 4, 2));
2042  } elseif (strstr($string, ',')) {
2043  $string = preg_replace('/[^,0-9]*/', '', $string);
2044  $strArr = explode(',', $string);
2045  $col[] = (int)$strArr[0];
2046  $col[] = (int)$strArr[1];
2047  $col[] = (int)$strArr[2];
2048  } else {
2049  $string = strtolower(trim($string));
2050  if ($this->colMap[$string]) {
2051  $col = $this->colMap[$string];
2052  } else {
2053  $col = array(0, 0, 0);
2054  }
2055  }
2056  // ... and possibly recalculating the value
2057  if (trim($cParts[1])) {
2058  $cParts[1] = trim($cParts[1]);
2059  if ($cParts[1][0] === '*') {
2060  $val = doubleval(substr($cParts[1], 1));
2061  $col[0] = MathUtility::forceIntegerInRange($col[0] * $val, 0, 255);
2062  $col[1] = MathUtility::forceIntegerInRange($col[1] * $val, 0, 255);
2063  $col[2] = MathUtility::forceIntegerInRange($col[2] * $val, 0, 255);
2064  } else {
2065  $val = (int)$cParts[1];
2066  $col[0] = MathUtility::forceIntegerInRange($col[0] + $val, 0, 255);
2067  $col[1] = MathUtility::forceIntegerInRange($col[1] + $val, 0, 255);
2068  $col[2] = MathUtility::forceIntegerInRange($col[2] + $val, 0, 255);
2069  }
2070  }
2071  return $col;
2072  }
2073 
2082  public function recodeString($string) {
2083  // Recode string to UTF-8 from $this->nativeCharset:
2084  if ($this->nativeCharset && $this->nativeCharset != 'utf-8') {
2085  // Convert to UTF-8
2086  $string = $this->csConvObj->utf8_encode($string, $this->nativeCharset);
2087  }
2088  return $string;
2089  }
2090 
2100  public function singleChars($theText, $returnUnicodeNumber = FALSE) {
2101  if ($this->nativeCharset) {
2102  // Get an array of separated UTF-8 chars
2103  return $this->csConvObj->utf8_to_numberarray($theText, 1, $returnUnicodeNumber ? 0 : 1);
2104  } else {
2105  $output = array();
2106  $c = strlen($theText);
2107  for ($a = 0; $a < $c; $a++) {
2108  $output[] = substr($theText, $a, 1);
2109  }
2110  return $output;
2111  }
2112  }
2113 
2125  public function objPosition($conf, $workArea, $BB) {
2126  // offset, align, valign, workarea
2127  $result = array();
2128  $result[2] = $BB[0];
2129  $result[3] = $BB[1];
2130  $w = $workArea[2];
2131  $h = $workArea[3];
2132  $align = explode(',', $conf['align']);
2133  $align[0] = strtolower(substr(trim($align[0]), 0, 1));
2134  $align[1] = strtolower(substr(trim($align[1]), 0, 1));
2135  switch ($align[0]) {
2136  case 'r':
2137  $result[0] = $w - $result[2];
2138  break;
2139  case 'c':
2140  $result[0] = round(($w - $result[2]) / 2);
2141  break;
2142  default:
2143  $result[0] = 0;
2144  }
2145  switch ($align[1]) {
2146  case 'b':
2147  // y pos
2148  $result[1] = $h - $result[3];
2149  break;
2150  case 'c':
2151  $result[1] = round(($h - $result[3]) / 2);
2152  break;
2153  default:
2154  $result[1] = 0;
2155  }
2156  $result = $this->applyOffset($result, GeneralUtility::intExplode(',', $conf['offset']));
2157  $result = $this->applyOffset($result, $workArea);
2158  return $result;
2159  }
2160 
2161  /***********************************
2162  *
2163  * Scaling, Dimensions of images
2164  *
2165  ***********************************/
2181  public function imageMagickConvert($imagefile, $newExt = '', $w = '', $h = '', $params = '', $frame = '', $options = array(), $mustCreate = FALSE) {
2182  if ($this->NO_IMAGE_MAGICK) {
2183  // Returning file info right away
2184  return $this->getImageDimensions($imagefile);
2185  }
2186  if ($info = $this->getImageDimensions($imagefile)) {
2187  $newExt = strtolower(trim($newExt));
2188  // If no extension is given the original extension is used
2189  if (!$newExt) {
2190  $newExt = $info[2];
2191  }
2192  if ($newExt == 'web') {
2193  if (GeneralUtility::inList($this->webImageExt, $info[2])) {
2194  $newExt = $info[2];
2195  } else {
2196  $newExt = $this->gif_or_jpg($info[2], $info[0], $info[1]);
2197  if (!$params) {
2198  $params = $this->cmds[$newExt];
2199  }
2200  }
2201  }
2202  if (GeneralUtility::inList($this->imageFileExt, $newExt)) {
2203  if (strstr($w . $h, 'm')) {
2204  $max = 1;
2205  } else {
2206  $max = 0;
2207  }
2208  $data = $this->getImageScale($info, $w, $h, $options);
2209  $w = $data['origW'];
2210  $h = $data['origH'];
2211  // If no conversion should be performed
2212  // this flag is TRUE if the width / height does NOT dictate
2213  // the image to be scaled!! (that is if no width / height is
2214  // given or if the destination w/h matches the original image
2215  // dimensions or if the option to not scale the image is set)
2216  $noScale = !$w && !$h || $data[0] == $info[0] && $data[1] == $info[1] || !empty($options['noScale']);
2217  if ($noScale && !$data['crs'] && !$params && !$frame && $newExt == $info[2] && !$mustCreate) {
2218  // Set the new width and height before returning,
2219  // if the noScale option is set
2220  if (!empty($options['noScale'])) {
2221  $info[0] = $data[0];
2222  $info[1] = $data[1];
2223  }
2224  $info[3] = $imagefile;
2225  return $info;
2226  }
2227  $info[0] = $data[0];
2228  $info[1] = $data[1];
2229  $frame = $this->noFramePrepended ? '' : (int)$frame;
2230  if (!$params) {
2231  $params = $this->cmds[$newExt];
2232  }
2233  // Cropscaling:
2234  if ($data['crs']) {
2235  if (!$data['origW']) {
2236  $data['origW'] = $data[0];
2237  }
2238  if (!$data['origH']) {
2239  $data['origH'] = $data[1];
2240  }
2241  $offsetX = (int)(($data[0] - $data['origW']) * ($data['cropH'] + 100) / 200);
2242  $offsetY = (int)(($data[1] - $data['origH']) * ($data['cropV'] + 100) / 200);
2243  $params .= ' -crop ' . $data['origW'] . 'x' . $data['origH'] . '+' . $offsetX . '+' . $offsetY . '! ';
2244  }
2245  $command = $this->scalecmd . ' ' . $info[0] . 'x' . $info[1] . '! ' . $params . ' ';
2246  $cropscale = $data['crs'] ? 'crs-V' . $data['cropV'] . 'H' . $data['cropH'] : '';
2247  if ($this->alternativeOutputKey) {
2248  $theOutputName = GeneralUtility::shortMD5($command . $cropscale . basename($imagefile) . $this->alternativeOutputKey . '[' . $frame . ']' . $w . $h . serialize($params) . serialize($options));
2249  } else {
2250  $theOutputName = GeneralUtility::shortMD5($command . $cropscale . $imagefile . filemtime($imagefile) . '[' . $frame . ']' . $w . $h . serialize($params) . serialize($options));
2251  }
2252  if ($this->imageMagickConvert_forceFileNameBody) {
2254  $this->imageMagickConvert_forceFileNameBody = '';
2255  }
2256  // Making the temporary filename:
2257  $this->createTempSubDir('pics/');
2258  $output = $this->absPrefix . $this->tempPath . 'pics/' . $this->filenamePrefix . $theOutputName . '.' . $newExt;
2259  // Register temporary filename:
2260  $GLOBALS['TEMP_IMAGES_ON_PAGE'][] = $output;
2261  if ($this->dontCheckForExistingTempFile || !$this->file_exists_typo3temp_file($output, $imagefile)) {
2262  $this->imageMagickExec($imagefile, $output, $command, $frame);
2263  }
2264  if (file_exists($output)) {
2265  $info[3] = $output;
2266  $info[2] = $newExt;
2267  // params could realisticly change some imagedata!
2268  if ($params) {
2269  $info = $this->getImageDimensions($info[3]);
2270  }
2271  if ($info[2] == $this->gifExtension && !$this->dontCompress) {
2272  // Compress with IM (lzw) or GD (rle) (Workaround for the absence of lzw-compression in GD)
2273  GeneralUtility::gif_compress($info[3], '');
2274  }
2275  return $info;
2276  }
2277  }
2278  }
2279  }
2280 
2289  public function getImageDimensions($imageFile) {
2290  preg_match('/([^\\.]*)$/', $imageFile, $reg);
2291  if (file_exists($imageFile) && GeneralUtility::inList($this->imageFileExt, strtolower($reg[0]))) {
2292  if ($returnArr = $this->getCachedImageDimensions($imageFile)) {
2293  return $returnArr;
2294  } else {
2295  if ($temp = @getImageSize($imageFile)) {
2296  $returnArr = array($temp[0], $temp[1], strtolower($reg[0]), $imageFile);
2297  } else {
2298  $returnArr = $this->imageMagickIdentify($imageFile);
2299  }
2300  if ($returnArr) {
2301  $this->cacheImageDimensions($returnArr);
2302  return $returnArr;
2303  }
2304  }
2305  }
2306  return NULL;
2307  }
2308 
2317  public function cacheImageDimensions($identifyResult) {
2318  // Create md5 hash of filemtime and filesize
2319  $fileStatus = stat($identifyResult[3]);
2320  $md5Hash = md5($fileStatus['mtime'] . $fileStatus['size']);
2321  $result = FALSE;
2322  if ($md5Hash) {
2323  $fieldArray = array(
2324  'md5hash' => $md5Hash,
2325  'md5filename' => md5($identifyResult[3]),
2326  'tstamp' => $GLOBALS['EXEC_TIME'],
2327  'filename' => $identifyResult[3],
2328  'imagewidth' => $identifyResult[0],
2329  'imageheight' => $identifyResult[1]
2330  );
2331  $GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_imagesizes', $fieldArray);
2332  if (!($err = $GLOBALS['TYPO3_DB']->sql_error())) {
2333  $result = TRUE;
2334  }
2335  }
2336  return $result;
2337  }
2338 
2346  public function getCachedImageDimensions($imageFile) {
2347  // Create md5 hash of filemtime and filesize
2348  $fileStatus = stat($imageFile);
2349  $md5Hash = md5($fileStatus['mtime'] . $fileStatus['size']);
2350  $cachedImageDimensions = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('md5hash, md5filename, imagewidth, imageheight', 'cache_imagesizes', 'md5filename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr(md5($imageFile), 'cache_imagesizes'));
2351  $result = FALSE;
2352  if (is_array($cachedImageDimensions)) {
2353  if ($cachedImageDimensions['md5hash'] != $md5Hash) {
2354  // File has changed, delete the row
2355  $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_imagesizes', 'md5filename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($cachedImageDimensions['md5filename'], 'cache_imagesizes'));
2356  } else {
2357  preg_match('/([^\\.]*)$/', $imageFile, $imageExtension);
2358  $result = array(
2359  (int)$cachedImageDimensions['imagewidth'],
2360  (int)$cachedImageDimensions['imageheight'],
2361  strtolower($imageExtension[0]),
2362  $imageFile
2363  );
2364  }
2365  }
2366  return $result;
2367  }
2368 
2381  public function getImageScale($info, $w, $h, $options) {
2382  if (strstr($w . $h, 'm')) {
2383  $max = 1;
2384  } else {
2385  $max = 0;
2386  }
2387  if (strstr($w . $h, 'c')) {
2388  $out['cropH'] = (int)substr(strstr($w, 'c'), 1);
2389  $out['cropV'] = (int)substr(strstr($h, 'c'), 1);
2390  $crs = TRUE;
2391  } else {
2392  $crs = FALSE;
2393  }
2394  $out['crs'] = $crs;
2395  $w = (int)$w;
2396  $h = (int)$h;
2397  // If there are max-values...
2398  if (!empty($options['maxW'])) {
2399  // If width is given...
2400  if ($w) {
2401  if ($w > $options['maxW']) {
2402  $w = $options['maxW'];
2403  // Height should follow
2404  $max = 1;
2405  }
2406  } else {
2407  if ($info[0] > $options['maxW']) {
2408  $w = $options['maxW'];
2409  // Height should follow
2410  $max = 1;
2411  }
2412  }
2413  }
2414  if (!empty($options['maxH'])) {
2415  // If height is given...
2416  if ($h) {
2417  if ($h > $options['maxH']) {
2418  $h = $options['maxH'];
2419  // Height should follow
2420  $max = 1;
2421  }
2422  } else {
2423  // Changed [0] to [1] 290801
2424  if ($info[1] > $options['maxH']) {
2425  $h = $options['maxH'];
2426  // Height should follow
2427  $max = 1;
2428  }
2429  }
2430  }
2431  $out['origW'] = $w;
2432  $out['origH'] = $h;
2433  $out['max'] = $max;
2434  if (!$this->mayScaleUp) {
2435  if ($w > $info[0]) {
2436  $w = $info[0];
2437  }
2438  if ($h > $info[1]) {
2439  $h = $info[1];
2440  }
2441  }
2442  // If scaling should be performed
2443  if ($w || $h) {
2444  if ($w && !$h) {
2445  $info[1] = ceil($info[1] * ($w / $info[0]));
2446  $info[0] = $w;
2447  }
2448  if (!$w && $h) {
2449  $info[0] = ceil($info[0] * ($h / $info[1]));
2450  $info[1] = $h;
2451  }
2452  if ($w && $h) {
2453  if ($max) {
2454  $ratio = $info[0] / $info[1];
2455  if ($h * $ratio > $w) {
2456  $h = round($w / $ratio);
2457  } else {
2458  $w = round($h * $ratio);
2459  }
2460  }
2461  if ($crs) {
2462  $ratio = $info[0] / $info[1];
2463  if ($h * $ratio < $w) {
2464  $h = round($w / $ratio);
2465  } else {
2466  $w = round($h * $ratio);
2467  }
2468  }
2469  $info[0] = $w;
2470  $info[1] = $h;
2471  }
2472  }
2473  $out[0] = $info[0];
2474  $out[1] = $info[1];
2475  // Set minimum-measures!
2476  if (isset($options['minW']) && $out[0] < $options['minW']) {
2477  if (($max || $crs) && $out[0]) {
2478  $out[1] = round($out[1] * $options['minW'] / $out[0]);
2479  }
2480  $out[0] = $options['minW'];
2481  }
2482  if (isset($options['minH']) && $out[1] < $options['minH']) {
2483  if (($max || $crs) && $out[1]) {
2484  $out[0] = round($out[0] * $options['minH'] / $out[1]);
2485  }
2486  $out[1] = $options['minH'];
2487  }
2488  return $out;
2489  }
2490 
2500  public function file_exists_typo3temp_file($output, $orig = '') {
2501  if ($this->enable_typo3temp_db_tracking) {
2502  // If file exists, then we return immediately
2503  if (file_exists($output)) {
2504  return 1;
2505  } else {
2506  // If not, we look up in the cache_typo3temp_log table to see if there is a image being rendered right now.
2507  $md5Hash = md5($output);
2508  $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('md5hash', 'cache_typo3temp_log', 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5Hash, 'cache_typo3temp_log') . ' AND tstamp>' . ($GLOBALS['EXEC_TIME'] - 30));
2509  // If there was a record, the image is being generated by another process (we assume)
2510  if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
2511  $GLOBALS['TYPO3_DB']->sql_free_result($res);
2512  if (is_object($GLOBALS['TSFE'])) {
2513  $GLOBALS['TSFE']->set_no_cache('Another process renders this file now: ' . $output);
2514  }
2515  // ...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...)
2516  if (is_object($GLOBALS['TT'])) {
2517  $GLOBALS['TT']->setTSlogMessage('typo3temp_log: Assume this file is being rendered now: ' . $output);
2518  }
2519  // Return 'success - 2'
2520  return 2;
2521  } else {
2522  // 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.
2523  $insertFields = array(
2524  'md5hash' => $md5Hash,
2525  'tstamp' => $GLOBALS['EXEC_TIME'],
2526  'filename' => $output,
2527  'orig_filename' => $orig
2528  );
2529  $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_typo3temp_log', 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5Hash, 'cache_typo3temp_log'));
2530  $GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_typo3temp_log', $insertFields);
2531  if (is_object($GLOBALS['TT'])) {
2532  $GLOBALS['TT']->setTSlogMessage('typo3temp_log: The row did not exist, so a new is written and file is being processed: ' . $output);
2533  }
2534  return 0;
2535  }
2536  }
2537  } else {
2538  return file_exists($output);
2539  }
2540  }
2541 
2542  /***********************************
2543  *
2544  * ImageMagick API functions
2545  *
2546  ***********************************/
2555  public function imageMagickIdentify($imagefile) {
2556  if (!$this->NO_IMAGE_MAGICK) {
2557  $frame = $this->noFramePrepended ? '' : '[0]';
2558  $cmd = GeneralUtility::imageMagickCommand('identify', $this->wrapFileName($imagefile) . $frame);
2559  $returnVal = array();
2561  $splitstring = array_pop($returnVal);
2562  $this->IM_commands[] = array('identify', $cmd, $splitstring);
2563  if ($splitstring) {
2564  preg_match('/([^\\.]*)$/', $imagefile, $reg);
2565  $splitinfo = explode(' ', $splitstring);
2566  foreach ($splitinfo as $key => $val) {
2567  $temp = '';
2568  if ($val) {
2569  $temp = explode('x', $val);
2570  }
2571  if ((int)$temp[0] && (int)$temp[1]) {
2572  $dim = $temp;
2573  break;
2574  }
2575  }
2576  if ($dim[0] && $dim[1]) {
2577  return array($dim[0], $dim[1], strtolower($reg[0]), $imagefile);
2578  }
2579  }
2580  }
2581  }
2582 
2594  public function imageMagickExec($input, $output, $params, $frame = 0) {
2595  if (!$this->NO_IMAGE_MAGICK) {
2596  // Unless noFramePrepended is set in the Install Tool, a frame number is added to
2597  // select a specific page of the image (by default this will be the first page)
2598  if (!$this->noFramePrepended) {
2599  $frame = '[' . (int)$frame . ']';
2600  } else {
2601  $frame = '';
2602  }
2603  $cmd = GeneralUtility::imageMagickCommand('convert', $params . ' ' . $this->wrapFileName($input . $frame) . ' ' . $this->wrapFileName($output));
2604  $this->IM_commands[] = array($output, $cmd);
2606  // Change the permissions of the file
2608  return $ret;
2609  }
2610  }
2611 
2624  public function combineExec($input, $overlay, $mask, $output, $handleNegation = FALSE) {
2625  if (!$this->NO_IMAGE_MAGICK) {
2626  $params = '-colorspace GRAY +matte';
2627  $theMask = $this->randomName() . '.' . $this->gifExtension;
2628  $this->imageMagickExec($mask, $theMask, $params);
2629  $cmd = GeneralUtility::imageMagickCommand('combine', '-compose over +matte ' . $this->wrapFileName($input) . ' ' . $this->wrapFileName($overlay) . ' ' . $this->wrapFileName($theMask) . ' ' . $this->wrapFileName($output));
2630  // +matte = no alpha layer in output
2631  $this->IM_commands[] = array($output, $cmd);
2633  // Change the permissions of the file
2635  if (is_file($theMask)) {
2636  @unlink($theMask);
2637  }
2638  return $ret;
2639  }
2640  }
2641 
2648  protected function wrapFileName($inputName) {
2649  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
2650  $currentLocale = setlocale(LC_CTYPE, 0);
2651  setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
2652  }
2653  $escapedInputName = escapeshellarg($inputName);
2654  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
2655  setlocale(LC_CTYPE, $currentLocale);
2656  }
2657  return $escapedInputName;
2658  }
2659 
2660  /***********************************
2661  *
2662  * Various IO functions
2663  *
2664  ***********************************/
2672  public function checkFile($file) {
2673  if (@is_file($file)) {
2674  return $file;
2675  } else {
2676  return '';
2677  }
2678  }
2679 
2687  public function createTempSubDir($dirName) {
2688  // Checking if the this->tempPath is already prefixed with PATH_site and if not, prefix it with that constant.
2689  if (GeneralUtility::isFirstPartOfStr($this->tempPath, PATH_site)) {
2690  $tmpPath = $this->tempPath;
2691  } else {
2692  $tmpPath = PATH_site . $this->tempPath;
2693  }
2694  // Making the temporary filename:
2695  if (!@is_dir(($tmpPath . $dirName))) {
2696  return GeneralUtility::mkdir($tmpPath . $dirName);
2697  }
2698  }
2699 
2708  public function applyImageMagickToPHPGif(&$im, $command) {
2709  $tmpStr = $this->randomName();
2710  $theFile = $tmpStr . '.' . $this->gifExtension;
2711  $this->ImageWrite($im, $theFile);
2712  $this->imageMagickExec($theFile, $theFile, $command);
2713  $tmpImg = $this->imageCreateFromFile($theFile);
2714  if ($tmpImg) {
2715  ImageDestroy($im);
2716  $im = $tmpImg;
2717  $this->w = imagesx($im);
2718  $this->h = imagesy($im);
2719  }
2720  if (!$this->dontUnlinkTempFiles) {
2721  unlink($theFile);
2722  }
2723  }
2724 
2735  public function gif_or_jpg($type, $w, $h) {
2736  if ($type == 'ai' || $w * $h < $this->pixelLimitGif) {
2737  return $this->gifExtension;
2738  } else {
2739  return 'jpg';
2740  }
2741  }
2742 
2753  public function output($file) {
2754  if ($file) {
2755  $reg = array();
2756  preg_match('/([^\\.]*)$/', $file, $reg);
2757  $ext = strtolower($reg[0]);
2758  switch ($ext) {
2759  case 'gif':
2760 
2761  case 'png':
2762  if ($this->ImageWrite($this->im, $file)) {
2763  // ImageMagick operations
2764  if ($this->setup['reduceColors'] || !$this->png_truecolor) {
2765  $reduced = $this->IMreduceColors($file, MathUtility::forceIntegerInRange($this->setup['reduceColors'], 256, $this->truecolorColors, 256));
2766  if ($reduced) {
2767  @copy($reduced, $file);
2768  @unlink($reduced);
2769  }
2770  }
2771  // Compress with IM! (adds extra compression, LZW from ImageMagick)
2772  // (Workaround for the absence of lzw-compression in GD)
2773  GeneralUtility::gif_compress($file, 'IM');
2774  }
2775  break;
2776  case 'jpg':
2777 
2778  case 'jpeg':
2779  // Use the default
2780  $quality = 0;
2781  if ($this->setup['quality']) {
2782  $quality = MathUtility::forceIntegerInRange($this->setup['quality'], 10, 100);
2783  }
2784  if ($this->ImageWrite($this->im, $file, $quality)) {
2785 
2786  }
2787  break;
2788  }
2789  $GLOBALS['TEMP_IMAGES_ON_PAGE'][] = $file;
2790  }
2791  return $file;
2792  }
2793 
2801  public function destroy() {
2802  ImageDestroy($this->im);
2803  }
2804 
2812  public function imgTag($imgInfo) {
2813  return '<img src="' . $imgInfo[3] . '" width="' . $imgInfo[0] . '" height="' . $imgInfo[1] . '" border="0" alt="" />';
2814  }
2815 
2826  public function ImageWrite($destImg, $theImage, $quality = 0) {
2827  imageinterlace($destImg, 0);
2828  $ext = strtolower(substr($theImage, strrpos($theImage, '.') + 1));
2829  $result = FALSE;
2830  switch ($ext) {
2831  case 'jpg':
2832 
2833  case 'jpeg':
2834  if (function_exists('imageJpeg')) {
2835  if ($quality == 0) {
2836  $quality = $this->jpegQuality;
2837  }
2838  $result = imageJpeg($destImg, $theImage, $quality);
2839  }
2840  break;
2841  case 'gif':
2842  if (function_exists('imageGif')) {
2843  imagetruecolortopalette($destImg, TRUE, 256);
2844  $result = imageGif($destImg, $theImage);
2845  }
2846  break;
2847  case 'png':
2848  if (function_exists('imagePng')) {
2849  $result = ImagePng($destImg, $theImage);
2850  }
2851  break;
2852  }
2853  if ($result) {
2854  GeneralUtility::fixPermissions($theImage);
2855  }
2856  return $result;
2857  }
2858 
2867  public function imageCreateFromFile($sourceImg) {
2868  $imgInf = pathinfo($sourceImg);
2869  $ext = strtolower($imgInf['extension']);
2870  switch ($ext) {
2871  case 'gif':
2872  if (function_exists('imagecreatefromgif')) {
2873  return imageCreateFromGif($sourceImg);
2874  }
2875  break;
2876  case 'png':
2877  if (function_exists('imagecreatefrompng')) {
2878  $imageHandle = imageCreateFromPng($sourceImg);
2879  if ($this->saveAlphaLayer) {
2880  imagesavealpha($imageHandle, TRUE);
2881  }
2882  return $imageHandle;
2883  }
2884  break;
2885  case 'jpg':
2886 
2887  case 'jpeg':
2888  if (function_exists('imagecreatefromjpeg')) {
2889  return imageCreateFromJpeg($sourceImg);
2890  }
2891  break;
2892  }
2893  // If non of the above:
2894  $i = @getimagesize($sourceImg);
2895  $im = imagecreatetruecolor($i[0], $i[1]);
2896  $Bcolor = ImageColorAllocate($im, 128, 128, 128);
2897  ImageFilledRectangle($im, 0, 0, $i[0], $i[1], $Bcolor);
2898  return $im;
2899  }
2900 
2908  public function hexColor($col) {
2909  $r = dechex($col[0]);
2910  if (strlen($r) < 2) {
2911  $r = '0' . $r;
2912  }
2913  $g = dechex($col[1]);
2914  if (strlen($g) < 2) {
2915  $g = '0' . $g;
2916  }
2917  $b = dechex($col[2]);
2918  if (strlen($b) < 2) {
2919  $b = '0' . $b;
2920  }
2921  return '#' . $r . $g . $b;
2922  }
2923 
2933  public function unifyColors(&$img, $colArr, $closest = FALSE) {
2934  $retCol = -1;
2935  if (is_array($colArr) && count($colArr) && function_exists('imagepng') && function_exists('imagecreatefrompng')) {
2936  $firstCol = array_shift($colArr);
2937  $firstColArr = $this->convertColor($firstCol);
2938  if (count($colArr) > 1) {
2939  $origName = ($preName = $this->randomName() . '.png');
2940  $postName = $this->randomName() . '.png';
2941  $this->imageWrite($img, $preName);
2942  $firstCol = $this->hexColor($firstColArr);
2943  foreach ($colArr as $transparentColor) {
2944  $transparentColor = $this->convertColor($transparentColor);
2945  $transparentColor = $this->hexColor($transparentColor);
2946  $cmd = '-fill "' . $firstCol . '" -opaque "' . $transparentColor . '"';
2947  $this->imageMagickExec($preName, $postName, $cmd);
2948  $preName = $postName;
2949  }
2950  $this->imageMagickExec($postName, $origName, '');
2951  if (@is_file($origName)) {
2952  $tmpImg = $this->imageCreateFromFile($origName);
2953  }
2954  } else {
2955  $tmpImg = $img;
2956  }
2957  if ($tmpImg) {
2958  $img = $tmpImg;
2959  if ($closest) {
2960  $retCol = ImageColorClosest($img, $firstColArr[0], $firstColArr[1], $firstColArr[2]);
2961  } else {
2962  $retCol = ImageColorExact($img, $firstColArr[0], $firstColArr[1], $firstColArr[2]);
2963  }
2964  }
2965  // Unlink files from process
2966  if (!$this->dontUnlinkTempFiles) {
2967  if ($origName) {
2968  @unlink($origName);
2969  }
2970  if ($postName) {
2971  @unlink($postName);
2972  }
2973  }
2974  }
2975  return $retCol;
2976  }
2977 
2978 }
singleChars($theText, $returnUnicodeNumber=FALSE)
static imageMagickCommand($command, $parameters, $path='')
copyGifOntoGif(&$im, $cpImg, $conf, $workArea)
makeEmboss(&$im, $conf, $workArea, $txtConf)
imagecopyresized(&$dstImg, $srcImg, $dstX, $dstY, $srcX, $srcY, $dstWidth, $dstHeight, $srcWidth, $srcHeight)
imageMagickConvert($imagefile, $newExt='', $w='', $h='', $params='', $frame='', $options=array(), $mustCreate=FALSE)
SpacedImageTTFText(&$im, $fontSize, $angle, $x, $y, $Fcolor, $fontFile, $text, $spacing, $wordSpacing, $splitRenderingConf, $sF=1)
static isFirstPartOfStr($str, $partStr)
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
if(preg_match($regexp, $sql)) $pairs
Remove pairs of single-quotes.
getBreakSpace($conf, array $boundingBox=NULL)
makeEllipse(&$im, array $conf, array $workArea)
imageMagickExec($input, $output, $params, $frame=0)
static intExplode($delimiter, $string, $removeEmptyValues=FALSE, $limit=0)
const TYPO3_MODE
Definition: init.php:40
static fixPermissions($path, $recursive=FALSE)
static trimExplode($delim, $string, $removeEmptyValues=FALSE, $limit=0)
ImageTTFTextWrapper($im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering, $sF=1)
static split_fileref($fileNameWithPath)
makeShadow(&$im, $conf, $workArea, $txtConf)
makeOutline(&$im, $conf, $workArea, $txtConf)
if($list_of_literals) if(!empty($literals)) if(!empty($literals)) $result
Analyse literals to prepend the N char to them if their contents aren&#39;t numeric.
static gif_compress($theFile, $type)
ImageTTFBBoxWrapper($fontSize, $angle, $fontFile, $string, $splitRendering, $sF=1)
unifyColors(&$img, $colArr, $closest=FALSE)
debug($variable='', $name=' *variable *', $line=' *line *', $file=' *file *', $recursiveDepth=3, $debugLevel=E_DEBUG)
renderTTFText(&$im, $fontSize, $angle, $x, $y, $color, $fontFile, $string, $splitRendering, $conf, $sF=1)
ImageWrite($destImg, $theImage, $quality=0)
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
splitString($string, $splitRendering, $fontSize, $fontFile)
static exec($command, &$output=NULL, &$returnValue=0)
static sortedKeyList($setupArr, $acceptOnlyProperties=FALSE)
combineExec($input, $overlay, $mask, $output, $handleNegation=FALSE)