diff --git a/docs/image-manipulations/optimizing-images.md b/docs/image-manipulations/optimizing-images.md index d0c924e5..5969c1ef 100644 --- a/docs/image-manipulations/optimizing-images.md +++ b/docs/image-manipulations/optimizing-images.md @@ -5,7 +5,7 @@ weight: 3 ## Requirements -Optimization of images is done by the underlying [spatie/image-optimizer](https://github.com/spatie/image-optimizer). It assumes that there are a few optimization tools, such as [JpegOptim](http://freecode.com/projects/jpegoptim) an [Pngquant](https://pngquant.org/) present on your system. For more info, check out [the relevant docs](https://github.com/spatie/image-optimizer#optimization-tools). +Optimization of images is done by the underlying [spatie/image-optimizer](https://github.com/spatie/image-optimizer) package. It assumes that there are a few optimization tools, such as [JpegOptim](http://freecode.com/projects/jpegoptim) an [Pngquant](https://pngquant.org/) present on your system. For more info, check out [the relevant docs](https://github.com/spatie/image-optimizer#optimization-tools). ## How to use @@ -28,17 +28,35 @@ No matter where or how many times you call `optimize` in you chain, it will alwa ## Customizing the optimization -To optimization of images is done by the underlying [spatie/image-optimizer](https://github.com/spatie/image-optimizer) package. You can pass your own customized chains as array. The keys should be fully qualified class names of optimizers and the values the options that they should get. Here's an example +You are able to customize the optimization by passing an options array to the `optimize` method. The `'optimizers'` settings key lets you to pass your own customized chain of optimizers as an array. The keys should be fully qualified class names of optimizers and the values should be the options that they should get. ```php Image::load('example.jpg') - ->optimize([Jpegoptim::class => [ - '--all-progressive', - ]]) + ->optimize([ + 'optimizers' => [ + Jpegoptim::class => [ + '--all-progressive', + ], + ], + ]) ->save(); ``` -If you need more control over the optimizer chain, you can still pass your own instance of `OptimizerChain`. It can be especially useful if you need to set a custom timeout or a custom binary path. You may not have enough privileges to install the necessary binaries on your server but you can still upload some [precompiled binaries](https://github.com/imagemin?q=bin&type=&language=). +Besides the options array you may pass the `$timeout` argument. It lets you to set a custom timeout in seconds other than the default 60s. Adjusting this setting may be inevitable while working with large images (see e.g. [#187](https://github.com/spatie/image/pull/187)). + +```php +Image::load('example.jpg') + ->optimize([ + 'optimizers' => [ + Jpegoptim::class => [ + '--all-progressive', + ], + ], + ], timeout: 120) + ->save(); +``` + +If you need more control over the optimizer chain, you can still pass your own instance of `OptimizerChain`. It can be especially useful if you need to set a custom binary path. You may not have enough privileges to install the necessary binaries on your server but you can still upload some [precompiled binaries](https://github.com/imagemin?q=bin&type=&language=). ```php $optimizer = new OptimizerChain(); diff --git a/src/Image.php b/src/Image.php index 891889b5..a6c57635 100644 --- a/src/Image.php +++ b/src/Image.php @@ -140,8 +140,17 @@ protected function performOptimization($path, array $optimizerChainConfiguration $optimizerChain = $this->optimizerChain ?? OptimizerChainFactory::create(); if (count($optimizerChainConfiguration)) { + if (isset($optimizerChainConfiguration['timeout'])) { + $optimizerChain->setTimeout($optimizerChainConfiguration['timeout']); + // unsetting the 'timeout' key for the backward compatibility for configuration arrays not having the 'optimizers' key + unset($optimizerChainConfiguration['timeout']); + } + + $optimizersOptions = isset($optimizerChainConfiguration['optimizers']) + ? $optimizerChainConfiguration['optimizers'] + // backward compatibility for configuration arrays not having the 'optimizers' key + : $optimizerChainConfiguration; $existingOptimizers = $optimizerChain->getOptimizers(); - $optimizers = array_map(function (array $optimizerOptions, string $optimizerClassName) use ($existingOptimizers) { $optimizer = array_values(array_filter($existingOptimizers, function ($optimizer) use ($optimizerClassName) { return $optimizer::class === $optimizerClassName; @@ -150,7 +159,7 @@ protected function performOptimization($path, array $optimizerChainConfiguration $optimizer = isset($optimizer[0]) && $optimizer[0] instanceof BaseOptimizer ? $optimizer[0] : new $optimizerClassName(); return $optimizer->setOptions($optimizerOptions)->setBinaryPath($optimizer->binaryPath); - }, $optimizerChainConfiguration, array_keys($optimizerChainConfiguration)); + }, $optimizersOptions, array_keys($optimizersOptions)); $optimizerChain->setOptimizers($optimizers); } diff --git a/src/Manipulations.php b/src/Manipulations.php index 92103133..b4b1eec9 100644 --- a/src/Manipulations.php +++ b/src/Manipulations.php @@ -508,8 +508,12 @@ public function watermarkOpacity(int $opacity): static /** * Shave off some kilobytes by optimizing the image. */ - public function optimize(array $optimizationOptions = []): static + public function optimize(array $optimizationOptions = [], ?int $timeout = null): static { + if ($timeout !== null) { + $optimizationOptions['timeout'] = $timeout; + } + return $this->addManipulation('optimize', json_encode($optimizationOptions)); } diff --git a/tests/Manipulations/OptimizeTest.php b/tests/Manipulations/OptimizeTest.php index 54aa476d..bdf11834 100644 --- a/tests/Manipulations/OptimizeTest.php +++ b/tests/Manipulations/OptimizeTest.php @@ -31,9 +31,25 @@ it('can optimize an image with the given optimization options', function () { $targetFile = $this->tempDir->path('optimized.jpg'); + Image::load(getTestFile('test.jpg')) + ->optimize([ + 'optimizers' => [ + Jpegoptim::class => [ + '--all-progressive', + ], + ], + ]) + ->save($targetFile); + + expect($targetFile)->toBeFile(); +}); + +it('can optimize an image with options format backward compatibility', function () { + $targetFile = $this->tempDir->path('optimized.jpg'); + Image::load(getTestFile('test.jpg')) ->optimize([Jpegoptim::class => [ - '--all-progressive', + '--all-progressive', ]]) ->save($targetFile); @@ -46,14 +62,63 @@ Image::load(getTestFile('test.jpg')) ->setOptimizeChain(OptimizerChainFactory::create()) ->optimize([ - Pngquant::class => [ - '--force', - ], - Jpegoptim::class => [ - '--all-progressive', - ], + 'optimizers' => [ + Pngquant::class => [ + '--force', + ], + Jpegoptim::class => [ + '--all-progressive', + ], + ], ]) ->save($targetFile); expect($targetFile)->toBeFile(); }); + +it('can optimize an image specifying a desired timeout with a configuration array key', function () { + $targetFile = $this->tempDir->path('optimized.jpg'); + + Image::load(getTestFile('test.jpg')) + ->setOptimizeChain(OptimizerChainFactory::create()) + ->optimize([ + 'timeout' => 120, + 'optimizers' => [ + Jpegoptim::class => [ + '--all-progressive', + ], + ], + ]) + ->save($targetFile); + + expect($targetFile)->toBeFile(); +}); + +it('can optimize an image specifying a desired timeout with a method argument', function () { + $targetFile = $this->tempDir->path('optimized.jpg'); + + Image::load(getTestFile('test.jpg')) + ->setOptimizeChain(OptimizerChainFactory::create()) + ->optimize([ + 'optimizers' => [ + Jpegoptim::class => [ + '--all-progressive', + ], + ], + ], timeout: 120) + ->save($targetFile); + + expect($targetFile)->toBeFile(); +}); + +it('can optimize an image with options format backward compatibility and timeout parameter', function () { + $targetFile = $this->tempDir->path('optimized.jpg'); + + Image::load(getTestFile('test.jpg')) + ->optimize([Jpegoptim::class => [ + '--all-progressive', + ]], timeout: 120) + ->save($targetFile); + + expect($targetFile)->toBeFile(); +});