TYPO3 API  SVNRelease
class.t3lib_spritemanager_spritegenerator.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003  *  Copyright notice
00004  *
00005  *  (c) 2010-2011 Steffen Ritter <info@steffen-ritter.net>
00006  *  All rights reserved
00007  *
00008  *  This script is part of the TYPO3 project. The TYPO3 project is
00009  *  free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version.
00013  *
00014  *  The GNU General Public License can be found at
00015  *  http://www.gnu.org/copyleft/gpl.html.
00016  *  A copy is found in the textfile GPL.txt and important notices to the license
00017  *  from the author is found in LICENSE.txt distributed with these scripts.
00018  *
00019  *
00020  *  This script is distributed in the hope that it will be useful,
00021  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023  *  GNU General Public License for more details.
00024  *
00025  *  This copyright notice MUST APPEAR in all copies of the script!
00026  ***************************************************************/
00027 
00028 
00029 /**
00030  * sprite generator
00031  *
00032  * @author  Steffen Ritter <info@steffen-ritter.net>
00033  * @package TYPO3
00034  * @subpackage t3lib
00035  */
00036 
00037 class t3lib_spritemanager_SpriteGenerator {
00038     /**
00039      * template creating CSS for the spritefile
00040      *
00041      * @var string
00042      */
00043     protected $templateSprite = '
00044 .###NAMESPACE###-###SPRITENAME### {
00045     background-image: url(\'###SPRITEURL###\') !important;
00046     height: ###DEFAULTHEIGHT###px;
00047     width: ###DEFAULTWIDTH###px;
00048 }
00049 ';
00050 
00051     /**
00052      *
00053      * template creating CSS for position and size of a single icon
00054      *
00055      * @var string
00056      */
00057     protected $templateIcon = '.###NAMESPACE###-###ICONNAME### {
00058     background-position: -###LEFT###px -###TOP###px !important;
00059 ###SIZE_INFO###
00060 }
00061 ';
00062 
00063     /**
00064      * most common icon-width in the sprite
00065      *
00066      * @var int
00067      */
00068     protected $defaultWidth = 0;
00069 
00070     /**
00071      * most common icon-height in the sprite
00072      *
00073      * @var int
00074      */
00075     protected $defaultHeight = 0;
00076 
00077     /**
00078      * calculated width of the sprite
00079      *
00080      * @var int
00081      */
00082     protected $spriteWidth = 0;
00083 
00084     /**
00085      * calculated height of the sprite
00086      * @var int
00087      */
00088     protected $spriteHeight = 0;
00089 
00090     /**
00091      * sprite name, will be the filename, too
00092      *
00093      * @var string
00094      */
00095     protected $spriteName = '';
00096 
00097     /**
00098      * the folder the sprite-images will be saved (relative to PATH_site)
00099      *
00100      * @var string
00101      */
00102     protected $spriteFolder = 'typo3temp/sprites/';
00103 
00104     /**
00105      * the folder the sprite-cs will be saved (relative to PATH_site)
00106      *
00107      * @var string
00108      */
00109     protected $cssFolder = 'typo3temp/sprites/';
00110 
00111     /**
00112      * the spriteName will not be included in icon names
00113      *
00114      * @var boolean
00115      */
00116     protected $ommitSpriteNameInIconName = FALSE;
00117 
00118     /**
00119      * @var boolean
00120      * @deprecated IE6 support will be dropped within 4.6 - then gifcopies are superflous
00121      */
00122     protected $generateGIFCopy = TRUE;
00123 
00124     /**
00125      * namespace of css classes
00126      *
00127      * @var string
00128      */
00129     protected $nameSpace = 't3-icon';
00130 
00131     /**
00132      * setting this to true, the timestamp of the creation will be included to the background import
00133      * helps to easily rebuild sprites without cache problems
00134      *
00135      * @var boolean
00136      */
00137     protected $includeTimestampInCSS = TRUE;
00138 
00139     /**
00140      * all bases/root-names included in the sprite which has to be in css
00141      * as sprite to include the background-image
00142      *
00143      * @var array
00144      */
00145     protected $spriteBases = array();
00146 
00147     /**
00148      * collects data about all icons to process
00149      *
00150      * @var array
00151      */
00152     protected $iconsData = array();
00153 
00154     /**
00155      * collects all sizes of icons within this sprite and there count
00156      *
00157      * @var array
00158      */
00159     protected $iconSizes = array();
00160 
00161     /**
00162      * maps icon-sizes to iconnames
00163      *
00164      * @var array
00165      */
00166     protected $iconNamesPerSize = array();
00167 
00168     /**
00169      * space in px between to icons in the sprite (gap)
00170      *
00171      * @var int
00172      */
00173     protected $space = 2;
00174 
00175     /**
00176      * Initializes the configuration of the spritegenerator
00177      *
00178      * @param string $spriteName    the name of the sprite to be generated
00179      * @return void
00180      */
00181     public function __construct($spriteName) {
00182         $this->spriteName = $spriteName;
00183     }
00184 
00185     /**
00186      * Sets namespace of css code
00187      *
00188      * @param string $string
00189      * @return t3lib_spritemanager_SpriteGenerator  an instance of $this, to enable chaining.
00190      */
00191     public function setNamespace($nameSpace) {
00192         $this->nameSpace = $nameSpace;
00193         return $this;
00194     }
00195 
00196     /**
00197      * Sets the spritename
00198      *
00199      * @param string $spriteName    the name of the sprite to be generated
00200      * @return t3lib_spritemanager_SpriteGenerator  an instance of $this, to enable chaining.
00201      */
00202     public function setSpriteName($spriteName) {
00203         $this->spriteName = $spriteName;
00204         return $this;
00205     }
00206 
00207     /**
00208      * Sets the sprite-graphics target-folder
00209      *
00210      * @param string $folder the target folder where the generated sprite is stored
00211      * @return t3lib_spritemanager_SpriteGenerator  an instance of $this, to enable chaining.
00212      */
00213     public function setSpriteFolder($folder) {
00214         $this->spriteFolder = $folder;
00215         return $this;
00216     }
00217 
00218     /**
00219      * Sets the sprite-css target-folder
00220      *
00221      * @param string $folder the target folder where the generated CSS files are stored
00222      * @return t3lib_spritemanager_SpriteGenerator  an instance of $this, to enable chaining.
00223      */
00224     public function setCSSFolder($folder) {
00225         $this->cssFolder = $folder;
00226         return $this;
00227     }
00228 
00229     /**
00230      * Setter do enable the exclusion of the sprites-name from iconnames
00231      *
00232      * @param boolean $value
00233      * @return t3lib_spritemanager_SpriteGenerator  an instance of $this, to enable chaining.
00234      */
00235     public function setOmmitSpriteNameInIconName($value) {
00236         $this->ommitSpriteNameInIconName = is_bool($value) ? $value : FALSE;
00237         return $this;
00238     }
00239 
00240     /**
00241      * Setter to adjust how much space is between to icons in the sprite
00242      *
00243      * @param int $value
00244      * @return t3lib_spritemanager_SpriteGenerator  an instance of $this, to enable chaining.
00245      */
00246     public function setIconSpace($value) {
00247         $this->space = intval($value);
00248         return $this;
00249     }
00250 
00251     /**
00252      * Setter to enable/disable generating a GIF-Copy of the sprite
00253      *
00254      * @param boolean $value
00255      * @deprecated IE6 support will be dropped within 4.6 - then gifcopies are superflous
00256      * @return t3lib_spritemanager_SpriteGenerator  an instance of $this, to enable chaining.
00257      */
00258     public function setGenerateGifCopy($value) {
00259         $this->generateGIFCopy = is_bool($value) ? $value : TRUE;
00260         return $this;
00261     }
00262 
00263     /**
00264      * Setter for timestamp inclusion: imageFiles will be included with ?timestamp
00265      *
00266      * @param boolean $value
00267      * @return t3lib_spritemanager_SpriteGenerator  an instance of $this, to enable chaining.
00268      */
00269     public function setIncludeTimestampInCSS($value) {
00270         $this->includeTimestampInCSS = is_bool($value) ? $value : TRUE;
00271         return $this;
00272     }
00273 
00274     /**
00275      * Teads all png,gif,jpg files from the passed folder name (including 1 subfolder level)
00276      * extracts size information and stores data in internal array,
00277      * afterwards triggers sprite generation.
00278      *
00279      * @param array $inputFolder folder from which files are read
00280      * @return  array
00281      */
00282     public function generateSpriteFromFolder(array $inputFolder) {
00283         $iconArray = array();
00284         foreach ($inputFolder as $folder) {
00285                 // detect all files to be included in sprites
00286             $iconArray = array_merge(
00287                 $iconArray,
00288                 $this->getFolder($folder)
00289             );
00290         }
00291         return $this->generateSpriteFromArray($iconArray);
00292     }
00293 
00294     /**
00295      * Method processes an array of files into an sprite,
00296      * the array can be build from files within an folder or
00297      * by hand (as the SpriteManager does)
00298      *
00299      * @param array $files array(icon name => icon file)
00300      * @return array
00301      */
00302     public function generateSpriteFromArray(array $files) {
00303         if (!$this->ommitSpriteNameInIconName) {
00304             $this->spriteBases[] = $this->spriteName;
00305         }
00306 
00307         $this->buildFileInformationCache($files);
00308             // calculate Icon Position in sprite
00309         $this->calculateSpritePositions();
00310 
00311         $this->generateGraphic();
00312 
00313         $this->generateCSS();
00314 
00315         $iconNames = array_keys($this->iconsData);
00316         natsort($iconNames);
00317 
00318         return array(
00319             'spriteImage' => PATH_site . $this->spriteFolder . $this->spriteName . '.png',
00320             'spriteGifImage' => PATH_site . $this->spriteFolder . $this->spriteName . '.gif',
00321             'cssFile' => PATH_site . $this->cssFolder . $this->spriteName . '.css',
00322             'cssGif' => PATH_site . $this->cssFolder . $this->spriteName . '-ie6.css',
00323             'iconNames' => $iconNames
00324         );
00325     }
00326 
00327     /**
00328      * Generates the css files
00329      *
00330      * @return void
00331      */
00332     protected function generateCSS() {
00333         $cssData = '';
00334         $cssIe6 = '';
00335 
00336         if ($this->includeTimestampInCSS) {
00337             $timestamp = '?' . time();
00338         } else {
00339             $timestamp = '';
00340         }
00341 
00342         $spritePathForCSS = $this->resolveSpritePath();
00343 
00344         $markerArray = array(
00345             '###NAMESPACE###' => $this->nameSpace,
00346             '###DEFAULTWIDTH###' => $this->defaultWidth,
00347             '###DEFAULTHEIGHT###' => $this->defaultHeight,
00348             '###SPRITENAME###' => '',
00349             '###SPRITEURL###' => ($spritePathForCSS ? $spritePathForCSS . '/' : '')
00350         );
00351         $markerArray['###SPRITEURL###'] .= $this->spriteName . '.png' . $timestamp;
00352 
00353         foreach ($this->spriteBases as $base) {
00354             $markerArray['###SPRITENAME###'] = $base;
00355             $cssData .= t3lib_parsehtml::substituteMarkerArray($this->templateSprite, $markerArray);
00356         }
00357 
00358         if ($this->generateGIFCopy) {
00359             $markerArray['###SPRITEURL###'] = str_replace('.png', '.gif', $markerArray['###SPRITEURL###']);
00360             foreach ($this->spriteBases as $base) {
00361                 $cssIe6 .= t3lib_parsehtml::substituteMarkerArray($this->templateSprite, $markerArray);
00362             }
00363         }
00364 
00365         foreach ($this->iconsData as $key => $data) {
00366             $temp = $data['iconNameParts'];
00367             array_shift($temp);
00368             $cssName = implode('-', $temp);
00369             $markerArrayIcons = array(
00370                 '###NAMESPACE###' => $this->nameSpace,
00371                 '###ICONNAME###' => $cssName,
00372                 '###LEFT###' => $data['left'],
00373                 '###TOP###' => $data['top'],
00374                 '###SIZE_INFO###' => ''
00375             );
00376             if ($data['height'] != $this->defaultHeight) {
00377                 $markerArrayIcons['###SIZE_INFO###'] .= TAB . 'height: ' . $data['height'] . 'px;' . LF;
00378             }
00379             if ($data['width'] != $this->defaultWidth) {
00380                 $markerArrayIcons['###SIZE_INFO###'] .= TAB . 'width: ' . $data['width'] . 'px;' . LF;
00381             }
00382             $cssData .= t3lib_parsehtml::substituteMarkerArray($this->templateIcon, $markerArrayIcons);
00383 
00384         }
00385 
00386         t3lib_div::writeFile(PATH_site . $this->cssFolder . $this->spriteName . '.css', $cssData);
00387         if ($this->generateGIFCopy) {
00388             t3lib_div::writeFile(PATH_site . $this->cssFolder . $this->spriteName . '-ie6.css', $cssIe6);
00389         }
00390     }
00391 
00392     /**
00393      * Compares image path to CSS path and creates the relative backpath from css to the sprites
00394      *
00395      * @return string
00396      */
00397     protected function resolveSpritePath() {
00398             // Fix window paths
00399         $this->cssFolder = str_replace('\\', '/', $this->cssFolder);
00400         $this->spriteFolder = str_replace('\\', '/', $this->spriteFolder);
00401 
00402         $cssPathSegments = t3lib_div::trimExplode('/', trim($this->cssFolder, '/'));
00403         $graphicPathSegments = t3lib_div::trimExplode('/', trim($this->spriteFolder, '/'));
00404 
00405         $i = 0;
00406         while (isset($cssPathSegments[$i]) && isset($graphicPathSegments[$i]) &&
00407                $cssPathSegments[$i] == $graphicPathSegments[$i]) {
00408             unset($cssPathSegments[$i]);
00409             unset($graphicPathSegments[$i]);
00410             ++$i;
00411         }
00412         foreach ($cssPathSegments as $key => $value) {
00413             $cssPathSegments[$key] = '..';
00414         }
00415         $completePath = array_merge($cssPathSegments, $graphicPathSegments);
00416         $path = implode('/', $completePath);
00417         return t3lib_div::resolveBackPath($path);
00418     }
00419 
00420     /**
00421      * The actual sprite generator, renders the command for Im/GM and executes
00422      *
00423      * @return void
00424      */
00425     protected function generateGraphic() {
00426         $iconParameters = array();
00427         $tempSprite = t3lib_div::tempnam($this->spriteName);
00428 
00429         $filePath = array(
00430             'mainFile' => PATH_site . $this->spriteFolder . $this->spriteName . '.png',
00431             'gifFile' => NULL
00432         );
00433             // create black true color image with given size
00434         $newSprite = imagecreatetruecolor($this->spriteWidth, $this->spriteHeight);
00435         imagesavealpha($newSprite, TRUE);
00436             // make it transparent
00437         imagefill($newSprite, 0, 0, imagecolorallocatealpha($newSprite, 0, 255, 255, 127));
00438         foreach ($this->iconsData as $icon) {
00439             $function = 'imagecreatefrom' . strtolower($icon['fileExtension']);
00440             if (function_exists($function)) {
00441                 $currentIcon = $function($icon['fileName']);
00442                 imagecopy($newSprite, $currentIcon, $icon['left'], $icon['top'], 0, 0, $icon['width'], $icon['height']);
00443             }
00444         }
00445         imagepng($newSprite, $tempSprite . '.png');
00446 
00447         if ($this->generateGIFCopy) {
00448             $filePath['gifFile'] = PATH_site . $this->spriteFolder . $this->spriteName . '.gif';
00449             $gifSprite = imagecreatetruecolor($this->spriteWidth, $this->spriteHeight);
00450                 // make it transparent
00451             imagefill($gifSprite, 0, 0, imagecolorallocate($gifSprite, 127, 127, 127));
00452             foreach ($this->iconsData as $icon) {
00453                 $function = 'imagecreatefrom' . strtolower($icon['fileExtension']);
00454                 if (function_exists($function)) {
00455                     $currentIcon = $function($icon['fileName']);
00456                     imagecopy($gifSprite, $currentIcon, $icon['left'], $icon['top'], 0, 0, $icon['width'], $icon['height']);
00457                 }
00458             }
00459             imagecolortransparent($gifSprite, imagecolorallocate($gifSprite, 127, 127, 127));
00460             imagegif($gifSprite, $tempSprite . '.gif');
00461         }
00462 
00463         t3lib_div::upload_copy_move($tempSprite . '.png', $filePath['mainFile']);
00464         t3lib_div::unlink_tempfile($tempSprite . '.png');
00465         if ($this->generateGIFCopy) {
00466             t3lib_div::upload_copy_move($tempSprite . '.gif', $filePath['gifFile']);
00467             t3lib_div::unlink_tempfile($tempSprite . '.gif');
00468         }
00469     }
00470 
00471     /**
00472      * Arranges icons in sprites,
00473      * afterwards all icons have information about ther position in sprite
00474      */
00475     protected function calculateSpritePositions() {
00476         $currentLeft = 0;
00477         $currentTop = 0;
00478             // calculate width of every icon-size-group
00479         $sizes = array();
00480         foreach ($this->iconSizes as $sizeTag => $count) {
00481             $size = $this->explodeSizeTag($sizeTag);
00482             $rowWidth = ceil(sqrt($count)) * $size['width'];
00483             while (isset($sizes[$rowWidth])) {
00484                 $rowWidth++;
00485             }
00486             $sizes[$rowWidth] = $sizeTag;
00487         }
00488             // reverse sorting: widest group to top
00489         krsort($sizes);
00490             // integerate all icons grouped by icons size into the sprite
00491         foreach ($sizes as $sizeTag) {
00492             $size = $this->explodeSizeTag($sizeTag);
00493             $currentLeft = 0;
00494             $rowCounter = 0;
00495 
00496             $rowSize = ceil(sqrt($this->iconSizes[$sizeTag]));
00497 
00498             $rowWidth = $rowSize * $size['width'] + ($rowSize - 1) * $this->space;
00499             $this->spriteWidth = ($rowWidth > $this->spriteWidth ? $rowWidth : $this->spriteWidth);
00500             $firstLine = TRUE;
00501 
00502             natsort($this->iconNamesPerSize[$sizeTag]);
00503             foreach ($this->iconNamesPerSize[$sizeTag] as $iconName) {
00504                 if ($rowCounter == $rowSize - 1) {
00505                     $rowCounter = -1;
00506                 } elseif ($rowCounter == 0) {
00507                     if (!$firstLine) {
00508                         $currentTop += $size['height'];
00509                         $currentTop += $this->space;
00510                     }
00511                     $firstLine = FALSE;
00512                     $currentLeft = 0;
00513                 }
00514                 $this->iconsData[$iconName]['left'] = $currentLeft;
00515                 $this->iconsData[$iconName]['top'] = $currentTop;
00516 
00517                 $currentLeft += $size['width'];
00518                 $currentLeft += $this->space;
00519 
00520                 $rowCounter++;
00521             }
00522             $currentTop += $size['height'];
00523             $currentTop += $this->space;
00524         }
00525         $this->spriteHeight = $currentTop;
00526     }
00527 
00528     /**
00529      * Function getFolder traverses the target directory,
00530      * locates all iconFiles and collects them into an array
00531      *
00532      * @param string path to an folder which contains images
00533      * @return array returns an array with all files key: iconname, value: fileName
00534      */
00535     protected function getFolder($directoryPath) {
00536         $subFolders = t3lib_div::get_dirs(PATH_site . $directoryPath);
00537         if (!$this->ommitSpriteNameInIconName) {
00538             $subFolders[] = '';
00539         }
00540         $resultArray = array();
00541 
00542         foreach ($subFolders as $folder) {
00543             if ($folder !== '.svn') {
00544                 $icons = t3lib_div::getFilesInDir(PATH_site . $directoryPath . $folder . '/', 'gif,png,jpg');
00545                 if (!in_array($folder, $this->spriteBases) && count($icons) && $folder !== '') {
00546                     $this->spriteBases[] = $folder;
00547                 }
00548                 foreach ($icons as $icon) {
00549                     $fileInfo = pathinfo($icon);
00550 
00551                     $iconName = ($folder ? $folder . '-' : '') . $fileInfo['filename'];
00552                     if (!$this->ommitSpriteNameInIconName) {
00553                         $iconName = $this->spriteName . '-' . $iconName;
00554                     }
00555 
00556                     $resultArray[$iconName] = $directoryPath . $folder . '/' . $icon;
00557                 }
00558             }
00559         }
00560         return $resultArray;
00561     }
00562 
00563     /**
00564      * Generates file information cache from file array
00565      *
00566      * @param array list of all files with their icon name
00567      * @return void
00568      */
00569     protected function buildFileInformationCache(array $files) {
00570         foreach ($files as $iconName => $iconFile) {
00571 
00572             $iconNameParts = t3lib_div::trimExplode('-', $iconName);
00573             if (!in_array($iconNameParts[0], $this->spriteBases)) {
00574                 $this->spriteBases[] = $iconNameParts[0];
00575             }
00576             $fileInfo = @pathinfo(PATH_site . $iconFile);
00577             $imageInfo = @getimagesize(PATH_site . $iconFile);
00578 
00579             $this->iconsData[$iconName] = array(
00580                 'iconName' => $iconName,
00581                 'iconNameParts' => $iconNameParts,
00582                 'singleName' => $fileInfo['filename'],
00583                 'fileExtension' => $fileInfo['extension'],
00584                 'fileName' => PATH_site . $iconFile,
00585                 'width' => $imageInfo[0],
00586                 'height' => $imageInfo[1],
00587                 'left' => 0,
00588                 'top' => 0
00589             );
00590 
00591             $sizeTag = $imageInfo[0] . 'x' . $imageInfo[1];
00592             if (isset($this->iconSizes[$sizeTag])) {
00593                 $this->iconSizes[$sizeTag] += 1;
00594             } else {
00595                 $this->iconSizes[$sizeTag] = 1;
00596                 $this->iconNamesPerSize[$sizeTag] = array();
00597             }
00598             $this->iconNamesPerSize[$sizeTag][] = $iconName;
00599         }
00600             // find most common image size, save it as default
00601         asort($this->iconSizes);
00602         $defaultSize = $this->explodeSizeTag(array_pop(array_keys($this->iconSizes)));
00603         $this->defaultWidth = $defaultSize['width'];
00604         $this->defaultHeight = $defaultSize['height'];
00605     }
00606 
00607     /**
00608      * Transforms size tag into size array
00609      *
00610      * @param string  a size tag at the cache arrays
00611      * @return array
00612      */
00613     protected function explodeSizeTag($tag = '') {
00614         $size = t3lib_div::trimExplode("x", $tag);
00615         return array(
00616             'width' => $size[0],
00617             'height' => $size[1]
00618         );
00619     }
00620 
00621 }
00622 
00623 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/spritemanager/class.t3lib_spritemanager_spritegenerator.php'])) {
00624     include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/spritemanager/class.t3lib_spritemanager_spritegenerator.php']);
00625 }
00626 ?>