TYPO3 CMS  TYPO3_6-2
SpriteGenerator.php
Go to the documentation of this file.
1 <?php
3 
19 
26 
32  protected $templateSprite = '
33 .###NAMESPACE###-###SPRITENAME### {
34  background-image: url(\'###SPRITEURL###\') !important;
35  height: ###DEFAULTHEIGHT###px;
36  width: ###DEFAULTWIDTH###px;
37 }
38 ';
39 
46 .backgroundsize .###NAMESPACE###-###SPRITENAME### {
47  background-image: url(\'###SPRITEURL###\') !important;
48  background-size:###BGWIDTH### ###BGHEIGHT###;
49 }
50 ';
56  protected $templateIcon = '.###NAMESPACE###-###ICONNAME### {
57  background-position: -###LEFT###px -###TOP###px !important;
58 ###SIZE_INFO###
59 }
60 ';
61 
65  protected $enableHighDensitySprite = TRUE;
66 
72  protected $defaultWidth = 0;
73 
79  protected $defaultHeight = 0;
80 
86  protected $spriteWidth = 0;
87 
93  protected $spriteHeight = 0;
94 
100  protected $spriteName = '';
101 
107  protected $spriteFolder = 'typo3temp/sprites/';
108 
114  protected $cssFolder = 'typo3temp/sprites/';
115 
121  protected $omitSpriteNameInIconName = FALSE;
122 
128  protected $nameSpace = 't3-icon';
129 
136  protected $includeTimestampInCSS = TRUE;
137 
144  protected $spriteBases = array();
145 
151  protected $iconsData = array();
152 
158  protected $iconSizes = array();
159 
165  protected $iconNamesPerSize = array();
166 
172  protected $space = 2;
173 
179  public function __construct($spriteName) {
180  $this->spriteName = $spriteName;
181  }
182 
189  public function setNamespace($nameSpace) {
190  $this->nameSpace = $nameSpace;
191  return $this;
192  }
193 
200  public function setSpriteName($spriteName) {
201  $this->spriteName = $spriteName;
202  return $this;
203  }
204 
211  public function setSpriteFolder($folder) {
212  $this->spriteFolder = $folder;
213  return $this;
214  }
215 
222  public function setCSSFolder($folder) {
223  $this->cssFolder = $folder;
224  return $this;
225  }
226 
233  public function setEnableHighDensitySprite($enable = TRUE) {
234  $this->enableHighDensitySprite = $enable;
235  return $this;
236  }
237 
245  public function setOmmitSpriteNameInIconName($value) {
247  return $this->setOmitSpriteNameInIconName($value);
248  }
249 
256  public function setOmitSpriteNameInIconName($value) {
257  $this->omitSpriteNameInIconName = is_bool($value) ? $value : FALSE;
258  return $this;
259  }
260 
267  public function setIconSpace($value) {
268  $this->space = (int)$value;
269  return $this;
270  }
271 
278  public function setIncludeTimestampInCSS($value) {
279  $this->includeTimestampInCSS = is_bool($value) ? $value : TRUE;
280  return $this;
281  }
282 
291  public function generateSpriteFromFolder(array $inputFolder) {
292  $iconArray = array();
293  foreach ($inputFolder as $folder) {
294  // Detect all files to be included in sprites
295  $iconArray = array_merge($iconArray, $this->getFolder($folder));
296  }
297  return $this->generateSpriteFromArray($iconArray);
298  }
299 
308  public function generateSpriteFromArray(array $files) {
309  if (!$this->omitSpriteNameInIconName) {
310  $this->spriteBases[] = $this->spriteName;
311  }
312  $this->buildFileInformationCache($files);
313  // Calculate Icon Position in sprite
314  $this->calculateSpritePositions();
315  $this->generateGraphic();
316  if ($this->enableHighDensitySprite) {
318  }
319  $this->generateCSS();
320  $iconNames = array_keys($this->iconsData);
321  natsort($iconNames);
322  return array(
323  'spriteImage' => PATH_site . $this->spriteFolder . $this->spriteName . '.png',
324  'cssFile' => PATH_site . $this->cssFolder . $this->spriteName . '.css',
325  'iconNames' => $iconNames
326  );
327  }
328 
334  protected function generateCSS() {
335  $cssData = '';
336  if ($this->includeTimestampInCSS) {
337  $timestamp = '?' . time();
338  } else {
339  $timestamp = '';
340  }
341  $spritePathForCSS = $this->resolveSpritePath();
342  $markerArray = array(
343  '###NAMESPACE###' => $this->nameSpace,
344  '###DEFAULTWIDTH###' => $this->defaultWidth,
345  '###DEFAULTHEIGHT###' => $this->defaultHeight,
346  '###SPRITENAME###' => '',
347  '###SPRITEURL###' => $spritePathForCSS ? $spritePathForCSS . '/' : ''
348  );
349  $markerArray['###SPRITEURL###'] .= $this->spriteName . '.png' . $timestamp;
350  foreach ($this->spriteBases as $base) {
351  $markerArray['###SPRITENAME###'] = $base;
352  $cssData .= HtmlParser::substituteMarkerArray($this->templateSprite, $markerArray);
353 
354  if ($this->enableHighDensitySprite) {
355  $highDensityMarkerArray = array_merge($markerArray, array(
356  '###BGWIDTH###' => $this->spriteWidth . 'px',
357  '###BGHEIGHT###' => $this->spriteHeight . 'px',
358  '###SPRITEURL###' => str_replace(
359  $this->spriteName . '.png',
360  $this->spriteName . '@x2.png',
361  $markerArray['###SPRITEURL###']
362  )
363  ));
364  $cssData .= HtmlParser::substituteMarkerArray($this->templateSpriteHighDensity, $highDensityMarkerArray);
365  }
366  }
367 
368  foreach ($this->iconsData as $data) {
369  $temp = $data['iconNameParts'];
370  array_shift($temp);
371  $cssName = implode('-', $temp);
372  $markerArrayIcons = array(
373  '###NAMESPACE###' => $this->nameSpace,
374  '###ICONNAME###' => $cssName,
375  '###LEFT###' => $data['left'],
376  '###TOP###' => $data['top'],
377  '###SIZE_INFO###' => ''
378  );
379  if ($data['height'] != $this->defaultHeight) {
380  $markerArrayIcons['###SIZE_INFO###'] .= TAB . 'height: ' . $data['height'] . 'px;' . LF;
381  }
382  if ($data['width'] != $this->defaultWidth) {
383  $markerArrayIcons['###SIZE_INFO###'] .= TAB . 'width: ' . $data['width'] . 'px;' . LF;
384  }
385  $cssData .= HtmlParser::substituteMarkerArray($this->templateIcon, $markerArrayIcons);
386  }
387  GeneralUtility::writeFile(PATH_site . $this->cssFolder . $this->spriteName . '.css', $cssData);
388  }
389 
395  protected function resolveSpritePath() {
396  // Fix window paths
397  $this->cssFolder = str_replace('\\', '/', $this->cssFolder);
398  $this->spriteFolder = str_replace('\\', '/', $this->spriteFolder);
399  $cssPathSegments = GeneralUtility::trimExplode('/', trim($this->cssFolder, '/'));
400  $graphicPathSegments = GeneralUtility::trimExplode('/', trim($this->spriteFolder, '/'));
401  $i = 0;
402  while (isset($cssPathSegments[$i]) && isset($graphicPathSegments[$i]) && $cssPathSegments[$i] == $graphicPathSegments[$i]) {
403  unset($cssPathSegments[$i]);
404  unset($graphicPathSegments[$i]);
405  ++$i;
406  }
407  foreach ($cssPathSegments as $key => $value) {
408  $cssPathSegments[$key] = '..';
409  }
410  $completePath = array_merge($cssPathSegments, $graphicPathSegments);
411  $path = implode('/', $completePath);
412  return GeneralUtility::resolveBackPath($path);
413  }
414 
420  protected function generateGraphic() {
421  $tempSprite = GeneralUtility::tempnam($this->spriteName, '.png');
422  $filePath = PATH_site . $this->spriteFolder . $this->spriteName . '.png';
423 
424  // Create black true color image with given size
425  $newSprite = imagecreatetruecolor($this->spriteWidth, $this->spriteHeight);
426  imagesavealpha($newSprite, TRUE);
427  // Make it transparent
428  imagefill($newSprite, 0, 0, imagecolorallocatealpha($newSprite, 0, 255, 255, 127));
429  foreach ($this->iconsData as $icon) {
430  $function = 'imagecreatefrom' . strtolower($icon['fileExtension']);
431  if (function_exists($function)) {
432  $currentIcon = $function($icon['fileName']);
433  imagecopy($newSprite, $currentIcon, $icon['left'], $icon['top'], 0, 0, $icon['width'], $icon['height']);
434  }
435  }
436  imagepng($newSprite, $tempSprite);
437  GeneralUtility::upload_copy_move($tempSprite, $filePath);
438  GeneralUtility::unlink_tempfile($tempSprite);
439  }
440 
446  protected function generateHighDensityGraphic() {
447  $tempSprite = GeneralUtility::tempnam($this->spriteName . '@x2', '.png');
448  $filePath = PATH_site . $this->spriteFolder . $this->spriteName . '@x2.png';
449 
450  // Create black true color image with given size
451  $newSprite = imagecreatetruecolor($this->spriteWidth * 2, $this->spriteHeight * 2);
452  imagesavealpha($newSprite, TRUE);
453  // Make it transparent
454  imagefill($newSprite, 0, 0, imagecolorallocatealpha($newSprite, 0, 255, 255, 127));
455  foreach ($this->iconsData as $icon) {
456  $function = 'imagecreatefrom' . strtolower($icon['fileExtension']);
457  if (function_exists($function)) {
458  if ($icon['fileNameHighDensity'] !== FALSE) {
459  // copy HighDensity file
460  $currentIcon = $function($icon['fileNameHighDensity']);
461  imagecopy($newSprite, $currentIcon, $icon['left'] * 2, $icon['top'] * 2, 0, 0, $icon['width'] * 2, $icon['height'] * 2);
462  } else {
463  // scale up normal file
464  $currentIcon = $function($icon['fileName']);
465  imagecopyresized($newSprite, $currentIcon, $icon['left'] * 2, $icon['top'] * 2, 0, 0, $icon['width'] * 2, $icon['height'] * 2, $icon['width'], $icon['height']);
466  }
467  }
468  }
469  imagepng($newSprite, $tempSprite);
470  GeneralUtility::upload_copy_move($tempSprite, $filePath);
471  GeneralUtility::unlink_tempfile($tempSprite);
472  }
477  protected function calculateSpritePositions() {
478  // Calculate width of every icon-size-group
479  $sizes = array();
480  foreach ($this->iconSizes as $sizeTag => $count) {
481  $size = $this->explodeSizeTag($sizeTag);
482  $rowWidth = (int)ceil(sqrt($count)) * $size['width'];
483  while (isset($sizes[$rowWidth])) {
484  $rowWidth++;
485  }
486  $sizes[$rowWidth] = $sizeTag;
487  }
488  // Reverse sorting: widest group to top
489  krsort($sizes);
490  $currentTop = 0;
491  // Integrate all icons grouped by icons size into the sprite
492  foreach ($sizes as $sizeTag) {
493  $size = $this->explodeSizeTag($sizeTag);
494  $currentLeft = 0;
495  $rowCounter = 0;
496  $rowSize = ceil(sqrt($this->iconSizes[$sizeTag]));
497  $rowWidth = $rowSize * $size['width'] + ($rowSize - 1) * $this->space;
498  $this->spriteWidth = $rowWidth > $this->spriteWidth ? $rowWidth : $this->spriteWidth;
499  $firstLine = TRUE;
500  natsort($this->iconNamesPerSize[$sizeTag]);
501  foreach ($this->iconNamesPerSize[$sizeTag] as $iconName) {
502  if ($rowCounter == $rowSize - 1) {
503  $rowCounter = -1;
504  } elseif ($rowCounter == 0) {
505  if (!$firstLine) {
506  $currentTop += $size['height'];
507  $currentTop += $this->space;
508  }
509  $firstLine = FALSE;
510  $currentLeft = 0;
511  }
512  $this->iconsData[$iconName]['left'] = $currentLeft;
513  $this->iconsData[$iconName]['top'] = $currentTop;
514  $currentLeft += $size['width'];
515  $currentLeft += $this->space;
516  $rowCounter++;
517  }
518  $currentTop += $size['height'];
519  $currentTop += $this->space;
520  }
521  $this->spriteHeight = $currentTop;
522  }
523 
531  protected function getFolder($directoryPath) {
532  $subFolders = GeneralUtility::get_dirs(PATH_site . $directoryPath);
533  if (!$this->omitSpriteNameInIconName) {
534  $subFolders[] = '';
535  }
536  $resultArray = array();
537  foreach ($subFolders as $folder) {
538  if ($folder !== '.svn') {
539  $icons = GeneralUtility::getFilesInDir(PATH_site . $directoryPath . $folder . '/', 'gif,png,jpg');
540  if (!in_array($folder, $this->spriteBases) && count($icons) && $folder !== '') {
541  $this->spriteBases[] = $folder;
542  }
543  foreach ($icons as $icon) {
544  $fileInfo = pathinfo($icon);
545  $iconName = ($folder ? $folder . '-' : '') . $fileInfo['filename'];
546  if (!$this->omitSpriteNameInIconName) {
547  $iconName = $this->spriteName . '-' . $iconName;
548  }
549  $resultArray[$iconName] = $directoryPath . $folder . '/' . $icon;
550  }
551  }
552  }
553  return $resultArray;
554  }
555 
562  protected function buildFileInformationCache(array $files) {
563  foreach ($files as $iconName => $iconFile) {
564  $iconNameParts = GeneralUtility::trimExplode('-', $iconName);
565  if (!in_array($iconNameParts[0], $this->spriteBases)) {
566  $this->spriteBases[] = $iconNameParts[0];
567  }
568  $fileInfo = @pathinfo((PATH_site . $iconFile));
569  $imageInfo = @getimagesize((PATH_site . $iconFile));
570  $this->iconsData[$iconName] = array(
571  'iconName' => $iconName,
572  'iconNameParts' => $iconNameParts,
573  'singleName' => $fileInfo['filename'],
574  'fileExtension' => $fileInfo['extension'],
575  'fileName' => PATH_site . $iconFile,
576  'width' => $imageInfo[0],
577  'height' => $imageInfo[1],
578  'left' => 0,
579  'top' => 0,
580  'fileNameHighDensity' => FALSE
581  );
582  if ($this->enableHighDensitySprite) {
583  $highDensityFile = str_replace('.' . $fileInfo['extension'], '@x2.' . $fileInfo['extension'], $iconFile);
584  if (@file_exists(PATH_site . $highDensityFile)) {
585  $this->iconsData[$iconName]['fileNameHighDensity'] = $highDensityFile;
586  }
587  }
588  $sizeTag = $imageInfo[0] . 'x' . $imageInfo[1];
589  if (isset($this->iconSizes[$sizeTag])) {
590  $this->iconSizes[$sizeTag] += 1;
591  } else {
592  $this->iconSizes[$sizeTag] = 1;
593  $this->iconNamesPerSize[$sizeTag] = array();
594  }
595  $this->iconNamesPerSize[$sizeTag][] = $iconName;
596  }
597  // Find most common image size, save it as default
598  asort($this->iconSizes);
599  $defaultSize = $this->explodeSizeTag(array_pop(array_keys($this->iconSizes)));
600  $this->defaultWidth = $defaultSize['width'];
601  $this->defaultHeight = $defaultSize['height'];
602  }
603 
610  protected function explodeSizeTag($tag = '') {
611  $size = GeneralUtility::trimExplode('x', $tag);
612  return array(
613  'width' => $size[0],
614  'height' => $size[1]
615  );
616  }
617 
618 }
static unlink_tempfile($uploadedTempFileName)
static writeFile($file, $content, $changePermissions=FALSE)
static trimExplode($delim, $string, $removeEmptyValues=FALSE, $limit=0)
if(!defined('TYPO3_MODE')) if(TYPO3_MODE=='BE' &&!(TYPO3_REQUESTTYPE &TYPO3_REQUESTTYPE_INSTALL)) $icons
Definition: ext_tables.php:52
static substituteMarkerArray($content, $markContentArray, $wrap='', $uppercase=FALSE, $deleteUnused=FALSE)
Definition: HtmlParser.php:189
static tempnam($filePrefix, $fileSuffix='')
static getFilesInDir($path, $extensionList='', $prependPath=FALSE, $order='', $excludePattern='')
static upload_copy_move($source, $destination)