/** * @param $file * @param null $filename * @return mixed|string * @throws \Exception */ public static function detect($file, $filename = null) { if (!file_exists($file)) { throw new \Exception("File " . $file . " doesn't exist"); } if (!$filename) { $filename = basename($file); } // check for an extension mapping first if ($filename) { $extension = \Pimcore\File::getFileExtension($filename); if (array_key_exists($extension, self::$extensionMapping)) { return self::$extensionMapping[$extension]; } } // check with fileinfo, if there's no extension mapping $finfo = finfo_open(FILEINFO_MIME); $type = finfo_file($finfo, $file); finfo_close($finfo); if ($type !== false && !empty($type)) { if (strstr($type, ';')) { $type = substr($type, 0, strpos($type, ';')); } return $type; } // return default mime-type if we're unable to detect it return "application/octet-stream"; }
public function scriptProxyAction() { $this->disableViewAutoRender(); $allowedFileTypes = ["js", "css"]; $scripts = explode(",", $this->getParam("scripts")); $scriptPath = $this->getParam("scriptPath"); $scriptsContent = ""; foreach ($scripts as $script) { $filePath = PIMCORE_DOCUMENT_ROOT . $scriptPath . $script; if (is_file($filePath) && is_readable($filePath) && in_array(\Pimcore\File::getFileExtension($script), $allowedFileTypes)) { $scriptsContent .= file_get_contents($filePath); } } $fileExtension = \Pimcore\File::getFileExtension($scripts[0]); $contentType = "text/javascript"; if ($fileExtension == "css") { $contentType = "text/css"; } $lifetime = 86400; $this->getResponse()->setHeader("Cache-Control", "max-age=" . $lifetime, true); $this->getResponse()->setHeader("Pragma", "", true); $this->getResponse()->setHeader("Content-Type", $contentType, true); $this->getResponse()->setHeader("Expires", gmdate("D, d M Y H:i:s", time() + $lifetime) . " GMT", true); echo $scriptsContent; }
/** * @param $name * @return mixed */ public static function locateConfigFile($name) { if (!isset(self::$configFileCache[$name])) { $pathsToCheck = [PIMCORE_WEBSITE_PATH . "/config", PIMCORE_CONFIGURATION_DIRECTORY]; $file = PIMCORE_CONFIGURATION_DIRECTORY . "/" . $name; // check for environment configuration $env = getenv("PIMCORE_ENVIRONMENT"); if ($env) { $fileExt = File::getFileExtension($name); $pureName = str_replace("." . $fileExt, "", $name); foreach ($pathsToCheck as $path) { $tmpFile = $path . "/" . $pureName . "." . $env . "." . $fileExt; if (file_exists($tmpFile)) { $file = $tmpFile; break; } } } foreach ($pathsToCheck as $path) { $tmpFile = $path . "/" . $name; if (file_exists($tmpFile)) { $file = $tmpFile; break; } } self::$configFileCache[$name] = $file; } return self::$configFileCache[$name]; }
/** * @param $path * @return string */ protected function preparePath($path) { if (!stream_is_local($path)) { // gs is only able to deal with local files // if your're using custom stream wrappers this wouldn't work, so we create a temp. local copy $tmpFilePath = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/imagick-tmp-" . uniqid() . "." . File::getFileExtension($path); copy($path, $tmpFilePath); $path = $tmpFilePath; $this->tmpFiles[] = $path; } return $path; }
/** * @param $imagePath * @param array $options * @return $this|self */ public function load($imagePath, $options = []) { $this->path = $imagePath; if (!($this->resource = @imagecreatefromstring(file_get_contents($this->path)))) { return false; } // set dimensions list($width, $height) = getimagesize($this->path); $this->setWidth($width); $this->setHeight($height); if (in_array(\Pimcore\File::getFileExtension($imagePath), ["png", "gif"])) { // in GD only gif and PNG can have an alphachannel $this->setIsAlphaPossible(true); } $this->setModified(false); return $this; }
/** * loads the image by the specified path * * @param $imagePath * @param array $options * @return ImageMagick */ public function load($imagePath, $options = []) { // support image URLs if (preg_match("@^https?://@", $imagePath)) { $tmpFilename = "imagick_auto_download_" . md5($imagePath) . "." . File::getFileExtension($imagePath); $tmpFilePath = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/" . $tmpFilename; $this->tmpFiles[] = $tmpFilePath; File::put($tmpFilePath, \Pimcore\Tool::getHttpData($imagePath)); $imagePath = $tmpFilePath; } if (!stream_is_local($imagePath)) { // imagick is only able to deal with local files // if your're using custom stream wrappers this wouldn't work, so we create a temp. local copy $tmpFilePath = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/imagick-tmp-" . uniqid() . "." . File::getFileExtension($imagePath); copy($imagePath, $tmpFilePath); $imagePath = $tmpFilePath; $this->tmpFiles[] = $imagePath; } $this->imagePath = $imagePath; $this->initResource(); $this->setModified(false); return $this; }
/** * @param $name - name of configuration file. slash is allowed for subdirectories. * @return mixed */ public static function locateConfigFile($name) { if (!isset(self::$configFileCache[$name])) { $pathsToCheck = [PIMCORE_CUSTOM_CONFIGURATION_DIRECTORY, PIMCORE_CONFIGURATION_DIRECTORY]; $file = null; // check for environment configuration $env = self::getEnvironment(); if ($env) { $fileExt = File::getFileExtension($name); $pureName = str_replace("." . $fileExt, "", $name); foreach ($pathsToCheck as $path) { $tmpFile = $path . "/" . $pureName . "." . $env . "." . $fileExt; if (file_exists($tmpFile)) { $file = $tmpFile; break; } } } //check for config file without environment configuration if (!$file) { foreach ($pathsToCheck as $path) { $tmpFile = $path . "/" . $name; if (file_exists($tmpFile)) { $file = $tmpFile; break; } } } //get default path in pimcore configuration directory if (!$file) { $file = PIMCORE_CONFIGURATION_DIRECTORY . "/" . $name; } self::$configFileCache[$name] = $file; } return self::$configFileCache[$name]; }
/** * @param null $imagePath * @return bool */ public function isVectorGraphic($imagePath = null) { if (!$imagePath) { $imagePath = $this->imagePath; } // we need to do this check first, because ImageMagick using the inkscape delegate returns "PNG" when calling // getimageformat() onto SVG graphics, this is a workaround to avoid problems if (in_array(File::getFileExtension($imagePath), ["svg", "svgz", "eps", "pdf", "ps"])) { // use file-extension if filename is provided return true; } try { $type = $this->resource->getimageformat(); $vectorTypes = array("EPT", "EPDF", "EPI", "EPS", "EPS2", "EPS3", "EPSF", "EPSI", "EPT", "PDF", "PFA", "PFB", "PFM", "PS", "PS2", "PS3", "SVG", "SVGZ", "MVG"); if (in_array(strtoupper($type), $vectorTypes)) { return true; } } catch (\Exception $e) { \Logger::err($e); } return false; }
public function getImageThumbnailAction() { $fileinfo = $this->getParam("fileinfo"); $image = Asset\Image::getById(intval($this->getParam("id"))); $thumbnail = null; if ($this->getParam("thumbnail")) { $thumbnail = $image->getThumbnailConfig($this->getParam("thumbnail")); } if (!$thumbnail) { if ($this->getParam("config")) { $thumbnail = $image->getThumbnailConfig(\Zend_Json::decode($this->getParam("config"))); } else { $thumbnail = $image->getThumbnailConfig($this->getAllParams()); } } else { // no high-res images in admin mode (editmode) // this is mostly because of the document's image editable, which doesn't know anything about the thumbnail // configuration, so the dimensions would be incorrect (double the size) $thumbnail->setHighResolution(1); } $format = strtolower($thumbnail->getFormat()); if ($format == "source" || $format == "print") { $thumbnail->setFormat("PNG"); $format = "png"; } if ($this->getParam("treepreview")) { $thumbnail = Asset\Image\Thumbnail\Config::getPreviewConfig(); } if ($this->getParam("cropPercent")) { $thumbnail->addItemAt(0, "cropPercent", array("width" => $this->getParam("cropWidth"), "height" => $this->getParam("cropHeight"), "y" => $this->getParam("cropTop"), "x" => $this->getParam("cropLeft"))); $hash = md5(Tool\Serialize::serialize($this->getAllParams())); $thumbnail->setName($thumbnail->getName() . "_auto_" . $hash); } if ($this->getParam("download")) { $downloadFilename = str_replace("." . File::getFileExtension($image->getFilename()), "." . $thumbnail->getFormat(), $image->getFilename()); $downloadFilename = strtolower($downloadFilename); header('Content-Disposition: attachment; filename="' . $downloadFilename . '"'); } $thumbnail = $image->getThumbnail($thumbnail); if ($fileinfo) { $this->_helper->json(array("width" => $thumbnail->getWidth(), "height" => $thumbnail->getHeight())); } $thumbnailFile = PIMCORE_DOCUMENT_ROOT . $thumbnail; $fileExtension = File::getFileExtension($thumbnailFile); if (in_array($fileExtension, array("gif", "jpeg", "jpeg", "png", "pjpeg"))) { header("Content-Type: image/" . $fileExtension, true); } else { header("Content-Type: " . $image->getMimetype(), true); } header("Content-Length: " . filesize($thumbnailFile), true); $this->sendThumbnailCacheHeaders(); while (@ob_end_flush()) { } flush(); readfile($thumbnailFile); exit; }
/** * Returns a uniqe key for the element in the $target-Path (recursive) * @static * @return ElementInterface|string * @param string $type * @param string $sourceKey * @param ElementInterface $target */ public static function getSaveCopyName($type, $sourceKey, $target) { if (self::pathExists($target->getFullPath() . "/" . $sourceKey, $type)) { // only for assets: add the prefix _copy before the file extension (if exist) not after to that source.jpg will be source_copy.jpg and not source.jpg_copy if ($type == "asset" && ($fileExtension = File::getFileExtension($sourceKey))) { $sourceKey = str_replace("." . $fileExtension, "_copy." . $fileExtension, $sourceKey); } else { $sourceKey .= "_copy"; } return self::getSaveCopyName($type, $sourceKey, $target); } return $sourceKey; }
/** * @param null $page * @param null $path * @return bool|string * @throws \Exception */ public function getText($page = null, $path = null) { $path = $path ? $this->preparePath($path) : $this->path; if ($page || parent::isFileTypeSupported($path)) { // for per page extraction we have to convert the document to PDF and extract the text via ghostscript return parent::getText($page, $this->getPdf($path)); } elseif (File::getFileExtension($path)) { // if we want to get the text of the whole document, we can use libreoffices text export feature $cmd = self::getLibreOfficeCli() . " --headless --nologo --nofirststartwizard --norestore --convert-to txt:Text --outdir " . escapeshellarg(PIMCORE_TEMPORARY_DIRECTORY) . " " . escapeshellarg($path); $out = Console::exec($cmd, null, 240); Logger::debug("LibreOffice Output was: " . $out); $tmpName = PIMCORE_TEMPORARY_DIRECTORY . "/" . preg_replace("/\\." . File::getFileExtension($path) . "\$/", ".txt", basename($path)); if (file_exists($tmpName)) { $text = file_get_contents($tmpName); $text = \Pimcore\Tool\Text::convertToUTF8($text); unlink($tmpName); return $text; } else { $message = "Couldn't convert document to PDF: " . $path . " with the command: '" . $cmd . "' - now trying to get the text out of the PDF ..."; Logger::error($message); return parent::getText(null, $this->getPdf($path)); } } return ""; // default empty string }
/** * @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; }
/** * @return string */ public function getFileExtension() { $mapping = ["image/png" => "png", "image/jpeg" => "jpg", "image/gif" => "gif", "image/tiff" => "tif", "image/svg+xml" => "svg"]; $mimeType = $this->getMimeType(); if (isset($mapping[$mimeType])) { return $mapping[$mimeType]; } if ($this->getAsset()) { return \Pimcore\File::getFileExtension($this->getAsset()->getFilename()); } return ""; }
public function wordExportDownloadAction() { $id = $this->getParam("id"); $exportFile = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/" . $id . ".html"; // add closing body/html //$f = fopen($exportFile, "a+"); //fwrite($f, "</body></html>"); //fclose($f); // should be done via Pimcore_Document(_Adapter_LibreOffice) in the future if (\Pimcore\Document::isFileTypeSupported("docx")) { $lockKey = "soffice"; Model\Tool\Lock::acquire($lockKey); // avoid parallel conversions of the same document $out = Tool\Console::exec(\Pimcore\Document\Adapter\LibreOffice::getLibreOfficeCli() . ' --headless --convert-to docx:"Office Open XML Text" --outdir ' . PIMCORE_TEMPORARY_DIRECTORY . " " . $exportFile); \Logger::debug("LibreOffice Output was: " . $out); $tmpName = PIMCORE_TEMPORARY_DIRECTORY . "/" . preg_replace("/\\." . File::getFileExtension($exportFile) . "\$/", ".docx", basename($exportFile)); Model\Tool\Lock::release($lockKey); // end what should be done in Pimcore_Document header("Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document"); header('Content-Disposition: attachment; filename="' . basename($tmpName) . '"'); } else { // no conversion, output html file $tmpName = $exportFile; header("Content-Type: text/html"); header('Content-Disposition: attachment; filename="' . basename($tmpName) . '"'); } while (@ob_end_flush()) { } flush(); readfile($tmpName); @unlink($exportFile); @unlink($tmpName); exit; }
/** * @param $path * @param null $format * @param null $quality * @return $this|mixed * @throws \Exception */ public function save($path, $format = null, $quality = null) { if (!$format) { $format = "png32"; } $format = strtolower($format); if ($format == "png") { // we need to force imagick to create png32 images, otherwise this can cause some strange effects // when used with gray-scale images $format = "png32"; } if ($format == "original") { $format = strtolower($this->resource->getImageFormat()); } $originalFilename = null; if (!$this->reinitializing) { if ($this->getUseContentOptimizedFormat()) { $format = "jpeg"; if ($this->hasAlphaChannel()) { $format = "png32"; } } } $i = $this->resource; // this is because of HHVM which has problems with $this->resource->writeImage(); if (in_array($format, ["jpeg", "pjpeg", "jpg"]) && $this->isAlphaPossible) { // set white background for transparent pixels $i->setImageBackgroundColor("#ffffff"); // Imagick version compatibility $alphaChannel = 11; // This works at least as far back as version 3.1.0~rc1-1 if (defined("Imagick::ALPHACHANNEL_REMOVE")) { // Imagick::ALPHACHANNEL_REMOVE has been added in 3.2.0b2 $alphaChannel = \Imagick::ALPHACHANNEL_REMOVE; } $i->setImageAlphaChannel($alphaChannel); $i->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN); } if (!$this->isPreserveMetaData()) { $i->stripImage(); } if (!$this->isPreserveColor()) { $i->profileImage('*', null); } $i->setImageFormat($format); if ($quality && !$this->isPreserveColor()) { $i->setCompressionQuality((int) $quality); $i->setImageCompressionQuality((int) $quality); } if ($format == "tiff") { $i->setCompression(\Imagick::COMPRESSION_LZW); } // force progressive JPEG if filesize >= 10k // normally jpeg images are bigger than 10k so we avoid the double compression (baseline => filesize check => if necessary progressive) // and check the dimensions here instead to faster generate the image // progressive JPEG - better compression, smaller filesize, especially for web optimization if ($format == "jpeg" && !$this->isPreserveColor()) { if ($this->getWidth() * $this->getHeight() > 35000) { $i->setInterlaceScheme(\Imagick::INTERLACE_PLANE); } } // Imagick isn't able to work with custom stream wrappers, so we make a workaround $realTargetPath = null; if (!stream_is_local($path)) { $realTargetPath = $path; $path = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/imagick-tmp-" . uniqid() . "." . File::getFileExtension($path); } if (defined("HHVM_VERSION")) { $success = $i->writeImage($path); } else { $success = $i->writeImage($format . ":" . $path); } if (!$success) { throw new \Exception("Unable to write image: ", $path); } if ($realTargetPath) { File::rename($path, $realTargetPath); } return $this; }
public function getFolderContentPreviewAction() { $folder = Asset::getById($this->getParam("id")); $start = 0; $limit = 10; if ($this->getParam("limit")) { $limit = $this->getParam("limit"); } if ($this->getParam("start")) { $start = $this->getParam("start"); } $condition = "path LIKE '" . ($folder->getRealFullPath() == "/" ? "/%'" : $folder->getRealFullPath() . "/%'") . " AND type != 'folder'"; $list = Asset::getList(["condition" => $condition, "limit" => $limit, "offset" => $start, "orderKey" => "filename", "order" => "asc"]); $assets = []; foreach ($list as $asset) { $thumbnailMethod = ""; if ($asset instanceof Asset\Image) { $thumbnailMethod = "getThumbnail"; } elseif ($asset instanceof Asset\Video && \Pimcore\Video::isAvailable()) { $thumbnailMethod = "getImageThumbnail"; } elseif ($asset instanceof Asset\Document && \Pimcore\Document::isAvailable()) { $thumbnailMethod = "getImageThumbnail"; } if (!empty($thumbnailMethod)) { $filenameDisplay = $asset->getFilename(); if (strlen($filenameDisplay) > 32) { $filenameDisplay = substr($filenameDisplay, 0, 25) . "..." . \Pimcore\File::getFileExtension($filenameDisplay); } $assets[] = ["id" => $asset->getId(), "type" => $asset->getType(), "filename" => $asset->getFilename(), "filenameDisplay" => $filenameDisplay, "url" => "/admin/asset/get-" . $asset->getType() . "-thumbnail/id/" . $asset->getId() . "/treepreview/true", "idPath" => $data["idPath"] = Element\Service::getIdPath($asset)]; } } $this->_helper->json(["assets" => $assets, "success" => true, "total" => $list->getTotalCount()]); }
/** * Get the height of the generated thumbnail image in pixels. * @return string HTTP Mime Type of the generated thumbnail image. */ public function getMimeType() { if (!$this->mimetype) { // get target mime type without actually generating the thumbnail (deferred) $mapping = ["png" => "image/png", "jpg" => "image/jpeg", "jpeg" => "image/jpeg", "pjpeg" => "image/jpeg", "gif" => "image/gif", "tiff" => "image/tiff", "svg" => "image/svg+xml"]; $targetFormat = strtolower($this->getConfig()->getFormat()); $format = $targetFormat; $fileExt = \Pimcore\File::getFileExtension($this->getAsset()->getFilename()); if ($targetFormat == "source" || empty($targetFormat)) { $format = Thumbnail\Processor::getAllowedFormat($fileExt, ["jpeg", "gif", "png"], "png"); } elseif ($targetFormat == "print") { $format = Thumbnail\Processor::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"; } } if (array_key_exists($format, $mapping)) { $this->mimetype = $mapping[$format]; } else { // unknown $this->mimetype = "application/octet-stream"; } } return $this->mimetype; }
/** * @param $path * @param int $page * @return $this|bool */ public function saveImage($path, $page = 1, $resolution = 200) { try { $realTargetPath = null; if (!stream_is_local($path)) { $realTargetPath = $path; $path = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/ghostscript-tmp-" . uniqid() . "." . File::getFileExtension($path); } Console::exec(self::getGhostscriptCli() . " -sDEVICE=png16m -dFirstPage=" . $page . " -dLastPage=" . $page . " -r" . $resolution . " -o " . escapeshellarg($path) . " " . escapeshellarg($this->path), null, 240); if ($realTargetPath) { File::rename($path, $realTargetPath); } return $this; } catch (\Exception $e) { Logger::error($e); return false; } }
/** * returns the asset type of a filename and mimetype * @param $mimeType * @param $filename * @return int|string */ public static function getTypeFromMimeMapping($mimeType, $filename) { if ($mimeType == "directory") { return "folder"; } $type = "unknown"; $mappings = array("image" => array("/image/", "/\\.eps\$/", "/\\.ai\$/", "/\\.svgz\$/", "/\\.pcx\$/", "/\\.iff\$/", "/\\.pct\$/", "/\\.wmf\$/"), "text" => array("/text/"), "audio" => array("/audio/"), "video" => array("/video/"), "document" => array("/msword/", "/pdf/", "/powerpoint/", "/office/", "/excel/", "/opendocument/"), "archive" => array("/zip/", "/tar/")); foreach ($mappings as $assetType => $patterns) { foreach ($patterns as $pattern) { if (preg_match($pattern, $mimeType . " ." . File::getFileExtension($filename))) { $type = $assetType; break; } } // break at first match if ($type != "unknown") { break; } } return $type; }
public function getVideoThumbnailAction() { if ($this->getParam("id")) { $video = Asset::getById(intval($this->getParam("id"))); } elseif ($this->getParam("path")) { $video = Asset::getByPath($this->getParam("path")); } $thumbnail = $this->getAllParams(); if ($this->getParam("treepreview")) { $thumbnail = Asset\Image\Thumbnail\Config::getPreviewConfig(); } $time = null; if ($this->getParam("time")) { $time = intval($this->getParam("time")); } if ($this->getParam("settime")) { $video->removeCustomSetting("image_thumbnail_asset"); $video->setCustomSetting("image_thumbnail_time", $time); $video->save(); } $image = null; if ($this->getParam("image")) { $image = Asset::getById(intval($this->getParam("image"))); } if ($this->getParam("setimage") && $image) { $video->removeCustomSetting("image_thumbnail_time"); $video->setCustomSetting("image_thumbnail_asset", $image->getId()); $video->save(); } $thumb = $video->getImageThumbnail($thumbnail, $time, $image); $thumbnailFile = $thumb->getFileSystemPath(); header("Content-type: image/" . File::getFileExtension($thumbnailFile), true); header("Content-Length: " . filesize($thumbnailFile), true); $this->sendThumbnailCacheHeaders(); while (@ob_end_flush()) { } flush(); readfile($thumbnailFile); exit; }
/** * @param null $timeOffset */ public function saveImage($file, $timeOffset = null) { if (!$timeOffset) { $timeOffset = 5; } $realTargetPath = null; if (!stream_is_local($file)) { $realTargetPath = $file; $file = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/ffmpeg-tmp-" . uniqid() . "." . File::getFileExtension($file); } $cmd = self::getFfmpegCli() . " -i " . escapeshellarg(realpath($this->file)) . " -vcodec png -vframes 1 -vf scale=iw*sar:ih -ss " . $timeOffset . " " . escapeshellarg(str_replace("/", DIRECTORY_SEPARATOR, $file)); Console::exec($cmd, null, 60); if ($realTargetPath) { File::rename($file, $realTargetPath); } }
/** * @param Model\Asset\Video $asset * @param $config * @param array $onlyFormats * @return Processor * @throws \Exception */ public static function process(Model\Asset\Video $asset, $config, $onlyFormats = []) { if (!\Pimcore\Video::isAvailable()) { throw new \Exception("No ffmpeg executable found, please configure the correct path in the system settings"); } $instance = new self(); $formats = empty($onlyFormats) ? ["mp4"] : $onlyFormats; $instance->setProcessId(uniqid()); $instance->setAssetId($asset->getId()); $instance->setConfig($config); // check for running or already created thumbnails $customSetting = $asset->getCustomSetting("thumbnails"); $existingFormats = []; if (is_array($customSetting) && array_key_exists($config->getName(), $customSetting)) { if ($customSetting[$config->getName()]["status"] == "inprogress") { if (TmpStore::get($instance->getJobStoreId($customSetting[$config->getName()]["processId"]))) { return; } } elseif ($customSetting[$config->getName()]["status"] == "finished") { // check if the files are there $formatsToConvert = []; foreach ($formats as $f) { if (!is_file($asset->getVideoThumbnailSavePath() . $customSetting[$config->getName()]["formats"][$f])) { $formatsToConvert[] = $f; } else { $existingFormats[$f] = $customSetting[$config->getName()]["formats"][$f]; $existingFormats[$f] = $customSetting[$config->getName()]["formats"][$f]; } } if (!empty($formatsToConvert)) { $formats = $formatsToConvert; } else { return; } } elseif ($customSetting[$config->getName()]["status"] == "error") { throw new \Exception("Unable to convert video, see logs for details."); } } foreach ($formats as $format) { $thumbDir = $asset->getVideoThumbnailSavePath() . "/thumb__" . $config->getName(); $filename = preg_replace("/\\." . preg_quote(File::getFileExtension($asset->getFilename())) . "/", "", $asset->getFilename()) . "." . $format; $fsPath = $thumbDir . "/" . $filename; $tmpPath = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/" . $filename; if (!is_dir(dirname($fsPath))) { File::mkdir(dirname($fsPath)); } if (is_file($fsPath)) { @unlink($fsPath); } $converter = \Pimcore\Video::getInstance(); $converter->load($asset->getFileSystemPath()); $converter->setAudioBitrate($config->getAudioBitrate()); $converter->setVideoBitrate($config->getVideoBitrate()); $converter->setFormat($format); $converter->setDestinationFile($tmpPath); $converter->setStorageFile($fsPath); $transformations = $config->getItems(); if (is_array($transformations) && count($transformations) > 0) { 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) { $arguments[$position] = $value; } } } ksort($arguments); if (count($mapping) == count($arguments)) { call_user_func_array([$converter, $transformation["method"]], $arguments); } else { $message = "Video Transform failed: cannot call method `" . $transformation["method"] . "´ with arguments `" . implode(",", $arguments) . "´ because there are too few arguments"; Logger::error($message); } } } } $instance->queue[] = $converter; } $customSetting = $asset->getCustomSetting("thumbnails"); $customSetting = is_array($customSetting) ? $customSetting : []; $customSetting[$config->getName()] = ["status" => "inprogress", "formats" => $existingFormats, "processId" => $instance->getProcessId()]; $asset->setCustomSetting("thumbnails", $customSetting); $asset->save(); $instance->convert(); return $instance; }
/** * @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 $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 $imagePath * @return bool */ public function isVectorGraphic($imagePath = null) { if ($imagePath) { // use file-extension if filename is provided return in_array(File::getFileExtension($imagePath), ["svg", "svgz", "eps", "pdf", "ps"]); } else { try { $type = $this->resource->getimageformat(); $vectorTypes = array("EPT", "EPDF", "EPI", "EPS", "EPS2", "EPS3", "EPSF", "EPSI", "EPT", "PDF", "PFA", "PFB", "PFM", "PS", "PS2", "PS3", "SVG", "SVGZ", "MVG"); if (in_array(strtoupper($type), $vectorTypes)) { return true; } } catch (\Exception $e) { \Logger::err($e); } } return false; }
/** * @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"); } } } }