[Pkg-owncloud-commits] [owncloud] 327/457: Fix max preview, some resizing and caching issues and force preview providers to resize their previews properly * introduces a method in OC_Image which doesn't stretch images when trying to make them fit in a box * adds the method to all key providers so that they can do their job, as expected by the Preview class * improves the caching mechanism of Preview in order to reduce I/O and to avoid filling the available disk space * fixes some long standing issues * **contains mostly tests**

David Prévot taffit at moszumanska.debian.org
Sun Jun 28 20:06:33 UTC 2015


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch stable8
in repository owncloud.

commit 71d65cb713ebfb85ee19f9f3cd17dd915360fe9b
Author: Olivier Paroz <github at oparoz.com>
Date:   Sat Jun 6 16:21:36 2015 +0200

    Fix max preview, some resizing and caching issues and force preview providers to resize their previews properly
    * introduces a method in OC_Image which doesn't stretch images when trying to make them fit in a box
    * adds the method to all key providers so that they can do their job, as expected by the Preview class
    * improves the caching mechanism of Preview in order to reduce I/O and to avoid filling the available disk space
    * fixes some long standing issues
    * **contains mostly tests**
---
 lib/private/image.php            |  20 +
 lib/private/preview.php          | 788 +++++++++++++++++++++++-----------
 lib/private/preview/image.php    |  10 +-
 lib/private/preview/movie.php    |   7 +-
 lib/private/preview/mp3.php      |   7 +-
 lib/private/preview/office.php   |  24 +-
 lib/private/preview/provider.php |   4 +-
 lib/private/preview/svg.php      |  13 +-
 lib/public/iimage.php            |  10 +
 tests/data/testimage-wide.png    | Bin 0 -> 173080 bytes
 tests/data/testimage.eps         | 175 ++++++++
 tests/data/testimage.mp3         | Bin 0 -> 6144 bytes
 tests/data/testimage.mp4         | Bin 0 -> 383631 bytes
 tests/data/testimage.odt         | Bin 0 -> 32925 bytes
 tests/data/testimagelarge.svg    |   2 +
 tests/lib/image.php              |  86 +++-
 tests/lib/preview.php            | 886 ++++++++++++++++++++++++++++++++-------
 tests/lib/preview/bitmap.php     |  36 ++
 tests/lib/preview/image.php      |  36 ++
 tests/lib/preview/movie.php      |  46 ++
 tests/lib/preview/mp3.php        |  36 ++
 tests/lib/preview/office.php     |  43 ++
 tests/lib/preview/provider.php   | 187 +++++++++
 tests/lib/preview/svg.php        |  41 ++
 tests/lib/preview/txt.php        |  37 ++
 25 files changed, 2063 insertions(+), 431 deletions(-)

diff --git a/lib/private/image.php b/lib/private/image.php
index d229ed4..54e5ef7 100644
--- a/lib/private/image.php
+++ b/lib/private/image.php
@@ -952,6 +952,8 @@ class OC_Image implements \OCP\IImage {
 	/**
 	 * Resizes the image to fit within a boundary while preserving ratio.
 	 *
+	 * Warning: Images smaller than $maxWidth x $maxHeight will end up being scaled up
+	 *
 	 * @param integer $maxWidth
 	 * @param integer $maxHeight
 	 * @return bool
@@ -973,6 +975,24 @@ class OC_Image implements \OCP\IImage {
 	}
 
 	/**
+	 * Shrinks larger images to fit within specified boundaries while preserving ratio.
+	 *
+	 * @param integer $maxWidth
+	 * @param integer $maxHeight
+	 * @return bool
+	 */
+	public function scaleDownToFit($maxWidth, $maxHeight) {
+		$widthOrig = imageSX($this->resource);
+		$heightOrig = imageSY($this->resource);
+
+		if ($widthOrig > $maxWidth || $heightOrig >$maxHeight) {
+			return $this->fitIn($maxWidth, $maxHeight);
+		}
+
+		return false;
+	}
+
+	/**
 	 * Destroys the current image and resets the object
 	 */
 	public function destroy() {
diff --git a/lib/private/preview.php b/lib/private/preview.php
index eab60e1..145b792 100644
--- a/lib/private/preview.php
+++ b/lib/private/preview.php
@@ -41,8 +41,10 @@ class Preview {
 
 	//config
 	private $maxScaleFactor;
-	private $configMaxX;
-	private $configMaxY;
+	/** @var int maximum width allowed for a preview */
+	private $configMaxWidth;
+	/** @var int maximum height allowed for a preview */
+	private $configMaxHeight;
 
 	//fileview object
 	private $fileView = null;
@@ -56,6 +58,16 @@ class Preview {
 	private $mimeType;
 	private $keepAspect = false;
 
+	//used to calculate the size of the preview to generate
+	/** @var int $maxPreviewWidth max width a preview can have */
+	private $maxPreviewWidth;
+	/** @var int $maxPreviewHeight max height a preview can have */
+	private $maxPreviewHeight;
+	/** @var int $previewWidth calculated width of the preview we're looking for */
+	private $previewWidth;
+	/** @var int $previewHeight calculated height of the preview we're looking for */
+	private $previewHeight;
+
 	//filemapper used for deleting previews
 	// index is path, value is fileinfo
 	static public $deleteFileMapper = array();
@@ -75,18 +87,28 @@ class Preview {
 
 	/**
 	 * check if thumbnail or bigger version of thumbnail of file is cached
+	 *
 	 * @param string $user userid - if no user is given, OC_User::getUser will be used
 	 * @param string $root path of root
 	 * @param string $file The path to the file where you want a thumbnail from
-	 * @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 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
+	 *
 	 * @throws \Exception
 	 * @return mixed (bool / string)
 	 *                    false if thumbnail does not exist
 	 *                    path to thumbnail if thumbnail exists
 	 */
-	public function __construct($user = '', $root = '/', $file = '', $maxX = 1, $maxY = 1, $scalingUp = true) {
+	public function __construct(
+		$user = '',
+		$root = '/',
+		$file = '', $maxX = 1,
+		$maxY = 1,
+		$scalingUp = true
+	) {
 		//init fileviews
 		if ($user === '') {
 			$user = \OC_User::getUser();
@@ -95,20 +117,25 @@ class Preview {
 		$this->userView = new \OC\Files\View('/' . $user);
 
 		//set config
-		$this->configMaxX = \OC::$server->getConfig()->getSystemValue('preview_max_x', 2048);
-		$this->configMaxY = \OC::$server->getConfig()->getSystemValue('preview_max_y', 2048);
-		$this->maxScaleFactor = \OC::$server->getConfig()->getSystemValue('preview_max_scale_factor', 2);
+		$sysConfig = \OC::$server->getConfig();
+		$this->configMaxWidth = $sysConfig->getSystemValue('preview_max_x', 2048);
+		$this->configMaxHeight = $sysConfig->getSystemValue('preview_max_y', 2048);
+		$this->maxScaleFactor = $sysConfig->getSystemValue('preview_max_scale_factor', 2);
 
 		//save parameters
 		$this->setFile($file);
-		$this->setMaxX($maxX);
-		$this->setMaxY($maxY);
+		$this->setMaxX((int)$maxX);
+		$this->setMaxY((int)$maxY);
 		$this->setScalingUp($scalingUp);
 
 		$this->preview = null;
 
 		//check if there are preview backends
-		if (!\OC::$server->getPreviewManager()->hasProviders() && \OC::$server->getConfig()->getSystemValue('enable_previews', true)) {
+		if (!\OC::$server->getPreviewManager()
+						 ->hasProviders()
+			&& \OC::$server->getConfig()
+						   ->getSystemValue('enable_previews', true)
+		) {
 			\OC_Log::write('core', 'No preview providers exist', \OC_Log::ERROR);
 			throw new \Exception('No preview providers');
 		}
@@ -116,6 +143,7 @@ class Preview {
 
 	/**
 	 * returns the path of the file you want a thumbnail from
+	 *
 	 * @return string
 	 */
 	public function getFile() {
@@ -124,6 +152,7 @@ class Preview {
 
 	/**
 	 * returns the max width of the preview
+	 *
 	 * @return integer
 	 */
 	public function getMaxX() {
@@ -132,6 +161,7 @@ class Preview {
 
 	/**
 	 * returns the max height of the preview
+	 *
 	 * @return integer
 	 */
 	public function getMaxY() {
@@ -140,6 +170,7 @@ class Preview {
 
 	/**
 	 * returns whether or not scalingup is enabled
+	 *
 	 * @return bool
 	 */
 	public function getScalingUp() {
@@ -148,6 +179,7 @@ class Preview {
 
 	/**
 	 * returns the name of the thumbnailfolder
+	 *
 	 * @return string
 	 */
 	public function getThumbnailsFolder() {
@@ -156,6 +188,7 @@ class Preview {
 
 	/**
 	 * returns the max scale factor
+	 *
 	 * @return string
 	 */
 	public function getMaxScaleFactor() {
@@ -164,31 +197,36 @@ class Preview {
 
 	/**
 	 * returns the max width set in ownCloud's config
+	 *
 	 * @return string
 	 */
 	public function getConfigMaxX() {
-		return $this->configMaxX;
+		return $this->configMaxWidth;
 	}
 
 	/**
 	 * returns the max height set in ownCloud's config
+	 *
 	 * @return string
 	 */
 	public function getConfigMaxY() {
-		return $this->configMaxY;
+		return $this->configMaxHeight;
 	}
 
 	/**
+	 * Returns the FileInfo object associated with the file to preview
+	 *
 	 * @return false|Files\FileInfo|\OCP\Files\FileInfo
 	 */
 	protected function getFileInfo() {
 		$absPath = $this->fileView->getAbsolutePath($this->file);
 		$absPath = Files\Filesystem::normalizePath($absPath);
-		if(array_key_exists($absPath, self::$deleteFileMapper)) {
+		if (array_key_exists($absPath, self::$deleteFileMapper)) {
 			$this->info = self::$deleteFileMapper[$absPath];
 		} else if (!$this->info) {
 			$this->info = $this->fileView->getFileInfo($this->file);
 		}
+
 		return $this->info;
 	}
 
@@ -208,9 +246,11 @@ class Preview {
 	}
 
 	/**
-	 * set the path of the file you want a thumbnail from
+	 * Sets the path of the file you want a preview of
+	 *
 	 * @param string $file
-	 * @return $this
+	 *
+	 * @return \OC\Preview
 	 */
 	public function setFile($file) {
 		$this->file = $file;
@@ -218,15 +258,17 @@ class Preview {
 
 		if ($file !== '') {
 			$this->getFileInfo();
-			if($this->info instanceof \OCP\Files\FileInfo) {
+			if ($this->info instanceof \OCP\Files\FileInfo) {
 				$this->mimeType = $this->info->getMimetype();
 			}
 		}
+
 		return $this;
 	}
 
 	/**
-	 * set mime type explicitly
+	 * Forces the use of a specific media type
+	 *
 	 * @param string $mimeType
 	 */
 	public function setMimetype($mimeType) {
@@ -234,82 +276,91 @@ class Preview {
 	}
 
 	/**
-	 * set the the max width of the preview
+	 * Sets the max width of the preview. It's capped by the maximum allowed size set in the
+	 * configuration
+	 *
 	 * @param int $maxX
+	 *
 	 * @throws \Exception
-	 * @return \OC\Preview $this
+	 * @return \OC\Preview
 	 */
 	public function setMaxX($maxX = 1) {
 		if ($maxX <= 0) {
 			throw new \Exception('Cannot set width of 0 or smaller!');
 		}
 		$configMaxX = $this->getConfigMaxX();
-		if (!is_null($configMaxX)) {
-			if ($maxX > $configMaxX) {
-				\OCP\Util::writeLog('core', 'maxX reduced from ' . $maxX . ' to ' . $configMaxX, \OCP\Util::DEBUG);
-				$maxX = $configMaxX;
-			}
-		}
+		$maxX = $this->limitMaxDim($maxX, $configMaxX, 'maxX');
 		$this->maxX = $maxX;
+
 		return $this;
 	}
 
 	/**
-	 * set the the max height of the preview
+	 * Sets the max height of the preview. It's capped by the maximum allowed size set in the
+	 * configuration
+	 *
 	 * @param int $maxY
+	 *
 	 * @throws \Exception
-	 * @return \OC\Preview $this
+	 * @return \OC\Preview
 	 */
 	public function setMaxY($maxY = 1) {
 		if ($maxY <= 0) {
 			throw new \Exception('Cannot set height of 0 or smaller!');
 		}
 		$configMaxY = $this->getConfigMaxY();
-		if (!is_null($configMaxY)) {
-			if ($maxY > $configMaxY) {
-				\OCP\Util::writeLog('core', 'maxX reduced from ' . $maxY . ' to ' . $configMaxY, \OCP\Util::DEBUG);
-				$maxY = $configMaxY;
-			}
-		}
+		$maxY = $this->limitMaxDim($maxY, $configMaxY, 'maxY');
 		$this->maxY = $maxY;
+
 		return $this;
 	}
 
 	/**
-	 * set whether or not scalingup is enabled
+	 * Sets whether we're allowed to scale up when generating a preview. It's capped by the maximum
+	 * allowed scale factor set in the configuration
+	 *
 	 * @param bool $scalingUp
-	 * @return \OC\Preview $this
+	 *
+	 * @return \OC\Preview
 	 */
 	public function setScalingup($scalingUp) {
 		if ($this->getMaxScaleFactor() === 1) {
 			$scalingUp = false;
 		}
 		$this->scalingUp = $scalingUp;
+
 		return $this;
 	}
 
 	/**
+	 * Sets whether we need to generate a preview which keeps the aspect ratio of the original file
+	 *
 	 * @param bool $keepAspect
-	 * @return $this
+	 *
+	 * @return \OC\Preview
 	 */
 	public function setKeepAspect($keepAspect) {
 		$this->keepAspect = $keepAspect;
+
 		return $this;
 	}
 
 	/**
-	 * check if all parameters are valid
+	 * Makes sure we were given a file to preview and that it exists in the filesystem
+	 *
 	 * @return bool
 	 */
 	public function isFileValid() {
 		$file = $this->getFile();
 		if ($file === '') {
 			\OCP\Util::writeLog('core', 'No filename passed', \OCP\Util::DEBUG);
+
 			return false;
 		}
 
 		if (!$this->fileView->file_exists($file)) {
 			\OCP\Util::writeLog('core', 'File:"' . $file . '" not found', \OCP\Util::DEBUG);
+
 			return false;
 		}
 
@@ -317,22 +368,28 @@ class Preview {
 	}
 
 	/**
-	 * deletes previews of a file with specific x and y
+	 * Deletes the preview of a file with specific width and height
+	 *
+	 * This should never delete the max preview, use deleteAllPreviews() instead
+	 *
 	 * @return bool
 	 */
 	public function deletePreview() {
 		$fileInfo = $this->getFileInfo();
-		if($fileInfo !== null && $fileInfo !== false) {
+		if ($fileInfo !== null && $fileInfo !== false) {
 			$fileId = $fileInfo->getId();
 
 			$previewPath = $this->buildCachePath($fileId);
-			return $this->userView->unlink($previewPath);
+			if (!strpos($previewPath, 'max')) {
+				return $this->userView->unlink($previewPath);
+			}
 		}
+
 		return false;
 	}
 
 	/**
-	 * deletes all previews of a file
+	 * Deletes all previews of a file
 	 */
 	public function deleteAllPreviews() {
 		$toDelete = $this->getChildren();
@@ -355,93 +412,210 @@ class Preview {
 	}
 
 	/**
-	 * Checks if thumbnail or bigger version of thumbnail of file is already cached
+	 * Checks if a preview matching the asked dimensions or a bigger version is already cached
 	 *
-	 * @param int $fileId fileId of the original image
-	 * @return string|false path to thumbnail if it exists or false
+	 *    * We first retrieve the size of the max preview since this is what we be used to create
+	 * all our preview. If it doesn't exist we return false, so that it can be generated
+	 *    * Using the dimensions of the max preview, we calculate what the size of the new
+	 * thumbnail should be
+	 *    * And finally, we look for a suitable candidate in the cache
+	 *
+	 * @param int $fileId fileId of the original file we need a preview of
+	 *
+	 * @return string|false path to the cached preview if it exists or false
 	 */
 	public function isCached($fileId) {
 		if (is_null($fileId)) {
 			return false;
 		}
 
-		// This gives us a calculated path to a preview of asked dimensions
-		// thumbnailFolder/fileId/my_image-<maxX>-<maxY>.png
-		$preview = $this->buildCachePath($fileId);
+		/**
+		 * Phase 1: Looking for the max preview
+		 */
+		$previewPath = $this->getPreviewPath($fileId);
+		// We currently can't look for a single file due to bugs related to #16478
+		$allThumbnails = $this->userView->getDirectoryContent($previewPath);
+		list($maxPreviewWidth, $maxPreviewHeight) = $this->getMaxPreviewSize($allThumbnails);
+
+		// Only use the cache if we have a max preview
+		if (!is_null($maxPreviewWidth) && !is_null($maxPreviewHeight)) {
+
+			/**
+			 * Phase 2: Calculating the size of the preview we need to send back
+			 */
+			$this->maxPreviewWidth = $maxPreviewWidth;
+			$this->maxPreviewHeight = $maxPreviewHeight;
+
+			list($previewWidth, $previewHeight) = $this->simulatePreviewDimensions();
+			if (empty($previewWidth) || empty($previewHeight)) {
+				return false;
+			}
+
+			$this->previewWidth = $previewWidth;
+			$this->previewHeight = $previewHeight;
+
+			/**
+			 * Phase 3: We look for a preview of the exact size
+			 */
+			// This gives us a calculated path to a preview of asked dimensions
+			// thumbnailFolder/fileId/<maxX>-<maxY>(-max|-with-aspect).png
+			$preview = $this->buildCachePath($fileId, $previewWidth, $previewHeight);
+
+			// This checks if we have a preview of those exact dimensions in the cache
+			if ($this->userView->file_exists($preview)) {
+				return $preview;
+			}
+
+			/**
+			 * Phase 4: We look for a larger preview, matching the aspect ratio
+			 */
+			if (($this->getMaxX() >= $maxPreviewWidth)
+				&& ($this->getMaxY() >= $maxPreviewHeight)
+			) {
+				// The preview we-re looking for is the exact size or larger than the max preview,
+				// so return that
+				return $this->buildCachePath($fileId, $maxPreviewWidth, $maxPreviewHeight);
+			} else {
+				// The last resort is to look for something bigger than what we've calculated,
+				// but still smaller than the max preview
+				return $this->isCachedBigger($fileId, $allThumbnails);
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Returns the dimensions of the max preview
+	 *
+	 * @param FileInfo[] $allThumbnails the list of all our cached thumbnails
+	 *
+	 * @return int[]
+	 */
+	private function getMaxPreviewSize($allThumbnails) {
+		$maxPreviewX = null;
+		$maxPreviewY = null;
+
+		foreach ($allThumbnails as $thumbnail) {
+			$name = $thumbnail['name'];
+			if (strpos($name, 'max')) {
+				list($maxPreviewX, $maxPreviewY) = $this->getDimensionsFromFilename($name);
+				break;
+			}
+		}
+
+		return [$maxPreviewX, $maxPreviewY];
+	}
+
+	/**
+	 * Determines the size of the preview we should be looking for in the cache
+	 *
+	 * @return int[]
+	 */
+	private function simulatePreviewDimensions() {
+		$askedWidth = $this->getMaxX();
+		$askedHeight = $this->getMaxY();
 
-		// This checks if a preview exists at that location
-		if ($this->userView->file_exists($preview)) {
-			return $preview;
+		if ($this->keepAspect) {
+			list($newPreviewWidth, $newPreviewHeight) =
+				$this->applyAspectRatio($askedWidth, $askedHeight);
+		} else {
+			list($newPreviewWidth, $newPreviewHeight) = $this->fixSize($askedWidth, $askedHeight);
 		}
 
-		return $this->isCachedBigger($fileId);
+		return [(int)$newPreviewWidth, (int)$newPreviewHeight];
 	}
 
 	/**
-	 * Checks if a bigger version of a file preview is cached and if not
-	 * return the preview of max allowed dimensions
+	 * Resizes the boundaries to match the aspect ratio
 	 *
-	 * @param int $fileId fileId of the original image
+	 * @param int $askedWidth
+	 * @param int $askedHeight
 	 *
-	 * @return string|false path to bigger thumbnail if it exists or false
+	 * @return \int[]
 	 */
-	private function isCachedBigger($fileId) {
+	private function applyAspectRatio($askedWidth, $askedHeight) {
+		$originalRatio = $this->maxPreviewWidth / $this->maxPreviewHeight;
+		// Defines the box in which the preview has to fit
+		$scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1;
+		$askedWidth = min($askedWidth, $this->maxPreviewWidth * $scaleFactor);
+		$askedHeight = min($askedHeight, $this->maxPreviewHeight * $scaleFactor);
+
+		if ($askedWidth / $originalRatio < $askedHeight) {
+			// width restricted
+			$askedHeight = round($askedWidth / $originalRatio);
+		} else {
+			$askedWidth = round($askedHeight * $originalRatio);
+		}
 
-		if (is_null($fileId)) {
-			return false;
+		return [(int)$askedWidth, (int)$askedHeight];
+	}
+
+	/**
+	 * Makes sure an upscaled preview doesn't end up larger than the max dimensions defined in the
+	 * config
+	 *
+	 * @param int $askedWidth
+	 * @param int $askedHeight
+	 *
+	 * @return \int[]
+	 */
+	private function fixSize($askedWidth, $askedHeight) {
+		if ($this->scalingUp) {
+			$askedWidth = min($this->configMaxWidth, $askedWidth);
+			$askedHeight = min($this->configMaxHeight, $askedHeight);
 		}
 
+		return [(int)$askedWidth, (int)$askedHeight];
+	}
+
+	/**
+	 * Checks if a bigger version of a file preview is cached and if not
+	 * return the preview of max allowed dimensions
+	 *
+	 * @param int $fileId fileId of the original image
+	 * @param FileInfo[] $allThumbnails the list of all our cached thumbnails
+	 *
+	 * @return string path to bigger thumbnail
+	 */
+	private function isCachedBigger($fileId, $allThumbnails) {
+		// This is used to eliminate any thumbnail narrower than what we need
 		$maxX = $this->getMaxX();
 
 		//array for usable cached thumbnails
-		// FIXME: Checking only the width could lead to issues
-		$possibleThumbnails = $this->getPossibleThumbnails($fileId);
+		$possibleThumbnails = $this->getPossibleThumbnails($allThumbnails);
 
 		foreach ($possibleThumbnails as $width => $path) {
-			if ($width === 'max' || $width < $maxX) {
+			if ($width < $maxX) {
 				continue;
 			} else {
 				return $path;
 			}
 		}
 
-		// At this stage, we didn't find a preview, so if the folder is not empty,
-		// we return the max preview we generated on the first run
-		if ($possibleThumbnails) {
-			return $possibleThumbnails['max'];
-		}
-
-		return false;
+		// At this stage, we didn't find a preview, so we return the max preview
+		return $this->buildCachePath($fileId, $this->maxPreviewWidth, $this->maxPreviewHeight);
 	}
 
 	/**
-	 * get possible bigger thumbnails of the given image
-	 * @param int $fileId fileId of the original image
-	 * @return array an array of paths to bigger thumbnails
-	*/
-	private function getPossibleThumbnails($fileId) {
-
-		if (is_null($fileId)) {
-			return array();
+	 * Get possible bigger thumbnails of the given image with the proper aspect ratio
+	 *
+	 * @param FileInfo[] $allThumbnails the list of all our cached thumbnails
+	 *
+	 * @return string[] an array of paths to bigger thumbnails
+	 */
+	private function getPossibleThumbnails($allThumbnails) {
+		if ($this->keepAspect) {
+			$wantedAspectRatio = (float)($this->maxPreviewWidth / $this->maxPreviewHeight);
+		} else {
+			$wantedAspectRatio = (float)($this->getMaxX() / $this->getMaxY());
 		}
 
-		$previewPath = $this->getPreviewPath($fileId);
-
-		$wantedAspectRatio = (float)($this->getMaxX() / $this->getMaxY());
-
 		//array for usable cached thumbnails
 		$possibleThumbnails = array();
-
-		$allThumbnails = $this->userView->getDirectoryContent($previewPath);
 		foreach ($allThumbnails as $thumbnail) {
 			$name = rtrim($thumbnail['name'], '.png');
-			// Always add the max preview to the array
-			if (strpos($name, 'max')) {
-				$possibleThumbnails['max'] = $thumbnail['path'];
-				continue;
-			}
 			list($x, $y, $aspectRatio) = $this->getDimensionsFromFilename($name);
-
 			if (abs($aspectRatio - $wantedAspectRatio) >= 0.000001
 				|| $this->unscalable($x, $y)
 			) {
@@ -456,20 +630,25 @@ class Preview {
 	}
 
 	/**
+	 * Looks at the preview filename from the cache and extracts the size of the preview
+	 *
 	 * @param string $name
-	 * @return array
+	 *
+	 * @return array<int,int,float>
 	 */
 	private function getDimensionsFromFilename($name) {
-			$size = explode('-', $name);
-			$x = (int) $size[0];
-			$y = (int) $size[1];
-			$aspectRatio = (float) ($x / $y);
-			return array($x, $y, $aspectRatio);
+		$size = explode('-', $name);
+		$x = (int)$size[0];
+		$y = (int)$size[1];
+		$aspectRatio = (float)($x / $y);
+
+		return array($x, $y, $aspectRatio);
 	}
 
 	/**
 	 * @param int $x
 	 * @param int $y
+	 *
 	 * @return bool
 	 */
 	private function unscalable($x, $y) {
@@ -481,14 +660,15 @@ class Preview {
 
 		if ($x < $maxX || $y < $maxY) {
 			if ($scalingUp) {
-				$scalefactor = $maxX / $x;
-				if ($scalefactor > $maxScaleFactor) {
+				$scaleFactor = $maxX / $x;
+				if ($scaleFactor > $maxScaleFactor) {
 					return true;
 				}
 			} else {
 				return true;
 			}
 		}
+
 		return false;
 	}
 
@@ -530,12 +710,15 @@ class Preview {
 	}
 
 	/**
+	 * Sends the preview, including the headers to client which requested it
+	 *
 	 * @param null|string $mimeType
+	 *
 	 * @throws NotFoundException
 	 */
 	public function showPreview($mimeType = null) {
 		// Check if file is valid
-		if($this->isFileValid() === false) {
+		if ($this->isFileValid() === false) {
 			throw new NotFoundException('File not found.');
 		}
 
@@ -563,13 +746,18 @@ class Preview {
 
 			$this->preview = $image->valid() ? $image : null;
 
-			$maxX = (int)$this->getMaxX();
-			$maxY = (int)$this->getMaxY();
-			$previewX = (int)$this->preview->width();
-			$previewY = (int)$this->preview->height();
-
-			if ($previewX !== $maxX && $previewY !== $maxY) {
-				$this->resizeAndStore($fileId);
+			if (!is_null($this->preview)) {
+				// Size of the preview we calculated
+				$maxX = $this->previewWidth;
+				$maxY = $this->previewHeight;
+				// Size of the preview we retrieved from the cache
+				$previewX = (int)$this->preview->width();
+				$previewY = (int)$this->preview->height();
+
+				// We don't have an exact match
+				if ($previewX !== $maxX || $previewY !== $maxY) {
+					$this->resizeAndStore($fileId);
+				}
 			}
 
 			fclose($stream);
@@ -582,50 +770,89 @@ class Preview {
 	 * @param int $fileId fileId of the original image
 	 */
 	private function resizeAndStore($fileId) {
-		// Resize and store
-		$this->resizeAndCrop();
-		// We save a copy in the cache to speed up future calls
-		$cachePath = $this->buildCachePath($fileId);
-		$this->userView->file_put_contents($cachePath, $this->preview->data());
-	}
-
-	/**
-	 * resize, crop and fix orientation
-	 *
-	 * @param bool $max
-	 */
-	private function resizeAndCrop($max = false) {
 		$image = $this->preview;
+		if (!($image instanceof \OCP\IImage)) {
+			\OCP\Util::writeLog(
+				'core', '$this->preview is not an instance of \OCP\IImage', \OCP\Util::DEBUG
+			);
 
-		list($x, $y, $scalingUp, $maxScaleFactor) = $this->getResizeData($max);
+			return;
+		}
+		$previewWidth = (int)$image->width();
+		$previewHeight = (int)$image->height();
+		$askedWidth = $this->getMaxX();
+		$askedHeight = $this->getMaxY();
+
+		/**
+		 * Phase 1: If required, adjust boundaries to keep aspect ratio
+		 */
+		if ($this->keepAspect) {
+			list($askedWidth, $askedHeight) =
+				$this->applyAspectRatio($askedWidth, $askedHeight);
+		}
+
+		/**
+		 * Phase 2: Resizes preview to try and match requirements.
+		 * Takes the scaling ratio into consideration
+		 */
+		list($newPreviewWidth, $newPreviewHeight) = $this->scale(
+			$image, $askedWidth, $askedHeight, $previewWidth, $previewHeight
+		);
+
+		// The preview has been resized and should now have the asked dimensions
+		if ($newPreviewWidth === $askedWidth && $newPreviewHeight === $askedHeight) {
+			$this->storePreview($fileId, $newPreviewWidth, $newPreviewHeight);
 
-		if (!($image instanceof \OCP\IImage)) {
-			\OCP\Util::writeLog('core', '$this->preview is not an instance of OC_Image', \OCP\Util::DEBUG);
 			return;
 		}
 
-		$realX = (int)$image->width();
-		$realY = (int)$image->height();
+		/**
+		 * Phase 3: We're still not there yet, so we're clipping and filling
+		 * to match the asked dimensions
+		 */
+		// It turns out the scaled preview is now too big, so we crop the image
+		if ($newPreviewWidth >= $askedWidth && $newPreviewHeight >= $askedHeight) {
+			list($newPreviewWidth, $newPreviewHeight) =
+				$this->crop($image, $askedWidth, $askedHeight, $newPreviewWidth, $newPreviewHeight);
+			$this->storePreview($fileId, $newPreviewWidth, $newPreviewHeight);
 
-		// compute $maxY and $maxX using the aspect of the generated preview
-		if ($this->keepAspect) {
-			$ratio = $realX / $realY;
-			if($x / $ratio < $y) {
-				// width restricted
-				$y = $x / $ratio;
-			} else {
-				$x = $y * $ratio;
-			}
+			return;
 		}
 
-		// The preview already has the asked dimensions
-		if ($x === $realX && $y === $realY) {
-			$this->preview = $image;
+		// At least one dimension of the scaled preview is too small,
+		// so we fill the space with a transparent background
+		if (($newPreviewWidth < $askedWidth || $newPreviewHeight < $askedHeight)) {
+			list($newPreviewWidth, $newPreviewHeight) =
+				$this->cropAndFill(
+					$image, $askedWidth, $askedHeight, $newPreviewWidth, $newPreviewHeight
+				);
+			$this->storePreview($fileId, $newPreviewWidth, $newPreviewHeight);
+
 			return;
 		}
+		// The preview is smaller, but we can't touch it
+		$this->storePreview($fileId, $newPreviewWidth, $newPreviewHeight);
+	}
 
-		$factorX = $x / $realX;
-		$factorY = $y / $realY;
+	/**
+	 * Calculates the new dimensions of the preview
+	 *
+	 * The new dimensions can be larger or smaller than the ones of the preview we have to resize
+	 *
+	 * @param \OCP\IImage $image
+	 * @param int $askedWidth
+	 * @param int $askedHeight
+	 * @param int $previewWidth
+	 * @param null $previewHeight
+	 *
+	 * @return int[]
+	 */
+	private function scale($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight) {
+		$scalingUp = $this->getScalingUp();
+		$maxScaleFactor = $this->getMaxScaleFactor();
+
+		$factorX = $askedWidth / $previewWidth;
+		$factorY = $askedHeight / $previewHeight;
 
 		if ($factorX >= $factorY) {
 			$factor = $factorX;
@@ -639,112 +866,145 @@ class Preview {
 			}
 		}
 
+		// We cap when upscaling
 		if (!is_null($maxScaleFactor)) {
 			if ($factor > $maxScaleFactor) {
-				\OCP\Util::writeLog('core', 'scale factor reduced from ' . $factor . ' to ' . $maxScaleFactor, \OCP\Util::DEBUG);
+				\OCP\Util::writeLog(
+					'core', 'scale factor reduced from ' . $factor . ' to ' . $maxScaleFactor,
+					\OCP\Util::DEBUG
+				);
 				$factor = $maxScaleFactor;
 			}
 		}
 
-		$newXSize = (int)($realX * $factor);
-		$newYSize = (int)($realY * $factor);
-
-		$image->preciseResize($newXSize, $newYSize);
+		$newPreviewWidth = round($previewWidth * $factor);
+		$newPreviewHeight = round($previewHeight * $factor);
 
-		// The preview has been upscaled and now has the asked dimensions
-		if ($newXSize === $x && $newYSize === $y) {
-			$this->preview = $image;
-			return;
-		}
+		$image->preciseResize($newPreviewWidth, $newPreviewHeight);
+		$this->preview = $image;
 
-		// One dimension of the upscaled preview is too big
-		if ($newXSize >= $x && $newYSize >= $y) {
-			$cropX = floor(abs($x - $newXSize) * 0.5);
-			//don't crop previews on the Y axis, this sucks if it's a document.
-			//$cropY = floor(abs($y - $newYsize) * 0.5);
-			$cropY = 0;
+		return [$newPreviewWidth, $newPreviewHeight];
+	}
 
-			$image->crop($cropX, $cropY, $x, $y);
+	/**
+	 * Crops a preview which is larger than the dimensions we've received
+	 *
+	 * @param \OCP\IImage $image
+	 * @param int $askedWidth
+	 * @param int $askedHeight
+	 * @param int $previewWidth
+	 * @param null $previewHeight
+	 *
+	 * @return \int[]
+	 */
+	private function crop($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight = null) {
+		$cropX = floor(abs($askedWidth - $previewWidth) * 0.5);
+		//don't crop previews on the Y axis, this sucks if it's a document.
+		//$cropY = floor(abs($y - $newPreviewHeight) * 0.5);
+		$cropY = 0;
+		$image->crop($cropX, $cropY, $askedWidth, $askedHeight);
+		$this->preview = $image;
+
+		return [$askedWidth, $askedHeight];
+	}
 
-			$this->preview = $image;
-			return;
+	/**
+	 * Crops an image if it's larger than the dimensions we've received and fills the empty space
+	 * with a transparent background
+	 *
+	 * @param \OCP\IImage $image
+	 * @param int $askedWidth
+	 * @param int $askedHeight
+	 * @param int $previewWidth
+	 * @param null $previewHeight
+	 *
+	 * @return \int[]
+	 */
+	private function cropAndFill($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight) {
+		if ($previewWidth > $askedWidth) {
+			$cropX = floor(($previewWidth - $askedWidth) * 0.5);
+			$image->crop($cropX, 0, $askedWidth, $previewHeight);
+			$previewWidth = $askedWidth;
 		}
 
-		// One dimension of the upscaled preview is too small and we're allowed to scale up
-		if (($newXSize < $x || $newYSize < $y) && $scalingUp) {
-			if ($newXSize > $x) {
-				$cropX = floor(($newXSize - $x) * 0.5);
-				$image->crop($cropX, 0, $x, $newYSize);
-			}
-
-			if ($newYSize > $y) {
-				$cropY = floor(($newYSize - $y) * 0.5);
-				$image->crop(0, $cropY, $newXSize, $y);
-			}
-
-			$newXSize = (int)$image->width();
-			$newYSize = (int)$image->height();
-
-			//create transparent background layer
-			$backgroundLayer = imagecreatetruecolor($x, $y);
-			$white = imagecolorallocate($backgroundLayer, 255, 255, 255);
-			imagefill($backgroundLayer, 0, 0, $white);
+		if ($previewHeight > $askedHeight) {
+			$cropY = floor(($previewHeight - $askedHeight) * 0.5);
+			$image->crop(0, $cropY, $previewWidth, $askedHeight);
+			$previewHeight = $askedHeight;
+		}
 
-			$image = $image->resource();
+		// Creates a transparent background
+		$backgroundLayer = imagecreatetruecolor($askedWidth, $askedHeight);
+		imagealphablending($backgroundLayer, false);
+		$transparency = imagecolorallocatealpha($backgroundLayer, 0, 0, 0, 127);
+		imagefill($backgroundLayer, 0, 0, $transparency);
+		imagesavealpha($backgroundLayer, true);
 
-			$mergeX = floor(abs($x - $newXSize) * 0.5);
-			$mergeY = floor(abs($y - $newYSize) * 0.5);
+		$image = $image->resource();
 
-			imagecopy($backgroundLayer, $image, $mergeX, $mergeY, 0, 0, $newXSize, $newYSize);
+		$mergeX = floor(abs($askedWidth - $previewWidth) * 0.5);
+		$mergeY = floor(abs($askedHeight - $previewHeight) * 0.5);
 
-			//$black = imagecolorallocate(0,0,0);
-			//imagecolortransparent($transparentlayer, $black);
+		// Pastes the preview on top of the background
+		imagecopy(
+			$backgroundLayer, $image, $mergeX, $mergeY, 0, 0, $previewWidth,
+			$previewHeight
+		);
 
-			$image = new \OC_Image($backgroundLayer);
+		$image = new \OC_Image($backgroundLayer);
 
-			$this->preview = $image;
+		$this->preview = $image;
 
-			return;
-		}
+		return [$askedWidth, $askedHeight];
 	}
 
 	/**
-	 * Returns data to be used to resize a preview
+	 * Saves a preview in the cache to speed up future calls
 	 *
-	 * @param $max
+	 * Do not nullify the preview as it might send the whole process in a loop
 	 *
-	 * @return array
+	 * @param int $fileId fileId of the original image
+	 * @param int $previewWidth
+	 * @param int $previewHeight
 	 */
-	private function getResizeData($max) {
-		if (!$max) {
-			$x = $this->getMaxX();
-			$y = $this->getMaxY();
-			$scalingUp = $this->getScalingUp();
-			$maxScaleFactor = $this->getMaxScaleFactor();
+	private function storePreview($fileId, $previewWidth, $previewHeight) {
+		if (empty($previewWidth) || empty($previewHeight)) {
+			\OCP\Util::writeLog(
+				'core', 'Cannot save preview of dimension ' . $previewWidth . 'x' . $previewHeight,
+				\OCP\Util::DEBUG
+			);
+
 		} else {
-			$x = $this->configMaxX;
-			$y = $this->configMaxY;
-			$scalingUp = false;
-			$maxScaleFactor =1;
+			$cachePath = $this->buildCachePath($fileId, $previewWidth, $previewHeight);
+			$this->userView->file_put_contents($cachePath, $this->preview->data());
 		}
-
-		return [$x, $y, $scalingUp, $maxScaleFactor];
 	}
 
 	/**
 	 * Returns the path to a preview based on its dimensions and aspect
 	 *
 	 * @param int $fileId
+	 * @param int|null $maxX
+	 * @param int|null $maxY
 	 *
 	 * @return string
 	 */
-	private function buildCachePath($fileId) {
-		$maxX = $this->getMaxX();
-		$maxY = $this->getMaxY();
+	private function buildCachePath($fileId, $maxX = null, $maxY = null) {
+		if (is_null($maxX)) {
+			$maxX = $this->getMaxX();
+		}
+		if (is_null($maxY)) {
+			$maxY = $this->getMaxY();
+		}
 
 		$previewPath = $this->getPreviewPath($fileId);
 		$previewPath = $previewPath . strval($maxX) . '-' . strval($maxY);
-		if ($this->keepAspect) {
+		$isMaxPreview =
+			($maxX === $this->maxPreviewWidth && $maxY === $this->maxPreviewHeight) ? true : false;
+		if ($isMaxPreview) {
+			$previewPath .= '-max';
+		}
+		if ($this->keepAspect && !$isMaxPreview) {
 			$previewPath .= '-with-aspect';
 		}
 		$previewPath .= '.png';
@@ -753,6 +1013,8 @@ class Preview {
 	}
 
 	/**
+	 * Returns the path to the folder where the previews are stored, identified by the fileId
+	 *
 	 * @param int $fileId
 	 *
 	 * @return string
@@ -762,12 +1024,14 @@ class Preview {
 	}
 
 	/**
-	 * Asks the provider to send a preview of the file of maximum dimensions
-	 * and after saving it in the cache, it is then resized to the asked dimensions
+	 * Asks the provider to send a preview of the file which respects the maximum dimensions
+	 * defined in the configuration and after saving it in the cache, it is then resized to the
+	 * asked dimensions
 	 *
 	 * This is only called once in order to generate a large PNG of dimensions defined in the
 	 * configuration file. We'll be able to quickly resize it later on.
-	 * We never upscale the original conversion as this will be done later by the resizing operation
+	 * We never upscale the original conversion as this will be done later by the resizing
+	 * operation
 	 *
 	 * @param int $fileId fileId of the original image
 	 */
@@ -775,49 +1039,51 @@ class Preview {
 		$file = $this->getFile();
 		$preview = null;
 
-		$previewProviders = \OC::$server->getPreviewManager()->getProviders();
-			foreach ($previewProviders as $supportedMimeType => $providers) {
-				if (!preg_match($supportedMimeType, $this->mimeType)) {
+		$previewProviders = \OC::$server->getPreviewManager()
+										->getProviders();
+		foreach ($previewProviders as $supportedMimeType => $providers) {
+			if (!preg_match($supportedMimeType, $this->mimeType)) {
+				continue;
+			}
+
+			foreach ($providers as $closure) {
+				$provider = $closure();
+				if (!($provider instanceof \OCP\Preview\IProvider)) {
 					continue;
 				}
 
-				foreach ($providers as $closure) {
-					$provider = $closure();
-					if (!($provider instanceof \OCP\Preview\IProvider)) {
-						continue;
-					}
-
-					\OCP\Util::writeLog(
-						'core', 'Generating preview for "' . $file . '" with "' . get_class($provider)
-						. '"', \OCP\Util::DEBUG
-					);
+				\OCP\Util::writeLog(
+					'core', 'Generating preview for "' . $file . '" with "' . get_class($provider)
+							. '"', \OCP\Util::DEBUG
+				);
 
-					/** @var $provider Provider */
-					$preview = $provider->getThumbnail(
-						$file, $this->configMaxX, $this->configMaxY, $scalingUp = false, $this->fileView
-					);
+				/** @var $provider Provider */
+				$preview = $provider->getThumbnail(
+					$file, $this->configMaxWidth, $this->configMaxHeight, $scalingUp = false,
+					$this->fileView
+				);
 
-					if (!($preview instanceof \OCP\IImage)) {
-						continue;
-					}
+				if (!($preview instanceof \OCP\IImage)) {
+					continue;
+				}
 
-					$this->preview = $preview;
-					$previewPath = $this->getPreviewPath($fileId);
+				$this->preview = $preview;
+				$previewPath = $this->getPreviewPath($fileId);
 
-					if ($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) {
-						$this->userView->mkdir($this->getThumbnailsFolder() . '/');
-					}
+				if ($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) {
+					$this->userView->mkdir($this->getThumbnailsFolder() . '/');
+				}
 
-					if ($this->userView->is_dir($previewPath) === false) {
-						$this->userView->mkdir($previewPath);
-					}
+				if ($this->userView->is_dir($previewPath) === false) {
+					$this->userView->mkdir($previewPath);
+				}
 
-					// This stores our large preview so that it can be used in subsequent resizing requests
-					$this->storeMaxPreview($previewPath);
+				// This stores our large preview so that it can be used in subsequent resizing requests
+				$this->storeMaxPreview($previewPath);
 
-					break 2;
-				}
+				break 2;
 			}
+		}
 
 		// The providers have been kind enough to give us a preview
 		if ($preview) {
@@ -831,7 +1097,7 @@ class Preview {
 	 * @param string $previewPath path to the preview
 	 */
 	private function storeMaxPreview($previewPath) {
-		$maxPreview = false;
+		$maxPreviewExists = false;
 		$preview = $this->preview;
 
 		$allThumbnails = $this->userView->getDirectoryContent($previewPath);
@@ -840,24 +1106,45 @@ class Preview {
 		foreach ($allThumbnails as $thumbnail) {
 			$name = rtrim($thumbnail['name'], '.png');
 			if (strpos($name, 'max')) {
-				$maxPreview = true;
+				$maxPreviewExists = true;
 				break;
 			}
 		}
 		// We haven't found the max preview, so we create it
-		if (!$maxPreview) {
-			// Most providers don't resize their thumbnails yet
-			$this->resizeAndCrop(true);
-
-			$maxX = $preview->width();
-			$maxY = $preview->height();
-			$previewPath = $previewPath . strval($maxX) . '-' . strval($maxY);
+		if (!$maxPreviewExists) {
+			$previewWidth = $preview->width();
+			$previewHeight = $preview->height();
+			$previewPath = $previewPath . strval($previewWidth) . '-' . strval($previewHeight);
 			$previewPath .= '-max.png';
 			$this->userView->file_put_contents($previewPath, $preview->data());
+			$this->maxPreviewWidth = $previewWidth;
+			$this->maxPreviewHeight = $previewHeight;
 		}
 	}
 
 	/**
+	 * Limits a dimension to the maximum dimension provided as argument
+	 *
+	 * @param int $dim
+	 * @param int $maxDim
+	 * @param string $dimName
+	 *
+	 * @return mixed
+	 */
+	private function limitMaxDim($dim, $maxDim, $dimName) {
+		if (!is_null($maxDim)) {
+			if ($dim > $maxDim) {
+				\OCP\Util::writeLog(
+					'core', $dimName . ' reduced from ' . $dim . ' to ' . $maxDim, \OCP\Util::DEBUG
+				);
+				$dim = $maxDim;
+			}
+		}
+
+		return $dim;
+	}
+
+	/**
 	 * @param array $args
 	 */
 	public static function post_write($args) {
@@ -875,7 +1162,7 @@ class Preview {
 	 * @param array $args
 	 * @param string $prefix
 	 */
-	public static function prepare_delete($args, $prefix='') {
+	public static function prepare_delete($args, $prefix = '') {
 		$path = $args['path'];
 		if (substr($path, 0, 1) === '/') {
 			$path = substr($path, 1);
@@ -902,6 +1189,7 @@ class Preview {
 	/**
 	 * @param \OC\Files\View $view
 	 * @param string $path
+	 *
 	 * @return array
 	 */
 	private static function getAllChildren($view, $path) {
@@ -939,7 +1227,7 @@ class Preview {
 	 * @param array $args
 	 * @param string $prefix
 	 */
-	public static function post_delete($args, $prefix='') {
+	public static function post_delete($args, $prefix = '') {
 		$path = Files\Filesystem::normalizePath($args['path']);
 
 		$preview = new Preview(\OC_User::getUser(), $prefix, $path);
diff --git a/lib/private/preview/image.php b/lib/private/preview/image.php
index 2c69d29..dbaf5de 100644
--- a/lib/private/preview/image.php
+++ b/lib/private/preview/image.php
@@ -33,7 +33,7 @@ abstract class Image extends Provider {
 	public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
 		//get fileinfo
 		$fileInfo = $fileview->getFileInfo($path);
-		if(!$fileInfo) {
+		if (!$fileInfo) {
 			return false;
 		}
 
@@ -46,15 +46,19 @@ abstract class Image extends Provider {
 
 		$image = new \OC_Image();
 
-		if($fileInfo['encrypted'] === true) {
+		if ($fileInfo['encrypted'] === true) {
 			$fileName = $fileview->toTmpFile($path);
 		} else {
 			$fileName = $fileview->getLocalFile($path);
 		}
 		$image->loadFromFile($fileName);
 		$image->fixOrientation();
+		if ($image->valid()) {
+			$image->scaleDownToFit($maxX, $maxY);
 
-		return $image->valid() ? $image : false;
+			return $image;
+		}
+		return false;
 	}
 
 }
diff --git a/lib/private/preview/movie.php b/lib/private/preview/movie.php
index 1773f91..43e49bf 100644
--- a/lib/private/preview/movie.php
+++ b/lib/private/preview/movie.php
@@ -91,7 +91,6 @@ class Movie extends Provider {
 			$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';
 		}
@@ -102,7 +101,11 @@ class Movie extends Provider {
 			$image = new \OC_Image();
 			$image->loadFromFile($tmpPath);
 			unlink($tmpPath);
-			return $image->valid() ? $image : false;
+			if ($image->valid()) {
+				$image->scaleDownToFit($maxX, $maxY);
+
+				return $image;
+			}
 		}
 		unlink($tmpPath);
 		return false;
diff --git a/lib/private/preview/mp3.php b/lib/private/preview/mp3.php
index ebeb5b9..49667d0 100644
--- a/lib/private/preview/mp3.php
+++ b/lib/private/preview/mp3.php
@@ -47,7 +47,12 @@ class MP3 extends Provider {
 			unlink($tmpPath);
 			$image = new \OC_Image();
 			$image->loadFromData($picture);
-			return $image->valid() ? $image : $this->getNoCoverThumbnail();
+
+			if ($image->valid()) {
+				$image->scaleDownToFit($maxX, $maxY);
+
+				return $image;
+			}
 		}
 
 		return $this->getNoCoverThumbnail();
diff --git a/lib/private/preview/office.php b/lib/private/preview/office.php
index 54c0c26..0a61a32 100644
--- a/lib/private/preview/office.php
+++ b/lib/private/preview/office.php
@@ -29,7 +29,7 @@ abstract class Office extends Provider {
 	 */
 	public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
 		$this->initCmd();
-		if(is_null($this->cmd)) {
+		if (is_null($this->cmd)) {
 			return false;
 		}
 
@@ -37,7 +37,7 @@ abstract class Office extends Provider {
 
 		$tmpDir = get_temp_dir();
 
-		$defaultParameters = ' -env:UserInstallation=file://' . escapeshellarg($tmpDir . '/owncloud-' . \OC_Util::getInstanceId().'/') . ' --headless --nologo --nofirststartwizard --invisible --norestore --convert-to pdf --outdir ';
+		$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);
@@ -46,8 +46,8 @@ abstract class Office extends Provider {
 
 		//create imagick object from pdf
 		$pdfPreview = null;
-		try{
-			list( $dirname, , , $filename ) = array_values( pathinfo($absPath) );
+		try {
+			list($dirname, , , $filename) = array_values(pathinfo($absPath));
 			$pdfPreview = $dirname . '/' . $filename . '.pdf';
 
 			$pdf = new \imagick($pdfPreview . '[0]');
@@ -65,27 +65,33 @@ abstract class Office extends Provider {
 		unlink($absPath);
 		unlink($pdfPreview);
 
-		return $image->valid() ? $image : false;
+		if ($image->valid()) {
+			$image->scaleDownToFit($maxX, $maxY);
+
+			return $image;
+		}
+		return false;
+
 	}
 
 	private function initCmd() {
 		$cmd = '';
 
-		if(is_string(\OC_Config::getValue('preview_libreoffice_path', null))) {
+		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)) {
+		if ($cmd === '' && !empty($whichLibreOffice)) {
 			$cmd = 'libreoffice';
 		}
 
 		$whichOpenOffice = shell_exec('command -v openoffice');
-		if($cmd === '' && !empty($whichOpenOffice)) {
+		if ($cmd === '' && !empty($whichOpenOffice)) {
 			$cmd = 'openoffice';
 		}
 
-		if($cmd === '') {
+		if ($cmd === '') {
 			$cmd = null;
 		}
 
diff --git a/lib/private/preview/provider.php b/lib/private/preview/provider.php
index ade91b8..ed1f3a1 100644
--- a/lib/private/preview/provider.php
+++ b/lib/private/preview/provider.php
@@ -1,7 +1,6 @@
 <?php
 /**
  * @author Georg Ehrke <georg at owncloud.com>
- * @author Georg Ehrke <georg at ownCloud.com>
  * @author Joas Schilling <nickvergessen at owncloud.com>
  * @author Jörn Friedrich Dreyer <jfd at butonic.de>
  * @author Robin Appelman <icewind at owncloud.com>
@@ -54,7 +53,8 @@ abstract class Provider implements IProvider {
 	}
 
 	/**
-	 * get thumbnail for file at path $path
+	 * Generates thumbnail which fits in $maxX and $maxY and keeps the aspect ratio, for file at path $path
+	 *
 	 * @param string $path Path of file
 	 * @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
diff --git a/lib/private/preview/svg.php b/lib/private/preview/svg.php
index 7c74fb6..92d21c0 100644
--- a/lib/private/preview/svg.php
+++ b/lib/private/preview/svg.php
@@ -35,17 +35,17 @@ class SVG extends Provider {
 	 * {@inheritDoc}
 	 */
 	public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
-		try{
+		try {
 			$svg = new \Imagick();
 			$svg->setBackgroundColor(new \ImagickPixel('transparent'));
 
 			$content = stream_get_contents($fileview->fopen($path, 'r'));
-			if(substr($content, 0, 5) !== '<?xml') {
+			if (substr($content, 0, 5) !== '<?xml') {
 				$content = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . $content;
 			}
 
 			// Do not parse SVG files with references
-			if(stripos($content, 'xlink:href') !== false) {
+			if (stripos($content, 'xlink:href') !== false) {
 				return false;
 			}
 
@@ -60,6 +60,11 @@ class SVG extends Provider {
 		$image = new \OC_Image();
 		$image->loadFromData($svg);
 		//check if image object is valid
-		return $image->valid() ? $image : false;
+		if ($image->valid()) {
+			$image->scaleDownToFit($maxX, $maxY);
+
+			return $image;
+		}
+		return false;
 	}
 }
diff --git a/lib/public/iimage.php b/lib/public/iimage.php
index 202adea..c62bf36 100644
--- a/lib/public/iimage.php
+++ b/lib/public/iimage.php
@@ -170,4 +170,14 @@ interface IImage {
 	 * @since 8.1.0
 	 */
 	public function fitIn($maxWidth, $maxHeight);
+
+	/**
+	 * Shrinks the image to fit within a boundary while preserving ratio.
+	 *
+	 * @param integer $maxWidth
+	 * @param integer $maxHeight
+	 * @return bool
+	 * @since 8.1.0
+	 */
+	public function scaleDownToFit($maxWidth, $maxHeight);
 }
diff --git a/tests/data/testimage-wide.png b/tests/data/testimage-wide.png
new file mode 100644
index 0000000..144e093
Binary files /dev/null and b/tests/data/testimage-wide.png differ
diff --git a/tests/data/testimage.eps b/tests/data/testimage.eps
new file mode 100644
index 0000000..8fa3ba4
--- /dev/null
+++ b/tests/data/testimage.eps
@@ -0,0 +1,175 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%Creator: cairo 1.14.1 (http://cairographics.org)
+%%CreationDate: Fri May 15 16:27:38 2015
+%%Pages: 1
+%%DocumentData: Clean7Bit
+%%LanguageLevel: 2
+%%BoundingBox: 0 -1 2400 1706
+%%EndComments
+%%BeginProlog
+save
+50 dict begin
+/q { gsave } bind def
+/Q { grestore } bind def
+/cm { 6 array astore concat } bind def
+/w { setlinewidth } bind def
+/J { setlinecap } bind def
+/j { setlinejoin } bind def
+/M { setmiterlimit } bind def
+/d { setdash } bind def
+/m { moveto } bind def
+/l { lineto } bind def
+/c { curveto } bind def
+/h { closepath } bind def
+/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto
+      0 exch rlineto 0 rlineto closepath } bind def
+/S { stroke } bind def
+/f { fill } bind def
+/f* { eofill } bind def
+/n { newpath } bind def
+/W { clip } bind def
+/W* { eoclip } bind def
+/BT { } bind def
+/ET { } bind def
+/pdfmark where { pop globaldict /?pdfmark /exec load put }
+    { globaldict begin /?pdfmark /pop load def /pdfmark
+    /cleartomark load def end } ifelse
+/BDC { mark 3 1 roll /BDC pdfmark } bind def
+/EMC { mark /EMC pdfmark } bind def
+/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def
+/Tj { show currentpoint cairo_store_point } bind def
+/TJ {
+  {
+    dup
+    type /stringtype eq
+    { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse
+  } forall
+  currentpoint cairo_store_point
+} bind def
+/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore
+    cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def
+/Tf { pop /cairo_font exch def /cairo_font_matrix where
+      { pop cairo_selectfont } if } bind def
+/Td { matrix translate cairo_font_matrix matrix concatmatrix dup
+      /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point
+      /cairo_font where { pop cairo_selectfont } if } bind def
+/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def
+      cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def
+/g { setgray } bind def
+/rg { setrgbcolor } bind def
+/d1 { setcachedevice } bind def
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+%%PageBoundingBox: 0 -1 2400 1706
+%%EndPageSetup
+q 0 -1 2400 1707 rectclip q
+0.113725 0.176471 0.266667 rg
+0 1705.263 2400 -1705.262 re f
+1 g
+1348.539 1275.525 m 1269.973 1275.525 1206.5 1212.052 1206.5 1133.486 c
+ 1206.5 1101.099 1217.285 1071.294 1235.465 1047.427 c 1274.906 1093.076
+ 1333.117 1122.064 1398.113 1122.064 c 1429.914 1122.064 1460.066 1114.986
+ 1487.238 1102.572 c 1489.438 1112.517 1490.578 1122.865 1490.578 1133.486
+ c 1490.578 1212.052 1427.105 1275.525 1348.539 1275.525 c h
+1163.055 1209.794 m 1122.137 1209.794 1089.246 1176.626 1089.246 1135.712
+ c 1089.246 1122.466 1092.664 1109.951 1098.719 1099.228 c 1123.406 1113.158
+ 1151.949 1121.232 1182.27 1121.232 c 1185.195 1121.232 1188.016 1121.111
+ 1190.902 1120.951 c 1190.578 1125.099 1190.348 1129.255 1190.348 1133.486
+ c 1190.348 1156.275 1195.297 1177.955 1203.992 1197.541 c 1192.285 1205.396
+ 1178.254 1209.794 1163.055 1209.794 c h
+1513.418 1158.83 m 1510.395 1158.83 1507.48 1158.458 1504.504 1158.271 
+c 1505.793 1150.154 1506.734 1141.958 1506.734 1133.486 c 1506.734 1120.294
+ 1505.055 1107.568 1501.996 1095.33 c 1537.844 1075.494 1567.609 1045.81
+ 1587.223 1009.826 c 1607.563 1020.416 1630.258 1027.15 1654.344 1028.767
+ c 1648.137 1101.56 1587.828 1158.83 1513.418 1158.83 c h
+1398.113 1105.912 m 1288.176 1105.912 1199.258 1017.001 1199.258 907.056
+ c 1199.258 797.123 1288.172 708.201 1398.113 708.201 c 1508.055 708.201
+ 1596.969 797.126 1596.969 907.056 c 1596.969 1017.001 1508.051 1105.912
+ 1398.113 1105.912 c h
+1182.27 1105.076 m 1096.98 1105.076 1027.977 1036.072 1027.977 950.783 
+c 1027.977 900.576 1051.898 856.126 1088.969 827.962 c 1104.598 858.103 
+1135.988 878.65 1172.242 878.65 c 1176.625 878.65 1180.832 878.119 1185.055
+ 877.537 c 1183.73 887.181 1183.105 897.048 1183.105 907.056 c 1183.105 
+954.908 1198.68 999.154 1225.16 1034.892 c 1209.309 1054.728 1197.965 1078.556
+ 1193.133 1104.521 c 1189.547 1104.767 1185.918 1105.076 1182.27 1105.076
+ c h
+1665.762 1013.169 m 1639.863 1013.169 1615.602 1006.556 1594.184 995.345
+ c 1606.348 968.404 1613.121 938.513 1613.121 907.056 c 1613.121 848.158
+ 1589.441 794.677 1551.016 755.826 c 1579.246 724.486 1620.203 704.861 1665.762
+ 704.861 c 1751.051 704.861 1820.055 773.865 1820.055 859.154 c 1820.055
+ 944.443 1751.051 1013.169 1665.762 1013.169 c h
+1012.938 992.837 m 934.367 992.837 870.617 929.646 870.617 851.076 c 870.617
+ 772.509 934.367 708.759 1012.938 708.759 c 1042.84 708.759 1070.543 718.076
+ 1093.426 733.826 c 1083.969 748.517 1078.387 766.083 1078.387 784.791 c
+ 1078.387 794.501 1079.82 803.83 1082.563 812.642 c 1039.723 843.603 1011.824
+ 893.986 1011.824 950.783 c 1011.824 965.212 1013.723 979.169 1017.113 992.56
+ c 1015.707 992.599 1014.355 992.837 1012.938 992.837 c h
+1848.184 870.849 m 1843.992 870.849 1839.938 870.373 1835.93 869.736 c 
+1836.152 866.193 1836.207 862.751 1836.207 859.154 c 1836.207 813.853 1818.332
+ 772.736 1789.418 742.181 c 1803.645 725.638 1824.539 715.166 1848.184 715.166
+ c 1891.254 715.166 1926.168 749.798 1926.168 792.869 c 1926.168 835.939
+ 1891.254 870.849 1848.184 870.849 c h
+1172.242 862.498 m 1129.176 862.498 1094.539 827.861 1094.539 784.791 c
+ 1094.539 741.724 1129.176 706.81 1172.242 706.81 c 1205.258 706.81 1233.277
+ 727.408 1244.656 756.384 c 1216.891 784.662 1196.938 820.74 1188.117 860.826
+ c 1182.961 861.892 1177.723 862.498 1172.242 862.498 c h
+2026.43 797.044 m 2026.43 643.029 l 1944.828 643.029 l 1891.484 643.029
+ 1848.184 599.455 1848.184 546.111 c 1848.184 492.775 1891.484 449.466 1944.828
+ 449.466 c 1976.578 449.466 l 1992.227 449.466 2007.852 457.068 2020.305
+ 468.685 c 2032.754 480.302 2042.254 496.392 2042.582 514.361 c 2043.145
+ 545.197 2042.582 634.955 2042.582 634.955 c 2042.582 797.044 l h
+1280.305 727.419 m 1280.305 514.361 l 1280.305 478.552 1309.668 449.466
+ 1345.477 449.466 c 1345.477 465.623 l 1318.383 465.623 1296.457 487.263
+ 1296.457 514.361 c 1296.457 717.392 l 1290.836 720.416 1285.617 723.923
+ 1280.305 727.419 c h
+1118.77 707.646 m 1090.16 680.345 1072.258 641.908 1072.258 599.306 c 1072.258
+ 516.642 1139.438 449.466 1222.098 449.466 c 1222.098 465.623 l 1148.152
+ 465.623 1088.414 525.357 1088.414 599.306 c 1088.414 639.076 1105.773 674.822
+ 1133.254 699.291 c 1128.117 701.654 1123.359 704.427 1118.77 707.646 c 
+h
+928.547 643.587 m 875.203 643.587 831.906 600.291 831.906 546.947 c 831.906
+ 457.544 l 848.059 457.544 l 848.059 546.947 l 848.059 591.572 883.922 627.712
+ 928.547 627.712 c 973.184 627.712 1009.316 591.572 1009.316 546.947 c 1009.316
+ 457.544 l 1025.469 457.544 l 1025.469 546.947 l 1025.469 600.291 981.898
+ 643.587 928.547 643.587 c h
+1470.805 643.029 m 1417.457 643.029 1374.164 599.455 1374.164 546.111 c
+ 1374.164 492.763 1417.457 449.189 1470.805 449.189 c 1524.152 449.189 1567.727
+ 492.763 1567.727 546.111 c 1567.727 599.455 1524.152 643.029 1470.805 643.029
+ c h
+392.418 642.751 m 339.078 642.751 295.496 599.451 295.496 546.111 c 295.496
+ 492.767 339.078 449.466 392.418 449.466 c 445.762 449.466 489.063 492.767
+ 489.063 546.111 c 489.063 599.451 445.762 642.751 392.418 642.751 c h
+537.523 635.513 m 537.523 514.361 l 537.523 478.552 566.613 449.466 602.414
+ 449.466 c 626.816 449.466 648.344 462.958 659.508 482.888 c 670.59 462.958
+ 691.93 449.466 716.324 449.466 c 752.133 449.466 781.496 478.552 781.496
+ 514.361 c 781.496 635.513 l 765.344 635.513 l 765.344 514.361 l 765.344
+ 487.267 743.418 465.623 716.324 465.623 c 689.23 465.623 667.586 487.267
+ 667.586 514.361 c 667.586 635.513 l 651.434 635.513 l 651.434 514.361 l
+ 651.434 487.267 629.52 465.623 602.414 465.623 c 575.328 465.623 553.676
+ 487.267 553.676 514.361 c 553.676 635.513 l h
+1610.059 635.513 m 1610.059 546.111 l 1610.059 492.775 1653.359 449.466
+ 1706.703 449.466 c 1760.047 449.466 1803.621 492.775 1803.621 546.111 c
+ 1803.621 635.513 l 1787.469 635.513 l 1787.469 546.111 l 1787.469 501.494
+ 1751.328 465.623 1706.703 465.623 c 1662.074 465.623 1626.215 501.49 1626.215
+ 546.111 c 1626.215 635.513 l h
+1470.805 626.876 m 1515.438 626.876 1551.574 590.74 1551.574 546.111 c 
+1551.574 501.478 1515.438 465.341 1470.805 465.341 c 1426.176 465.341 1390.316
+ 501.478 1390.316 546.111 c 1390.316 590.74 1426.176 626.876 1470.805 626.876
+ c h
+1944.828 626.876 m 2026.43 626.876 l 2026.492 616.205 2026.938 542.685 
+2026.43 514.638 c 2026.203 502.158 2019.355 489.634 2009.441 480.38 c 1999.527
+ 471.13 1986.695 465.623 1976.578 465.623 c 1944.828 465.623 l 1900.199 
+465.623 1864.34 501.49 1864.34 546.111 c 1864.34 590.74 1900.199 626.876
+ 1944.828 626.876 c h
+392.418 626.599 m 437.047 626.599 472.906 590.736 472.906 546.111 c 472.906
+ 501.482 437.047 465.623 392.418 465.623 c 347.793 465.623 311.652 501.482
+ 311.652 546.111 c 311.652 590.736 347.793 626.599 392.418 626.599 c h
+392.418 626.599 m f
+Q Q
+showpage
+%%Trailer
+end restore
+%%EOF
diff --git a/tests/data/testimage.mp3 b/tests/data/testimage.mp3
new file mode 100644
index 0000000..d2eea3c
Binary files /dev/null and b/tests/data/testimage.mp3 differ
diff --git a/tests/data/testimage.mp4 b/tests/data/testimage.mp4
new file mode 100644
index 0000000..1fc4788
Binary files /dev/null and b/tests/data/testimage.mp4 differ
diff --git a/tests/data/testimage.odt b/tests/data/testimage.odt
new file mode 100644
index 0000000..8564642
Binary files /dev/null and b/tests/data/testimage.odt differ
diff --git a/tests/data/testimagelarge.svg b/tests/data/testimagelarge.svg
new file mode 100644
index 0000000..2d505c4
--- /dev/null
+++ b/tests/data/testimagelarge.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 600 600" xml:space="preserve" height="2e3" width="3e3" version="1.0" y="0px" x="0px" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 3000 2000"><g transform="matrix(5,0,0,5,0,-500)"><circle cy="300" cx="300" r="300" fill="#c31927"/><path fill="#fff" d="m300 550c-66.8 0-129.6-26-176.8-73.2s-73.2-110-73.2-176.8 26-129. [...]
diff --git a/tests/lib/image.php b/tests/lib/image.php
index a22e210..e74c75b 100644
--- a/tests/lib/image.php
+++ b/tests/lib/image.php
@@ -238,21 +238,81 @@ class Test_Image extends \Test\TestCase {
 		$this->assertEquals(15, $img->height());
 	}
 
-	public function testFitIn() {
-		$img = new \OC_Image(OC::$SERVERROOT.'/tests/data/testimage.png');
-		$this->assertTrue($img->fitIn(200, 100));
-		$this->assertEquals(100, $img->width());
-		$this->assertEquals(100, $img->height());
+	public static function sampleProvider() {
+		return [
+			['testimage.png', [200, 100], [100, 100]],
+			['testimage.jpg', [840, 840], [840, 525]],
+			['testimage.gif', [200, 250], [200, 200]]
+		];
+	}
 
-		$img = new \OC_Image(file_get_contents(OC::$SERVERROOT.'/tests/data/testimage.jpg'));
-		$this->assertTrue($img->fitIn(840, 840));
-		$this->assertEquals(840, $img->width());
-		$this->assertEquals(525, $img->height());
+	/**
+	 * @dataProvider sampleProvider
+	 *
+	 * @param string $filename
+	 * @param int[] $asked
+	 * @param int[] $expected
+	 */
+	public function testFitIn($filename, $asked, $expected) {
+		$img = new \OC_Image(OC::$SERVERROOT . '/tests/data/' . $filename);
+		$this->assertTrue($img->fitIn($asked[0], $asked[1]));
+		$this->assertEquals($expected[0], $img->width());
+		$this->assertEquals($expected[1], $img->height());
+	}
 
-		$img = new \OC_Image(base64_encode(file_get_contents(OC::$SERVERROOT.'/tests/data/testimage.gif')));
-		$this->assertTrue($img->fitIn(200, 250));
-		$this->assertEquals(200, $img->width());
-		$this->assertEquals(200, $img->height());
+	public static function sampleFilenamesProvider() {
+		return [
+			['testimage.png'],
+			['testimage.jpg'],
+			['testimage.gif']
+		];
+	}
+
+	/**
+	 * Image should not be resized if it's already smaller than what is required
+	 *
+	 * @dataProvider sampleFilenamesProvider
+	 *
+	 * @param string $filename
+	 */
+	public function testScaleDownToFitWhenSmallerAlready($filename) {
+		$img = new \OC_Image(OC::$SERVERROOT . '/tests/data/' . $filename);
+		$currentWidth = $img->width();
+		$currentHeight = $img->height();
+		// We pick something larger than the image we want to scale down
+		$this->assertFalse($img->scaleDownToFit(4000, 4000));
+		// The dimensions of the image should not have changed since it's smaller already
+		$resizedWidth = $img->width();
+		$resizedHeight = $img->height();
+		$this->assertEquals(
+			$currentWidth, $img->width(), "currentWidth $currentWidth resizedWidth $resizedWidth \n"
+		);
+		$this->assertEquals(
+			$currentHeight, $img->height(),
+			"currentHeight $currentHeight resizedHeight $resizedHeight \n"
+		);
+	}
+
+	public static function largeSampleProvider() {
+		return [
+			['testimage.png', [200, 100], [100, 100]],
+			['testimage.jpg', [840, 840], [840, 525]],
+		];
+	}
+
+	/**
+	 * @dataProvider largeSampleProvider
+	 *
+	 * @param string $filename
+	 * @param int[] $asked
+	 * @param int[] $expected
+	 */
+	public function testScaleDownWhenBigger($filename, $asked, $expected) {
+		$img = new \OC_Image(OC::$SERVERROOT . '/tests/data/' . $filename);
+		//$this->assertTrue($img->scaleDownToFit($asked[0], $asked[1]));
+		$img->scaleDownToFit($asked[0], $asked[1]);
+		$this->assertEquals($expected[0], $img->width());
+		$this->assertEquals($expected[1], $img->height());
 	}
 
 	function convertDataProvider() {
diff --git a/tests/lib/preview.php b/tests/lib/preview.php
index 20e4209..2741018 100644
--- a/tests/lib/preview.php
+++ b/tests/lib/preview.php
@@ -1,9 +1,23 @@
 <?php
 /**
- * Copyright (c) 2013 Georg Ehrke <georg at ownCloud.com>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
+ * @author Georg Ehrke <georg at owncloud.com>
+ * @author Olivier Paroz <owncloud at interfasys.ch>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
  */
 
 namespace Test;
@@ -12,121 +26,99 @@ class Preview extends TestCase {
 
 	const TEST_PREVIEW_USER1 = "test-preview-user1";
 
-	/**
-	 * @var \OC\Files\View
-	 */
+	/** @var \OC\Files\View */
 	private $rootView;
+	/**
+	 * Note that using 756 with an image with a ratio of 1.6 brings interesting rounding issues
+	 *
+	 * @var int maximum width allowed for a preview
+	 * */
+	private $configMaxWidth = 756;
+	/** @var int maximum height allowed for a preview */
+	private $configMaxHeight = 756;
+	private $keepAspect;
+	private $scalingUp;
+
+	private $samples = [];
+	private $sampleFileId;
+	private $sampleFilename;
+	private $sampleWidth;
+	private $sampleHeight;
+	private $maxScaleFactor;
+	/** @var int width of the max preview */
+	private $maxPreviewWidth;
+	/** @var int height of the max preview */
+	private $maxPreviewHeight;
+	/** @var int height of the max preview, which is the same as the one of the original image */
+	private $maxPreviewRatio;
+	private $cachedBigger = [];
 
-	/** @var \OC\Files\Storage\Storage */
-	private $originalStorage;
-
+	/**
+	 * Make sure your configuration file doesn't contain any additional providers
+	 */
 	protected function setUp() {
 		parent::setUp();
 
-		// FIXME: use proper tearDown with $this->loginAsUser() and $this->logout()
-		// (would currently break the tests for some reason)
-		$this->originalStorage = \OC\Files\Filesystem::getStorage('/');
-
-		// create a new user with his own filesystem view
-		// this gets called by each test in this test class
+		$userManager = \OC::$server->getUserManager();
+		$userManager->clearBackends();
 		$backend = new \OC_User_Dummy();
-		\OC_User::useBackend($backend);
+		$userManager->registerBackend($backend);
 		$backend->createUser(self::TEST_PREVIEW_USER1, self::TEST_PREVIEW_USER1);
-		$user = \OC::$server->getUserManager()->get(self::TEST_PREVIEW_USER1);
-		\OC::$server->getUserSession()->setUser($user);
-		\OC\Files\Filesystem::init(self::TEST_PREVIEW_USER1, '/' . self::TEST_PREVIEW_USER1 . '/files');
+		$this->loginAsUser(self::TEST_PREVIEW_USER1);
 
-		\OC\Files\Filesystem::mount('OC\Files\Storage\Temporary', array(), '/');
+		$storage = new \OC\Files\Storage\Temporary([]);
+		\OC\Files\Filesystem::mount($storage, [], '/' . self::TEST_PREVIEW_USER1 . '/');
 
 		$this->rootView = new \OC\Files\View('');
-		$this->rootView->mkdir('/'.self::TEST_PREVIEW_USER1);
-		$this->rootView->mkdir('/'.self::TEST_PREVIEW_USER1.'/files');
+		$this->rootView->mkdir('/' . self::TEST_PREVIEW_USER1);
+		$this->rootView->mkdir('/' . self::TEST_PREVIEW_USER1 . '/files');
+
+		// We simulate the max dimension set in the config
+		\OC::$server->getConfig()
+					->setSystemValue('preview_max_x', $this->configMaxWidth);
+		\OC::$server->getConfig()
+					->setSystemValue('preview_max_y', $this->configMaxHeight);
+		// Used to test upscaling
+		$this->maxScaleFactor = 2;
+		\OC::$server->getConfig()
+					->setSystemValue('preview_max_scale_factor', $this->maxScaleFactor);
+
+		// We need to enable the providers we're going to use in the tests
+		$providers = [
+			'OC\\Preview\\JPEG',
+			'OC\\Preview\\PNG',
+			'OC\\Preview\\GIF',
+			'OC\\Preview\\TXT',
+			'OC\\Preview\\Postscript'
+		];
+		\OC::$server->getConfig()
+					->setSystemValue('enabledPreviewProviders', $providers);
+
+		// Sample is 1680x1050 JPEG
+		$this->prepareSample('testimage.jpg', 1680, 1050);
+		// Sample is 2400x1707 EPS
+		$this->prepareSample('testimage.eps', 2400, 1707);
+		// Sample is 1200x450 PNG
+		$this->prepareSample('testimage-wide.png', 1200, 450);
+		// Sample is 64x64 GIF
+		$this->prepareSample('testimage.gif', 64, 64);
 	}
 
 	protected function tearDown() {
-		\OC\Files\Filesystem::clearMounts();
-		\OC\Files\Filesystem::mount($this->originalStorage, array(), '/');
+		$this->logout();
 
 		parent::tearDown();
 	}
 
-	public function testIsMaxSizeWorking() {
-		// Max size from config
-		$maxX = 1024;
-		$maxY = 1024;
-
-		\OC::$server->getConfig()->setSystemValue('preview_max_x', $maxX);
-		\OC::$server->getConfig()->setSystemValue('preview_max_y', $maxY);
-
-		// Sample is 1680x1050 JPEG
-		$sampleFile = '/' . self::TEST_PREVIEW_USER1 . '/files/testimage.jpg';
-		$this->rootView->file_put_contents($sampleFile, file_get_contents(\OC::$SERVERROOT.'/tests/data/testimage.jpg'));
-		$fileInfo = $this->rootView->getFileInfo($sampleFile);
-		$fileId = $fileInfo['fileid'];
-
-		$largeX = 1920;
-		$largeY = 1080;
-		$preview = new \OC\Preview(self::TEST_PREVIEW_USER1, 'files/', 'testimage.jpg', $largeX, $largeY);
-
-		$this->assertEquals($preview->isFileValid(), true);
-
-		// There should be no cached copy
-		$isCached = $preview->isCached($fileId);
-
-		$this->assertNotEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $maxX . '-' . $maxY . '-max.png', $isCached);
-		$this->assertNotEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $maxX . '-' . $maxY . '.png', $isCached);
-		$this->assertNotEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $largeX . '-' . $largeY . '.png', $isCached);
-
-		// The returned preview should be of max size
-		$image = $preview->getPreview();
-
-		$this->assertEquals($image->width(), $maxX);
-		$this->assertEquals($image->height(), $maxY);
-
-		// The max thumbnail should be created
-		$maxThumbCacheFile = '/' . self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $maxX . '-' . $maxY . '-max.png';
-
-		$this->assertEquals($this->rootView->file_exists($maxThumbCacheFile), true);
-
-		// A preview of the asked size should not have been created
-		$thumbCacheFile = \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $largeX . '-' . $largeY . '.png';
-
-		$this->assertEquals($this->rootView->file_exists($thumbCacheFile), false);
-
-		// 2nd request should indicate that we have a cached copy of max dimension
-		$isCached = $preview->isCached($fileId);
-		$this->assertEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $maxX . '-' . $maxY . '.png', $isCached);
-
-		// Smaller previews should be based on the cached max preview
-		$smallX = 50;
-		$smallY = 50;
-		$preview = new \OC\Preview(self::TEST_PREVIEW_USER1, 'files/', 'testimage.jpg', $smallX, $smallY);
-		$isCached = $preview->isCached($fileId);
-
-		$this->assertEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $maxX . '-' . $maxY . '.png', $isCached);
-
-		// A small preview should be created
-		$image = $preview->getPreview();
-		$this->assertEquals($image->width(), $smallX);
-		$this->assertEquals($image->height(), $smallY);
-
-		// The cache should contain the small preview
-		$thumbCacheFile = '/' . self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $smallX . '-' . $smallY . '.png';
-
-		$this->assertEquals($this->rootView->file_exists($thumbCacheFile), true);
-
-		// 2nd request should indicate that we have a cached copy of the exact dimension
-		$isCached = $preview->isCached($fileId);
-
-		$this->assertEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $smallX . '-' . $smallY . '.png', $isCached);
-	}
-
+	/**
+	 * Tests if a preview can be deleted
+	 */
 	public function testIsPreviewDeleted() {
 
-		$sampleFile = '/'.self::TEST_PREVIEW_USER1.'/files/test.txt';
+		$sampleFile = '/' . self::TEST_PREVIEW_USER1 . '/files/test.txt';
 
 		$this->rootView->file_put_contents($sampleFile, 'dummy file data');
-		
+
 		$x = 50;
 		$y = 50;
 
@@ -134,23 +126,30 @@ class Preview extends TestCase {
 		$preview->getPreview();
 
 		$fileInfo = $this->rootView->getFileInfo($sampleFile);
+		/** @var int $fileId */
 		$fileId = $fileInfo['fileid'];
+		$thumbCacheFile = $this->buildCachePath($fileId, $x, $y, true);
 
-		$thumbCacheFile = '/' . self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $x . '-' . $y . '.png';
-		
-		$this->assertEquals($this->rootView->file_exists($thumbCacheFile), true);
+		$this->assertSame(
+			true, $this->rootView->file_exists($thumbCacheFile), "$thumbCacheFile \n"
+		);
 
 		$preview->deletePreview();
 
-		$this->assertEquals($this->rootView->file_exists($thumbCacheFile), false);
+		$this->assertSame(false, $this->rootView->file_exists($thumbCacheFile));
 	}
 
+	/**
+	 * Tests if all previews can be deleted
+	 *
+	 * We test this first to make sure we'll be able to cleanup after each preview generating test
+	 */
 	public function testAreAllPreviewsDeleted() {
 
-		$sampleFile = '/'.self::TEST_PREVIEW_USER1.'/files/test.txt';
+		$sampleFile = '/' . self::TEST_PREVIEW_USER1 . '/files/test.txt';
 
 		$this->rootView->file_put_contents($sampleFile, 'dummy file data');
-		
+
 		$x = 50;
 		$y = 50;
 
@@ -158,104 +157,697 @@ class Preview extends TestCase {
 		$preview->getPreview();
 
 		$fileInfo = $this->rootView->getFileInfo($sampleFile);
+		/** @var int $fileId */
 		$fileId = $fileInfo['fileid'];
-		
-		$thumbCacheFolder = '/' . self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/';
-		
-		$this->assertEquals($this->rootView->is_dir($thumbCacheFolder), true);
+
+		$thumbCacheFolder = '/' . self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER .
+							'/' . $fileId . '/';
+
+		$this->assertSame(true, $this->rootView->is_dir($thumbCacheFolder), "$thumbCacheFolder \n");
 
 		$preview->deleteAllPreviews();
 
-		$this->assertEquals($this->rootView->is_dir($thumbCacheFolder), false);
+		$this->assertSame(false, $this->rootView->is_dir($thumbCacheFolder));
 	}
 
 	public function txtBlacklist() {
 		$txt = 'random text file';
 
-		return array(
-			array('txt', $txt, false),
-		);
+		return [
+			['txt', $txt, false],
+		];
 	}
 
 	/**
 	 * @dataProvider txtBlacklist
+	 *
+	 * @param $extension
+	 * @param $data
+	 * @param $expectedResult
 	 */
 	public function testIsTransparent($extension, $data, $expectedResult) {
 
 		$x = 32;
 		$y = 32;
 
-		$sample = '/'.self::TEST_PREVIEW_USER1.'/files/test.'.$extension;
+		$sample = '/' . self::TEST_PREVIEW_USER1 . '/files/test.' . $extension;
 		$this->rootView->file_put_contents($sample, $data);
-		$preview = new \OC\Preview(self::TEST_PREVIEW_USER1, 'files/', 'test.'.$extension, $x, $y);
+		$preview = new \OC\Preview(
+			self::TEST_PREVIEW_USER1, 'files/', 'test.' . $extension, $x,
+			$y
+		);
 		$image = $preview->getPreview();
 		$resource = $image->resource();
 
 		//http://stackoverflow.com/questions/5702953/imagecolorat-and-transparency
 		$colorIndex = imagecolorat($resource, 1, 1);
 		$colorInfo = imagecolorsforindex($resource, $colorIndex);
-		$this->assertEquals(
+		$this->assertSame(
 			$expectedResult,
 			$colorInfo['alpha'] === 127,
 			'Failed asserting that only previews for text files are transparent.'
 		);
 	}
 
-	public function testCreationFromCached() {
+	/**
+	 * We generate the data to use as it makes it easier to adjust in case we need to test
+	 * something different
+	 *
+	 * @return array
+	 */
+	public static function dimensionsDataProvider() {
+		$data = [];
+		$samples = [
+			[200, 800],
+			[200, 800],
+			[50, 400],
+			[4, 60],
+		];
+		$keepAspect = false;
+		$scalingUp = false;
+
+		for ($a = 0; $a < sizeof($samples); $a++) {
+			for ($b = 0; $b < 2; $b++) {
+				for ($c = 0; $c < 2; $c++) {
+					for ($d = 0; $d < 4; $d++) {
+						$coordinates = [
+							[
+								-rand($samples[$a][0], $samples[$a][1]),
+								-rand($samples[$a][0], $samples[$a][1])
+							],
+							[
+								rand($samples[$a][0], $samples[$a][1]),
+								rand($samples[$a][0], $samples[$a][1])
+							],
+							[
+								-rand($samples[$a][0], $samples[$a][1]),
+								rand($samples[$a][0], $samples[$a][1])
+							],
+							[
+								rand($samples[$a][0], $samples[$a][1]),
+								-rand($samples[$a][0], $samples[$a][1])
+							]
+						];
+						$row = [$a];
+						$row[] = $coordinates[$d][0];
+						$row[] = $coordinates[$d][1];
+						$row[] = $keepAspect;
+						$row[] = $scalingUp;
+						$data[] = $row;
+					}
+					$scalingUp = !$scalingUp;
+				}
+				$keepAspect = !$keepAspect;
+			}
+		}
+
+		return $data;
+	}
 
-		$sampleFile = '/'.self::TEST_PREVIEW_USER1.'/files/test.txt';
+	/**
+	 * Tests if a preview of max dimensions gets created
+	 *
+	 * @dataProvider dimensionsDataProvider
+	 *
+	 * @param int $sampleId
+	 * @param int $widthAdjustment
+	 * @param int $heightAdjustment
+	 * @param bool $keepAspect
+	 * @param bool $scalingUp
+	 */
+	public function testCreateMaxAndNormalPreviewsAtFirstRequest(
+		$sampleId, $widthAdjustment, $heightAdjustment, $keepAspect = false, $scalingUp = false
+	) {
+		//$this->markTestSkipped('Not testing this at this time');
+
+		// Get the right sample for the experiment
+		$this->getSample($sampleId);
+		$sampleWidth = $this->sampleWidth;
+		$sampleHeight = $this->sampleHeight;
+		$sampleFileId = $this->sampleFileId;
+
+		// Adjust the requested size so that we trigger various test cases
+		$previewWidth = $sampleWidth + $widthAdjustment;
+		$previewHeight = $sampleHeight + $heightAdjustment;
+		$this->keepAspect = $keepAspect;
+		$this->scalingUp = $scalingUp;
+
+		// Generates the max preview
+		$preview = $this->createPreview($previewWidth, $previewHeight);
+
+		// There should be no cached thumbnails
+		$thumbnailFolder = '/' . self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER .
+						   '/' . $sampleFileId;
+		$this->assertSame(false, $this->rootView->is_dir($thumbnailFolder));
 
-		$this->rootView->file_put_contents($sampleFile, 'dummy file data');
+		$image = $preview->getPreview();
+		$this->assertNotSame(false, $image);
 
-		// create base preview
-		$x = 150;
-		$y = 150;
+		$maxThumbCacheFile = $this->buildCachePath(
+			$sampleFileId, $this->maxPreviewWidth, $this->maxPreviewHeight, true, '-max'
+		);
 
-		$preview = new \OC\Preview(self::TEST_PREVIEW_USER1, 'files/', 'test.txt', $x, $y);
-		$preview->getPreview();
+		$this->assertSame(
+			true, $this->rootView->file_exists($maxThumbCacheFile), "$maxThumbCacheFile \n"
+		);
 
-		$fileInfo = $this->rootView->getFileInfo($sampleFile);
-		$fileId = $fileInfo['fileid'];
+		// We check the dimensions of the file we've just stored
+		$maxPreview = imagecreatefromstring($this->rootView->file_get_contents($maxThumbCacheFile));
 
-		$thumbCacheFile = '/' . self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $x . '-' . $y . '.png';
+		$this->assertEquals($this->maxPreviewWidth, imagesx($maxPreview));
+		$this->assertEquals($this->maxPreviewHeight, imagesy($maxPreview));
 
-		$this->assertEquals($this->rootView->file_exists($thumbCacheFile), true);
+		// A thumbnail of the asked dimensions should also have been created (within the constraints of the max preview)
+		list($limitedPreviewWidth, $limitedPreviewHeight) =
+			$this->simulatePreviewDimensions($previewWidth, $previewHeight);
 
+		$actualWidth = $image->width();
+		$actualHeight = $image->height();
 
-		// create smaller previews
-		$preview = new \OC\Preview(self::TEST_PREVIEW_USER1, 'files/', 'test.txt', 50, 50);
-		$isCached = $preview->isCached($fileId);
+		$this->assertEquals(
+			(int)$limitedPreviewWidth, $image->width(), "$actualWidth x $actualHeight \n"
+		);
+		$this->assertEquals((int)$limitedPreviewHeight, $image->height());
+
+		// And it should be cached
+		$this->checkCache($sampleFileId, $limitedPreviewWidth, $limitedPreviewHeight);
 
-		$this->assertEquals(self::TEST_PREVIEW_USER1 . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/150-150.png', $isCached);
+		$preview->deleteAllPreviews();
+	}
+
+	/**
+	 * Tests if the second preview will be based off the cached max preview
+	 *
+	 * @dataProvider dimensionsDataProvider
+	 *
+	 * @param int $sampleId
+	 * @param int $widthAdjustment
+	 * @param int $heightAdjustment
+	 * @param bool $keepAspect
+	 * @param bool $scalingUp
+	 */
+	public function testSecondPreviewsGetCachedMax(
+		$sampleId, $widthAdjustment, $heightAdjustment, $keepAspect = false, $scalingUp = false
+	) {
+		//$this->markTestSkipped('Not testing this at this time');
+
+		$this->getSample($sampleId);
+		$sampleWidth = $this->sampleWidth;
+		$sampleHeight = $this->sampleHeight;
+		$sampleFileId = $this->sampleFileId;
+
+		//Creates the Max preview which will be used in the rest of the test
+		$this->createMaxPreview();
+
+		// Adjust the requested size so that we trigger various test cases
+		$previewWidth = $sampleWidth + $widthAdjustment;
+		$previewHeight = $sampleHeight + $heightAdjustment;
+		$this->keepAspect = $keepAspect;
+		$this->scalingUp = $scalingUp;
+
+		$preview = $this->createPreview($previewWidth, $previewHeight);
+
+		// A cache query should return the thumbnail of max dimension
+		$isCached = $preview->isCached($sampleFileId);
+		$cachedMaxPreview = $this->buildCachePath(
+			$sampleFileId, $this->maxPreviewWidth, $this->maxPreviewHeight, false, '-max'
+		);
+		$this->assertSame($cachedMaxPreview, $isCached);
 	}
 
-	/*
-	public function testScalingUp() {
+	/**
+	 * Make sure that the max preview can never be deleted
+	 *
+	 * For this test to work, the preview we generate first has to be the size of max preview
+	 */
+	public function testMaxPreviewCannotBeDeleted() {
+		//$this->markTestSkipped('Not testing this at this time');
 
-		$sampleFile = '/'.$this->user.'/files/test.txt';
+		$this->keepAspect = true;
+		$this->getSample(0);
+		$fileId = $this->sampleFileId;
 
-		$this->rootView->file_put_contents($sampleFile, 'dummy file data');
+		//Creates the Max preview which we will try to delete
+		$preview = $this->createMaxPreview();
 
-		// create base preview
-		$x = 150;
-		$y = 150;
+		// We try to deleted the preview
+		$preview->deletePreview();
+		$this->assertNotSame(false, $preview->isCached($fileId));
 
-		$preview = new \OC\Preview($this->user, 'files/', 'test.txt', $x, $y);
+		$preview->deleteAllPreviews();
+	}
+
+	public static function aspectDataProvider() {
+		$data = [];
+		$samples = 4;
+		$keepAspect = false;
+		$scalingUp = false;
+		for ($a = 0; $a < $samples; $a++) {
+			for ($b = 0; $b < 2; $b++) {
+				for ($c = 0; $c < 2; $c++) {
+					$row = [$a];
+					$row[] = $keepAspect;
+					$row[] = $scalingUp;
+					$data[] = $row;
+					$scalingUp = !$scalingUp;
+				}
+				$keepAspect = !$keepAspect;
+			}
+		}
+
+		return $data;
+	}
+
+	/**
+	 * We ask for a preview larger than what is set in the configuration,
+	 * so we should be getting either the max preview or a preview the size
+	 * of the dimensions set in the config
+	 *
+	 * @dataProvider aspectDataProvider
+	 *
+	 * @param int $sampleId
+	 * @param bool $keepAspect
+	 * @param bool $scalingUp
+	 */
+	public function testDoNotCreatePreviewsLargerThanConfigMax(
+		$sampleId, $keepAspect = false, $scalingUp = false
+	) {
+		//$this->markTestSkipped('Not testing this at this time');
+
+		$this->getSample($sampleId);
+
+		//Creates the Max preview which will be used in the rest of the test
+		$this->createMaxPreview();
+
+		// Now we will create the real preview
+		$previewWidth = 4000;
+		$previewHeight = 4000;
+		$this->keepAspect = $keepAspect;
+		$this->scalingUp = $scalingUp;
+
+		// Tries to create the very large preview
+		$preview = $this->createPreview($previewWidth, $previewHeight);
+
+		$image = $preview->getPreview();
+		$this->assertNotSame(false, $image);
+
+		list($expectedWidth, $expectedHeight) =
+			$this->simulatePreviewDimensions($previewWidth, $previewHeight);
+		$this->assertEquals($expectedWidth, $image->width());
+		$this->assertEquals($expectedHeight, $image->height());
+
+		// A preview of the asked size should not have been created since it's larger that our max dimensions
+		$postfix = $this->getThumbnailPostfix($previewWidth, $previewHeight);
+		$thumbCacheFile = $this->buildCachePath(
+			$this->sampleFileId, $previewWidth, $previewHeight, false, $postfix
+		);
+		$this->assertSame(
+			false, $this->rootView->file_exists($thumbCacheFile), "$thumbCacheFile \n"
+		);
+
+		$preview->deleteAllPreviews();
+	}
+
+	/**
+	 * Makes sure we're getting the proper cached thumbnail
+	 *
+	 * When we start by generating a preview which keeps the aspect ratio
+	 * 200-125-with-aspect
+	 * 300-300    ✓
+	 *
+	 * When we start by generating a preview of exact dimensions
+	 * 200-200    ✓
+	 * 300-188-with-aspect
+	 *
+	 * @dataProvider aspectDataProvider
+	 *
+	 * @param int $sampleId
+	 * @param bool $keepAspect
+	 * @param bool $scalingUp
+	 */
+	public function testIsBiggerWithAspectRatioCached(
+		$sampleId, $keepAspect = false, $scalingUp = false
+	) {
+		//$this->markTestSkipped('Not testing this at this time');
+
+		$previewWidth = 400;
+		$previewHeight = 400;
+		$this->getSample($sampleId);
+		$fileId = $this->sampleFileId;
+		$this->keepAspect = $keepAspect;
+		$this->scalingUp = $scalingUp;
+
+		// Caching the max preview in our preview array for the test
+		$this->cachedBigger[] = $this->buildCachePath(
+			$fileId, $this->maxPreviewWidth, $this->maxPreviewHeight, false, '-max'
+		);
+
+		$this->getSmallerThanMaxPreview($fileId, $previewWidth, $previewHeight);
+		// We switch the aspect ratio, to generate a thumbnail we should not be picked up
+		$this->keepAspect = !$keepAspect;
+		$this->getSmallerThanMaxPreview($fileId, $previewWidth + 100, $previewHeight + 100);
+
+		// Small thumbnails are always cropped
+		$this->keepAspect = false;
+		// Smaller previews should be based on the previous, larger preview, with the correct aspect ratio
+		$this->createThumbnailFromBiggerCachedPreview($fileId, 36, 36);
+
+		// 2nd cache query should indicate that we have a cached copy of the exact dimension
+		$this->getCachedSmallThumbnail($fileId, 36, 36);
+
+		// We create a preview in order to be able to delete the cache
+		$preview = $this->createPreview(rand(), rand());
+		$preview->deleteAllPreviews();
+		$this->cachedBigger = [];
+	}
+
+	/**
+	 * Initialises the preview
+	 *
+	 * @param int $width
+	 * @param int $height
+	 *
+	 * @return \OC\Preview
+	 */
+	private function createPreview($width, $height) {
+		$preview = new \OC\Preview(
+			self::TEST_PREVIEW_USER1, 'files/', $this->sampleFilename, $width,
+			$height
+		);
+
+		$this->assertSame(true, $preview->isFileValid());
+
+		$preview->setKeepAspect($this->keepAspect);
+		$preview->setScalingup($this->scalingUp);
+
+		return $preview;
+	}
+
+	/**
+	 * Creates the Max preview which will be used in the rest of the test
+	 *
+	 * @return \OC\Preview
+	 */
+	private function createMaxPreview() {
+		$this->keepAspect = true;
+		$preview = $this->createPreview($this->maxPreviewWidth, $this->maxPreviewHeight);
 		$preview->getPreview();
 
-		$fileInfo = $this->rootView->getFileInfo($sampleFile);
-		$fileId = $fileInfo['fileid'];
+		return $preview;
+	}
+
+	/**
+	 * Makes sure the preview which was just created has been saved to disk
+	 *
+	 * @param int $fileId
+	 * @param int $previewWidth
+	 * @param int $previewHeight
+	 */
+	private function checkCache($fileId, $previewWidth, $previewHeight) {
+		$postfix = $this->getThumbnailPostfix($previewWidth, $previewHeight);
+
+		$thumbCacheFile = $this->buildCachePath(
+			$fileId, $previewWidth, $previewHeight, true, $postfix
+		);
+
+		$this->assertSame(
+			true, $this->rootView->file_exists($thumbCacheFile), "$thumbCacheFile \n"
+		);
+	}
+
+	/**
+	 * Computes special filename postfixes
+	 *
+	 * @param int $width
+	 * @param int $height
+	 *
+	 * @return string
+	 */
+	private function getThumbnailPostfix($width, $height) {
+		// Need to take care of special postfix added to the dimensions
+		$postfix = '';
+		$isMaxPreview = ($width === $this->maxPreviewWidth
+						 && $height === $this->maxPreviewHeight) ? true : false;
+		if ($isMaxPreview) {
+			$postfix = '-max';
+		}
+		if ($this->keepAspect && !$isMaxPreview) {
+			$postfix = '-with-aspect';
+		}
+
+		return $postfix;
+	}
+
+	private function getSmallerThanMaxPreview($fileId, $previewWidth, $previewHeight) {
+		$preview = $this->createPreview($previewWidth, $previewHeight);
+
+		$image = $preview->getPreview();
+		$this->assertNotSame(false, $image);
 
-		$thumbCacheFile = '/' . $this->user . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $x . '-' . $y . '.png';
+		// A thumbnail of the asked dimensions should also have been created (within the constraints of the max preview)
+		list($limitedPreviewWidth, $limitedPreviewHeight) =
+			$this->simulatePreviewDimensions($previewWidth, $previewHeight);
 
-		$this->assertEquals($this->rootView->file_exists($thumbCacheFile), true);
+		$this->assertEquals($limitedPreviewWidth, $image->width());
+		$this->assertEquals($limitedPreviewHeight, $image->height());
 
+		// And it should be cached
+		$this->checkCache($fileId, $limitedPreviewWidth, $limitedPreviewHeight);
 
-		// create bigger previews - with scale up
-		$preview = new \OC\Preview($this->user, 'files/', 'test.txt', 250, 250);
+		$this->cachedBigger[] = $preview->isCached($fileId);
+	}
+
+	private function createThumbnailFromBiggerCachedPreview($fileId, $width, $height) {
+		$preview = $this->createPreview($width, $height);
+
+		// A cache query should return a thumbnail of slightly larger dimensions
+		// and with the proper aspect ratio
 		$isCached = $preview->isCached($fileId);
+		$expectedCachedBigger = $this->getExpectedCachedBigger();
+
+		$this->assertSame($expectedCachedBigger, $isCached);
+
+		$image = $preview->getPreview();
+		$this->assertNotSame(false, $image);
+	}
+
+	/**
+	 * Picks the bigger cached preview with the correct aspect ratio or the max preview if it's
+	 * smaller than that
+	 *
+	 * For non-upscaled images, we pick the only picture without aspect ratio
+	 *
+	 * @return string
+	 */
+	private function getExpectedCachedBigger() {
+		$foundPreview = null;
+		$foundWidth = null;
+		$foundHeight = null;
+		$maxPreview = null;
+		$maxWidth = null;
+		$maxHeight = null;
+
+		foreach ($this->cachedBigger as $cached) {
+			$size = explode('-', basename($cached));
+			$width = (int)$size[0];
+			$height = (int)$size[1];
+
+			if (strpos($cached, 'max')) {
+				$maxWidth = $width;
+				$maxHeight = $height;
+				$maxPreview = $cached;
+				continue;
+			}
+
+			// We pick the larger preview with no aspect ratio
+			if (!strpos($cached, 'aspect') && !strpos($cached, 'max')) {
+				$foundPreview = $cached;
+				$foundWidth = $width;
+				$foundHeight = $height;
+			}
+		}
+		if ($foundWidth > $maxWidth && $foundHeight > $maxHeight) {
+			$foundPreview = $maxPreview;
+		}
+
+		return $foundPreview;
+	}
+
+	/**
+	 * A small thumbnail of exact dimensions should be in the cache
+	 *
+	 * @param int $fileId
+	 * @param int $width
+	 * @param int $height
+	 */
+	private function getCachedSmallThumbnail($fileId, $width, $height) {
+		$preview = $this->createPreview($width, $height);
+
+		$isCached = $preview->isCached($fileId);
+		$thumbCacheFile = $this->buildCachePath($fileId, $width, $height);
+
+		$this->assertSame($thumbCacheFile, $isCached, "$thumbCacheFile \n");
+	}
+
+	/**
+	 * Builds the complete path to a cached thumbnail starting from the user folder
+	 *
+	 * @param int $fileId
+	 * @param int $width
+	 * @param int $height
+	 * @param bool $user
+	 * @param string $postfix
+	 *
+	 * @return string
+	 */
+	private function buildCachePath($fileId, $width, $height, $user = false, $postfix = '') {
+		$userPath = '';
+		if ($user) {
+			$userPath = '/' . self::TEST_PREVIEW_USER1 . '/';
+		}
+
+		return $userPath . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId
+			   . '/' . $width . '-' . $height . $postfix . '.png';
+	}
+
+	/**
+	 * Stores the sample in the filesystem and stores it in the $samples array
+	 *
+	 * @param string $fileName
+	 * @param int $sampleWidth
+	 * @param int $sampleHeight
+	 */
+	private function prepareSample($fileName, $sampleWidth, $sampleHeight) {
+		$imgData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $fileName);
+		$imgPath = '/' . self::TEST_PREVIEW_USER1 . '/files/' . $fileName;
+		$this->rootView->file_put_contents($imgPath, $imgData);
+		$fileInfo = $this->rootView->getFileInfo($imgPath);
+
+		list($maxPreviewWidth, $maxPreviewHeight) =
+			$this->setMaxPreview($sampleWidth, $sampleHeight);
+
+		$this->samples[] =
+			[
+				'sampleFileId'     => $fileInfo['fileid'],
+				'sampleFileName'   => $fileName,
+				'sampleWidth'      => $sampleWidth,
+				'sampleHeight'     => $sampleHeight,
+				'maxPreviewWidth'  => $maxPreviewWidth,
+				'maxPreviewHeight' => $maxPreviewHeight
+			];
+	}
+
+	/**
+	 * Sets the variables used to define the boundaries which need to be respected when using a
+	 * specific sample
+	 *
+	 * @param $sampleId
+	 */
+	private function getSample($sampleId) {
+		// Corrects a rounding difference when using the EPS (Imagick converted) sample
+		$filename = $this->samples[$sampleId]['sampleFileName'];
+		$splitFileName = pathinfo($filename);
+		$extension = $splitFileName['extension'];
+		$correction = ($extension === 'eps') ? 1 : 0;
+		$maxPreviewHeight = $this->samples[$sampleId]['maxPreviewHeight'];
+		$maxPreviewHeight = $maxPreviewHeight - $correction;
+
+		$this->sampleFileId = $this->samples[$sampleId]['sampleFileId'];
+		$this->sampleFilename = $this->samples[$sampleId]['sampleFileName'];
+		$this->sampleWidth = $this->samples[$sampleId]['sampleWidth'];
+		$this->sampleHeight = $this->samples[$sampleId]['sampleHeight'];
+		$this->maxPreviewWidth = $this->samples[$sampleId]['maxPreviewWidth'];
+		$this->maxPreviewHeight = $maxPreviewHeight;
+		$ratio = $this->maxPreviewWidth / $this->maxPreviewHeight;
+		$this->maxPreviewRatio = $ratio;
+	}
+
+	/**
+	 * Defines the size of the max preview
+	 *
+	 * @fixme the Imagick previews don't have the exact same size on disk as they're calculated here
+	 *
+	 * @param int $sampleWidth
+	 * @param int $sampleHeight
+	 *
+	 * @return array
+	 */
+	private function setMaxPreview($sampleWidth, $sampleHeight) {
+		// Max previews are never scaled up
+		$this->scalingUp = false;
+		// Max previews always keep the aspect ratio
+		$this->keepAspect = true;
+		// We set this variable in order to be able to calculate the max preview with the proper aspect ratio
+		$this->maxPreviewRatio = $sampleWidth / $sampleHeight;
+		$maxPreviewWidth = min($sampleWidth, $this->configMaxWidth);
+		$maxPreviewHeight = min($sampleHeight, $this->configMaxHeight);
+		list($maxPreviewWidth, $maxPreviewHeight) =
+			$this->applyAspectRatio($maxPreviewWidth, $maxPreviewHeight);
+
+		return [$maxPreviewWidth, $maxPreviewHeight];
+	}
+
+	/**
+	 * Calculates the expected dimensions of the preview to be able to assess if we've got the
+	 * right result
+	 *
+	 * @param int $askedWidth
+	 * @param int $askedHeight
+	 *
+	 * @return array
+	 */
+	private function simulatePreviewDimensions($askedWidth, $askedHeight) {
+		$askedWidth = min($askedWidth, $this->configMaxWidth);
+		$askedHeight = min($askedHeight, $this->configMaxHeight);
+
+		if ($this->keepAspect) {
+			// Defines the box in which the preview has to fit
+			$scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1;
+			$newPreviewWidth = min($askedWidth, $this->maxPreviewWidth * $scaleFactor);
+			$newPreviewHeight = min($askedHeight, $this->maxPreviewHeight * $scaleFactor);
+			list($newPreviewWidth, $newPreviewHeight) =
+				$this->applyAspectRatio($newPreviewWidth, $newPreviewHeight);
+		} else {
+			list($newPreviewWidth, $newPreviewHeight) =
+				$this->fixSize($askedWidth, $askedHeight);
+		}
+
+		return [(int)$newPreviewWidth, (int)$newPreviewHeight];
+	}
+
+	/**
+	 * Resizes the boundaries to match the aspect ratio
+	 *
+	 * @param int $askedWidth
+	 * @param int $askedHeight
+	 *
+	 * @return \int[]
+	 */
+	private function applyAspectRatio($askedWidth, $askedHeight) {
+		$originalRatio = $this->maxPreviewRatio;
+		if ($askedWidth / $originalRatio < $askedHeight) {
+			$askedHeight = round($askedWidth / $originalRatio);
+		} else {
+			$askedWidth = round($askedHeight * $originalRatio);
+		}
+
+		return [(int)$askedWidth, (int)$askedHeight];
+	}
+
+	/**
+	 * Clips or stretches the dimensions so that they fit in the boundaries
+	 *
+	 * @param int $askedWidth
+	 * @param int $askedHeight
+	 *
+	 * @return array
+	 */
+	private function fixSize($askedWidth, $askedHeight) {
+		if ($this->scalingUp) {
+			$askedWidth = min($this->configMaxWidth, $askedWidth);
+			$askedHeight = min($this->configMaxHeight, $askedHeight);
+		}
 
-		$this->assertEquals($this->user . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/150-150.png', $isCached);
+		return [(int)$askedWidth, (int)$askedHeight];
 	}
-	*/
 }
diff --git a/tests/lib/preview/bitmap.php b/tests/lib/preview/bitmap.php
new file mode 100644
index 0000000..4911285
--- /dev/null
+++ b/tests/lib/preview/bitmap.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * @author Olivier Paroz <owncloud at interfasys.ch>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\Preview;
+
+class Bitmap extends Provider {
+
+	public function setUp() {
+		parent::setUp();
+
+		$fileName = 'testimage.eps';
+		$this->imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
+		$this->width = 2400;
+		$this->height = 1707;
+		$this->provider = new \OC\Preview\Postscript;
+	}
+
+}
diff --git a/tests/lib/preview/image.php b/tests/lib/preview/image.php
new file mode 100644
index 0000000..af46f4e
--- /dev/null
+++ b/tests/lib/preview/image.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * @author Olivier Paroz <owncloud at interfasys.ch>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\Preview;
+
+class Image extends Provider {
+
+	public function setUp() {
+		parent::setUp();
+
+		$fileName = 'testimage.jpg';
+		$this->imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
+		$this->width = 1680;
+		$this->height = 1050;
+		$this->provider = new \OC\Preview\JPEG();
+	}
+
+}
diff --git a/tests/lib/preview/movie.php b/tests/lib/preview/movie.php
new file mode 100644
index 0000000..c6b0c0f
--- /dev/null
+++ b/tests/lib/preview/movie.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @author Olivier Paroz <owncloud at interfasys.ch>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\Preview;
+
+class Movie extends Provider {
+
+	public function setUp() {
+		$avconvBinary = \OC_Helper::findBinaryPath('avconv');
+		$ffmpegBinary = ($avconvBinary) ? null : \OC_Helper::findBinaryPath('ffmpeg');
+
+		if ($avconvBinary || $ffmpegBinary) {
+			parent::setUp();
+
+			\OC\Preview\Movie::$avconvBinary = $avconvBinary;
+			\OC\Preview\Movie::$ffmpegBinary = $ffmpegBinary;
+
+			$fileName = 'testimage.mp4';
+			$this->imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
+			$this->width = 560;
+			$this->height = 320;
+			$this->provider = new \OC\Preview\Movie;
+		} else {
+			$this->markTestSkipped('No Movie provider present');
+		}
+	}
+
+}
diff --git a/tests/lib/preview/mp3.php b/tests/lib/preview/mp3.php
new file mode 100644
index 0000000..ac3ab07
--- /dev/null
+++ b/tests/lib/preview/mp3.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * @author Olivier Paroz <owncloud at interfasys.ch>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\Preview;
+
+class MP3 extends Provider {
+
+	public function setUp() {
+		parent::setUp();
+
+		$fileName = 'testimage.mp3';
+		$this->imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
+		$this->width = 200;
+		$this->height = 200;
+		$this->provider = new \OC\Preview\MP3;
+	}
+
+}
diff --git a/tests/lib/preview/office.php b/tests/lib/preview/office.php
new file mode 100644
index 0000000..22eeb0a
--- /dev/null
+++ b/tests/lib/preview/office.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * @author Olivier Paroz <owncloud at interfasys.ch>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\Preview;
+
+class Office extends Provider {
+
+	public function setUp() {
+		$libreofficeBinary = \OC_Helper::findBinaryPath('libreoffice');
+		$openofficeBinary = ($libreofficeBinary) ? null : \OC_Helper::findBinaryPath('openoffice');
+
+		if ($libreofficeBinary || $openofficeBinary) {
+			parent::setUp();
+
+			$fileName = 'testimage.odt';
+			$this->imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
+			$this->width = 595;
+			$this->height = 842;
+			$this->provider = new \OC\Preview\OpenDocument;
+		} else {
+			$this->markTestSkipped('No Office provider present');
+		}
+	}
+
+}
diff --git a/tests/lib/preview/provider.php b/tests/lib/preview/provider.php
new file mode 100644
index 0000000..62b5dde
--- /dev/null
+++ b/tests/lib/preview/provider.php
@@ -0,0 +1,187 @@
+<?php
+/**
+ * @author Olivier Paroz <owncloud at interfasys.ch>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\Preview;
+
+abstract class Provider extends \Test\TestCase {
+
+	/** @var string */
+	protected $imgPath;
+	/** @var int */
+	protected $width;
+	/** @var int */
+	protected $height;
+	/** @var \OC\Preview\Provider */
+	protected $provider;
+	/** @var int */
+	protected $maxWidth = 1024;
+	/** @var int */
+	protected $maxHeight = 1024;
+	/** @var bool */
+	protected $scalingUp = false;
+	/** @var int */
+	protected $userId;
+	/** @var \OC\Files\View */
+	protected $rootView;
+	/** @var \OC\Files\Storage\Storage */
+	protected $storage;
+
+	protected function setUp() {
+		parent::setUp();
+
+		$userManager = \OC::$server->getUserManager();
+		$userManager->clearBackends();
+		$backend = new \OC_User_Dummy();
+		$userManager->registerBackend($backend);
+
+		$userId = $this->getUniqueID();
+		$backend->createUser($userId, $userId);
+		$this->loginAsUser($userId);
+
+		$this->storage = new \OC\Files\Storage\Temporary([]);
+		\OC\Files\Filesystem::mount($this->storage, [], '/' . $userId . '/');
+
+		$this->rootView = new \OC\Files\View('');
+		$this->rootView->mkdir('/' . $userId);
+		$this->rootView->mkdir('/' . $userId . '/files');
+
+		$this->userId = $userId;
+	}
+
+	protected function tearDown() {
+		$this->logout();
+
+		parent::tearDown();
+	}
+
+	public static function dimensionsDataProvider() {
+		return [
+			[-rand(5, 100), -rand(5, 100)],
+			[rand(5, 100), rand(5, 100)],
+			[-rand(5, 100), rand(5, 100)],
+			[rand(5, 100), -rand(5, 100)],
+		];
+	}
+
+	/**
+	 * Launches all the tests we have
+	 *
+	 * @dataProvider dimensionsDataProvider
+	 *
+	 * @param int $widthAdjustment
+	 * @param int $heightAdjustment
+	 */
+	public function testGetThumbnail($widthAdjustment, $heightAdjustment) {
+		$ratio = round($this->width / $this->height, 2);
+		$this->maxWidth = $this->width - $widthAdjustment;
+		$this->maxHeight = $this->height - $heightAdjustment;
+
+		// Testing code
+		/*print_r("w $this->width ");
+		print_r("h $this->height ");
+		print_r("r $ratio ");*/
+
+		$preview = $this->getPreview($this->provider);
+		// The TXT provider uses the max dimensions to create its canvas,
+		// so the ratio will always be the one of the max dimension canvas
+		if (!$this->provider instanceof \OC\Preview\TXT) {
+			$this->doesRatioMatch($preview, $ratio);
+		}
+		$this->doesPreviewFit($preview);
+	}
+
+	/**
+	 * Adds the test file to the filesystem
+	 *
+	 * @param string $fileName name of the file to create
+	 * @param string $fileContent path to file to use for test
+	 *
+	 * @return string
+	 */
+	protected function prepareTestFile($fileName, $fileContent) {
+		$imgData = file_get_contents($fileContent);
+		$imgPath = '/' . $this->userId . '/files/' . $fileName;
+		$this->rootView->file_put_contents($imgPath, $imgData);
+
+		$scanner = $this->storage->getScanner();
+		$scanner->scan('');
+
+		return $imgPath;
+	}
+
+	/**
+	 * Retrieves a max size thumbnail can be created
+	 *
+	 * @param \OC\Preview\Provider $provider
+	 *
+	 * @return bool|\OCP\IImage
+	 */
+	private function getPreview($provider) {
+		$preview = $provider->getThumbnail($this->imgPath, $this->maxWidth, $this->maxHeight, $this->scalingUp, $this->rootView);
+
+		$this->assertNotEquals(false, $preview);
+		$this->assertEquals(true, $preview->valid());
+
+		return $preview;
+	}
+
+	/**
+	 * Checks if the preview ratio matches the original ratio
+	 *
+	 * @param \OCP\IImage $preview
+	 * @param int $ratio
+	 */
+	private function doesRatioMatch($preview, $ratio) {
+		$previewRatio = round($preview->width() / $preview->height(), 2);
+		$this->assertEquals($ratio, $previewRatio);
+	}
+
+	/**
+	 * Tests if a max size preview of smaller dimensions can be created
+	 *
+	 * @param \OCP\IImage $preview
+	 */
+	private function doesPreviewFit($preview) {
+		$maxDimRatio = round($this->maxWidth / $this->maxHeight, 2);
+		$previewRatio = round($preview->width() / $preview->height(), 2);
+
+		// Testing code
+		/*print_r("mw $this->maxWidth ");
+		print_r("mh $this->maxHeight ");
+		print_r("mr $maxDimRatio ");
+		$pw = $preview->width();
+		$ph = $preview->height();
+		print_r("pw $pw ");
+		print_r("ph $ph ");
+		print_r("pr $previewRatio ");*/
+
+		if ($maxDimRatio < $previewRatio) {
+			$this->assertLessThanOrEqual($this->maxWidth, $preview->width());
+			$this->assertLessThan($this->maxHeight, $preview->height());
+		} elseif ($maxDimRatio > $previewRatio) {
+			$this->assertLessThan($this->maxWidth, $preview->width());
+			$this->assertLessThanOrEqual($this->maxHeight, $preview->height());
+		} else { // Original had to be resized
+			$this->assertLessThanOrEqual($this->maxWidth, $preview->width());
+			$this->assertLessThanOrEqual($this->maxHeight, $preview->height());
+		}
+	}
+}
diff --git a/tests/lib/preview/svg.php b/tests/lib/preview/svg.php
new file mode 100644
index 0000000..768569c
--- /dev/null
+++ b/tests/lib/preview/svg.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * @author Olivier Paroz <owncloud at interfasys.ch>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\Preview;
+
+class SVG extends Provider {
+
+	public function setUp() {
+		$checkImagick = new \Imagick();
+		if (count($checkImagick->queryFormats('SVG')) === 1) {
+			parent::setUp();
+
+			$fileName = 'testimagelarge.svg';
+			$this->imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
+			$this->width = 3000;
+			$this->height = 2000;
+			$this->provider = new \OC\Preview\SVG;
+		} else {
+			$this->markTestSkipped('No SVG provider present');
+		}
+	}
+
+}
diff --git a/tests/lib/preview/txt.php b/tests/lib/preview/txt.php
new file mode 100644
index 0000000..8bda86f
--- /dev/null
+++ b/tests/lib/preview/txt.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * @author Olivier Paroz <owncloud at interfasys.ch>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\Preview;
+
+class TXT extends Provider {
+
+	public function setUp() {
+		parent::setUp();
+
+		$fileName = 'lorem-big.txt';
+		$this->imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
+		// Arbitrary width and length which won't be used to calculate the ratio
+		$this->width = 500;
+		$this->height = 200;
+		$this->provider = new \OC\Preview\TXT;
+	}
+
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-owncloud/owncloud.git



More information about the Pkg-owncloud-commits mailing list