Index: t3lib/spritemanager/class.t3lib_spritemanager_spritegenerator.php =================================================================== --- t3lib/spritemanager/class.t3lib_spritemanager_spritegenerator.php (Revision 0) +++ t3lib/spritemanager/class.t3lib_spritemanager_spritegenerator.php (Revision 0) @@ -0,0 +1,572 @@ + +* All rights reserved +* +* This script is part of the TYPO3 project. The TYPO3 project is +* free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* The GNU General Public License can be found at +* http://www.gnu.org/copyleft/gpl.html. +* A copy is found in the textfile GPL.txt and important notices to the license +* from the author is found in LICENSE.txt distributed with these scripts. +* +* +* This script is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* This copyright notice MUST APPEAR in all copies of the script! +***************************************************************/ + + +/** + * sprite generator + * + * @author Steffen Ritter + * @package TYPO3 + * @subpackage t3lib + */ + +class t3lib_spritemanager_SpriteGenerator { + /** + * template creating CSS for the spritefile + * @var string + */ + private $templateSprite = ' +.NAMESPACE-SPRITENAME { + background-image: url(\'SPRITEURL\'); + height: DEFAULTHEIGHTpx; + width: DEFAULTWIDTHpx; +} +'; + + /** + * + * template creating CSS for position and size of a single icon + * @var string + */ + private $templateIcon = ' +.NAMESPACE-ICONNAME { + background-position: -LEFTpx -TOPpx; + SIZE_INFO +} +'; + + /** + * most common icon-width in the sprite + * @var int + */ + private $defaultWidth = 0; + + /** + * most common icon-width in the sprite + * @var int + */ + private $defaultHeight = 0; + + /** + * calculated width of the sprite + * @var int + */ + private $spriteWidth = 0; + + /** + * calculated height of the sprite + * @var int + */ + private $spriteHeight = 0; + + /** + * sprite name, will be the filename, too + * @var string + */ + private $spriteName = ''; + + /** + * the folder the sprite-images will be saved (relative to PATH_site) + * @var string + */ + private $spriteFolder = 'typo3temp/sprites/'; + + /** + * the folder the sprite-cs will be saved (relative to PATH_site) + * @var string + */ + private $cssFolder = 'typo3temp/sprites/'; + + /** + * the spriteName will not be included in icon names + * @var boolean + */ + private $ommitSpriteNameInIconName = FALSE; + + /** + * @var boolean + * @deprecated IE6 support will be dropped within 4.6 - then gifcopies are superflous + */ + private $generateGIFCopy = TRUE; + + /** + * namepsce of css classes + * @var string + */ + private $nameSpace = 't3-icon'; + + /** + * setting this to true, the timestamp of the creation will be included to the background import + * helps to easily rebuild sprites without cache problems + * @var boolean + */ + private $includeTimestampInCSS = TRUE; + + /** + * all bases/root-names included in the sprite which has to be in css + * as sprite to include the background-image + * @var array + */ + private $spriteBases = array(); + + /** + * collects data about all icons to proces + * @var array + */ + private $iconsData = array(); + + /** + * collects all sizes of icons within this sprite and there count + * @var array + */ + private $iconSizes = array(); + + /** + * maps icon-sizes to iconnames + * @var array + */ + private $iconNamesPerSize = array(); + + /** + * space in px between to icons in the sprite (gap) + * @var int + */ + private $space = 2; + + /** + * initializes the configuration of the spritegenerator + * + * @param string $spriteName the name of the sprite to be generated + * @return void + */ + public function __construct($spriteName) { + $this->spriteName = $spriteName; + } + + /** + * sets namespace of css code + * @param string $string + * @return $this + */ + public function setNamespace($nameSpace) { + $this->nameSpace = $nameSpace; + return $this; + } + + /** + * sets the spritename + * @param string $spriteName the name of the sprite to be generated + * @return $this + */ + public function setSpriteName($spriteName) { + $this->spriteName = $spriteName; + return $this; + } + + /** + * sets the sprite-graphics target-folder + * @param string $folder the target folder where the generated sprite is stored + * @return $this + */ + public function setSpriteFolder($folder) { + $this->spriteFolder = $folder; + return $this; + } + + /** + * sets the sprite-css target-folder + * @param string $folder the target folder where the generated CSS files are stored + * @return $this + */ + public function setCSSFolder($folder) { + $this->cssFolder = $folder; + return $this; + } + + /** + * setter do enable the exclusion of the sprites-name from iconnames + * @param boolean $value + * @return $this + */ + public function setOmmitSpriteNameInIconName($value) { + $this->ommitSpriteNameInIconName = is_bool($value) ? $value : FALSE; + return $this; + } + + /** + * setter to adjust how much space is between to icons in the sprite + * @param int $value + * @return $this + */ + public function setIconSpace($value) { + $this->space = intval($value); + return $this; + } + + /** + * setter to enable/disable generating a GIF-Copy of the sprite + * @param boolean $value + * @deprecated IE6 support will be dropped within 4.6 - then gifcopies are superflous + * @return $this + */ + public function setGenerateGifCopy($value) { + $this->generateGIFCopy = is_bool($value) ? $value : TRUE; + return $this; + } + + /** + * setter for timestamp inclusion: imageFiles will be included with ?timestamp + * @param boolean $value + */ + public function setIncludeTimestampInCSS($value) { + $this->includeTimestampInCSS = is_bool($value) ? $value : TRUE; + return $this; + } + + /** + * reads all png,gif,jpg files from the passed foldername (including 1 subfolder level) + * extracts size information and stores data in internal array, + * afterwards triggers sprite generation. + * @param array $inputFolder folder from which files are read + * @return array + */ + public function generateSpriteFromFolder(array $inputFolder) { + $iconArray = array(); + foreach ($inputFolder as $folder) { + //detect all files to be included in sprites + $iconArray = array_merge( + $iconArray, + $this->getFolder($folder) + ); + } + return $this->generateSpriteFromArray($iconArray); + } + + /** + * + * @param array $files + * @return array + */ + public function generateSpriteFromArray(array $files) { + if (!$this->ommitSpriteNameInIconName) { + $this->spriteBases[] = $this->spriteName; + } + t3lib_div::debug($this->spriteBases); + $this->buildFileInformationCache($files); + // calculate Icon Position in sprite + $this->calculateSpritePositions(); + + $this->generateGraphic(); + + $this->generateCSS(); + + $iconNames = array_keys($this->iconsData); + natsort($iconNames); + + return array( + 'spriteImage' => PATH_site . $this->spriteFolder . $this->spriteName . '.png', + 'spriteGifImage'=> PATH_site . $this->spriteFolder . $this->spriteName . '.gif', + 'cssFile' => PATH_site . $this->cssFolder . $this->spriteName . '.css', + 'cssGif' => PATH_site . $this->cssFolder . $this->spriteName . '-ie6.css', + 'iconNames' => $iconNames + ); + } + + /** + * generates the css files + * @return void + */ + private function generateCSS() { + $cssData = ''; + $cssIe6 = ''; + + if($this->includeTimestampInCSS) { + $timestamp = '?' . time(); + } else { + $timestamp = ''; + } + + $spritePathForCSS = $this->resolveSpritePath(); + + $markerArray = array( + 'NAMESPACE' => $this->nameSpace, + 'DEFAULTWIDTH' => $this->defaultWidth, + 'DEFAULTHEIGHT' => $this->defaultHeight, + 'SPRITENAME' => '', + 'SPRITEURL' => ($spritePathForCSS ? $spritePathForCSS . DIRECTORY_SEPARATOR : '') + ); + $markerArray['SPRITEURL'] .= $this->spriteName . '.png' . $timestamp; + + foreach ($this->spriteBases as $base) { + $markerArray['SPRITENAME'] = $base; + $cssData .= t3lib_parsehtml::substituteMarkerArray($this->templateSprite, $markerArray); + } + + if ($this->generateGIFCopy) { + $markerArray['SPRITEURL'] = str_replace('.png','.gif',$markerArray['SPRITEURL']); + foreach ($this->spriteBases as $base) { + $cssIe6 .= t3lib_parsehtml::substituteMarkerArray($this->templateSprite, $markerArray); + } + } + + foreach ($this->iconsData as $key => $data) { + $markerArrayIcons = array( + 'NAMESPACE' => $this->nameSpace, + 'ICONNAME' => $data['singleName'], + 'LEFT' => $data['left'], + 'TOP' => $data['top'], + 'SIZE_INFO' => '' + ); + if ($data['height'] != $this->defaultHeight) { + $markerArrayIcons['SIZE_INFO'] .= "\theight: " . $data['height'] . 'px;' . LF; + } + if ($data['width'] != $this->defaultWidth) { + $markerArrayIcons['SIZE_INFO'] .= "\twidth: " . $data['width'] . 'px;' . LF; + } + $cssData .= t3lib_parsehtml::substituteMarkerArray($this->templateIcon, $markerArrayIcons); + + } + + t3lib_div::writeFile(PATH_site . $this->cssFolder . $this->spriteName . '.css', $cssData); + if ($this->generateGIFCopy) { + t3lib_div::writeFile(PATH_site . $this->cssFolder . $this->spriteName . '-ie6.css', $cssIe6); + } + } + + /** + * compares Img-Path to CSS path and creates the relative backpath from css to the sprites + * @return string + */ + private function resolveSpritePath() { + $cssPathSegments = t3lib_div::trimExplode(DIRECTORY_SEPARATOR, trim($this->cssFolder, '/')); + $graphicPathSegments = t3lib_div::trimExplode(DIRECTORY_SEPARATOR, trim($this->spriteFolder, '/')); + + $i = 0; + while (isset($cssPathSegments[$i]) && isset($graphicPathSegments[$i]) && + $cssPathSegments[$i] == $graphicPathSegments[$i]) { + unset($cssPathSegments[$i]); + unset($graphicPathSegments[$i]); + ++$i; + } + foreach ($cssPathSegments AS $key => $value) { + $cssPathSegments[$key] = '..'; + } + $completePath = array_merge($cssPathSegments, $graphicPathSegments); + $path = implode(DIRECTORY_SEPARATOR, $completePath); + return t3lib_div::resolveBackPath($path); + } + + /** + * the actual sprite generator, renders the command for Im/GM and executes + * @return void + */ + private function generateGraphic() { + $iconParameters = array(); + $tempSprite = t3lib_div::tempnam($this->spriteName); + + $filePath = array( + 'mainFile' => PATH_site . $this->spriteFolder . $this->spriteName . '.png', + 'gifFile' => NULL + ); + // create black true color image with given size + $newSprite = imagecreatetruecolor($this->spriteWidth, $this->spriteHeight); + imagesavealpha($newSprite, true); + // make it transparent + imagefill($newSprite, 0, 0, imagecolorallocatealpha($newSprite, 0, 255, 255, 127)); + foreach ($this->iconsData AS $icon) { + $function = 'imagecreatefrom' . strtolower($icon['fileExtension']); + if(function_exists($function)) { + $currentIcon = $function($icon['fileName']); + imagecopy($newSprite, $currentIcon, $icon['left'], $icon['top'], 0, 0, $icon['width'], $icon['height']); + } + } + imagepng($newSprite, $tempSprite . '.png'); + + if ($this->generateGIFCopy) { + $filePath['gifFile'] = PATH_site . $this->spriteFolder . $this->spriteName . '.gif'; + $gifSprite = imagecreatetruecolor($this->spriteWidth, $this->spriteHeight); + // make it transparent + imagefill($gifSprite, 0, 0, imagecolorallocate($gifSprite, 127, 127, 127)); + foreach ($this->iconsData AS $icon) { + $function = 'imagecreatefrom' . strtolower($icon['fileExtension']); + if(function_exists($function)) { + $currentIcon = $function($icon['fileName']); + imagecopy($gifSprite, $currentIcon, $icon['left'], $icon['top'], 0, 0, $icon['width'], $icon['height']); + } + } + imagecolortransparent($gifSprite, imagecolorallocate($gifSprite, 127, 127, 127)); + imagegif($gifSprite, $tempSprite . '.gif'); + } + + t3lib_div::upload_copy_move($tempSprite . '.png', $filePath['mainFile']); + t3lib_div::unlink_tempfile($tempSprite . '.png'); + if ($this->generateGIFCopy) { + t3lib_div::upload_copy_move($tempSprite . '.gif', $filePath['gifFile']); + t3lib_div::unlink_tempfile($tempSprite . '.gif'); + } + } + /** + * aranges icons in sprites, + * afterwards all icons have information about ther position in sprite + */ + private function calculateSpritePositions() { + $currentLeft = 0; + $currentTop = 0; + // calculate width of every icon-size-group + $sizes = array(); + foreach ($this->iconSizes as $sizeTag => $count) { + $size = $this->explodeSizeTag($sizeTag); + $sizes[ceil(sqrt($count)) * $size['width']] = $sizeTag; + } + // reverse sorting: widest group to top + krsort($sizes); + // integerate all icons grouped by icons size into the sprite + foreach ($sizes as $sizeTag) { + $size = $this->explodeSizeTag($sizeTag); + $currentLeft = 0; + $rowCounter = 0; + + + $rowSize = ceil(sqrt($this->iconSizes[$sizeTag])); + + + $rowWidth = $rowSize * $size['width'] + ($rowSize - 1) * $this->space; + $this->spriteWidth = ($rowWidth > $this->spriteWidth ? $rowWidth : $this->spriteWidth); + $firstLine = true; + + natsort($this->iconNamesPerSize[$sizeTag]); + foreach ($this->iconNamesPerSize[$sizeTag] as $iconName) { + if ($rowCounter == $rowSize - 1) { + $rowCounter = -1; + } else if ($rowCounter == 0) { + if(!$firstLine) { + $currentTop += $size['height']; + $currentTop += $this->space; + } + $firstLine = false; + $currentLeft = 0; + } + $this->iconsData[$iconName]['left'] = $currentLeft; + $this->iconsData[$iconName]['top'] = $currentTop; + + $currentLeft += $size['width']; + $currentLeft += $this->space; + + $rowCounter++; + } + $currentTop += $size['height']; + $currentTop += $this->space; + } + $this->spriteHeight = $currentTop; + } + + /** + * getFiles traverses the target directory, + * locates all iconFiles and + * @param string path to an folder which contains images + * @return array returns an array with all files key: iconname, value: fileName + */ + private function getFolder($directoryPath) { + $subFolders = t3lib_div::get_dirs(PATH_site . $directoryPath); + if (!$this->ommitSpriteNameInIconName) { + $subFolders[] = ''; + } + $resultArray = array(); + + foreach ($subFolders as $folder) { + if ($folder != '.svn') { + $icons = t3lib_div::getFilesInDir(PATH_site . $directoryPath . $folder . DIRECTORY_SEPARATOR, 'gif,png,jpg'); + if (!in_array($folder, $this->spriteBases) && count($icons)) { + $this->spriteBases[] = $folder; + } + foreach ($icons AS $icon) { + $fileInfo = pathinfo($icon); + + $iconName = ($folder ? $folder . '-' : '') . $fileInfo['filename']; + if (!$this->ommitSpriteNameInIconName) { + $iconName = $this->spriteName . '-' . $iconName; + } + + $resultArray[$iconName] = $directoryPath . $folder . DIRECTORY_SEPARATOR . $icon; + } + } + } + return $resultArray; + } + + /** + * generates fileInformation Cache from file Array + * @param array list of all files with their icon name + * @return void + */ + private function buildFileInformationCache(array $files) { + foreach ($files as $iconName => $iconFile) { + $fileInfo = pathinfo(PATH_site . $iconFile); + $imageInfo = getimagesize(PATH_site . $iconFile); + + $this->iconsData[$iconName] = array( + 'iconName' => $iconName, + 'singleName'=> $fileInfo['filename'], + 'fileExtension'=> $fileInfo['extension'], + 'fileName' => PATH_site . $iconFile, + 'width' => $imageInfo[0], + 'height' => $imageInfo[1], + 'left' => 0, + 'top' => 0 + ); + + $sizeTag = $imageInfo[0] . 'x' . $imageInfo[1]; + if (isset($this->iconSizes[$sizeTag])) { + $this->iconSizes[$sizeTag] += 1; + } else { + $this->iconSizes[$sizeTag] = 1; + $this->iconNamesPerSize[$sizeTag] = array(); + } + $this->iconNamesPerSize[$sizeTag][] = $iconName; + } + // find most common imagesize, save it as default + asort($this->iconSizes); + $defaultSize = $this->explodeSizeTag(array_pop(array_keys($this->iconSizes))); + $this->defaultWidth = $defaultSize['width']; + $this->defaultHeight = $defaultSize['height']; + } + + /** + * transforms size tag into size array + * @param string a size tag at the cache arrays + * @return array + */ + private function explodeSizeTag($tag = '') { + $size = t3lib_div::trimExplode("x", $tag); + return array( + 'width' => $size[0], + 'height'=> $size[1] + ); + } + +} +?> \ No newline at end of file Index: t3lib/core_autoload.php =================================================================== --- t3lib/core_autoload.php (Revision 8447) +++ t3lib/core_autoload.php (Arbeitskopie) @@ -135,6 +135,7 @@ 't3lib_spritemanager' => PATH_t3lib . 'class.t3lib_spritemanager.php', 't3lib_spritemanager_spriteicongenerator' => PATH_t3lib . 'interfaces/interface.t3lib_spritemanager_spriteicongenerator.php', 't3lib_spritemanager_simplehandler' => PATH_t3lib . 'spritemanager/class.t3lib_spritemanager_simplehandler.php', + 't3lib_spritemanager_spritegenerator' => PATH_t3lib . 'spritemanager/class.t3lib_spritemanager_spritegenerator.php', ); $tslibClasses = require(PATH_typo3 . 'sysext/cms/ext_autoload.php');