[TASK] Replace Space Indent into Tab indent (CGL Cleanup)
[Packages/TYPO3.CMS.git] / t3lib / spritemanager / class.t3lib_spritemanager_spritegenerator.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2010-2011 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###\') !important;
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 !important;
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 * namespace of css classes
120 *
121 * @var string
122 */
123 protected $nameSpace = 't3-icon';
124
125 /**
126 * setting this to TRUE, the timestamp of the creation will be included to the background import
127 * helps to easily rebuild sprites without cache problems
128 *
129 * @var boolean
130 */
131 protected $includeTimestampInCSS = TRUE;
132
133 /**
134 * all bases/root-names included in the sprite which has to be in css
135 * as sprite to include the background-image
136 *
137 * @var array
138 */
139 protected $spriteBases = array();
140
141 /**
142 * collects data about all icons to process
143 *
144 * @var array
145 */
146 protected $iconsData = array();
147
148 /**
149 * collects all sizes of icons within this sprite and there count
150 *
151 * @var array
152 */
153 protected $iconSizes = array();
154
155 /**
156 * maps icon-sizes to iconnames
157 *
158 * @var array
159 */
160 protected $iconNamesPerSize = array();
161
162 /**
163 * space in px between to icons in the sprite (gap)
164 *
165 * @var int
166 */
167 protected $space = 2;
168
169 /**
170 * Initializes the configuration of the spritegenerator
171 *
172 * @param string $spriteName the name of the sprite to be generated
173 * @return void
174 */
175 public function __construct($spriteName) {
176 $this->spriteName = $spriteName;
177 }
178
179 /**
180 * Sets namespace of css code
181 *
182 * @param string $string
183 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
184 */
185 public function setNamespace($nameSpace) {
186 $this->nameSpace = $nameSpace;
187 return $this;
188 }
189
190 /**
191 * Sets the spritename
192 *
193 * @param string $spriteName the name of the sprite to be generated
194 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
195 */
196 public function setSpriteName($spriteName) {
197 $this->spriteName = $spriteName;
198 return $this;
199 }
200
201 /**
202 * Sets the sprite-graphics target-folder
203 *
204 * @param string $folder the target folder where the generated sprite is stored
205 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
206 */
207 public function setSpriteFolder($folder) {
208 $this->spriteFolder = $folder;
209 return $this;
210 }
211
212 /**
213 * Sets the sprite-css target-folder
214 *
215 * @param string $folder the target folder where the generated CSS files are stored
216 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
217 */
218 public function setCSSFolder($folder) {
219 $this->cssFolder = $folder;
220 return $this;
221 }
222
223 /**
224 * Setter do enable the exclusion of the sprites-name from iconnames
225 *
226 * @param boolean $value
227 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
228 */
229 public function setOmmitSpriteNameInIconName($value) {
230 $this->ommitSpriteNameInIconName = is_bool($value) ? $value : FALSE;
231 return $this;
232 }
233
234 /**
235 * Setter to adjust how much space is between to icons in the sprite
236 *
237 * @param int $value
238 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
239 */
240 public function setIconSpace($value) {
241 $this->space = intval($value);
242 return $this;
243 }
244
245 /**
246 * Setter for timestamp inclusion: imageFiles will be included with ?timestamp
247 *
248 * @param boolean $value
249 * @return t3lib_spritemanager_SpriteGenerator an instance of $this, to enable chaining.
250 */
251 public function setIncludeTimestampInCSS($value) {
252 $this->includeTimestampInCSS = is_bool($value) ? $value : TRUE;
253 return $this;
254 }
255
256 /**
257 * Reads all png,gif,jpg files from the passed folder name (including 1 subfolder level)
258 * extracts size information and stores data in internal array,
259 * afterwards triggers sprite generation.
260 *
261 * @param array $inputFolder folder from which files are read
262 * @return array
263 */
264 public function generateSpriteFromFolder(array $inputFolder) {
265 $iconArray = array();
266 foreach ($inputFolder as $folder) {
267 // detect all files to be included in sprites
268 $iconArray = array_merge(
269 $iconArray,
270 $this->getFolder($folder)
271 );
272 }
273 return $this->generateSpriteFromArray($iconArray);
274 }
275
276 /**
277 * Method processes an array of files into an sprite,
278 * the array can be build from files within an folder or
279 * by hand (as the SpriteManager does)
280 *
281 * @param array $files array(icon name => icon file)
282 * @return array
283 */
284 public function generateSpriteFromArray(array $files) {
285 if (!$this->ommitSpriteNameInIconName) {
286 $this->spriteBases[] = $this->spriteName;
287 }
288
289 $this->buildFileInformationCache($files);
290 // calculate Icon Position in sprite
291 $this->calculateSpritePositions();
292
293 $this->generateGraphic();
294
295 $this->generateCSS();
296
297 $iconNames = array_keys($this->iconsData);
298 natsort($iconNames);
299
300 return array(
301 'spriteImage' => PATH_site . $this->spriteFolder . $this->spriteName . '.png',
302 'cssFile' => PATH_site . $this->cssFolder . $this->spriteName . '.css',
303 'iconNames' => $iconNames
304 );
305 }
306
307 /**
308 * Generates the css files
309 *
310 * @return void
311 */
312 protected function generateCSS() {
313 $cssData = '';
314
315 if ($this->includeTimestampInCSS) {
316 $timestamp = '?' . time();
317 } else {
318 $timestamp = '';
319 }
320
321 $spritePathForCSS = $this->resolveSpritePath();
322
323 $markerArray = array(
324 '###NAMESPACE###' => $this->nameSpace,
325 '###DEFAULTWIDTH###' => $this->defaultWidth,
326 '###DEFAULTHEIGHT###' => $this->defaultHeight,
327 '###SPRITENAME###' => '',
328 '###SPRITEURL###' => ($spritePathForCSS ? $spritePathForCSS . '/' : '')
329 );
330 $markerArray['###SPRITEURL###'] .= $this->spriteName . '.png' . $timestamp;
331
332 foreach ($this->spriteBases as $base) {
333 $markerArray['###SPRITENAME###'] = $base;
334 $cssData .= t3lib_parsehtml::substituteMarkerArray($this->templateSprite, $markerArray);
335 }
336
337 foreach ($this->iconsData as $key => $data) {
338 $temp = $data['iconNameParts'];
339 array_shift($temp);
340 $cssName = implode('-', $temp);
341 $markerArrayIcons = array(
342 '###NAMESPACE###' => $this->nameSpace,
343 '###ICONNAME###' => $cssName,
344 '###LEFT###' => $data['left'],
345 '###TOP###' => $data['top'],
346 '###SIZE_INFO###' => ''
347 );
348 if ($data['height'] != $this->defaultHeight) {
349 $markerArrayIcons['###SIZE_INFO###'] .= TAB . 'height: ' . $data['height'] . 'px;' . LF;
350 }
351 if ($data['width'] != $this->defaultWidth) {
352 $markerArrayIcons['###SIZE_INFO###'] .= TAB . 'width: ' . $data['width'] . 'px;' . LF;
353 }
354 $cssData .= t3lib_parsehtml::substituteMarkerArray($this->templateIcon, $markerArrayIcons);
355
356 }
357
358 t3lib_div::writeFile(PATH_site . $this->cssFolder . $this->spriteName . '.css', $cssData);
359 }
360
361 /**
362 * Compares image path to CSS path and creates the relative backpath from css to the sprites
363 *
364 * @return string
365 */
366 protected function resolveSpritePath() {
367 // Fix window paths
368 $this->cssFolder = str_replace('\\', '/', $this->cssFolder);
369 $this->spriteFolder = str_replace('\\', '/', $this->spriteFolder);
370
371 $cssPathSegments = t3lib_div::trimExplode('/', trim($this->cssFolder, '/'));
372 $graphicPathSegments = t3lib_div::trimExplode('/', trim($this->spriteFolder, '/'));
373
374 $i = 0;
375 while (isset($cssPathSegments[$i]) && isset($graphicPathSegments[$i]) &&
376 $cssPathSegments[$i] == $graphicPathSegments[$i]) {
377 unset($cssPathSegments[$i]);
378 unset($graphicPathSegments[$i]);
379 ++$i;
380 }
381 foreach ($cssPathSegments as $key => $value) {
382 $cssPathSegments[$key] = '..';
383 }
384 $completePath = array_merge($cssPathSegments, $graphicPathSegments);
385 $path = implode('/', $completePath);
386 return t3lib_div::resolveBackPath($path);
387 }
388
389 /**
390 * The actual sprite generator, renders the command for Im/GM and executes
391 *
392 * @return void
393 */
394 protected function generateGraphic() {
395 $tempSprite = t3lib_div::tempnam($this->spriteName);
396
397 $filePath = array(
398 'mainFile' => PATH_site . $this->spriteFolder . $this->spriteName . '.png',
399 );
400 // create black true color image with given size
401 $newSprite = imagecreatetruecolor($this->spriteWidth, $this->spriteHeight);
402 imagesavealpha($newSprite, TRUE);
403 // make it transparent
404 imagefill($newSprite, 0, 0, imagecolorallocatealpha($newSprite, 0, 255, 255, 127));
405 foreach ($this->iconsData as $icon) {
406 $function = 'imagecreatefrom' . strtolower($icon['fileExtension']);
407 if (function_exists($function)) {
408 $currentIcon = $function($icon['fileName']);
409 imagecopy($newSprite, $currentIcon, $icon['left'], $icon['top'], 0, 0, $icon['width'], $icon['height']);
410 }
411 }
412 imagepng($newSprite, $tempSprite . '.png');
413
414 t3lib_div::upload_copy_move($tempSprite . '.png', $filePath['mainFile']);
415 t3lib_div::unlink_tempfile($tempSprite . '.png');
416 }
417
418 /**
419 * Arranges icons in sprites,
420 * afterwards all icons have information about ther position in sprite
421 */
422 protected function calculateSpritePositions() {
423 $currentLeft = 0;
424 $currentTop = 0;
425 // calculate width of every icon-size-group
426 $sizes = array();
427 foreach ($this->iconSizes as $sizeTag => $count) {
428 $size = $this->explodeSizeTag($sizeTag);
429 $rowWidth = ceil(sqrt($count)) * $size['width'];
430 while (isset($sizes[$rowWidth])) {
431 $rowWidth++;
432 }
433 $sizes[$rowWidth] = $sizeTag;
434 }
435 // reverse sorting: widest group to top
436 krsort($sizes);
437 // integerate all icons grouped by icons size into the sprite
438 foreach ($sizes as $sizeTag) {
439 $size = $this->explodeSizeTag($sizeTag);
440 $currentLeft = 0;
441 $rowCounter = 0;
442
443 $rowSize = ceil(sqrt($this->iconSizes[$sizeTag]));
444
445 $rowWidth = $rowSize * $size['width'] + ($rowSize - 1) * $this->space;
446 $this->spriteWidth = ($rowWidth > $this->spriteWidth ? $rowWidth : $this->spriteWidth);
447 $firstLine = TRUE;
448
449 natsort($this->iconNamesPerSize[$sizeTag]);
450 foreach ($this->iconNamesPerSize[$sizeTag] as $iconName) {
451 if ($rowCounter == $rowSize - 1) {
452 $rowCounter = -1;
453 } elseif ($rowCounter == 0) {
454 if (!$firstLine) {
455 $currentTop += $size['height'];
456 $currentTop += $this->space;
457 }
458 $firstLine = FALSE;
459 $currentLeft = 0;
460 }
461 $this->iconsData[$iconName]['left'] = $currentLeft;
462 $this->iconsData[$iconName]['top'] = $currentTop;
463
464 $currentLeft += $size['width'];
465 $currentLeft += $this->space;
466
467 $rowCounter++;
468 }
469 $currentTop += $size['height'];
470 $currentTop += $this->space;
471 }
472 $this->spriteHeight = $currentTop;
473 }
474
475 /**
476 * Function getFolder traverses the target directory,
477 * locates all iconFiles and collects them into an array
478 *
479 * @param string path to an folder which contains images
480 * @return array returns an array with all files key: iconname, value: fileName
481 */
482 protected function getFolder($directoryPath) {
483 $subFolders = t3lib_div::get_dirs(PATH_site . $directoryPath);
484 if (!$this->ommitSpriteNameInIconName) {
485 $subFolders[] = '';
486 }
487 $resultArray = array();
488
489 foreach ($subFolders as $folder) {
490 if ($folder !== '.svn') {
491 $icons = t3lib_div::getFilesInDir(PATH_site . $directoryPath . $folder . '/', 'gif,png,jpg');
492 if (!in_array($folder, $this->spriteBases) && count($icons) && $folder !== '') {
493 $this->spriteBases[] = $folder;
494 }
495 foreach ($icons as $icon) {
496 $fileInfo = pathinfo($icon);
497
498 $iconName = ($folder ? $folder . '-' : '') . $fileInfo['filename'];
499 if (!$this->ommitSpriteNameInIconName) {
500 $iconName = $this->spriteName . '-' . $iconName;
501 }
502
503 $resultArray[$iconName] = $directoryPath . $folder . '/' . $icon;
504 }
505 }
506 }
507 return $resultArray;
508 }
509
510 /**
511 * Generates file information cache from file array
512 *
513 * @param array list of all files with their icon name
514 * @return void
515 */
516 protected function buildFileInformationCache(array $files) {
517 foreach ($files as $iconName => $iconFile) {
518
519 $iconNameParts = t3lib_div::trimExplode('-', $iconName);
520 if (!in_array($iconNameParts[0], $this->spriteBases)) {
521 $this->spriteBases[] = $iconNameParts[0];
522 }
523 $fileInfo = @pathinfo(PATH_site . $iconFile);
524 $imageInfo = @getimagesize(PATH_site . $iconFile);
525
526 $this->iconsData[$iconName] = array(
527 'iconName' => $iconName,
528 'iconNameParts' => $iconNameParts,
529 'singleName' => $fileInfo['filename'],
530 'fileExtension' => $fileInfo['extension'],
531 'fileName' => PATH_site . $iconFile,
532 'width' => $imageInfo[0],
533 'height' => $imageInfo[1],
534 'left' => 0,
535 'top' => 0
536 );
537
538 $sizeTag = $imageInfo[0] . 'x' . $imageInfo[1];
539 if (isset($this->iconSizes[$sizeTag])) {
540 $this->iconSizes[$sizeTag] += 1;
541 } else {
542 $this->iconSizes[$sizeTag] = 1;
543 $this->iconNamesPerSize[$sizeTag] = array();
544 }
545 $this->iconNamesPerSize[$sizeTag][] = $iconName;
546 }
547 // find most common image size, save it as default
548 asort($this->iconSizes);
549 $defaultSize = $this->explodeSizeTag(array_pop(array_keys($this->iconSizes)));
550 $this->defaultWidth = $defaultSize['width'];
551 $this->defaultHeight = $defaultSize['height'];
552 }
553
554 /**
555 * Transforms size tag into size array
556 *
557 * @param string a size tag at the cache arrays
558 * @return array
559 */
560 protected function explodeSizeTag($tag = '') {
561 $size = t3lib_div::trimExplode("x", $tag);
562 return array(
563 'width' => $size[0],
564 'height' => $size[1]
565 );
566 }
567 }
568
569 ?>