public function buildContent() { $RQ = RequestArrayAdapter::inst(); $mode = $RQ->str('mode', self::MODE_SPRITES_LIST); $name = $RQ->str('name'); $navigation = AdminPageNavigation::inst(); $sprites = CssSpritesManager::getAllDirsSptites(); switch ($mode) { case self::MODE_SPRITE: if (!array_key_exists($name, $sprites)) { $mode = self::MODE_SPRITES_LIST; break; } $smartyParams['sprite'] = CssSprite::inst($name)->rebuild(); break; } switch ($mode) { case self::MODE_SPRITES_LIST: $navigation->setCurrent('Список спрайтов'); break; case self::MODE_SPRITE: $navigation->addPath(self::urlSpritesList(), 'Список спрайтов'); $navigation->setCurrent($name); break; } $smartyParams['mode'] = $mode; $smartyParams['name'] = $name; $smartyParams['sprites'] = $sprites; return $this->getFoldedEntity()->fetchTpl($smartyParams); }
public function CreateSprite(CssSprite $sprite) { if ($sprite->exists()) { return; //--- Спрайт существует } $this->LOGGER->info("Waiting to make {$sprite}"); PsLock::lockMethod(__CLASS__, __FUNCTION__); try { if ($sprite->exists()) { $this->LOGGER->info('Sprite was created in another thread, skipping'); } else { $this->PROFILER->start('Sprite creation'); $this->CssSpriteGen->CreateSprite($sprite); $this->LOGGER->info('Sprite was successfully created, path: ' . $sprite->getCssDi()->getAbsPath()); $this->PROFILER->stop(); } } catch (Exception $ex) { PsLock::unlock(); throw $ex; } PsLock::unlock(); }
/** @return CssSprite */ public static function getSprite($item) { return CssSprite::inst($item); }
ini_set('memory_limit', '64M'); if (!is_dir($dir)) { throw new Exception('Images not found - Either you didn\'t upload any or your session expired'); } $padding = isset($_REQUEST['padding']) ? (int) $_REQUEST['padding'] : 3; $padding = min(max(0, $padding), 25); $type = 'png'; if (!empty($_REQUEST['output_type']) && in_array($_REQUEST['output_type'], ['jpeg', 'gif'])) { $type = $_REQUEST['output_type']; } $reduceArtefacts = !empty($_REQUEST['jpeg_reduce_artefacts']) && $type == 'jpeg'; $prefix = '.'; if (!empty($_REQUEST['css_prefix'])) { $prefix .= preg_replace('/[^a-z0-9_-]/ui', '', $_REQUEST['css_prefix']); } $s = new CssSprite($padding, $reduceArtefacts, $prefix); $s->addDirectory($dir); $s->pack(); $options = []; if ($type == 'jpeg' && isset($_REQUEST['jpeg_quality'])) { $options = [min(100, max(0, (int) $_REQUEST['jpeg_quality']))]; } elseif ($type == 'png') { $options = [9, PNG_ALL_FILTERS]; } $filepath = 'uploads/' . $token . '.' . $type; file_put_contents($filepath, $s->getImage($type, $options)); $server = $_SERVER['HTTP_HOST'] == 'localhost:96' ? 'dev.justsayplease.co.uk:96' : $_SERVER['HTTP_HOST']; $response->url = 'http://' . $server . dirname($_SERVER['PHP_SELF']) . '/uploads/' . $token . '.' . $type; $response->css = $s->getCss(); //$s->smush($filepath, $response->url); $oldSize = 0;
case UPLOAD_ERR_CANT_WRITE: $imageErrors[] = $name . ': Failed to write file to disk.'; break; case UPLOAD_ERR_EXTENSION: $imageErrors[] = $name . ': File upload stopped by extension.'; break; default: throw new Exception('Unexpected file error number "' . $_FILES['images']['error'][$i] . '"'); } } if (empty($imageErrors)) { if (count($images) < 2) { $errors['images'] = 'Please select at least 2 image files'; } else { $options = ['output_type' => empty($_POST['output_type']) ? 'png' : $_POST['output_type'], 'jpeg_quality' => !isset($_POST['jpeg_quality']) ? 75 : (int) $_POST['jpeg_quality'], 'css_prefix' => empty($_POST['css_prefix']) ? '' : $_POST['css_prefix'], 'padding' => empty($_POST['padding']) ? 0 : (int) $_POST['padding'], 'jpeg_reduce_artefacts' => !empty($_POST['jpeg_reduce_artefacts'])]; $sprite = new CssSprite(); try { $sprite->run($images, $options); $done = true; } catch (Exception $e) { $errors['sprite'] = 'There was an error creating your sprite: ' . $e->getMessage(); } } } else { $errors['images'] = implode(' ', $imageErrors); } } } if ($done) { $spriteDataUri = $sprite->getDataUri(); }
/** @return CssSprite */ public function getSprite($ident) { return $this->isSpritable() ? CssSprite::inst($this->getFoldedEntity($ident)) : null; }
function smarty_function_linkup_sprite($params, Smarty_Internal_Template &$smarty) { echo PsHtml::linkCss(CssSprite::inst($params['name'])->getCssDi()); }
public function CreateSprite(CssSprite $sprite) { $images = $sprite->getImages(); if (empty($images)) { return; //--- } $name = $sprite->getName(); $this->aFormValues['class-prefix'] = "sprite-{$name}-"; $DM = CssSprite::autogenWs(array($name, '.temp')); $DM->clearDir(); //Временная директория, в которой должны содержаться картинки, добавляемые в спрайт $sFolderMD5 = ensure_dir_endswith_dir_separator($DM->absDirPath()); /** @var $img DirItem */ foreach ($images as $img) { $img->copyTo($sFolderMD5 . $img->getName()); } // set up variable defaults used when calculating offsets etc $aFilesInfo = array(); $aFilesMD5 = array(); $bResize = false; if ($this->aFormValues['build-direction'] == 'horizontal') { $iRowCount = 1; $iTotalWidth = 0; $iTotalHeight = 0; $aMaxRowHeight = array(); $iMaxVOffset = 0; } else { $iColumnCount = 1; $iTotalWidth = 0; $iTotalHeight = 0; $aMaxColumnWidth = array(); $iMaxHOffset = 0; } $iMaxWidth = 0; $iMaxHeight = 0; $i = 0; $bValidImages = false; $sOutputFormat = strtolower($this->aFormValues['image-output']); // read the contents of the directory passed $oDir = dir($sFolderMD5); /* * **************************************** */ /* this section calculates all offsets etc */ /* * **************************************** */ // loop through directory (files will be processed in whatever the OS's default file ordering scheme is) // save to array so we can sort alphabetically $aFiles = array(); while (false !== ($sFile = $oDir->read())) { $aFiles[] = $sFile; } sort($aFiles); foreach ($aFiles as $sFile) { // do we want to scale down the source images // scaling up isn't supported as that would result in poorer quality images $bResize = $this->aFormValues['width-resize'] != 100 && $this->aFormValues['height-resize'] != 100; // grab path information $sFilePath = $sFolderMD5 . $sFile; $aPathParts = pathinfo($sFilePath); // are we matching filenames against a regular expression // if so it's likely not all images from the ZIP file will end up in the generated sprite image if (!empty($this->aFormValues['file-regex'])) { // forward slashes should be escaped - it's likely not doing this might be a security risk also // one might be able to break out and change the modifiers (to for example run PHP code) $this->aFormValues['file-regex'] = str_replace('/', '\\/', $this->aFormValues['file-regex']); // if the regular expression matches grab the first match and store for use as the class name if (preg_match('/^' . $this->aFormValues['file-regex'] . '$/i', $sFile, $aMatches)) { $sFileClass = $aMatches[1]; } else { $sFileClass = ''; } } else { // not using regular expressions - set the class name to the base part of the filename (excluding extension) $sFileClass = $aPathParts['basename']; } // format the class name - it should only contain certain characters // this strips out any which aren't $sFileClass = $this->FormatClassName($sFileClass); //$iImageType = exif_imagetype($sFilePath); // if we've got an empty class name then the file wasn't valid and shouldn't be included in the sprite image // the file also isn't valid if its extension doesn't match one of the image formats supported by the tool if (!empty($sFileClass) && isset($aPathParts['extension']) && in_array(strtoupper($aPathParts['extension']), $this->aImageTypes) && substr($sFile, 0, 1) != '.') { // grab the file extension $sExtension = $aPathParts['extension']; // get MD5 of file (this can be used to compare if a file's content is exactly the same as another's) $sFileMD5 = md5(file_get_contents($sFilePath)); // check if this file's MD5 already exists in array of MD5s recorded so far // if so it's a duplicate of another file in the ZIP if (($sKey = array_search($sFileMD5, $aFilesMD5)) !== false) { // do we want to drop duplicate files and merge CSS rules // if so CSS will end up like .filename1, .filename2 { } if ($this->aFormValues['ignore-duplicates'] == 'merge') { if (isset($aFilesInfo[$sKey]['class'])) { $aFilesInfo[$sKey]['class'] = $aFilesInfo[$sKey]['class'] . $this->aFormValues['selector-suffix'] . ', ' . $this->aFormValues['selector-prefix'] . '.' . $this->aFormValues['class-prefix'] . $sFileClass; continue; } } } // add MD5 to array to check future files against $aFilesMD5[$i] = $sFileMD5; // store generated class selector details $aFilesInfo[$i]['class'] = ".{$this->aFormValues['class-prefix']}{$sFileClass}"; // store file path information and extension $aFilesInfo[$i]['path'] = $sFilePath; $aFilesInfo[$i]['ext'] = $sExtension; // get dimensions of image $aImageInfo = getimagesize($sFilePath); $iWidth = $aImageInfo[0]; $iHeight = $aImageInfo[1]; if ($this->aFormValues['build-direction'] == 'horizontal') { // get the current width of the sprite image - after images processed so far $iCurrentWidth = $iTotalWidth + $this->aFormValues['horizontal-offset'] + $iWidth; // store the maximum width reached so far // if we're on a new column current height might be less than the maximum if ($iMaxWidth < $iCurrentWidth) { $iMaxWidth = $iCurrentWidth; } } else { // get the current height of the sprite image - after images processed so far $iCurrentHeight = $iTotalHeight + $this->aFormValues['vertical-offset'] + $iHeight; // store the maximum height reached so far // if we're on a new column current height might be less than the maximum if ($iMaxHeight < $iCurrentHeight) { $iMaxHeight = $iCurrentHeight; } } // store the original width and height of the image // we'll need this later if the image is to be resized $aFilesInfo[$i]['original-width'] = $iWidth; $aFilesInfo[$i]['original-height'] = $iHeight; // store the width and height of the image // if we're resizing they'll be less than the original $aFilesInfo[$i]['width'] = $bResize ? round($iWidth / 100 * $this->aFormValues['width-resize']) : $iWidth; $aFilesInfo[$i]['height'] = $bResize ? round($iHeight / 100 * $this->aFormValues['height-resize']) : $iHeight; if ($this->aFormValues['build-direction'] == 'horizontal') { // opera (9.0 and below) has a bug which prevents it recognising offsets of less than -2042px // all subsequent values are treated as -2042px // if we've hit 2000 pixels and we care about this (as set in the interface) then wrap to a new row // increment row count and reset current height if ($iTotalWidth + $this->aFormValues['horizontal-offset'] >= 2000 && !empty($this->aFormValues['wrap-columns'])) { $iRowCount++; $iTotalWidth = 0; } // if the current image is higher than any other in the current row then set the maximum height to that // it will be used to set the height of the current row if ($aFilesInfo[$i]['height'] > $iMaxHeight) { $iMaxHeight = $aFilesInfo[$i]['height']; } // keep track of the height of rows added so far $aMaxRowHeight[$iRowCount] = $iMaxHeight; // calculate the current maximum vertical offset so far $iMaxVOffset = $this->aFormValues['vertical-offset'] * ($iRowCount - 1); // get the x position of current image in overall sprite $aFilesInfo[$i]['x'] = $iTotalWidth; $iTotalWidth += $aFilesInfo[$i]['width'] + $this->aFormValues['horizontal-offset']; // get the y position of current image in overall sprite if ($iRowCount == 1) { $aFilesInfo[$i]['y'] = 0; } else { $aFilesInfo[$i]['y'] = $this->aFormValues['vertical-offset'] * ($iRowCount - 1) + (array_sum($aMaxRowHeight) - $aMaxRowHeight[$iRowCount]); } $aFilesInfo[$i]['currentCombinedWidth'] = $iTotalWidth; $aFilesInfo[$i]['rowNumber'] = $iRowCount; } else { if ($iTotalHeight + $this->aFormValues['vertical-offset'] >= 2000 && !empty($this->aFormValues['wrap-columns'])) { $iColumnCount++; $iTotalHeight = 0; } // if the current image is wider than any other in the current column then set the maximum width to that // it will be used to set the width of the current column if ($aFilesInfo[$i]['width'] > $iMaxWidth) { $iMaxWidth = $aFilesInfo[$i]['width']; } // keep track of the width of columns added so far $aMaxColumnWidth[$iColumnCount] = $iMaxWidth; // calculate the current maximum horizontal offset so far $iMaxHOffset = $this->aFormValues['horizontal-offset'] * ($iColumnCount - 1); // get the y position of current image in overall sprite $aFilesInfo[$i]['y'] = $iTotalHeight; $iTotalHeight += $aFilesInfo[$i]['height'] + $this->aFormValues['vertical-offset']; // get the x position of current image in overall sprite if ($iColumnCount == 1) { $aFilesInfo[$i]['x'] = 0; } else { $aFilesInfo[$i]['x'] = $this->aFormValues['horizontal-offset'] * ($iColumnCount - 1) + (array_sum($aMaxColumnWidth) - $aMaxColumnWidth[$iColumnCount]); } $aFilesInfo[$i]['currentCombinedHeight'] = $iTotalHeight; $aFilesInfo[$i]['columnNumber'] = $iColumnCount; } $i++; } } // close the dir handle $oDir->close(); /* * **************************************** */ /* this section generates the sprite image */ /* and CSS rules */ /* * **************************************** */ // if $i is greater than 1 then we managed to generate enough info to create a sprite if ($i > 1) { // if Imagick throws an exception we want the script to terminate cleanly so that // temporary files are cleaned up try { // get the sprite width and height if ($this->aFormValues['build-direction'] == 'horizontal') { $iSpriteWidth = $iMaxWidth - $this->aFormValues['horizontal-offset']; $iSpriteHeight = array_sum($aMaxRowHeight) + $iMaxVOffset; } else { $iSpriteHeight = $iMaxHeight - $this->aFormValues['vertical-offset']; $iSpriteWidth = array_sum($aMaxColumnWidth) + $iMaxHOffset; } // get background colour - remove # if added $sBgColour = str_replace('#', '', $this->aFormValues['background']); // convert 3 digit hex values to 6 digit equivalent if (strlen($sBgColour) == 3) { $sBgColour = substr($sBgColour, 0, 1) . substr($sBgColour, 0, 1) . substr($sBgColour, 1, 1) . substr($sBgColour, 1, 1) . substr($sBgColour, 2, 1) . substr($sBgColour, 2, 1); } // should the image be transparent $this->bTransparent = !empty($this->aFormValues['use-transparency']) && in_array($this->aFormValues['image-output'], array('GIF', 'PNG')); // if using Imagick library create new instance of library class if ($this->sImageLibrary == 'imagick') { $oSprite = new Imagick(); // create a new image - set background according to transparency if (!empty($this->aFormValues['background'])) { $oSprite->newImage($iSpriteWidth, $iSpriteHeight, new ImagickPixel("#{$sBgColour}"), $sOutputFormat); } else { if ($this->bTransparent) { $oSprite->newImage($iSpriteWidth, $iSpriteHeight, new ImagickPixel('#000000'), $sOutputFormat); } else { $oSprite->newImage($iSpriteWidth, $iSpriteHeight, new ImagickPixel('#ffffff'), $sOutputFormat); } } } else { // using GD - do the same thing if ($this->bTransparent && !empty($this->aFormValues['background'])) { $oSprite = imagecreate($iSpriteWidth, $iSpriteHeight); } else { $oSprite = imagecreatetruecolor($iSpriteWidth, $iSpriteHeight); } } // check for transparency option if ($this->bTransparent) { if ($this->sImageLibrary == 'imagick') { // set background colour to transparent // if no background colour use black if (!empty($this->aFormValues['background'])) { $oSprite->paintTransparentImage(new ImagickPixel("#{$sBgColour}"), 0.0, 0); } else { $oSprite->paintTransparentImage(new ImagickPixel("#000000"), 0.0, 0); } } else { // using GD - do the same thing if (!empty($this->aFormValues['background'])) { $iBgColour = hexdec($sBgColour); $iBgColour = imagecolorallocate($oSprite, 0xff & $iBgColour >> 0x10, 0xff & $iBgColour >> 0x8, 0xff & $iBgColour); imagecolortransparent($oSprite, $iBgColour); } else { imagealphablending($oSprite, false); imagesavealpha($oSprite, true); $col = imagecolorallocatealpha($oSprite, 255, 255, 255, 127); imagefill($oSprite, 0, 0, $col); } } } else { // set background colour if not using transparency and using GD if ($this->sImageLibrary != 'imagick') { if (empty($sBgColour)) { $sBgColour = 'ffffff'; } $iBgColour = hexdec($sBgColour); $iBgColour = imagecolorallocate($oSprite, 0xff & $iBgColour >> 0x10, 0xff & $iBgColour >> 0x8, 0xff & $iBgColour); imagefill($oSprite, 0, 0, $iBgColour); } } // initalise variable to store CSS rules $this->sCss = ".sprite-{$name} {background-image: url({$name}.{$sOutputFormat});}\n"; // loop through file info for valid images for ($i = 0; $i < count($aFilesInfo); $i++) { // create a new image object for current file if (!($oCurrentImage = $this->CreateImage($aFilesInfo[$i]['path'], $aFilesInfo[$i]['ext']))) { // if we've got here then a valid but corrupt image was found // at this stage we've already allocated space for the image so create // a blank one to fill the space instead // this should happen very rarely $oCurrentImage = new Imagick(); $oCurrentImage->newImage($aFilesInfo[$i]['original-width'], $aFilesInfo[$i]['original-height'], new ImagickPixel('#ffffff')); } // if resizing get image width and height and resample to new dimensions (percentage of original) // and copy to sprite image if ($bResize) { if ($this->sImageLibrary == 'imagick') { // resample image should work but doesn't seem to - using thumbnailImage instead // which achieves the same effect $oCurrentImage->thumbnailImage($aFilesInfo[$i]['width'], $aFilesInfo[$i]['height']); } else { imagecopyresampled($oSprite, $oCurrentImage, $aFilesInfo[$i]['x'], $aFilesInfo[$i]['y'], 0, 0, $aFilesInfo[$i]['width'], $aFilesInfo[$i]['height'], $aFilesInfo[$i]['original-width'], $aFilesInfo['original-height']); } } // copy image to sprite if ($this->sImageLibrary == 'imagick') { $oSprite->compositeImage($oCurrentImage, $oCurrentImage->getImageCompose(), $aFilesInfo[$i]['x'], $aFilesInfo[$i]['y']); } else { // if using GD and already resized the image will have been copied as part of the resize if (!$bResize) { imagecopy($oSprite, $oCurrentImage, $aFilesInfo[$i]['x'], $aFilesInfo[$i]['y'], 0, 0, $aFilesInfo[$i]['width'], $aFilesInfo[$i]['height']); } } // get CSS x & y values $iX = $aFilesInfo[$i]['x'] != 0 ? '-' . $aFilesInfo[$i]['x'] . 'px' : '0'; $iY = $aFilesInfo[$i]['y'] != 0 ? '-' . $aFilesInfo[$i]['y'] . 'px' : '0'; // create CSS rules and append to overall CSS rules $this->sCss .= "{$this->aFormValues['selector-prefix']}{$aFilesInfo[$i]['class']} {$this->aFormValues['selector-suffix']}{background-position: {$iX} {$iY}; "; // If add widths and heights the sprite image width and height are added to the CSS if ($this->aFormValues['add-width-height-to-css'] == 'on') { $this->sCss .= "width: {$aFilesInfo[$i]['width']}px; height: {$aFilesInfo[$i]['height']}px;"; } $this->sCss .= "} \n"; // destroy object created for current image to save memory if ($this->sImageLibrary == 'imagick') { $oCurrentImage->destroy(); } else { imagedestroy($oCurrentImage); } } // path to sprite img $imgDestPath = $sprite->getImgDi()->getAbsPath(); // write image to file (deleted by cron script after a limited time period) $this->WriteImage($oSprite, $sOutputFormat, $imgDestPath); // destroy object created for sprite image to save memory if ($this->sImageLibrary == 'imagick') { $oSprite->destroy(); } else { imagedestroy($oSprite); } // set flag to indicate valid images created $this->bValidImages = true; //Сохраним .css $sprite->getCssDi()->writeToFile($this->sCss, true); } catch (ImagickException $e) { error_log($e->getMessage()); } } //Очистим временную директорию $DM->clearDir(null, true); }