diff --git a/lib/private/preview.php b/lib/private/preview.php index c9d8810be6..f6c8c485d0 100644 --- a/lib/private/preview.php +++ b/lib/private/preview.php @@ -16,14 +16,6 @@ namespace OC; use OC\Preview\Provider; use OCP\Files\NotFoundException; -require_once 'preview/image.php'; -require_once 'preview/movie.php'; -require_once 'preview/mp3.php'; -require_once 'preview/svg.php'; -require_once 'preview/txt.php'; -require_once 'preview/office.php'; -require_once 'preview/bitmap.php'; - class Preview { //the thumbnail folder const THUMBNAILS_FOLDER = 'thumbnails'; @@ -744,10 +736,11 @@ class Preview { return; } - if (count(self::$providers) > 0) { + if (!empty(self::$providers)) { return; } + self::registerCoreProviders(); foreach (self::$registeredProviders as $provider) { $class = $provider['class']; $options = $provider['options']; @@ -759,7 +752,74 @@ class Preview { $keys = array_map('strlen', array_keys(self::$providers)); array_multisort($keys, SORT_DESC, self::$providers); + } + protected static function registerCoreProviders() { + self::registerProvider('OC\Preview\TXT'); + self::registerProvider('OC\Preview\MarkDown'); + self::registerProvider('OC\Preview\Image'); + self::registerProvider('OC\Preview\MP3'); + + // SVG, Office and Bitmap require imagick + if (extension_loaded('imagick')) { + $checkImagick = new \Imagick(); + + $imagickProviders = array( + 'SVG' => 'OC\Preview\SVG', + 'TIFF' => 'OC\Preview\TIFF', + 'PDF' => 'OC\Preview\PDF', + 'AI' => 'OC\Preview\Illustrator', + 'PSD' => 'OC\Preview\Photoshop', + // Requires adding 'eps' => array('application/postscript', null), to lib/private/mimetypes.list.php + 'EPS' => 'OC\Preview\Postscript', + ); + + foreach ($imagickProviders as $queryFormat => $provider) { + if (count($checkImagick->queryFormats($queryFormat)) === 1) { + self::registerProvider($provider); + } + } + + if (count($checkImagick->queryFormats('PDF')) === 1) { + // Office previews are currently not supported on Windows + if (!\OC_Util::runningOnWindows() && \OC_Helper::is_function_enabled('shell_exec')) { + $officeFound = is_string(\OC::$server->getConfig()->getSystemValue('preview_libreoffice_path', null)); + + if (!$officeFound) { + //let's see if there is libreoffice or openoffice on this machine + $whichLibreOffice = shell_exec('command -v libreoffice'); + $officeFound = !empty($whichLibreOffice); + if (!$officeFound) { + $whichOpenOffice = shell_exec('command -v openoffice'); + $officeFound = !empty($whichOpenOffice); + } + } + + if ($officeFound) { + self::registerProvider('OC\Preview\MSOfficeDoc'); + self::registerProvider('OC\Preview\MSOffice2003'); + self::registerProvider('OC\Preview\MSOffice2007'); + self::registerProvider('OC\Preview\OpenDocument'); + self::registerProvider('OC\Preview\StarOffice'); + } + } + } + } + + // Video requires avconv or ffmpeg and is therefor + // currently not supported on Windows. + if (!\OC_Util::runningOnWindows()) { + $avconvBinary = \OC_Helper::findBinaryPath('avconv'); + $ffmpegBinary = ($avconvBinary) ? null : \OC_Helper::findBinaryPath('ffmpeg'); + + if ($avconvBinary || $ffmpegBinary) { + // FIXME // a bit hacky but didn't want to use subclasses + \OC\Preview\Movie::$avconvBinary = $avconvBinary; + \OC\Preview\Movie::$ffmpegBinary = $ffmpegBinary; + + self::registerProvider('OC\Preview\Movie'); + } + } } /** diff --git a/lib/private/preview/bitmap.php b/lib/private/preview/bitmap.php index 748a63a6af..25f65cf7fc 100644 --- a/lib/private/preview/bitmap.php +++ b/lib/private/preview/bitmap.php @@ -5,113 +5,33 @@ * later. * See the COPYING-README file. */ + namespace OC\Preview; -use Imagick; +abstract class Bitmap extends Provider { + /** + * {@inheritDoc} + */ + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + $tmpPath = $fileview->toTmpFile($path); -if (extension_loaded('imagick')) { + //create imagick object from bitmap or vector file + try { + // Layer 0 contains either the bitmap or + // a flat representation of all vector layers + $bp = new \Imagick($tmpPath . '[0]'); - $checkImagick = new Imagick(); - - class Bitmap extends Provider { - - public function getMimeType() { - return null; + $bp->setImageFormat('png'); + } catch (\Exception $e) { + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + return false; } - public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - $tmpPath = $fileview->toTmpFile($path); + unlink($tmpPath); - //create imagick object from bitmap or vector file - try{ - // Layer 0 contains either the bitmap or - // a flat representation of all vector layers - $bp = new Imagick($tmpPath . '[0]'); - - $bp->setImageFormat('png'); - } catch (\Exception $e) { - \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); - return false; - } - - unlink($tmpPath); - - //new bitmap image object - $image = new \OC_Image($bp); - //check if image object is valid - return $image->valid() ? $image : false; - } - - } - - if(count($checkImagick->queryFormats('PDF')) === 1) { - - //.pdf - class PDF extends Bitmap { - - public function getMimeType() { - return '/application\/pdf/'; - } - - } - - \OC\Preview::registerProvider('OC\Preview\PDF'); - } - - if(count($checkImagick->queryFormats('TIFF')) === 1) { - - //.tiff - class TIFF extends Bitmap { - - public function getMimeType() { - return '/image\/tiff/'; - } - - } - - \OC\Preview::registerProvider('OC\Preview\TIFF'); - } - - if(count($checkImagick->queryFormats('AI')) === 1) { - - //.ai - class Illustrator extends Bitmap { - - public function getMimeType() { - return '/application\/illustrator/'; - } - - } - - \OC\Preview::registerProvider('OC\Preview\Illustrator'); - } - - // Requires adding 'eps' => array('application/postscript', null), to lib/private/mimetypes.list.php - if(count($checkImagick->queryFormats('EPS')) === 1) { - - //.eps - class Postscript extends Bitmap { - - public function getMimeType() { - return '/application\/postscript/'; - } - - } - - \OC\Preview::registerProvider('OC\Preview\Postscript'); - } - - if(count($checkImagick->queryFormats('PSD')) === 1) { - - //.psd - class Photoshop extends Bitmap { - - public function getMimeType() { - return '/application\/x-photoshop/'; - } - - } - - \OC\Preview::registerProvider('OC\Preview\Photoshop'); + //new bitmap image object + $image = new \OC_Image($bp); + //check if image object is valid + return $image->valid() ? $image : false; } } diff --git a/lib/private/preview/illustrator.php b/lib/private/preview/illustrator.php new file mode 100644 index 0000000000..e88c305f70 --- /dev/null +++ b/lib/private/preview/illustrator.php @@ -0,0 +1,19 @@ +getFileInfo($path); @@ -35,5 +40,3 @@ class Image extends Provider { } } - -\OC\Preview::registerProvider('OC\Preview\Image'); diff --git a/lib/private/preview/markdown.php b/lib/private/preview/markdown.php new file mode 100644 index 0000000000..1be01fcdd4 --- /dev/null +++ b/lib/private/preview/markdown.php @@ -0,0 +1,18 @@ +getFileInfo($path); + $useFileDirectly = (!$fileInfo->isEncrypted() && !$fileInfo->isMounted()); - public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - // TODO: use proc_open() and stream the source file ? + if ($useFileDirectly) { + $absPath = $fileview->getLocalFile($path); + } else { + $absPath = \OC_Helper::tmpFile(); - $fileInfo = $fileview->getFileInfo($path); - $useFileDirectly = (!$fileInfo->isEncrypted() && !$fileInfo->isMounted()); + $handle = $fileview->fopen($path, 'rb'); - if ($useFileDirectly) { - $absPath = $fileview->getLocalFile($path); - } else { - $absPath = \OC_Helper::tmpFile(); + // we better use 5MB (1024 * 1024 * 5 = 5242880) instead of 1MB. + // in some cases 1MB was no enough to generate thumbnail + $firstmb = stream_get_contents($handle, 5242880); + file_put_contents($absPath, $firstmb); + } - $handle = $fileview->fopen($path, 'rb'); - - // we better use 5MB (1024 * 1024 * 5 = 5242880) instead of 1MB. - // in some cases 1MB was no enough to generate thumbnail - $firstmb = stream_get_contents($handle, 5242880); - file_put_contents($absPath, $firstmb); - } - - $result = $this->generateThumbNail($maxX, $maxY, $absPath, 5); - if ($result === false) { - $result = $this->generateThumbNail($maxX, $maxY, $absPath, 1); - if ($result === false) { - $result = $this->generateThumbNail($maxX, $maxY, $absPath, 0); - } - } - - if (!$useFileDirectly) { - unlink($absPath); - } - - return $result; - } - - /** - * @param int $maxX - * @param int $maxY - * @param string $absPath - * @param string $tmpPath - * @param int $second - * @return bool|\OC_Image - */ - private function generateThumbNail($maxX, $maxY, $absPath, $second) - { - $tmpPath = \OC_Helper::tmpFile(); - - if (self::$avconvBinary) { - $cmd = self::$avconvBinary . ' -an -y -ss ' . escapeshellarg($second) . - ' -i ' . escapeshellarg($absPath) . - ' -f mjpeg -vframes 1 -vsync 1 ' . escapeshellarg($tmpPath) . - ' > /dev/null 2>&1'; - } else { - $cmd = self::$ffmpegBinary . ' -y -ss ' . escapeshellarg($second) . - ' -i ' . escapeshellarg($absPath) . - ' -f mjpeg -vframes 1' . - ' -s ' . escapeshellarg($maxX) . 'x' . escapeshellarg($maxY) . - ' ' . escapeshellarg($tmpPath) . - ' > /dev/null 2>&1'; - } - - exec($cmd, $output, $returnCode); - - if ($returnCode === 0) { - $image = new \OC_Image(); - $image->loadFromFile($tmpPath); - unlink($tmpPath); - return $image->valid() ? $image : false; - } - unlink($tmpPath); - return false; + $result = $this->generateThumbNail($maxX, $maxY, $absPath, 5); + if ($result === false) { + $result = $this->generateThumbNail($maxX, $maxY, $absPath, 1); + if ($result === false) { + $result = $this->generateThumbNail($maxX, $maxY, $absPath, 0); } } - // a bit hacky but didn't want to use subclasses - Movie::$avconvBinary = $avconvBinary; - Movie::$ffmpegBinary = $ffmpegBinary; + if (!$useFileDirectly) { + unlink($absPath); + } - \OC\Preview::registerProvider('OC\Preview\Movie'); + return $result; + } + + /** + * @param int $maxX + * @param int $maxY + * @param string $absPath + * @param int $second + * @return bool|\OC_Image + */ + private function generateThumbNail($maxX, $maxY, $absPath, $second) { + $tmpPath = \OC_Helper::tmpFile(); + + if (self::$avconvBinary) { + $cmd = self::$avconvBinary . ' -an -y -ss ' . escapeshellarg($second) . + ' -i ' . escapeshellarg($absPath) . + ' -f mjpeg -vframes 1 -vsync 1 ' . escapeshellarg($tmpPath) . + ' > /dev/null 2>&1'; + } else { + $cmd = self::$ffmpegBinary . ' -y -ss ' . escapeshellarg($second) . + ' -i ' . escapeshellarg($absPath) . + ' -f mjpeg -vframes 1' . + ' -s ' . escapeshellarg($maxX) . 'x' . escapeshellarg($maxY) . + ' ' . escapeshellarg($tmpPath) . + ' > /dev/null 2>&1'; + } + + exec($cmd, $output, $returnCode); + + if ($returnCode === 0) { + $image = new \OC_Image(); + $image->loadFromFile($tmpPath); + unlink($tmpPath); + return $image->valid() ? $image : false; + } + unlink($tmpPath); + return false; } } - diff --git a/lib/private/preview/mp3.php b/lib/private/preview/mp3.php index bb4d3dfce8..f1a50d99e1 100644 --- a/lib/private/preview/mp3.php +++ b/lib/private/preview/mp3.php @@ -8,11 +8,16 @@ namespace OC\Preview; class MP3 extends Provider { - + /** + * {@inheritDoc} + */ public function getMimeType() { return '/audio\/mpeg/'; } + /** + * {@inheritDoc} + */ public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { $getID3 = new \getID3(); @@ -31,6 +36,12 @@ class MP3 extends Provider { return $this->getNoCoverThumbnail(); } + /** + * Generates a default image when the file has no cover + * + * @return false|\OC_Image False if the default image is missing or invalid, + * otherwise the image is returned as \OC_Image + */ private function getNoCoverThumbnail() { $icon = \OC::$SERVERROOT . '/core/img/filetypes/audio.png'; @@ -44,5 +55,3 @@ class MP3 extends Provider { } } - -\OC\Preview::registerProvider('OC\Preview\MP3'); diff --git a/lib/private/preview/msoffice2003.php b/lib/private/preview/msoffice2003.php new file mode 100644 index 0000000000..55fbe70843 --- /dev/null +++ b/lib/private/preview/msoffice2003.php @@ -0,0 +1,18 @@ +initCmd(); - if(is_null($this->cmd)) { - return false; - } - - $absPath = $fileview->toTmpFile($path); - - $tmpDir = get_temp_dir(); - - $defaultParameters = ' -env:UserInstallation=file://' . escapeshellarg($tmpDir . '/owncloud-' . \OC_Util::getInstanceId().'/') . ' --headless --nologo --nofirststartwizard --invisible --norestore --convert-to pdf --outdir '; - $clParameters = \OCP\Config::getSystemValue('preview_office_cl_parameters', $defaultParameters); - - $exec = $this->cmd . $clParameters . escapeshellarg($tmpDir) . ' ' . escapeshellarg($absPath); - - shell_exec($exec); - - //create imagick object from pdf - try{ - $pdf = new \imagick($absPath . '.pdf' . '[0]'); - $pdf->setImageFormat('jpg'); - } catch (\Exception $e) { - unlink($absPath); - unlink($absPath . '.pdf'); - \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); - return false; - } - - $image = new \OC_Image(); - $image->loadFromData($pdf); - - unlink($absPath); - unlink($absPath . '.pdf'); - - return $image->valid() ? $image : false; - } - - private function initCmd() { - $cmd = ''; - - if(is_string(\OC_Config::getValue('preview_libreoffice_path', null))) { - $cmd = \OC_Config::getValue('preview_libreoffice_path', null); - } - - $whichLibreOffice = shell_exec('command -v libreoffice'); - if($cmd === '' && !empty($whichLibreOffice)) { - $cmd = 'libreoffice'; - } - - $whichOpenOffice = shell_exec('command -v openoffice'); - if($cmd === '' && !empty($whichOpenOffice)) { - $cmd = 'openoffice'; - } - - if($cmd === '') { - $cmd = null; - } - - $this->cmd = $cmd; - } - } - - //.doc, .dot - class MSOfficeDoc extends Office { - - public function getMimeType() { - return '/application\/msword/'; - } - - } - - \OC\Preview::registerProvider('OC\Preview\MSOfficeDoc'); - - //.docm, .dotm, .xls(m), .xlt(m), .xla(m), .ppt(m), .pot(m), .pps(m), .ppa(m) - class MSOffice2003 extends Office { - - public function getMimeType() { - return '/application\/vnd.ms-.*/'; - } - - } - - \OC\Preview::registerProvider('OC\Preview\MSOffice2003'); - - //.docx, .dotx, .xlsx, .xltx, .pptx, .potx, .ppsx - class MSOffice2007 extends Office { - - public function getMimeType() { - return '/application\/vnd.openxmlformats-officedocument.*/'; - } - - } - - \OC\Preview::registerProvider('OC\Preview\MSOffice2007'); - - //.odt, .ott, .oth, .odm, .odg, .otg, .odp, .otp, .ods, .ots, .odc, .odf, .odb, .odi, .oxt - class OpenDocument extends Office { - - public function getMimeType() { - return '/application\/vnd.oasis.opendocument.*/'; - } - - } - - \OC\Preview::registerProvider('OC\Preview\OpenDocument'); - - //.sxw, .stw, .sxc, .stc, .sxd, .std, .sxi, .sti, .sxg, .sxm - class StarOffice extends Office { - - public function getMimeType() { - return '/application\/vnd.sun.xml.*/'; - } - - } - - \OC\Preview::registerProvider('OC\Preview\StarOffice'); -} diff --git a/lib/private/preview/office.php b/lib/private/preview/office.php index b47cbc6e08..5bd61bde3b 100644 --- a/lib/private/preview/office.php +++ b/lib/private/preview/office.php @@ -5,24 +5,72 @@ * later. * See the COPYING-README file. */ -//both, libreoffice backend and php fallback, need imagick -if (extension_loaded('imagick')) { +namespace OC\Preview; - $checkImagick = new Imagick(); +abstract class Office extends Provider { + private $cmd; - if(count($checkImagick->queryFormats('PDF')) === 1) { - $isShellExecEnabled = \OC_Helper::is_function_enabled('shell_exec'); - - // LibreOffice preview is currently not supported on Windows - if (!\OC_Util::runningOnWindows()) { - $whichLibreOffice = ($isShellExecEnabled ? shell_exec('command -v libreoffice') : ''); - $isLibreOfficeAvailable = !empty($whichLibreOffice); - $whichOpenOffice = ($isShellExecEnabled ? shell_exec('command -v openoffice') : ''); - $isOpenOfficeAvailable = !empty($whichOpenOffice); - //let's see if there is libreoffice or openoffice on this machine - if($isShellExecEnabled && ($isLibreOfficeAvailable || $isOpenOfficeAvailable || is_string(\OC_Config::getValue('preview_libreoffice_path', null)))) { - require_once('office-cl.php'); - } + /** + * {@inheritDoc} + */ + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + $this->initCmd(); + if(is_null($this->cmd)) { + return false; } + + $absPath = $fileview->toTmpFile($path); + + $tmpDir = get_temp_dir(); + + $defaultParameters = ' -env:UserInstallation=file://' . escapeshellarg($tmpDir . '/owncloud-' . \OC_Util::getInstanceId().'/') . ' --headless --nologo --nofirststartwizard --invisible --norestore --convert-to pdf --outdir '; + $clParameters = \OCP\Config::getSystemValue('preview_office_cl_parameters', $defaultParameters); + + $exec = $this->cmd . $clParameters . escapeshellarg($tmpDir) . ' ' . escapeshellarg($absPath); + + shell_exec($exec); + + //create imagick object from pdf + try{ + $pdf = new \imagick($absPath . '.pdf' . '[0]'); + $pdf->setImageFormat('jpg'); + } catch (\Exception $e) { + unlink($absPath); + unlink($absPath . '.pdf'); + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + return false; + } + + $image = new \OC_Image(); + $image->loadFromData($pdf); + + unlink($absPath); + unlink($absPath . '.pdf'); + + return $image->valid() ? $image : false; + } + + private function initCmd() { + $cmd = ''; + + if(is_string(\OC_Config::getValue('preview_libreoffice_path', null))) { + $cmd = \OC_Config::getValue('preview_libreoffice_path', null); + } + + $whichLibreOffice = shell_exec('command -v libreoffice'); + if($cmd === '' && !empty($whichLibreOffice)) { + $cmd = 'libreoffice'; + } + + $whichOpenOffice = shell_exec('command -v openoffice'); + if($cmd === '' && !empty($whichOpenOffice)) { + $cmd = 'openoffice'; + } + + if($cmd === '') { + $cmd = null; + } + + $this->cmd = $cmd; } } diff --git a/lib/private/preview/opendocument.php b/lib/private/preview/opendocument.php new file mode 100644 index 0000000000..fe1468ee94 --- /dev/null +++ b/lib/private/preview/opendocument.php @@ -0,0 +1,18 @@ +options=$options; + $this->options = $options; } + /** + * @return string Regex with the mimetypes that are supported by this provider + */ abstract public function getMimeType(); /** * Check if a preview can be generated for $path * - * @param string $path + * @param \OC\Files\FileInfo $file * @return bool */ - public function isAvailable($path) { + public function isAvailable($file) { return true; } @@ -26,11 +29,10 @@ abstract class Provider { * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image * @param bool $scalingup Disable/Enable upscaling of previews - * @param object $fileview fileview object of user folder + * @param \OC\Files\View $fileview fileview object of user folder * @return mixed * false if no preview was generated * OC_Image object of the preview */ abstract public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview); - } diff --git a/lib/private/preview/staroffice.php b/lib/private/preview/staroffice.php new file mode 100644 index 0000000000..73ad368b34 --- /dev/null +++ b/lib/private/preview/staroffice.php @@ -0,0 +1,18 @@ +setBackgroundColor(new \ImagickPixel('transparent')); - $checkImagick = new Imagick(); - - if(count($checkImagick->queryFormats('SVG')) === 1) { - - class SVG extends Provider { - - public function getMimeType() { - return '/image\/svg\+xml/'; + $content = stream_get_contents($fileview->fopen($path, 'r')); + if(substr($content, 0, 5) !== '' . $content; } - public function getThumbnail($path,$maxX,$maxY,$scalingup,$fileview) { - try{ - $svg = new Imagick(); - $svg->setBackgroundColor(new \ImagickPixel('transparent')); - - $content = stream_get_contents($fileview->fopen($path, 'r')); - if(substr($content, 0, 5) !== '' . $content; - } - - // Do not parse SVG files with references - if(stripos($content, 'xlink:href') !== false) { - return false; - } - - $svg->readImageBlob($content); - $svg->setImageFormat('png32'); - } catch (\Exception $e) { - \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); - return false; - } - - - //new image object - $image = new \OC_Image(); - $image->loadFromData($svg); - //check if image object is valid - return $image->valid() ? $image : false; + // Do not parse SVG files with references + if(stripos($content, 'xlink:href') !== false) { + return false; } + $svg->readImageBlob($content); + $svg->setImageFormat('png32'); + } catch (\Exception $e) { + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + return false; } - \OC\Preview::registerProvider('OC\Preview\SVG'); + //new image object + $image = new \OC_Image(); + $image->loadFromData($svg); + //check if image object is valid + return $image->valid() ? $image : false; } } diff --git a/lib/private/preview/tiff.php b/lib/private/preview/tiff.php new file mode 100644 index 0000000000..0a1e8e8ece --- /dev/null +++ b/lib/private/preview/tiff.php @@ -0,0 +1,19 @@ +getSize() > 5; } /** - * @param string $path - * @param int $maxX - * @param int $maxY - * @param boolean $scalingup - * @param \OC\Files\View $fileview - * @return bool|\OC_Image + * {@inheritDoc} */ public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - $content = $fileview->fopen($path, 'r'); $content = stream_get_contents($content,3000); @@ -79,15 +72,3 @@ class TXT extends Provider { return $image->valid() ? $image : false; } } - -\OC\Preview::registerProvider('OC\Preview\TXT'); - -class MarkDown extends TXT { - - public function getMimeType() { - return '/text\/(x-)?markdown/'; - } - -} - -\OC\Preview::registerProvider('OC\Preview\MarkDown');