Removed some MS-DOS line endings from PHP files
[Packages/TYPO3.CMS.git] / t3lib / spritemanager / class.t3lib_spritemanager_spritegenerator.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2010 Steffen Ritter <info@steffen-ritter.net>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28
29 /**
30 * sprite generator
31 *
32 * @author Steffen Ritter <info@steffen-ritter.net>
33 * @package TYPO3
34 * @subpackage t3lib
35 */
36
37 class t3lib_spritemanager_SpriteGenerator {
38 /**
39 * template creating CSS for the spritefile
40 *
41 * @var string
42 */
43 protected $templateSprite = '
44 .###NAMESPACE###-###SPRITENAME### {
45 background-image: url(\'###SPRITEURL###\');
46 height: ###DEFAULTHEIGHT###px;
47 width: ###DEFAULTWIDTH###px;
48 }
49 ';
50
51 /**
52 *
53 * template creating CSS for position and size of a single icon
54 *
55 * @var string
56 */
57 protected $templateIcon = '.###NAMESPACE###-###ICONNAME### {
58 background-position: -###LEFT###px -###TOP###px;
59 ###SIZE_INFO###
60 }
61 ';
62
63 /**
64 * most common icon-width in the sprite
65 *
66 * @var int
67 */
68 protected $defaultWidth = 0;
69
70 /**
71 * most common icon-height in the sprite
72 *
73 * @var int
74 */
75 protected $defaultHeight = 0;
76
77 /**
78 * calculated width of the sprite
79 *
80 * @var int
81 */
82 protected $spriteWidth = 0;
83
84 /**
85 * calculated height of the sprite
86 * @var int
87 */
88 protected $spriteHeight = 0;
89
90 /**
91 * sprite name, will be the filename, too
92 *
93 * @var string
94 */
95 protected $spriteName = '';
96
97 /**
98 * the folder the sprite-images will be saved (relative to PATH_site)
99 *
100 * @var string
101 */
102 protected $spriteFolder = 'typo3temp/sprites/';
103
104 /**
105 * the folder the sprite-cs will be saved (relative to PATH_site)
106 *
107 * @var string
108 */
109 protected $cssFolder = 'typo3temp/sprites/';
110
111 /**
112 * the spriteName will not be included in icon names
113 *
114 * @var boolean
115 */
116 protected $ommitSpriteNameInIconName = FALSE;
117
118 /**
119 * @var boolean
120 * @deprecated IE6 support will be dropped within 4.6 - then gifcopies are superflous
121 */
122 protected $generateGIFCopy = TRUE;
123
124 /**
125 * namespace of css classes
126 *
127 * @var string
128 */
129 protected $nameSpace = 't3-icon';
130
131 /**
132 * setting this to true, the timestamp of the creation will be included to the background import
133 * helps to easily rebuild sprites without cache problems
134 *
135 * @var boolean
136 */
137 protected $includeTimestampInCSS = TRUE;
138
139 /**
140 * all bases/root-names included in the sprite which has to be in css
141 * as sprite to include the background-image
142 *
143 * @var array
144 */
145 protected $spriteBases = array();
146
147 /**
148 * collects data about all icons to process
149 *
150 * @var array
151 */
152 protected $iconsData = array();
153
154 /**
155 * collects all sizes of icons within this sprite and there count
156 *
157 * @var array
158 */
159 protected $iconSizes = array();
160
161 /**
162 * maps icon-sizes to iconnames
163 *
164 * @var array
165 */
166 protected $iconNamesPerSize = array();
167
168 /**
169 * space in px between to icons in the sprite (gap)
170 *
171 * @var int
172 */
173 protected $space = 2;
174
175 /**
176 * Initializes the configuration of the spritegenerator
177 *
178 * @param string $spriteName the name of the sprite to be generated
179 * @return void
180 */
181 public function __construct($spriteName) {
182 $this->spriteName = $spriteName;
183 }
184
185 /**
186 * Sets namespace of css code
187 *
188 * @param string $string
189 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
190 */
191 public function setNamespace($nameSpace) {
192 $this->nameSpace = $nameSpace;
193 return $this;
194 }
195
196 /**
197 * Sets the spritename
198 *
199 * @param string $spriteName the name of the sprite to be generated
200 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
201 */
202 public function setSpriteName($spriteName) {
203 $this->spriteName = $spriteName;
204 return $this;
205 }
206
207 /**
208 * Sets the sprite-graphics target-folder
209 *
210 * @param string $folder the target folder where the generated sprite is stored
211 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
212 */
213 public function setSpriteFolder($folder) {
214 $this->spriteFolder = $folder;
215 return $this;
216 }
217
218 /**
219 * Sets the sprite-css target-folder
220 *
221 * @param string $folder the target folder where the generated CSS files are stored
222 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
223 */
224 public function setCSSFolder($folder) {
225 $this->cssFolder = $folder;
226 return $this;
227 }
228
229 /**
230 * Setter do enable the exclusion of the sprites-name from iconnames
231 *
232 * @param boolean $value
233 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
234 */
235 public function setOmmitSpriteNameInIconName($value) {
236 $this->ommitSpriteNameInIconName = is_bool($value) ? $value : FALSE;
237 return $this;
238 }
239
240 /**
241 * Setter to adjust how much space is between to icons in the sprite
242 *
243 * @param int $value
244 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
245 */
246 public function setIconSpace($value) {
247 $this->space = intval($value);
248 return $this;
249 }
250
251 /**
252 * Setter to enable/disable generating a GIF-Copy of the sprite
253 *
254 * @param boolean $value
255 * @deprecated IE6 support will be dropped within 4.6 - then gifcopies are superflous
256 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
257 */
258 public function setGenerateGifCopy($value) {
259 $this->generateGIFCopy = is_bool($value) ? $value : TRUE;
260 return $this;
261 }
262
263 /**
264 * Setter for timestamp inclusion: imageFiles will be included with ?timestamp
265 *
266 * @param boolean $value
267 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
268 */
269 public function setIncludeTimestampInCSS($value) {
270 $this->includeTimestampInCSS = is_bool($value) ? $value : TRUE;
271 return $this;
272 }
273
274 /**
275 * Teads all png,gif,jpg files from the passed folder name (including 1 subfolder level)
276 * extracts size information and stores data in internal array,
277 * afterwards triggers sprite generation.
278 *
279 * @param array $inputFolder folder from which files are read
280 * @return array
281 */
282 public function generateSpriteFromFolder(array $inputFolder) {
283 $iconArray = array();
284 foreach ($inputFolder as $folder) {
285 // detect all files to be included in sprites
286 $iconArray = array_merge(
287 $iconArray,
288 $this->getFolder($folder)
289 );
290 }
291 return $this->generateSpriteFromArray($iconArray);
292 }
293
294 /**
295 * Method processes an array of files into an sprite,
296 * the array can be build from files within an folder or
297 * by hand (as the SpriteManager does)
298 *
299 * @param array $files array(icon name => icon file)
300 * @return array
301 */
302 public function generateSpriteFromArray(array $files) {
303 if (!$this->ommitSpriteNameInIconName) {
304 $this->spriteBases[] = $this->spriteName;
305 }
306
307 $this->buildFileInformationCache($files);
308 // calculate Icon Position in sprite
309 $this->calculateSpritePositions();
310
311 $this->generateGraphic();
312
313 $this->generateCSS();
314
315 $iconNames = array_keys($this->iconsData);
316 natsort($iconNames);
317
318 return array(
319 'spriteImage' => PATH_site . $this->spriteFolder . $this->spriteName . '.png',
320 'spriteGifImage'=> PATH_site . $this->spriteFolder . $this->spriteName . '.gif',
321 'cssFile' => PATH_site . $this->cssFolder . $this->spriteName . '.css',
322 'cssGif' => PATH_site . $this->cssFolder . $this->spriteName . '-ie6.css',
323 'iconNames' => $iconNames
324 );
325 }
326
327 /**
328 * Generates the css files
329 *
330 * @return void
331 */
332 protected function generateCSS() {
333 $cssData = '';
334 $cssIe6 = '';
335
336 if ($this->includeTimestampInCSS) {
337 $timestamp = '?' . time();
338 } else {
339 $timestamp = '';
340 }
341
342 $spritePathForCSS = $this->resolveSpritePath();
343
344 $markerArray = array(
345 '###NAMESPACE###' => $this->nameSpace,
346 '###DEFAULTWIDTH###' => $this->defaultWidth,
347 '###DEFAULTHEIGHT###' => $this->defaultHeight,
348 '###SPRITENAME###' => '',
349 '###SPRITEURL###' => ($spritePathForCSS ? $spritePathForCSS . '/' : '')
350 );
351 $markerArray['###SPRITEURL###'] .= $this->spriteName . '.png' . $timestamp;
352
353 foreach ($this->spriteBases as $base) {
354 $markerArray['###SPRITENAME###'] = $base;
355 $cssData .= t3lib_parsehtml::substituteMarkerArray($this->templateSprite, $markerArray);
356 }
357
358 if ($this->generateGIFCopy) {
359 $markerArray['###SPRITEURL###'] = str_replace('.png', '.gif', $markerArray['###SPRITEURL###']);
360 foreach ($this->spriteBases as $base) {
361 $cssIe6 .= t3lib_parsehtml::substituteMarkerArray($this->templateSprite, $markerArray);
362 }
363 }
364
365 foreach ($this->iconsData as $key => $data) {
366 $markerArrayIcons = array(
367 '###NAMESPACE###' => $this->nameSpace,
368 '###ICONNAME###' => $data['singleName'],
369 '###LEFT###' => $data['left'],
370 '###TOP###' => $data['top'],
371 '###SIZE_INFO###' => ''
372 );
373 if ($data['height'] != $this->defaultHeight) {
374 $markerArrayIcons['###SIZE_INFO###'] .= TAB . 'height: ' . $data['height'] . 'px;' . LF;
375 }
376 if ($data['width'] != $this->defaultWidth) {
377 $markerArrayIcons['###SIZE_INFO###'] .= TAB . 'width: ' . $data['width'] . 'px;' . LF;
378 }
379 $cssData .= t3lib_parsehtml::substituteMarkerArray($this->templateIcon, $markerArrayIcons);
380
381 }
382
383 t3lib_div::writeFile(PATH_site . $this->cssFolder . $this->spriteName . '.css', $cssData);
384 if ($this->generateGIFCopy) {
385 t3lib_div::writeFile(PATH_site . $this->cssFolder . $this->spriteName . '-ie6.css', $cssIe6);
386 }
387 }
388
389 /**
390 * Compares image path to CSS path and creates the relative backpath from css to the sprites
391 *
392 * @return string
393 */
394 protected function resolveSpritePath() {
395 // Fix window paths
396 $this->cssFolder = str_replace('\\', '/', $this->cssFolder);
397 $this->spriteFolder = str_replace('\\', '/', $this->spriteFolder);
398
399 $cssPathSegments = t3lib_div::trimExplode('/', trim($this->cssFolder, '/'));
400 $graphicPathSegments = t3lib_div::trimExplode('/', trim($this->spriteFolder, '/'));
401
402 $i = 0;
403 while (isset($cssPathSegments[$i]) && isset($graphicPathSegments[$i]) &&
404 $cssPathSegments[$i] == $graphicPathSegments[$i]) {
405 unset($cssPathSegments[$i]);
406 unset($graphicPathSegments[$i]);
407 ++$i;
408 }
409 foreach ($cssPathSegments AS $key => $value) {
410 $cssPathSegments[$key] = '..';
411 }
412 $completePath = array_merge($cssPathSegments, $graphicPathSegments);
413 $path = implode('/', $completePath);
414 return t3lib_div::resolveBackPath($path);
415 }
416
417 /**
418 * The actual sprite generator, renders the command for Im/GM and executes
419 *
420 * @return void
421 */
422 protected function generateGraphic() {
423 $iconParameters = array();
424 $tempSprite = t3lib_div::tempnam($this->spriteName);
425
426 $filePath = array(
427 'mainFile' => PATH_site . $this->spriteFolder . $this->spriteName . '.png',
428 'gifFile' => NULL
429 );
430 // create black true color image with given size
431 $newSprite = imagecreatetruecolor($this->spriteWidth, $this->spriteHeight);
432 imagesavealpha($newSprite, true);
433 // make it transparent
434 imagefill($newSprite, 0, 0, imagecolorallocatealpha($newSprite, 0, 255, 255, 127));
435 foreach ($this->iconsData AS $icon) {
436 $function = 'imagecreatefrom' . strtolower($icon['fileExtension']);
437 if(function_exists($function)) {
438 $currentIcon = $function($icon['fileName']);
439 imagecopy($newSprite, $currentIcon, $icon['left'], $icon['top'], 0, 0, $icon['width'], $icon['height']);
440 }
441 }
442 imagepng($newSprite, $tempSprite . '.png');
443
444 if ($this->generateGIFCopy) {
445 $filePath['gifFile'] = PATH_site . $this->spriteFolder . $this->spriteName . '.gif';
446 $gifSprite = imagecreatetruecolor($this->spriteWidth, $this->spriteHeight);
447 // make it transparent
448 imagefill($gifSprite, 0, 0, imagecolorallocate($gifSprite, 127, 127, 127));
449 foreach ($this->iconsData AS $icon) {
450 $function = 'imagecreatefrom' . strtolower($icon['fileExtension']);
451 if(function_exists($function)) {
452 $currentIcon = $function($icon['fileName']);
453 imagecopy($gifSprite, $currentIcon, $icon['left'], $icon['top'], 0, 0, $icon['width'], $icon['height']);
454 }
455 }
456 imagecolortransparent($gifSprite, imagecolorallocate($gifSprite, 127, 127, 127));
457 imagegif($gifSprite, $tempSprite . '.gif');
458 }
459
460 t3lib_div::upload_copy_move($tempSprite . '.png', $filePath['mainFile']);
461 t3lib_div::unlink_tempfile($tempSprite . '.png');
462 if ($this->generateGIFCopy) {
463 t3lib_div::upload_copy_move($tempSprite . '.gif', $filePath['gifFile']);
464 t3lib_div::unlink_tempfile($tempSprite . '.gif');
465 }
466 }
467 /**
468 * Arranges icons in sprites,
469 * afterwards all icons have information about ther position in sprite
470 */
471 protected function calculateSpritePositions() {
472 $currentLeft = 0;
473 $currentTop = 0;
474 // calculate width of every icon-size-group
475 $sizes = array();
476 foreach ($this->iconSizes as $sizeTag => $count) {
477 $size = $this->explodeSizeTag($sizeTag);
478 $sizes[ceil(sqrt($count)) * $size['width']] = $sizeTag;
479 }
480 // reverse sorting: widest group to top
481 krsort($sizes);
482 // integerate all icons grouped by icons size into the sprite
483 foreach ($sizes as $sizeTag) {
484 $size = $this->explodeSizeTag($sizeTag);
485 $currentLeft = 0;
486 $rowCounter = 0;
487
488 $rowSize = ceil(sqrt($this->iconSizes[$sizeTag]));
489
490 $rowWidth = $rowSize * $size['width'] + ($rowSize - 1) * $this->space;
491 $this->spriteWidth = ($rowWidth > $this->spriteWidth ? $rowWidth : $this->spriteWidth);
492 $firstLine = TRUE;
493
494 natsort($this->iconNamesPerSize[$sizeTag]);
495 foreach ($this->iconNamesPerSize[$sizeTag] as $iconName) {
496 if ($rowCounter == $rowSize - 1) {
497 $rowCounter = -1;
498 } elseif ($rowCounter == 0) {
499 if (!$firstLine) {
500 $currentTop += $size['height'];
501 $currentTop += $this->space;
502 }
503 $firstLine = FALSE;
504 $currentLeft = 0;
505 }
506 $this->iconsData[$iconName]['left'] = $currentLeft;
507 $this->iconsData[$iconName]['top'] = $currentTop;
508
509 $currentLeft += $size['width'];
510 $currentLeft += $this->space;
511
512 $rowCounter++;
513 }
514 $currentTop += $size['height'];
515 $currentTop += $this->space;
516 }
517 $this->spriteHeight = $currentTop;
518 }
519
520 /**
521 * Function getFolder traverses the target directory,
522 * locates all iconFiles and collects them into an array
523 *
524 * @param string path to an folder which contains images
525 * @return array returns an array with all files key: iconname, value: fileName
526 */
527 protected function getFolder($directoryPath) {
528 $subFolders = t3lib_div::get_dirs(PATH_site . $directoryPath);
529 if (!$this->ommitSpriteNameInIconName) {
530 $subFolders[] = '';
531 }
532 $resultArray = array();
533
534 foreach ($subFolders as $folder) {
535 if ($folder !== '.svn') {
536 $icons = t3lib_div::getFilesInDir(PATH_site . $directoryPath . $folder . '/', 'gif,png,jpg');
537 if (!in_array($folder, $this->spriteBases) && count($icons) && $folder !== '') {
538 $this->spriteBases[] = $folder;
539 }
540 foreach ($icons AS $icon) {
541 $fileInfo = pathinfo($icon);
542
543 $iconName = ($folder ? $folder . '-' : '') . $fileInfo['filename'];
544 if (!$this->ommitSpriteNameInIconName) {
545 $iconName = $this->spriteName . '-' . $iconName;
546 }
547
548 $resultArray[$iconName] = $directoryPath . $folder . '/' . $icon;
549 }
550 }
551 }
552 return $resultArray;
553 }
554
555 /**
556 * Generates file information cache from file array
557 *
558 * @param array list of all files with their icon name
559 * @return void
560 */
561 protected function buildFileInformationCache(array $files) {
562 foreach ($files as $iconName => $iconFile) {
563 $fileInfo = pathinfo(PATH_site . $iconFile);
564 $imageInfo = getimagesize(PATH_site . $iconFile);
565
566 $this->iconsData[$iconName] = array(
567 'iconName' => $iconName,
568 'singleName' => $fileInfo['filename'],
569 'fileExtension' => $fileInfo['extension'],
570 'fileName' => PATH_site . $iconFile,
571 'width' => $imageInfo[0],
572 'height' => $imageInfo[1],
573 'left' => 0,
574 'top' => 0
575 );
576
577 $sizeTag = $imageInfo[0] . 'x' . $imageInfo[1];
578 if (isset($this->iconSizes[$sizeTag])) {
579 $this->iconSizes[$sizeTag] += 1;
580 } else {
581 $this->iconSizes[$sizeTag] = 1;
582 $this->iconNamesPerSize[$sizeTag] = array();
583 }
584 $this->iconNamesPerSize[$sizeTag][] = $iconName;
585 }
586 // find most common image size, save it as default
587 asort($this->iconSizes);
588 $defaultSize = $this->explodeSizeTag(array_pop(array_keys($this->iconSizes)));
589 $this->defaultWidth = $defaultSize['width'];
590 $this->defaultHeight = $defaultSize['height'];
591 }
592
593 /**
594 * Transforms size tag into size array
595 *
596 * @param string a size tag at the cache arrays
597 * @return array
598 */
599 protected function explodeSizeTag($tag = '') {
600 $size = t3lib_div::trimExplode("x", $tag);
601 return array(
602 'width' => $size[0],
603 'height'=> $size[1]
604 );
605 }
606
607 }
608
609 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/spritemanager/class.t3lib_spritemanager_spritegenerator.php']) {
610 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/spritemanager/class.t3lib_spritemanager_spritegenerator.php']);
611 }
612 ?>