storeAs('', $path, $disk); return false; } Storage::disk($disk)->put($path, $optimised); return true; } /** * Re-encoded image bytes, or null when the file should be stored as-is. */ public static function encode(UploadedFile $file): ?string { if (! extension_loaded('gd')) { return null; } $maxDimension = (int) config('qr.image.max_dimension', 2000); $quality = (int) config('qr.image.jpeg_quality', 82); try { $realPath = $file->getRealPath(); if ($realPath === false || ! is_readable($realPath)) { return null; } $info = @getimagesize($realPath); if ($info === false) { return null; // Not a raster image GD understands (e.g. SVG). } [$width, $height, $type] = $info; if (! in_array($type, [IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_WEBP], true)) { // GIF excluded on purpose: GD would drop animation frames. return null; } // Already small enough and not a heavyweight file: leave it alone rather // than lossily re-encoding something that costs nothing to serve. $longEdge = max($width, $height); if ($longEdge <= $maxDimension && $file->getSize() <= (int) config('qr.image.passthrough_bytes', 512000)) { return null; } $source = match ($type) { IMAGETYPE_JPEG => @imagecreatefromjpeg($realPath), IMAGETYPE_PNG => @imagecreatefrompng($realPath), IMAGETYPE_WEBP => @imagecreatefromwebp($realPath), }; if (! $source) { return null; } $target = self::resize($source, $width, $height, $maxDimension, $type); ob_start(); $ok = match ($type) { IMAGETYPE_JPEG => imagejpeg($target, null, $quality), // PNG 6 is a size/CPU compromise; PNG re-encode is lossless. IMAGETYPE_PNG => imagepng($target, null, 6), IMAGETYPE_WEBP => imagewebp($target, null, $quality), }; $bytes = (string) ob_get_clean(); imagedestroy($source); if ($target !== $source) { imagedestroy($target); } if (! $ok || $bytes === '') { return null; } // Never make a file bigger than it started. return strlen($bytes) < $file->getSize() ? $bytes : null; } catch (Throwable $e) { Log::warning('image optimisation failed, storing original', [ 'file' => $file->getClientOriginalName(), 'error' => $e->getMessage(), ]); return null; } } /** * @param \GdImage $source * @return \GdImage */ private static function resize($source, int $width, int $height, int $maxDimension, int $type) { $longEdge = max($width, $height); if ($longEdge <= $maxDimension) { return $source; } $scale = $maxDimension / $longEdge; $newWidth = max(1, (int) round($width * $scale)); $newHeight = max(1, (int) round($height * $scale)); $target = imagecreatetruecolor($newWidth, $newHeight); if ($type === IMAGETYPE_PNG || $type === IMAGETYPE_WEBP) { // Keep alpha; without this transparent areas come back black. imagealphablending($target, false); imagesavealpha($target, true); $transparent = imagecolorallocatealpha($target, 0, 0, 0, 127); imagefilledrectangle($target, 0, 0, $newWidth, $newHeight, $transparent); } imagecopyresampled($target, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height); return $target; } }