/** * Returns the configuration for the image thumbnail with the given ID. */ public function imageThumbnailAction() { $this->checkUserPermission("thumbnails"); try { $id = $this->getParam("id"); if ($id) { $config = Asset\Image\Thumbnail\Config::getByName($id); if (!$config instanceof Asset\Image\Thumbnail\Config) { throw new \Exception("Thumbnail '" . $id . "' file doesn't exists"); } $this->encoder->encode(["success" => true, "data" => $config->getForWebserviceExport()]); return; } } catch (\Exception $e) { \Logger::error($e); $this->encoder->encode(["success" => false, "msg" => (string) $e]); } $this->encoder->encode(["success" => false]); }
protected function execute(InputInterface $input, OutputInterface $output) { // get all thumbnails $dir = Asset\Image\Thumbnail\Config::getWorkingDir(); $thumbnails = array(); $files = scandir($dir); foreach ($files as $file) { if (strpos($file, ".xml")) { $thumbnails[] = str_replace(".xml", "", $file); } } $allowedThumbs = array(); if ($input->getOption("thumbnails")) { $allowedThumbs = explode(",", $input->getOption("thumbnails")); } // get only images $conditions = array("type = 'image'"); if ($input->getOption("parent")) { $parent = Asset::getById($input->getOption("parent")); if ($parent instanceof Asset\Folder) { $conditions[] = "path LIKE '" . $parent->getFullPath() . "/%'"; } else { $this->writeError($input->getOption("parent") . " is not a valid asset folder ID!"); exit; } } $list = new Asset\Listing(); $list->setCondition(implode(" AND ", $conditions)); $total = $list->getTotalCount(); $perLoop = 10; for ($i = 0; $i < ceil($total / $perLoop); $i++) { $list->setLimit($perLoop); $list->setOffset($i * $perLoop); $images = $list->load(); foreach ($images as $image) { foreach ($thumbnails as $thumbnail) { if (empty($allowedThumbs) && !$input->getOption("system") || in_array($thumbnail, $allowedThumbs)) { if ($input->getOption("force")) { $image->clearThumbnail($thumbnail); } $this->output->writeln("generating thumbnail for image: " . $image->getFullpath() . " | " . $image->getId() . " | Thumbnail: " . $thumbnail . " : " . formatBytes(memory_get_usage())); $this->output->writeln("generated thumbnail: " . $image->getThumbnail($thumbnail)); } } if ($input->getOption("system")) { $thumbnail = Asset\Image\Thumbnail\Config::getPreviewConfig(); if ($input->getOption("force")) { $image->clearThumbnail($thumbnail->getName()); } $this->output->writeln("generating thumbnail for image: " . $image->getFullpath() . " | " . $image->getId() . " | Thumbnail: System Preview (tree) : " . formatBytes(memory_get_usage())); $this->output->writeln("generated thumbnail: " . $image->getThumbnail($thumbnail)); } } \Pimcore::collectGarbage(); } }
/** * Loads a list of predefined properties for the specicifies parameters, returns an array of Property\Predefined elements * * @return array */ public function load() { $properties = array(); $propertiesData = $this->db->fetchAll($this->model->getFilter(), $this->model->getOrder()); foreach ($propertiesData as $propertyData) { $properties[] = Config::getByName($propertyData["id"]); } $this->model->setThumbnails($properties); return $properties; }
/** * @param $thumbnailName * @param int $page * @param bool $deferred $deferred deferred means that the image will be generated on-the-fly (details see below) * @return mixed|string */ public function getImageThumbnail($thumbnailName, $page = 1, $deferred = false) { // just 4 testing //$this->clearThumbnails(true); if (!\Pimcore\Document::isAvailable()) { \Logger::error("Couldn't create image-thumbnail of document " . $this->getFullPath() . " no document adapter is available"); return "/pimcore/static/img/filetype-not-supported.png"; } $thumbnail = Image\Thumbnail\Config::getByAutoDetect($thumbnailName); $thumbnail->setName("document_" . $thumbnail->getName() . "-" . $page); try { if (!$deferred) { $converter = \Pimcore\Document::getInstance(); $converter->load($this->getFileSystemPath()); $path = PIMCORE_TEMPORARY_DIRECTORY . "/document-image-cache/document_" . $this->getId() . "__thumbnail_" . $page . ".png"; if (!is_dir(dirname($path))) { \Pimcore\File::mkdir(dirname($path)); } $lockKey = "document-thumbnail-" . $this->getId() . "-" . $page; if (!is_file($path) && !Model\Tool\Lock::isLocked($lockKey)) { Model\Tool\Lock::lock($lockKey); $converter->saveImage($path, $page); Model\Tool\Lock::release($lockKey); } else { if (Model\Tool\Lock::isLocked($lockKey)) { return "/pimcore/static/img/please-wait.png"; } } } if ($thumbnail) { $path = Image\Thumbnail\Processor::process($this, $thumbnail, $path, $deferred); } return preg_replace("@^" . preg_quote(PIMCORE_DOCUMENT_ROOT) . "@", "", $path); } catch (\Exception $e) { \Logger::error("Couldn't create image-thumbnail of document " . $this->getFullPath()); \Logger::error($e); } return "/pimcore/static/img/filetype-not-supported.png"; }
/** * @return void */ protected function update() { // only do this if the file exists and contains data if ($this->getDataChanged() || !$this->getCustomSetting("imageDimensionsCalculated")) { try { // save the current data into a tmp file to calculate the dimensions, otherwise updates wouldn't be updated // because the file is written in parent::update(); $tmpFile = $this->getTemporaryFile(); $dimensions = $this->getDimensions($tmpFile, true); unlink($tmpFile); if ($dimensions && $dimensions["width"]) { $this->setCustomSetting("imageWidth", $dimensions["width"]); $this->setCustomSetting("imageHeight", $dimensions["height"]); } } catch (\Exception $e) { Logger::error("Problem getting the dimensions of the image with ID " . $this->getId()); } // this is to be downward compatible so that the controller can check if the dimensions are already calculated // and also to just do the calculation once, because the calculation can fail, an then the controller tries to // calculate the dimensions on every request an also will create a version, ... $this->setCustomSetting("imageDimensionsCalculated", true); } parent::update(); $this->clearThumbnails(); // now directly create "system" thumbnails (eg. for the tree, ...) if ($this->getDataChanged()) { try { $path = $this->getThumbnail(Image\Thumbnail\Config::getPreviewConfig())->getFileSystemPath(); // set the modification time of the thumbnail to the same time from the asset // so that the thumbnail check doesn't fail in Asset\Image\Thumbnail\Processor::process(); // we need the @ in front of touch because of some stream wrapper (eg. s3) which don't support touch() @touch($path, $this->getModificationDate()); } catch (\Exception $e) { Logger::error("Problem while creating system-thumbnails for image " . $this->getRealFullPath()); Logger::error($e); } } }
} } $list = new Asset\Listing(); $list->setCondition(implode(" AND ", $conditions)); $total = $list->getTotalCount(); $perLoop = 10; for ($i = 0; $i < ceil($total / $perLoop); $i++) { $list->setLimit($perLoop); $list->setOffset($i * $perLoop); $images = $list->load(); foreach ($images as $image) { foreach ($thumbnails as $thumbnail) { if (empty($allowedThumbs) && !$opts->getOption("system") || in_array($thumbnail, $allowedThumbs)) { if ($opts->getOption("force")) { $image->clearThumbnail($thumbnail); } echo "generating thumbnail for image: " . $image->getFullpath() . " | " . $image->getId() . " | Thumbnail: " . $thumbnail . " : " . formatBytes(memory_get_usage()) . " \n"; echo "generated thumbnail: " . $image->getThumbnail($thumbnail) . "\n"; } } if ($opts->getOption("system")) { $thumbnail = Asset\Image\Thumbnail\Config::getPreviewConfig(); if ($opts->getOption("force")) { $image->clearThumbnail($thumbnail->getName()); } echo "generating thumbnail for image: " . $image->getFullpath() . " | " . $image->getId() . " | Thumbnail: System Preview (tree) : " . formatBytes(memory_get_usage()) . " \n"; echo "generated thumbnail: " . $image->getThumbnail($thumbnail) . "\n"; } } \Pimcore::collectGarbage(); }
/** * @param $selector * @return bool|static */ protected function createConfig($selector) { $config = Image\Thumbnail\Config::getByAutoDetect($selector); if ($config) { $format = strtolower($config->getFormat()); if ($format == "source") { $config->setFormat("PNG"); } } return $config; }
/** * Returns a list of all image thumbnails. */ public function imageThumbnailsAction() { $this->checkUserPermission("thumbnails"); $dir = Asset\Image\Thumbnail\Config::getWorkingDir(); $pipelines = array(); $files = scandir($dir); foreach ($files as $file) { if (strpos($file, ".xml")) { $name = str_replace(".xml", "", $file); $pipelines[] = array("id" => $name, "text" => $name); } } $this->encoder->encode(array("success" => true, "data" => $pipelines)); }
public function thumbnailUpdateAction() { $this->checkPermission("thumbnails"); $pipe = Asset\Image\Thumbnail\Config::getByName($this->getParam("name")); $settingsData = \Zend_Json::decode($this->getParam("settings")); $mediaData = \Zend_Json::decode($this->getParam("medias")); foreach ($settingsData as $key => $value) { $setter = "set" . ucfirst($key); if (method_exists($pipe, $setter)) { $pipe->{$setter}($value); } } $pipe->resetItems(); foreach ($mediaData as $mediaName => $items) { foreach ($items as $item) { $type = $item["type"]; unset($item["type"]); $pipe->addItem($type, $item, $mediaName); } } $pipe->save(); $this->deleteThumbnailTmpFiles($pipe); $this->_helper->json(array("success" => true)); }
/** * @see Document\Tag\TagInterface::frontend * @return string */ public function frontend() { if (!is_array($this->options)) { $this->options = array(); } $image = $this->getImage(); if ($image instanceof Asset) { if (isset($this->options["thumbnail"]) && $this->options["thumbnail"] || $this->cropPercent) { // create a thumbnail first $autoName = false; $thumbConfig = $image->getThumbnailConfig($this->options["thumbnail"]); if (!$thumbConfig && $this->cropPercent) { $thumbConfig = new Asset\Image\Thumbnail\Config(); } if ($this->cropPercent) { $cropConfig = array("width" => $this->cropWidth, "height" => $this->cropHeight, "y" => $this->cropTop, "x" => $this->cropLeft); $thumbConfig->addItemAt(0, "cropPercent", $cropConfig); // also crop media query specific configs if ($thumbConfig->hasMedias()) { foreach ($thumbConfig->getMedias() as $mediaName => $mediaItems) { $thumbConfig->addItemAt(0, "cropPercent", $cropConfig, $mediaName); } } $autoName = true; } if ($this->options["highResolution"] && $this->options["highResolution"] > 1) { $thumbConfig->setHighResolution($this->options["highResolution"]); } // autogenerate a name for the thumbnail because it's different from the original if ($autoName) { $hash = md5(Serialize::serialize($thumbConfig->getItems())); $thumbConfig->setName($thumbConfig->getName() . "_auto_" . $hash); } $imagePath = $image->getThumbnail($thumbConfig); } else { $imagePath = $image->getFullPath(); } $altText = $this->alt; $titleText = $this->alt; if (empty($titleText)) { if ($this->getImage()->getMetadata("title")) { $titleText = $this->getImage()->getMetadata("title"); } } if (empty($altText)) { if ($this->getImage()->getMetadata("alt")) { $altText = $this->getImage()->getMetadata("alt"); } else { $altText = $titleText; } } // get copyright from asset if ($this->getImage()->getMetadata("copyright")) { if (!empty($altText)) { $altText .= " | "; } if (!empty($titleText)) { $titleText .= " | "; } $altText .= "© " . $this->getImage()->getMetadata("copyright"); $titleText .= "© " . $this->getImage()->getMetadata("copyright"); } $defaultAttributes = array("alt" => $altText); if (!empty($titleText)) { $defaultAttributes["title"] = $titleText; } // add attributes to image $allowedAttributes = array("alt", "align", "border", "height", "hspace", "ismap", "longdesc", "usemap", "vspace", "width", "class", "dir", "id", "lang", "style", "title", "xml:lang", "onmouseover", "onabort", "onclick", "ondblclick", "onmousedown", "onmousemove", "onmouseout", "onmouseup", "onkeydown", "onkeypress", "onkeyup", "itemprop", "itemscope", "itemtype", "disableWidthHeightAttributes"); $htmlEscapeAttributes = array("alt", "align", "border", "height", "hspace", "longdesc", "usemap", "vspace", "width", "class", "dir", "id", "lang", "title"); $customAttributes = array(); if (array_key_exists("attributes", $this->options) && is_array($this->options["attributes"])) { $customAttributes = $this->options["attributes"]; } $availableAttribs = array_merge($this->options, $defaultAttributes, $customAttributes); $attribs = []; $attribsRaw = []; foreach ($availableAttribs as $key => $value) { if ((is_string($value) || is_numeric($value) || is_bool($value)) && (in_array($key, $allowedAttributes) || array_key_exists($key, $customAttributes))) { $attribsRaw[$key] = $value; if (in_array($key, $htmlEscapeAttributes)) { $value = htmlspecialchars($value); } $attribs[] = $key . '="' . $value . '"'; } } if ($imagePath instanceof Asset\Image\Thumbnail) { // thumbnail's HTML is always generated by the thumbnail itself return $imagePath->getHTML($attribsRaw); } else { return '<img src="' . $imagePath . '" ' . implode(" ", $attribs) . ' />'; } } }
/** * Get a thumbnail image configuration. * @param mixed $selector Name, array or object describing a thumbnail configuration. * @return Thumbnail\Config */ protected function createConfig($selector) { return Thumbnail\Config::getByAutoDetect($selector); }
/** * @param \Zend_Controller_Request_Abstract $request */ public function routeStartup(\Zend_Controller_Request_Abstract $request) { // this is a filter which checks for common used files (by browser, crawlers, ...) and prevent the default // error page, because this is more resource-intensive than exiting right here if (preg_match("@^/website/var/tmp/image-thumbnails(.*)?/([0-9]+)/thumb__([a-zA-Z0-9_\\-]+)([^\\@]+)(\\@[0-9.]+x)?\\.([a-zA-Z]{2,5})@", $request->getPathInfo(), $matches)) { $assetId = $matches[2]; $thumbnailName = $matches[3]; $format = $matches[6]; if ($asset = Asset::getById($assetId)) { try { $page = 1; $thumbnailFile = null; $thumbnailConfig = null; $deferredConfigId = "thumb_" . $assetId . "__" . md5($request->getPathInfo()); if ($thumbnailConfigItem = TmpStore::get($deferredConfigId)) { $thumbnailConfig = $thumbnailConfigItem->getData(); TmpStore::delete($deferredConfigId); if (!$thumbnailConfig instanceof Asset\Image\Thumbnail\Config) { throw new \Exception("Deferred thumbnail config file doesn't contain a valid \\Asset\\Image\\Thumbnail\\Config object"); } $tmpPage = array_pop(explode("-", $thumbnailName)); if (is_numeric($tmpPage)) { $page = $tmpPage; } } else { //get thumbnail for e.g. pdf page thumb__document_pdfPage-5 if (preg_match("|document_(.*)\\-(\\d+)\$|", $thumbnailName, $matchesThumbs)) { $thumbnailName = $matchesThumbs[1]; $page = (int) $matchesThumbs[2]; } // just check if the thumbnail exists -> throws exception otherwise $thumbnailConfig = Asset\Image\Thumbnail\Config::getByName($thumbnailName); } if ($asset instanceof Asset\Document) { $thumbnailConfig->setName(preg_replace("/\\-[\\d]+/", "", $thumbnailConfig->getName())); $thumbnailConfig->setName(str_replace("document_", "", $thumbnailConfig->getName())); $thumbnailFile = PIMCORE_DOCUMENT_ROOT . $asset->getImageThumbnail($thumbnailConfig, $page); } else { if ($asset instanceof Asset\Image) { //check if high res image is called if (array_key_exists(5, $matches)) { $highResFactor = (double) str_replace(array("@", "x"), "", $matches[5]); $thumbnailConfig->setHighResolution($highResFactor); } $thumbnailFile = PIMCORE_DOCUMENT_ROOT . $asset->getThumbnail($thumbnailConfig); } } if ($thumbnailFile && file_exists($thumbnailFile)) { $fileExtension = \Pimcore\File::getFileExtension($thumbnailFile); if (in_array($fileExtension, array("gif", "jpeg", "jpeg", "png", "pjpeg"))) { header("Content-Type: image/" . $fileExtension, true); } else { header("Content-Type: " . $asset->getMimetype(), true); } header("Content-Length: " . filesize($thumbnailFile), true); while (@ob_end_flush()) { } flush(); readfile($thumbnailFile); exit; } } catch (\Exception $e) { // nothing to do \Logger::error("Thumbnail with name '" . $thumbnailName . "' doesn't exist"); } } } }
/** * @param \Zend_Controller_Request_Abstract $request */ public function routeStartup(\Zend_Controller_Request_Abstract $request) { // this is a filter which checks for common used files (by browser, crawlers, ...) and prevent the default // error page, because this is more resource-intensive than exiting right here if (preg_match("@/image-thumbnails(.*)?/([0-9]+)/thumb__([a-zA-Z0-9_\\-]+)([^\\@]+)(\\@[0-9.]+x)?\\.([a-zA-Z]{2,5})@", rawurldecode($request->getPathInfo()), $matches)) { $assetId = $matches[2]; $thumbnailName = $matches[3]; if ($asset = Asset::getById($assetId)) { try { $page = 1; // default $thumbnailFile = null; $thumbnailConfig = null; //get thumbnail for e.g. pdf page thumb__document_pdfPage-5 if (preg_match("|document_(.*)\\-(\\d+)\$|", $thumbnailName, $matchesThumbs)) { $thumbnailName = $matchesThumbs[1]; $page = (int) $matchesThumbs[2]; } // just check if the thumbnail exists -> throws exception otherwise $thumbnailConfig = Asset\Image\Thumbnail\Config::getByName($thumbnailName); if (!$thumbnailConfig) { // check if there's an item in the TmpStore $deferredConfigId = "thumb_" . $assetId . "__" . md5($matches[0]); if ($thumbnailConfigItem = TmpStore::get($deferredConfigId)) { $thumbnailConfig = $thumbnailConfigItem->getData(); TmpStore::delete($deferredConfigId); if (!$thumbnailConfig instanceof Asset\Image\Thumbnail\Config) { throw new \Exception("Deferred thumbnail config file doesn't contain a valid \\Asset\\Image\\Thumbnail\\Config object"); } } } if (!$thumbnailConfig) { throw new \Exception("Thumbnail '" . $thumbnailName . "' file doesn't exists"); } if ($asset instanceof Asset\Document) { $thumbnailConfig->setName(preg_replace("/\\-[\\d]+/", "", $thumbnailConfig->getName())); $thumbnailConfig->setName(str_replace("document_", "", $thumbnailConfig->getName())); $thumbnailFile = $asset->getImageThumbnail($thumbnailConfig, $page)->getFileSystemPath(); } elseif ($asset instanceof Asset\Image) { //check if high res image is called if (array_key_exists(5, $matches)) { $highResFactor = (double) str_replace(["@", "x"], "", $matches[5]); $thumbnailConfig->setHighResolution($highResFactor); } // check if a media query thumbnail was requested if (preg_match("#~\\-~([\\d]+w)#", $matches[4], $mediaQueryResult)) { $thumbnailConfig->selectMedia($mediaQueryResult[1]); } $thumbnailFile = $asset->getThumbnail($thumbnailConfig)->getFileSystemPath(); } if ($thumbnailFile && file_exists($thumbnailFile)) { // set appropriate caching headers // see also: https://github.com/pimcore/pimcore/blob/1931860f0aea27de57e79313b2eb212dcf69ef13/.htaccess#L86-L86 $lifetime = 86400 * 7; // 1 week lifetime, same as direct delivery in .htaccess header("Cache-Control: public, max-age=" . $lifetime); header("Expires: " . date("D, d M Y H:i:s T", time() + $lifetime)); $fileExtension = \Pimcore\File::getFileExtension($thumbnailFile); if (in_array($fileExtension, ["gif", "jpeg", "jpeg", "png", "pjpeg"])) { header("Content-Type: image/" . $fileExtension, true); } else { header("Content-Type: " . $asset->getMimetype(), true); } header("Content-Length: " . filesize($thumbnailFile), true); while (@ob_end_flush()) { } flush(); readfile($thumbnailFile); exit; } } catch (\Exception $e) { // nothing to do Logger::error("Thumbnail with name '" . $thumbnailName . "' doesn't exist"); } } } }
/** * how many frames, delay in seconds between frames, pimcore thumbnail configuration * * @param int $frames * @param int $delay * @param null $thumbnail * @return string */ public function getPreviewAnimatedGif($frames = 10, $delay = 200, $thumbnail = null) { if (!$frames) { $frames = 10; } if (!$delay) { $delay = 200; // no clue which unit this has ;-) } $thumbnailUniqueId = md5(serialize([$thumbnail, $frames, $delay])); $animGifPath = PIMCORE_TEMPORARY_DIRECTORY . "/video-image-cache/video_" . $this->getId() . "_" . $thumbnailUniqueId . ".gif"; if (!is_file($animGifPath)) { $duration = $this->getDuration(); $sampleRate = floor($duration / $frames); $thumbnails = []; $delays = []; $thumbnailConfig = $this->getImageThumbnailConfig($thumbnail); if (!$thumbnailConfig) { $thumbnailConfig = new Image\Thumbnail\Config(); } $thumbnailConfig->setFormat("GIF"); for ($i = 0; $i <= $frames; $i++) { $frameImage = $this->getImageThumbnail($thumbnailConfig, $i * $sampleRate); $frameImage = PIMCORE_DOCUMENT_ROOT . $frameImage; if (preg_match("/\\.gif\$/", $frameImage) && filesize($frameImage) > 10) { // check if the image is correct and not a "not supported" placeholder $thumbnails[] = $frameImage; $delays[] = $delay; } } try { $animator = new \Pimcore\Image\GifAnimator($thumbnails, $delays, 0, 2, 255, 255, 255, "url"); $animGifContent = $animator->GetAnimation(); } catch (\Exception $e) { \Logger::error($e); $animGifContent = file_get_contents($thumbnails[0]); } File::put($animGifPath, $animGifContent); } $animGifPath = preg_replace("@^" . preg_quote(PIMCORE_DOCUMENT_ROOT, "@") . "@", "", $animGifPath); return $animGifPath; }
/** * @param $asset * @param Config $config * @param null $fileSystemPath * @param bool $deferred deferred means that the image will be generated on-the-fly (details see below) * @param bool $returnAbsolutePath * @param bool $generated * @return mixed|string */ public static function process($asset, Config $config, $fileSystemPath = null, $deferred = false, $returnAbsolutePath = false, &$generated = false) { $generated = false; $errorImage = PIMCORE_PATH . "/static6/img/filetype-not-supported.png"; $format = strtolower($config->getFormat()); $contentOptimizedFormat = false; if (!$fileSystemPath && $asset instanceof Asset) { $fileSystemPath = $asset->getFileSystemPath(); } if ($asset instanceof Asset) { $id = $asset->getId(); } else { $id = "dyn~" . crc32($fileSystemPath); } $fileExt = File::getFileExtension(basename($fileSystemPath)); // simple detection for source type if SOURCE is selected if ($format == "source" || empty($format)) { $format = self::getAllowedFormat($fileExt, ["jpeg", "gif", "png"], "png"); $contentOptimizedFormat = true; // format can change depending of the content (alpha-channel, ...) } if ($format == "print") { $format = self::getAllowedFormat($fileExt, ["svg", "jpeg", "png", "tiff"], "png"); if (($format == "tiff" || $format == "svg") && \Pimcore\Tool::isFrontentRequestByAdmin()) { // return a webformat in admin -> tiff cannot be displayed in browser $format = "png"; $deferred = false; // deferred is default, but it's not possible when using isFrontentRequestByAdmin() } elseif ($format == "tiff") { $transformations = $config->getItems(); if (is_array($transformations) && count($transformations) > 0) { foreach ($transformations as $transformation) { if (!empty($transformation)) { if ($transformation["method"] == "tifforiginal") { return self::returnPath($fileSystemPath, $returnAbsolutePath); } } } } } elseif ($format == "svg") { return self::returnPath($fileSystemPath, $returnAbsolutePath); } } elseif ($format == "tiff") { if (\Pimcore\Tool::isFrontentRequestByAdmin()) { // return a webformat in admin -> tiff cannot be displayed in browser $format = "png"; $deferred = false; // deferred is default, but it's not possible when using isFrontentRequestByAdmin() } } $thumbDir = $asset->getImageThumbnailSavePath() . "/thumb__" . $config->getName(); $filename = preg_replace("/\\." . preg_quote(File::getFileExtension($asset->getFilename())) . "/", "", $asset->getFilename()); // add custom suffix if available if ($config->getFilenameSuffix()) { $filename .= "~-~" . $config->getFilenameSuffix(); } // add high-resolution modifier suffix to the filename if ($config->getHighResolution() > 1) { $filename .= "@" . $config->getHighResolution() . "x"; } $fileExtension = $format; if ($format == "original") { $fileExtension = \Pimcore\File::getFileExtension($fileSystemPath); } $filename .= "." . $fileExtension; $fsPath = $thumbDir . "/" . $filename; // deferred means that the image will be generated on-the-fly (when requested by the browser) // the configuration is saved for later use in Pimcore\Controller\Plugin\Thumbnail::routeStartup() // so that it can be used also with dynamic configurations if ($deferred) { // only add the config to the TmpStore if necessary (the config is auto-generated) if (!Config::getByName($config->getName())) { $configId = "thumb_" . $id . "__" . md5(str_replace(PIMCORE_TEMPORARY_DIRECTORY, "", $fsPath)); TmpStore::add($configId, $config, "thumbnail_deferred"); } return self::returnPath($fsPath, $returnAbsolutePath); } // all checks on the file system should be below the deferred part for performance reasons (remote file systems) if (!file_exists($fileSystemPath)) { return self::returnPath($errorImage, $returnAbsolutePath); } if (!is_dir(dirname($fsPath))) { File::mkdir(dirname($fsPath)); } $path = self::returnPath($fsPath, false); // check for existing and still valid thumbnail if (is_file($fsPath) and filemtime($fsPath) >= filemtime($fileSystemPath)) { return self::returnPath($fsPath, $returnAbsolutePath); } // transform image $image = Asset\Image::getImageTransformInstance(); $image->setPreserveColor($config->isPreserveColor()); $image->setPreserveMetaData($config->isPreserveMetaData()); if (!$image->load($fileSystemPath)) { return self::returnPath($errorImage, $returnAbsolutePath); } $image->setUseContentOptimizedFormat($contentOptimizedFormat); $startTime = StopWatch::microtime_float(); $transformations = $config->getItems(); // check if the original image has an orientation exif flag // if so add a transformation at the beginning that rotates and/or mirrors the image if (function_exists("exif_read_data")) { $exif = @exif_read_data($fileSystemPath); if (is_array($exif)) { if (array_key_exists("Orientation", $exif)) { $orientation = intval($exif["Orientation"]); if ($orientation > 1) { $angleMappings = [2 => 180, 3 => 180, 4 => 180, 5 => 90, 6 => 90, 7 => 90, 8 => 270]; if (array_key_exists($orientation, $angleMappings)) { array_unshift($transformations, ["method" => "rotate", "arguments" => ["angle" => $angleMappings[$orientation]]]); } // values that have to be mirrored, this is not very common, but should be covered anyway $mirrorMappings = [2 => "vertical", 4 => "horizontal", 5 => "vertical", 7 => "horizontal"]; if (array_key_exists($orientation, $mirrorMappings)) { array_unshift($transformations, ["method" => "mirror", "arguments" => ["mode" => $mirrorMappings[$orientation]]]); } } } } } if (is_array($transformations) && count($transformations) > 0) { $sourceImageWidth = PHP_INT_MAX; $sourceImageHeight = PHP_INT_MAX; if ($asset instanceof Asset\Image) { $sourceImageWidth = $asset->getWidth(); $sourceImageHeight = $asset->getHeight(); } $highResFactor = $config->getHighResolution(); $calculateMaxFactor = function ($factor, $original, $new) { $newFactor = $factor * $original / $new; if ($newFactor < 1) { // don't go below factor 1 $newFactor = 1; } return $newFactor; }; // sorry for the goto/label - but in this case it makes life really easier and the code more readable prepareTransformations: foreach ($transformations as $transformation) { if (!empty($transformation)) { $arguments = []; $mapping = self::$argumentMapping[$transformation["method"]]; if (is_array($transformation["arguments"])) { foreach ($transformation["arguments"] as $key => $value) { $position = array_search($key, $mapping); if ($position !== false) { // high res calculations if enabled if (!in_array($transformation["method"], ["cropPercent"]) && in_array($key, ["width", "height", "x", "y"])) { if ($highResFactor && $highResFactor > 1) { $value *= $highResFactor; $value = (int) ceil($value); // check if source image is big enough otherwise adjust the high-res factor if (in_array($key, ["width", "x"])) { if ($sourceImageWidth < $value) { $highResFactor = $calculateMaxFactor($highResFactor, $sourceImageWidth, $value); goto prepareTransformations; } } elseif (in_array($key, ["height", "y"])) { if ($sourceImageHeight < $value) { $highResFactor = $calculateMaxFactor($highResFactor, $sourceImageHeight, $value); goto prepareTransformations; } } } } $arguments[$position] = $value; } } } ksort($arguments); if (method_exists($image, $transformation["method"])) { call_user_func_array([$image, $transformation["method"]], $arguments); } } } } $image->save($fsPath, $format, $config->getQuality()); $generated = true; if ($contentOptimizedFormat) { $tmpStoreKey = str_replace(PIMCORE_TEMPORARY_DIRECTORY . "/", "", $fsPath); TmpStore::add($tmpStoreKey, "-", "image-optimize-queue"); } clearstatcache(); Logger::debug("Thumbnail " . $path . " generated in " . (StopWatch::microtime_float() - $startTime) . " seconds"); // set proper permissions @chmod($fsPath, File::getDefaultMode()); // quick bugfix / workaround, it seems that imagemagick / image optimizers creates sometimes empty PNG chunks (total size 33 bytes) // no clue why it does so as this is not continuous reproducible, and this is the only fix we can do for now // if the file is corrupted the file will be created on the fly when requested by the browser (because it's deleted here) if (is_file($fsPath) && filesize($fsPath) < 50) { unlink($fsPath); } return self::returnPath($fsPath, $returnAbsolutePath); }
/** * @param null $thumbnailName * @return mixed */ public function getThumbnail($thumbnailName = null) { if (!$this->getImage()) { return ""; } $crop = null; if (is_array($this->getCrop())) { $crop = $this->getCrop(); } $thumbConfig = $this->getImage()->getThumbnailConfig($thumbnailName); if (!$thumbConfig && $crop) { $thumbConfig = new Asset\Image\Thumbnail\Config(); } if ($crop) { $thumbConfig->addItemAt(0, "cropPercent", array("width" => $crop["cropWidth"], "height" => $crop["cropHeight"], "y" => $crop["cropTop"], "x" => $crop["cropLeft"])); $hash = md5(\Pimcore\Tool\Serialize::serialize($thumbConfig->getItems())); $thumbConfig->setName($thumbConfig->getName() . "_auto_" . $hash); } return $this->getImage()->getThumbnail($thumbConfig); }
public function getDocumentThumbnailAction() { $document = Asset::getById(intval($this->getParam("id"))); $thumbnail = Asset\Image\Thumbnail\Config::getByAutoDetect($this->getAllParams()); $format = strtolower($thumbnail->getFormat()); if ($format == "source") { $thumbnail->setFormat("jpeg"); // default format for documents is JPEG not PNG (=too big) } if ($this->getParam("treepreview")) { $thumbnail = Asset\Image\Thumbnail\Config::getPreviewConfig(); } $page = 1; if (is_numeric($this->getParam("page"))) { $page = (int) $this->getParam("page"); } $thumbnailFile = PIMCORE_DOCUMENT_ROOT . $document->getImageThumbnail($thumbnail, $page); $format = "png"; header("Content-type: image/" . $format, true); header("Content-Length: " . filesize($thumbnailFile), true); $this->sendThumbnailCacheHeaders(); while (@ob_end_flush()) { } flush(); readfile($thumbnailFile); exit; }
/** * @param $asset * @param Config $config * @param null $fileSystemPath * @param bool $deferred deferred means that the image will be generated on-the-fly (details see below) * @return mixed|string */ public static function process($asset, Config $config, $fileSystemPath = null, $deferred = false) { $format = strtolower($config->getFormat()); $contentOptimizedFormat = false; if (!$fileSystemPath && $asset instanceof Asset) { $fileSystemPath = $asset->getFileSystemPath(); } if ($asset instanceof Asset) { $id = $asset->getId(); } else { $id = "dyn~" . crc32($fileSystemPath); } if (!file_exists($fileSystemPath)) { return "/pimcore/static/img/filetype-not-supported.png"; } $modificationDate = filemtime($fileSystemPath); $fileExt = File::getFileExtension(basename($fileSystemPath)); // simple detection for source type if SOURCE is selected if ($format == "source" || empty($format)) { $format = self::getAllowedFormat($fileExt, array("jpeg", "gif", "png"), "png"); $contentOptimizedFormat = true; // format can change depending of the content (alpha-channel, ...) } if ($format == "print") { $format = self::getAllowedFormat($fileExt, array("svg", "jpeg", "png", "tiff"), "png"); if (($format == "tiff" || $format == "svg") && \Pimcore\Tool::isFrontentRequestByAdmin()) { // return a webformat in admin -> tiff cannot be displayed in browser $format = "png"; } else { if ($format == "tiff") { $transformations = $config->getItems(); if (is_array($transformations) && count($transformations) > 0) { foreach ($transformations as $transformation) { if (!empty($transformation)) { if ($transformation["method"] == "tifforiginal") { return str_replace(PIMCORE_DOCUMENT_ROOT, "", $fileSystemPath); } } } } } else { if ($format == "svg") { return str_replace(PIMCORE_DOCUMENT_ROOT, "", $fileSystemPath); } } } } $thumbDir = $asset->getImageThumbnailSavePath() . "/thumb__" . $config->getName(); $filename = preg_replace("/\\." . preg_quote(File::getFileExtension($asset->getFilename())) . "/", "", $asset->getFilename()); // add custom suffix if available if ($config->getFilenameSuffix()) { $filename .= "~-~" . $config->getFilenameSuffix(); } // add high-resolution modifier suffix to the filename if ($config->getHighResolution() > 1) { $filename .= "@" . $config->getHighResolution() . "x"; } $filename .= "." . $format; $fsPath = $thumbDir . "/" . $filename; if (!is_dir(dirname($fsPath))) { File::mkdir(dirname($fsPath)); } $path = str_replace(PIMCORE_DOCUMENT_ROOT, "", $fsPath); // check for existing and still valid thumbnail if (is_file($fsPath) and filemtime($fsPath) >= $modificationDate) { return $path; } // deferred means that the image will be generated on-the-fly (when requested by the browser) // the configuration is saved for later use in Pimcore\Controller\Plugin\Thumbnail::routeStartup() // so that it can be used also with dynamic configurations if ($deferred) { $configId = "thumb_" . $id . "__" . md5($path); TmpStore::add($configId, $config, "thumbnail_deferred"); return $path; } // transform image $image = Asset\Image::getImageTransformInstance(); if (!$image->load($fileSystemPath)) { return "/pimcore/static/img/filetype-not-supported.png"; } $image->setUseContentOptimizedFormat($contentOptimizedFormat); $startTime = StopWatch::microtime_float(); $transformations = $config->getItems(); // check if the original image has an orientation exif flag // if so add a transformation at the beginning that rotates and/or mirrors the image if (function_exists("exif_read_data")) { $exif = @exif_read_data($fileSystemPath); if (is_array($exif)) { if (array_key_exists("Orientation", $exif)) { $orientation = intval($exif["Orientation"]); if ($orientation > 1) { $angleMappings = [2 => 180, 3 => 180, 4 => 180, 5 => 90, 6 => 90, 7 => 90, 8 => 270]; if (array_key_exists($orientation, $angleMappings)) { array_unshift($transformations, ["method" => "rotate", "arguments" => ["angle" => $angleMappings[$orientation]]]); } // values that have to be mirrored, this is not very common, but should be covered anyway $mirrorMappings = [2 => "vertical", 4 => "horizontal", 5 => "vertical", 7 => "horizontal"]; if (array_key_exists($orientation, $mirrorMappings)) { array_unshift($transformations, ["method" => "mirror", "arguments" => ["mode" => $mirrorMappings[$orientation]]]); } } } } } if (is_array($transformations) && count($transformations) > 0) { foreach ($transformations as $transformation) { if (!empty($transformation)) { $arguments = array(); $mapping = self::$argumentMapping[$transformation["method"]]; if (is_array($transformation["arguments"])) { foreach ($transformation["arguments"] as $key => $value) { $position = array_search($key, $mapping); if ($position !== false) { // high res calculations if enabled if (!in_array($transformation["method"], ["cropPercent"]) && in_array($key, array("width", "height", "x", "y"))) { if ($config->getHighResolution() && $config->getHighResolution() > 1) { $value *= $config->getHighResolution(); } } $arguments[$position] = $value; } } } ksort($arguments); call_user_func_array(array($image, $transformation["method"]), $arguments); } } } $image->save($fsPath, $format, $config->getQuality()); if ($contentOptimizedFormat) { $tmpStoreKey = str_replace(PIMCORE_TEMPORARY_DIRECTORY . "/", "", $fsPath); TmpStore::add($tmpStoreKey, "-", "image-optimize-queue"); } clearstatcache(); \Logger::debug("Thumbnail " . $path . " generated in " . (StopWatch::microtime_float() - $startTime) . " seconds"); // set proper permissions @chmod($fsPath, File::getDefaultMode()); // quick bugfix / workaround, it seems that imagemagick / image optimizers creates sometimes empty PNG chunks (total size 33 bytes) // no clue why it does so as this is not continuous reproducible, and this is the only fix we can do for now // if the file is corrupted the file will be created on the fly when requested by the browser (because it's deleted here) if (is_file($fsPath) && filesize($fsPath) < 50) { unlink($fsPath); } return $path; }
// create legacy config folder $legacyFolder = PIMCORE_CONFIGURATION_DIRECTORY . "/LEGACY"; if (!is_dir($legacyFolder)) { mkdir($legacyFolder, 0777, true); } // IMAGE THUMBNAILS $dir = PIMCORE_CONFIGURATION_DIRECTORY . "/imagepipelines"; if (is_dir($dir)) { $file = Pimcore\Config::locateConfigFile("image-thumbnails"); $json = \Pimcore\Db\JsonFileTable::get($file); $json->truncate(); $files = scandir($dir); foreach ($files as $file) { if (strpos($file, ".xml")) { $name = str_replace(".xml", "", $file); $thumbnail = Asset\Image\Thumbnail\Config::getByName($name); $thumbnail = object2array($thumbnail); $thumbnail["id"] = $thumbnail["name"]; unset($thumbnail["name"]); $json->insertOrUpdate($thumbnail, $thumbnail["id"]); } } // move data rename($dir, $legacyFolder . "/imagepipelines"); } // VIDEO THUMBNAILS $dir = PIMCORE_CONFIGURATION_DIRECTORY . "/videopipelines"; if (is_dir($dir)) { $file = Pimcore\Config::locateConfigFile("video-thumbnails"); $json = \Pimcore\Db\JsonFileTable::get($file); $json->truncate();