diff --git a/apps/theming/lib/ImageManager.php b/apps/theming/lib/ImageManager.php index 6f9892af10e38..7c3d4a2e4d684 100644 --- a/apps/theming/lib/ImageManager.php +++ b/apps/theming/lib/ImageManager.php @@ -232,29 +232,67 @@ public function updateImage(string $key, string $tmpFile): string { } if ($key === 'background' && strpos($detectedMimeType, 'image/svg') === false && strpos($detectedMimeType, 'image/gif') === false) { - // Optimize the image since some people may upload images that will be - // either to big or are not progressive rendering. - $newImage = @imagecreatefromstring(file_get_contents($tmpFile)); + $tmpFile = $this->getResizedFilePath($tmpFile, $detectedMimeType); + } - // Preserve transparency - imagesavealpha($newImage, true); - imagealphablending($newImage, true); + $target->putContent(file_get_contents($tmpFile)); + return $detectedMimeType; + } - $tmpFile = $this->tempManager->getTemporaryFile(); - $newWidth = (int)(imagesx($newImage) < 4096 ? imagesx($newImage) : 4096); - $newHeight = (int)(imagesy($newImage) / (imagesx($newImage) / $newWidth)); - $outputImage = imagescale($newImage, $newWidth, $newHeight); + /** + * For png and webp files: Returns the file path of the file resized to a maximum of 4096 pixels wide (preserving aspect ratio) + * but only if the resizing results in an image that has a smaller file size than the uploaded file. For jpeg files: + * Will always re-save the file at quality 80 (not too low, not too high to create large files). + * + * @param string $originalTmpFile The tmpFile(path) as uploaded by the user + * @param string $detectedMimeType The the MIME type of the file as uploaded by the user + * @return string Location of the resized file, or the original if that was the smaller of the two + */ + private function getResizedFilePath(string $originalTmpFile, string $detectedMimeType): string + { + // Optimize the image since some people may upload images that will be + // either to big or are not progressive rendering. + $newImage = @imagecreatefromstring(file_get_contents($originalTmpFile)); - imageinterlace($outputImage, 1); - imagepng($outputImage, $tmpFile, 8); - imagedestroy($outputImage); + // Preserve transparency + imagesavealpha($newImage, true); + imagealphablending($newImage, true); - $target->putContent(file_get_contents($tmpFile)); - } else { - $target->putContent(file_get_contents($tmpFile)); + $newImageTmpFile = $this->tempManager->getTemporaryFile(); + $newWidth = (int)(imagesx($newImage) < 4096 ? imagesx($newImage) : 4096); + $newHeight = (int)(imagesy($newImage) / (imagesx($newImage) / $newWidth)); + $outputImage = imagescale($newImage, $newWidth, $newHeight); + + imageinterlace($outputImage, 1); + + $checkIfFileSizeWasReduced = false; + + // Preserve the original file format + switch($detectedMimeType) { + case 'image/png': + imagepng($outputImage, $newImageTmpFile, 8); + $checkIfFileSizeWasReduced = true; + break; + case 'image/webp': + imagewebp($outputImage, $newImageTmpFile, 80); + $checkIfFileSizeWasReduced = true; + break; + case 'image/jpeg': + imagejpeg($outputImage, $newImageTmpFile, 80); + // we have no convenient way if knowing if the original file was a progressive + // jpeg, so we just need to re-save it. + $checkIfFileSizeWasReduced = false; + break; } - return $detectedMimeType; + imagedestroy($outputImage); + + $newImageIsLarger = filesize($newImageTmpFile) >= filesize($originalTmpFile); + if($checkIfFileSizeWasReduced && $newImageIsLarger) { + return $originalTmpFile; + } + + return $newImageTmpFile; } /**