/** * @param null $path * @return null|string|void * @throws \Exception */ public function getPdf($path = null) { if ($path) { $path = $this->preparePath($path); } $pdfPath = null; if (!$path && $this->path) { $path = $this->path; } try { // if the document is already an PDF, delegate the call directly to parent::getPdf() (Ghostscript) $pdfPath = parent::getPdf($path); return $pdfPath; } catch (\Exception $e) { // nothing to do, delegate to libreoffice } $pdfFile = PIMCORE_TEMPORARY_DIRECTORY . "/document-pdf-cache/document_" . md5($path . filemtime($path)) . "__libreoffice.pdf"; if (!is_dir(dirname($pdfFile))) { File::mkdir(dirname($pdfFile)); } $lockKey = "soffice"; if (!file_exists($pdfFile)) { // a list of all available filters is here: // http://cgit.freedesktop.org/libreoffice/core/tree/filter/source/config/fragments/filters $cmd = self::getLibreOfficeCli() . " --headless --nologo --nofirststartwizard --norestore --convert-to pdf:writer_web_pdf_Export --outdir " . escapeshellarg(PIMCORE_SYSTEM_TEMP_DIRECTORY) . " " . escapeshellarg($path); Model\Tool\Lock::acquire($lockKey); // avoid parallel conversions $out = Console::exec($cmd, PIMCORE_LOG_DIRECTORY . "/libreoffice-pdf-convert.log", 240); Model\Tool\Lock::release($lockKey); Logger::debug("LibreOffice Output was: " . $out); $tmpName = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/" . preg_replace("/\\." . File::getFileExtension($path) . "\$/", ".pdf", basename($path)); if (file_exists($tmpName)) { File::rename($tmpName, $pdfFile); $pdfPath = $pdfFile; } else { $message = "Couldn't convert document to PDF: " . $path . " with the command: '" . $cmd . "'"; Logger::error($message); throw new \Exception($message); } } else { $pdfPath = $pdfFile; } return $pdfPath; }
/** * @return $this * @throws \Exception */ public function save() { $isUpdate = false; if ($this->getId()) { $isUpdate = true; \Pimcore::getEventManager()->trigger("asset.preUpdate", $this); } else { \Pimcore::getEventManager()->trigger("asset.preAdd", $this); } $this->correctPath(); // we wrap the save actions in a loop here, so that we can restart the database transactions in the case it fails // if a transaction fails it gets restarted $maxRetries times, then the exception is thrown out // this is especially useful to avoid problems with deadlocks in multi-threaded environments (forked workers, ...) $maxRetries = 5; for ($retries = 0; $retries < $maxRetries; $retries++) { $this->beginTransaction(); try { if (!$isUpdate) { $this->getDao()->create(); } // get the old path from the database before the update is done $oldPath = null; if ($isUpdate) { $oldPath = $this->getDao()->getCurrentFullPath(); } $this->update(); // if the old path is different from the new path, update all children $updatedChildren = []; if ($oldPath && $oldPath != $this->getRealFullPath()) { $oldFullPath = PIMCORE_ASSET_DIRECTORY . $oldPath; if (is_file($oldFullPath) || is_dir($oldFullPath)) { if (!@File::rename(PIMCORE_ASSET_DIRECTORY . $oldPath, $this->getFileSystemPath())) { $error = error_get_last(); throw new \Exception("Unable to rename asset " . $this->getId() . " on the filesystem: " . $oldFullPath . " - Reason: " . $error["message"]); } $this->getDao()->updateWorkspaces(); $updatedChildren = $this->getDao()->updateChildsPaths($oldPath); } } $this->commit(); break; // transaction was successfully completed, so we cancel the loop here -> no restart required } catch (\Exception $e) { try { $this->rollBack(); } catch (\Exception $er) { // PDO adapter throws exceptions if rollback fails \Logger::error($er); } // we try to start the transaction $maxRetries times again (deadlocks, ...) if ($retries < $maxRetries - 1) { $run = $retries + 1; $waitTime = 100000; // microseconds \Logger::warn("Unable to finish transaction (" . $run . ". run) because of the following reason '" . $e->getMessage() . "'. --> Retrying in " . $waitTime . " microseconds ... (" . ($run + 1) . " of " . $maxRetries . ")"); usleep($waitTime); // wait specified time until we restart the transaction } else { // if the transaction still fail after $maxRetries retries, we throw out the exception throw $e; } } } $additionalTags = []; if (isset($updatedChildren) && is_array($updatedChildren)) { foreach ($updatedChildren as $assetId) { $tag = "asset_" . $assetId; $additionalTags[] = $tag; // remove the child also from registry (internal cache) to avoid path inconsistencies during long running scripts, such as CLI \Zend_Registry::set($tag, null); } } $this->clearDependentCache($additionalTags); $this->setDataChanged(false); if ($isUpdate) { \Pimcore::getEventManager()->trigger("asset.postUpdate", $this); } else { \Pimcore::getEventManager()->trigger("asset.postAdd", $this); } return $this; }
/** * @param $processId */ public static function execute($processId) { $instance = new self(); $instance->setProcessId($processId); $instanceItem = TmpStore::get($instance->getJobStoreId($processId)); $instance = $instanceItem->getData(); $formats = []; $conversionStatus = "finished"; // check if there is already a transcoding process running, wait if so ... Model\Tool\Lock::acquire("video-transcoding", 7200, 10); // expires after 2 hrs, refreshes every 10 secs $asset = Model\Asset::getById($instance->getAssetId()); // start converting foreach ($instance->queue as $converter) { try { Logger::info("start video " . $converter->getFormat() . " to " . $converter->getDestinationFile()); $success = $converter->save(); Logger::info("finished video " . $converter->getFormat() . " to " . $converter->getDestinationFile()); File::rename($converter->getDestinationFile(), $converter->getStorageFile()); // set proper permissions @chmod($converter->getStorageFile(), File::getDefaultMode()); if ($success) { $formats[$converter->getFormat()] = str_replace($asset->getVideoThumbnailSavePath(), "", $converter->getStorageFile()); } else { $conversionStatus = "error"; } $converter->destroy(); } catch (\Exception $e) { Logger::error($e); } } Model\Tool\Lock::release("video-transcoding"); if ($asset) { $customSetting = $asset->getCustomSetting("thumbnails"); $customSetting = is_array($customSetting) ? $customSetting : []; if (array_key_exists($instance->getConfig()->getName(), $customSetting) && array_key_exists("formats", $customSetting[$instance->getConfig()->getName()]) && is_array($customSetting[$instance->getConfig()->getName()]["formats"])) { $formats = array_merge($customSetting[$instance->getConfig()->getName()]["formats"], $formats); } $customSetting[$instance->getConfig()->getName()] = ["status" => $conversionStatus, "formats" => $formats]; $asset->setCustomSetting("thumbnails", $customSetting); $asset->save(); } TmpStore::delete($instance->getJobStoreId()); }
/** * @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; } }
/** * @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; }
/** * @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 $path * @param null $format * @param null $quality * @param null $colorProfile * @return $this|mixed * @throws \Exception */ public function save($path, $format = null, $quality = null, $colorProfile = 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"; } $i = $this->resource; // this is because of HHVM which has problems with $this->resource->writeImage(); $originalFilename = null; if (!$this->reinitializing) { if ($this->getUseContentOptimizedFormat()) { $format = "jpeg"; if ($this->hasAlphaChannel()) { $format = "png32"; } } } $i->stripimage(); $i->profileImage('*', null); $i->setImageFormat($format); if ($quality) { $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") { 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; }