diff --git a/src/Bridge/Nette/DI/ImageStorageExtension.php b/src/Bridge/Nette/DI/ImageStorageExtension.php index 1b4022a..d58965b 100644 --- a/src/Bridge/Nette/DI/ImageStorageExtension.php +++ b/src/Bridge/Nette/DI/ImageStorageExtension.php @@ -147,7 +147,8 @@ public function getConfigSchema(): Schema [ new Statement(Applicator\Orientation::class), new Statement(Applicator\Resize::class), - new Statement(Applicator\Format::class), # must be last + new Statement(Applicator\Format::class), + new Statement(Applicator\StripMeta::class), # must be last ], ); diff --git a/src/Modifier/Applicator/Format.php b/src/Modifier/Applicator/Format.php index e405847..d6c7e92 100644 --- a/src/Modifier/Applicator/Format.php +++ b/src/Modifier/Applicator/Format.php @@ -8,7 +8,6 @@ use Intervention\Image\Image; use SixtyEightPublishers\FileStorage\Config\ConfigInterface; use SixtyEightPublishers\FileStorage\PathInfoInterface; -use SixtyEightPublishers\ImageStorage\Config\Config; use SixtyEightPublishers\ImageStorage\Exception\InvalidArgumentException; use SixtyEightPublishers\ImageStorage\Helper\SupportedType; use SixtyEightPublishers\ImageStorage\Modifier\Collection\ModifierValues; @@ -17,7 +16,7 @@ final class Format implements ModifierApplicatorInterface { - public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): ?Image + public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): iterable { $extension = $this->getFileExtension($image, $pathInfo); $quality = $values->getOptional(Quality::class); @@ -29,7 +28,7 @@ public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues } if (!$needEncode) { - return null; + return; } if (in_array($extension, ['jpg', 'pjpg'], true)) { @@ -43,7 +42,12 @@ public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues } } - return $image->encode($extension, (int) ($quality ?? $config[Config::ENCODE_QUALITY])); + yield self::OutImage => $image; + yield self::OutFormat => $extension; + + if (null !== $quality) { + yield self::OutQuality => (int) $quality; + } } private function getFileExtension(Image $image, PathInfoInterface $pathInfo): string diff --git a/src/Modifier/Applicator/ModifierApplicatorInterface.php b/src/Modifier/Applicator/ModifierApplicatorInterface.php index c29b307..f27e510 100644 --- a/src/Modifier/Applicator/ModifierApplicatorInterface.php +++ b/src/Modifier/Applicator/ModifierApplicatorInterface.php @@ -11,8 +11,19 @@ interface ModifierApplicatorInterface { + public const OutImage = 'image'; + public const OutFormat = 'format'; + public const OutQuality = 'quality'; + /** - * Returns NULL of image is not modified + * Allowed outputs: + * "image": Image + * "format": string + * "quality": int + * + * If nothing changed, then nothing should be returned. + * + * @return iterable */ - public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): ?Image; + public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): iterable; } diff --git a/src/Modifier/Applicator/Orientation.php b/src/Modifier/Applicator/Orientation.php index def0b76..cb1cf23 100644 --- a/src/Modifier/Applicator/Orientation.php +++ b/src/Modifier/Applicator/Orientation.php @@ -14,24 +14,24 @@ final class Orientation implements ModifierApplicatorInterface { - public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): ?Image + public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): iterable { $orientation = $values->getOptional(OrientationModifier::class); if (!is_string($orientation) && !is_numeric($orientation)) { - return null; + return; } if ('auto' === $orientation) { $exifOrientation = $image->exif('Orientation'); if (2 <= $exifOrientation && 8 >= $exifOrientation) { - return $image->orientate(); + yield self::OutImage => $image->orientate(); } - return null; + return; } - return $image->rotate((float) $orientation); + yield self::OutImage => $image->rotate((float) $orientation); } } diff --git a/src/Modifier/Applicator/Resize.php b/src/Modifier/Applicator/Resize.php index f0a08cf..f52f7eb 100644 --- a/src/Modifier/Applicator/Resize.php +++ b/src/Modifier/Applicator/Resize.php @@ -27,7 +27,7 @@ final class Resize implements ModifierApplicatorInterface { - public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): ?Image + public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): iterable { $width = $values->getOptional(Width::class); $height = $values->getOptional(Height::class); @@ -70,28 +70,24 @@ public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $height = (int) ($height * $pd); if ($width === $imageWidth && $height === $imageHeight) { - return null; + return; } - switch ($fit) { - case Fit::CONTAIN: - return $image->resize($width, $height, static function (Constraint $constraint) { - $constraint->aspectRatio(); - }); - - case Fit::STRETCH: - return $image->resize($width, $height); - case Fit::FILL: - return $image->resize($width, $height, static function (Constraint $constraint) { - $constraint->aspectRatio(); - $constraint->upsize(); - })->resizeCanvas($width, $height, 'center'); - } - - if (0 === strncmp($fit, 'crop-', 5)) { - $fit = substr($fit, 5); - } - - return $image->fit($width, $height, null, $fit); + yield self::OutImage => match ($fit) { + Fit::CONTAIN => $image->resize($width, $height, static function (Constraint $constraint) { + $constraint->aspectRatio(); + }), + Fit::STRETCH => $image->resize($width, $height), + Fit::FILL => $image->resize($width, $height, static function (Constraint $constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + })->resizeCanvas($width, $height, 'center'), + default => $image->fit( + $width, + $height, + null, + 0 === strncmp($fit, 'crop-', 5) ? substr($fit, 5) : $fit, + ), + }; } } diff --git a/src/Modifier/Applicator/StripMeta.php b/src/Modifier/Applicator/StripMeta.php new file mode 100644 index 0000000..ce29a92 --- /dev/null +++ b/src/Modifier/Applicator/StripMeta.php @@ -0,0 +1,41 @@ +getOptional('__stripMeta', false)) { + return []; + } + + $core = $image->getCore(); + + if (!($core instanceof Imagick)) { + return []; + } + + $profiles = $core->getImageProfiles('icc'); + + $core->stripImage(); + + if (isset($profiles['icc'])) { + $core->profileImage('icc', $profiles['icc']); + } + + return []; + } +} diff --git a/src/Modifier/Collection/ModifierValues.php b/src/Modifier/Collection/ModifierValues.php index 29abd10..7523bec 100644 --- a/src/Modifier/Collection/ModifierValues.php +++ b/src/Modifier/Collection/ModifierValues.php @@ -44,7 +44,7 @@ public function getOptional(string $name, mixed $default = null): mixed return $this->has($name) ? $this->values[$name] : $default; } - private function add(string $name, mixed $value): void + public function add(string $name, mixed $value): void { $this->values[$name] = $value; } diff --git a/src/Modifier/Facade/ModifierFacade.php b/src/Modifier/Facade/ModifierFacade.php index 1454b48..86b4cc7 100644 --- a/src/Modifier/Facade/ModifierFacade.php +++ b/src/Modifier/Facade/ModifierFacade.php @@ -102,7 +102,7 @@ public function getCodec(): CodecInterface return $this->codec; } - public function modifyImage(Image $image, PathInfoInterface $info, string|array $modifiers): ModifyResult + public function modifyImage(Image $image, PathInfoInterface $info, string|array $modifiers, bool $stripMeta = false): ModifyResult { if (!is_array($modifiers)) { $modifiers = $this->getCodec()->decode(new PresetValue($modifiers)); @@ -114,24 +114,46 @@ public function modifyImage(Image $image, PathInfoInterface $info, string|array $values = $this->modifierCollection->parseValues($modifiers); + if ($stripMeta) { + $values->add('__stripMeta', true); + } + foreach ($this->validators as $validator) { $validator->validate($values, $this->config); } $modified = false; + $encodeFormat = null; + $encodeQuality = null; foreach ($this->applicators as $applicator) { - $modifiedImage = $applicator->apply($image, $info, $values, $this->config); + foreach ($applicator->apply($image, $info, $values, $this->config) as $key => $value) { + if (ModifierApplicatorInterface::OutImage === $key && $value instanceof Image) { + $image = $value; + $modified = true; + + continue; + } + + if (ModifierApplicatorInterface::OutFormat === $key) { + $encodeFormat = $value; + $modified = true; + + continue; + } - if (null !== $modifiedImage) { - $image = $modifiedImage; - $modified = true; + if (ModifierApplicatorInterface::OutQuality === $key) { + $encodeQuality = $value; + $modified = true; + } } } return new ModifyResult( image: $image, modified: $modified, + encodeFormat: $encodeFormat, + encodeQuality: $encodeQuality, ); } } diff --git a/src/Modifier/Facade/ModifierFacadeInterface.php b/src/Modifier/Facade/ModifierFacadeInterface.php index 199c3e7..f4cc58a 100644 --- a/src/Modifier/Facade/ModifierFacadeInterface.php +++ b/src/Modifier/Facade/ModifierFacadeInterface.php @@ -41,5 +41,5 @@ public function getCodec(): CodecInterface; /** * @param string|array $modifiers */ - public function modifyImage(Image $image, PathInfoInterface $info, string|array $modifiers): ModifyResult; + public function modifyImage(Image $image, PathInfoInterface $info, string|array $modifiers, bool $stripMeta = false): ModifyResult; } diff --git a/src/Modifier/Facade/ModifyResult.php b/src/Modifier/Facade/ModifyResult.php index bbf1522..dfa1d7c 100644 --- a/src/Modifier/Facade/ModifyResult.php +++ b/src/Modifier/Facade/ModifyResult.php @@ -11,5 +11,7 @@ final class ModifyResult public function __construct( public readonly Image $image, public readonly bool $modified, + public readonly ?string $encodeFormat, + public readonly ?int $encodeQuality, ) {} } diff --git a/src/Persistence/ImagePersister.php b/src/Persistence/ImagePersister.php index 2765db1..72657fe 100644 --- a/src/Persistence/ImagePersister.php +++ b/src/Persistence/ImagePersister.php @@ -18,7 +18,6 @@ use SixtyEightPublishers\ImageStorage\Resource\ResourceInterface as ImageResourceInterface; use SixtyEightPublishers\ImageStorage\Resource\TmpFileImageResource; use function assert; -use function is_scalar; use function preg_match; use function preg_quote; use function sprintf; @@ -53,7 +52,7 @@ public function save(ResourceInterface $resource, array $config = []): string $pathInfo = $this->assertPathInfo($resource->getPathInfo(), __METHOD__); if (null !== $pathInfo->getModifiers()) { - $resource = $resource->modifyImage($pathInfo->getModifiers()); + $resource = $resource->modifyImage($pathInfo->getModifiers(), true); $prefix = self::FILESYSTEM_PREFIX_CACHE; } else { @@ -138,9 +137,10 @@ private function encodeImage(ImageResourceInterface $resource): string } } - $quality = $this->config[Config::ENCODE_QUALITY]; + $quality = (int) ($resource->getEncodeQuality() ?? $this->config[Config::ENCODE_QUALITY] ?? 90); + $format = $resource->getEncodeFormat() ?? ''; $image = $resource->getSource(); - $image = $image->isEncoded() ? $image : $image->encode('', is_scalar($quality) ? (int) $quality : 90); + $image = $image->encode($format, $quality); return $image->getEncoded(); } diff --git a/src/Resource/ImageResource.php b/src/Resource/ImageResource.php index ccaee45..04caf2b 100644 --- a/src/Resource/ImageResource.php +++ b/src/Resource/ImageResource.php @@ -12,6 +12,10 @@ class ImageResource implements ResourceInterface { private bool $modified = false; + private ?string $encodeFormat = null; + + private ?int $encodeQuality = null; + public function __construct( private PathInfoInterface $pathInfo, private Image $image, @@ -47,12 +51,23 @@ public function withPathInfo(PathInfoInterface $pathInfo): self return $resource; } - public function modifyImage(string|array $modifiers): self + public function modifyImage(string|array $modifiers, bool $stripMeta = false): self { $resource = clone $this; - $modifyResult = $this->modifierFacade->modifyImage($this->image, $this->pathInfo, $modifiers); + $modifyResult = $this->modifierFacade->modifyImage($this->image, $this->pathInfo, $modifiers, $stripMeta); $resource->image = $modifyResult->image; - $resource->modified = $modifyResult->modified; + + if ($modifyResult->modified) { + $resource->modified = $modifyResult->modified; + } + + if (null !== $modifyResult->encodeFormat) { + $resource->encodeFormat = $modifyResult->encodeFormat; + } + + if (null !== $modifyResult->encodeQuality) { + $resource->encodeQuality = $modifyResult->encodeQuality; + } return $resource; } @@ -68,4 +83,14 @@ public function getFilesize(): ?int return false !== $filesize ? (int) $filesize : null; } + + public function getEncodeQuality(): ?int + { + return $this->encodeQuality; + } + + public function getEncodeFormat(): ?string + { + return $this->encodeFormat; + } } diff --git a/src/Resource/ResourceInterface.php b/src/Resource/ResourceInterface.php index 74ddefd..57dbec0 100644 --- a/src/Resource/ResourceInterface.php +++ b/src/Resource/ResourceInterface.php @@ -18,5 +18,9 @@ public function hasBeenModified(): bool; /** * @param string|array $modifiers */ - public function modifyImage(string|array $modifiers): self; + public function modifyImage(string|array $modifiers, bool $stripMeta = false): self; + + public function getEncodeQuality(): ?int; + + public function getEncodeFormat(): ?string; } diff --git a/tests/Bridge/Nette/DI/ImageStorageExtensionTest.php b/tests/Bridge/Nette/DI/ImageStorageExtensionTest.php index eeb8cd8..10e73fc 100644 --- a/tests/Bridge/Nette/DI/ImageStorageExtensionTest.php +++ b/tests/Bridge/Nette/DI/ImageStorageExtensionTest.php @@ -146,6 +146,7 @@ public function testExtensionShouldBeIntegratedWithMinimalConfiguration(): void Applicator\Orientation::class, Applicator\Resize::class, Applicator\Format::class, + Applicator\StripMeta::class, ], validatorTypes: [ Validator\AllowedResolutionValidator::class, @@ -250,6 +251,7 @@ public function testExtensionShouldBeIntegratedWithCustomModifiersAndApplicators Applicator\Orientation::class, Applicator\Resize::class, Applicator\Format::class, + Applicator\StripMeta::class, ], validatorTypes: [ TestValidator::class, diff --git a/tests/Fixtures/TestApplicator.php b/tests/Fixtures/TestApplicator.php index 164bc39..1e8c775 100644 --- a/tests/Fixtures/TestApplicator.php +++ b/tests/Fixtures/TestApplicator.php @@ -12,7 +12,8 @@ final class TestApplicator implements ModifierApplicatorInterface { - public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): Image + public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): iterable { + return []; } } diff --git a/tests/Modifier/Applicator/FormatTest.phpt b/tests/Modifier/Applicator/FormatTest.phpt index a1337ff..ef33495 100644 --- a/tests/Modifier/Applicator/FormatTest.phpt +++ b/tests/Modifier/Applicator/FormatTest.phpt @@ -36,7 +36,7 @@ final class FormatTest extends TestCase $applicator = new Format(); - Assert::null($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same([], iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config))); } public function testNullShouldBeReturnedIfQualityIsNotSpecifiedAndPathInfoExtensionIsNull(): void @@ -52,7 +52,7 @@ final class FormatTest extends TestCase $applicator = new Format(); - Assert::null($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same([], iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config))); } public function testImageShouldBeEncodedInDefaultFormatIfPathInfoExtensionIsNullAndImageMimeTypeIsUnsupported(): void @@ -60,7 +60,7 @@ final class FormatTest extends TestCase $image = Mockery::mock(Image::class); $pathInfo = $this->createPathInfo(null); $modifierValues = $this->createModifierValues(null); - $config = $this->createConfigForEncode(); + $config = Mockery::mock(ConfigInterface::class); $image->shouldReceive('mime') ->withNoArgs() @@ -68,42 +68,34 @@ final class FormatTest extends TestCase $modifiedImage = $this->setupJpgExpectationsOnImage($image, false); - $modifiedImage->shouldReceive('encode') - ->once() - ->with('jpg', 90) - ->andReturn($modifiedImage); - $applicator = new Format(); - Assert::same($modifiedImage, $applicator->apply($image, $pathInfo, $modifierValues, $config)); + $result = iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same($modifiedImage, $result['image']); + Assert::same('jpg', $result['format']); } public function testImageShouldBeEncodedIfPathInfoExtensionIsDifferentThanImageMimeType(): void { $image = Mockery::mock(Image::class); - $modifiedImage = Mockery::mock(Image::class); $pathInfo = $this->createPathInfo('webp'); $modifierValues = $this->createModifierValues(null); - $config = $this->createConfigForEncode(); + $config = Mockery::mock(ConfigInterface::class); $image->shouldReceive('mime') ->withNoArgs() ->andReturn('image/jpeg'); - $image->shouldReceive('encode') - ->once() - ->with('webp', 90) - ->andReturn($modifiedImage); - $applicator = new Format(); - Assert::same($modifiedImage, $applicator->apply($image, $pathInfo, $modifierValues, $config)); + $result = iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same($image, $result['image']); + Assert::same('webp', $result['format']); } public function testImageShouldBeEncodedIfQualityIsSpecified(): void { $image = Mockery::mock(Image::class); - $modifiedImage = Mockery::mock(Image::class); $pathInfo = $this->createPathInfo(null); $modifierValues = $this->createModifierValues(75); $config = Mockery::mock(ConfigInterface::class); @@ -112,14 +104,12 @@ final class FormatTest extends TestCase ->withNoArgs() ->andReturn('image/png'); - $image->shouldReceive('encode') - ->once() - ->with('png', 75) - ->andReturn($modifiedImage); - $applicator = new Format(); - Assert::same($modifiedImage, $applicator->apply($image, $pathInfo, $modifierValues, $config)); + $result = iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same($image, $result['image']); + Assert::same('png', $result['format']); + Assert::same(75, $result['quality']); } public function testImageShouldBeEncodedToJpegFromDifferentFormat(): void @@ -127,7 +117,7 @@ final class FormatTest extends TestCase $image = Mockery::mock(Image::class); $pathInfo = $this->createPathInfo('jpg'); $modifierValues = $this->createModifierValues(null); - $config = $this->createConfigForEncode(); + $config = Mockery::mock(ConfigInterface::class); $image->shouldReceive('mime') ->withNoArgs() @@ -135,14 +125,11 @@ final class FormatTest extends TestCase $modifiedImage = $this->setupJpgExpectationsOnImage($image, false); - $modifiedImage->shouldReceive('encode') - ->once() - ->with('jpg', 90) - ->andReturn($modifiedImage); - $applicator = new Format(); - Assert::same($modifiedImage, $applicator->apply($image, $pathInfo, $modifierValues, $config)); + $result = iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same($modifiedImage, $result['image']); + Assert::same('jpg', $result['format']); } /** @@ -153,7 +140,7 @@ final class FormatTest extends TestCase $image = Mockery::mock(Image::class); $pathInfo = $this->createPathInfo('pjpg'); $modifierValues = $this->createModifierValues(null); - $config = $this->createConfigForEncode(); + $config = Mockery::mock(ConfigInterface::class); $image->shouldReceive('mime') ->withNoArgs() @@ -165,14 +152,11 @@ final class FormatTest extends TestCase $modifiedImage = $this->setupJpgExpectationsOnImage($image, true); - $modifiedImage->shouldReceive('encode') - ->once() - ->with('jpg', 90) - ->andReturn($modifiedImage); - $applicator = new Format(); - Assert::same($modifiedImage, $applicator->apply($image, $pathInfo, $modifierValues, $config)); + $result = iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same($modifiedImage, $result['image']); + Assert::same('jpg', $result['format']); } public function testImageShouldNotBeEncodedToProgressiveJpeg(): void @@ -198,7 +182,7 @@ final class FormatTest extends TestCase $applicator = new Format(); - Assert::null($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same([], iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config))); } public function provideImageCoresForProgressiveJpegWithInvalidInterlaceScheme(): array diff --git a/tests/Modifier/Applicator/OrientationTest.phpt b/tests/Modifier/Applicator/OrientationTest.phpt index f991a46..eb09f4c 100644 --- a/tests/Modifier/Applicator/OrientationTest.phpt +++ b/tests/Modifier/Applicator/OrientationTest.phpt @@ -32,7 +32,7 @@ final class OrientationTest extends TestCase $applicator = new Orientation(); - Assert::null($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same([], iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config))); } /** @@ -58,7 +58,7 @@ final class OrientationTest extends TestCase $applicator = new Orientation(); - Assert::null($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same([], iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config))); } /** @@ -89,7 +89,8 @@ final class OrientationTest extends TestCase $applicator = new Orientation(); - Assert::same($modifiedImage, $applicator->apply($image, $pathInfo, $modifierValues, $config)); + $result = iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same($modifiedImage, $result['image']); } public function testImageShouldBeRotated(): void @@ -112,7 +113,8 @@ final class OrientationTest extends TestCase $applicator = new Orientation(); - Assert::same($modifiedImage, $applicator->apply($image, $pathInfo, $modifierValues, $config)); + $result = iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same($modifiedImage, $result['image']); } public function provideNormalExifOrientations(): array diff --git a/tests/Modifier/Applicator/ResizeTest.phpt b/tests/Modifier/Applicator/ResizeTest.phpt index 5278c5e..8db1890 100644 --- a/tests/Modifier/Applicator/ResizeTest.phpt +++ b/tests/Modifier/Applicator/ResizeTest.phpt @@ -36,7 +36,7 @@ final class ResizeTest extends TestCase $applicator = new Resize(); Assert::exception( - static fn () => $applicator->apply($image, $pathInfo, $modifierValues, $config), + static fn () => iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)), ModifierException::class, 'The only one dimension (width or height) must be defined if an aspect ratio is used. Passed values: w=null, h=null, ar=16x9.', ); @@ -52,7 +52,7 @@ final class ResizeTest extends TestCase $applicator = new Resize(); Assert::exception( - static fn () => $applicator->apply($image, $pathInfo, $modifierValues, $config), + static fn () => iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)), ModifierException::class, 'The only one dimension (width or height) must be defined if an aspect ratio is used. Passed values: w=100, h=200, ar=16x9.', ); @@ -80,7 +80,7 @@ final class ResizeTest extends TestCase $applicator = new Resize(); - Assert::null($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same([], iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config))); } public function testImageShouldBeModifiedWithContainFit(): void @@ -118,7 +118,8 @@ final class ResizeTest extends TestCase $applicator = new Resize(); - Assert::same($image, $applicator->apply($image, $pathInfo, $modifierValues, $config)); + $result = iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same($image, $result['image']); } public function testImageShouldBeModifiedWithStretchFit(): void @@ -145,7 +146,8 @@ final class ResizeTest extends TestCase $applicator = new Resize(); - Assert::same($image, $applicator->apply($image, $pathInfo, $modifierValues, $config)); + $result = iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same($image, $result['image']); } public function testImageShouldBeModifiedWithFillFit(): void @@ -200,7 +202,8 @@ final class ResizeTest extends TestCase $applicator = new Resize(); - Assert::same($image, $applicator->apply($image, $pathInfo, $modifierValues, $config)); + $result = iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same($image, $result['image']); } public function testImageShouldBeModifiedWithCropFit(): void @@ -227,7 +230,8 @@ final class ResizeTest extends TestCase $applicator = new Resize(); - Assert::same($image, $applicator->apply($image, $pathInfo, $modifierValues, $config)); + $result = iterator_to_array($applicator->apply($image, $pathInfo, $modifierValues, $config)); + Assert::same($image, $result['image']); } public function getSameImageDimensionsData(): array diff --git a/tests/Modifier/Applicator/StripMetaTest.phpt b/tests/Modifier/Applicator/StripMetaTest.phpt new file mode 100644 index 0000000..48f0e3b --- /dev/null +++ b/tests/Modifier/Applicator/StripMetaTest.phpt @@ -0,0 +1,141 @@ +shouldReceive('getOptional') + ->once() + ->with('__stripMeta', false) + ->andReturn(false); + + $applicator = new StripMeta(); + $result = $applicator->apply($image, $pathInfo, $modifierValues, $config); + + Assert::same([], $result instanceof Traversable ? iterator_to_array($result) : $result); + } + + public function testEmptyGeneratorShouldBeReturnedIfCoreIsNotImagick(): void + { + $image = Mockery::mock(Image::class); + $pathInfo = Mockery::mock(PathInfoInterface::class); + $modifierValues = Mockery::mock(ModifierValues::class); + $config = Mockery::mock(ConfigInterface::class); + $core = new stdClass(); + + $modifierValues->shouldReceive('getOptional') + ->once() + ->with('__stripMeta', false) + ->andReturn(true); + + $image->shouldReceive('getCore') + ->once() + ->withNoArgs() + ->andReturn($core); + + $applicator = new StripMeta(); + $result = $applicator->apply($image, $pathInfo, $modifierValues, $config); + + Assert::same([], $result instanceof Traversable ? iterator_to_array($result) : $result); + } + + public function testMetadataShouldBeStrippedButIccProfileShouldBeKept(): void + { + $image = Mockery::mock(Image::class); + $pathInfo = Mockery::mock(PathInfoInterface::class); + $modifierValues = Mockery::mock(ModifierValues::class); + $config = Mockery::mock(ConfigInterface::class); + $core = Mockery::mock(Imagick::class); + + $modifierValues->shouldReceive('getOptional') + ->once() + ->with('__stripMeta', false) + ->andReturn(true); + + $image->shouldReceive('getCore') + ->once() + ->withNoArgs() + ->andReturn($core); + + $core->shouldReceive('getImageProfiles') + ->once() + ->with('icc') + ->andReturn(['icc' => 'icc_profile_data']); + + $core->shouldReceive('stripImage') + ->once() + ->withNoArgs(); + + $core->shouldReceive('profileImage') + ->once() + ->with('icc', 'icc_profile_data'); + + $applicator = new StripMeta(); + $result = $applicator->apply($image, $pathInfo, $modifierValues, $config); + + Assert::same([], $result instanceof Traversable ? iterator_to_array($result) : $result); + } + + public function testMetadataShouldBeStrippedWithoutIccProfile(): void + { + $image = Mockery::mock(Image::class); + $pathInfo = Mockery::mock(PathInfoInterface::class); + $modifierValues = Mockery::mock(ModifierValues::class); + $config = Mockery::mock(ConfigInterface::class); + $core = Mockery::mock(Imagick::class); + + $modifierValues->shouldReceive('getOptional') + ->once() + ->with('__stripMeta', false) + ->andReturn(true); + + $image->shouldReceive('getCore') + ->once() + ->withNoArgs() + ->andReturn($core); + + $core->shouldReceive('getImageProfiles') + ->once() + ->with('icc') + ->andReturn([]); + + $core->shouldReceive('stripImage') + ->once() + ->withNoArgs(); + + $applicator = new StripMeta(); + $result = $applicator->apply($image, $pathInfo, $modifierValues, $config); + + Assert::same([], $result instanceof Traversable ? iterator_to_array($result) : $result); + } + + protected function tearDown(): void + { + Mockery::close(); + } +} + +(new StripMetaTest())->run(); diff --git a/tests/Modifier/Facade/ModifierFacadeTest.phpt b/tests/Modifier/Facade/ModifierFacadeTest.phpt index 7772ef3..800094d 100644 --- a/tests/Modifier/Facade/ModifierFacadeTest.phpt +++ b/tests/Modifier/Facade/ModifierFacadeTest.phpt @@ -217,7 +217,7 @@ final class ModifierFacadeTest extends TestCase $applicator->shouldReceive('apply') ->once() ->with($image, $pathInfo, $modifierValues, $config) - ->andReturn($image); + ->andReturn([ModifierApplicatorInterface::OutImage => $image]); $facade = $this->createModifierFacade(config: $config, modifierCollection: $modifierCollection); @@ -228,6 +228,8 @@ final class ModifierFacadeTest extends TestCase new ModifyResult( image: $image, modified: true, + encodeFormat: null, + encodeQuality: null, ), $facade->modifyImage($image, $pathInfo, $modifiers), ); @@ -257,7 +259,7 @@ final class ModifierFacadeTest extends TestCase $applicator->shouldReceive('apply') ->once() ->with($image, $pathInfo, $modifierValues, $config) - ->andReturn(null); + ->andReturn([]); $facade = $this->createModifierFacade(config: $config, modifierCollection: $modifierCollection); @@ -268,6 +270,8 @@ final class ModifierFacadeTest extends TestCase new ModifyResult( image: $image, modified: false, + encodeFormat: null, + encodeQuality: null, ), $facade->modifyImage($image, $pathInfo, $modifiers), ); @@ -308,7 +312,7 @@ final class ModifierFacadeTest extends TestCase $applicator->shouldReceive('apply') ->once() ->with($image, $pathInfo, $modifierValues, $config) - ->andReturn($image); + ->andReturn([ModifierApplicatorInterface::OutImage => $image]); $facade = $this->createModifierFacade(config: $config, codec: $codec, modifierCollection: $modifierCollection); @@ -319,6 +323,8 @@ final class ModifierFacadeTest extends TestCase new ModifyResult( image: $image, modified: true, + encodeFormat: null, + encodeQuality: null, ), $facade->modifyImage($image, $pathInfo, $preset), ); @@ -359,7 +365,7 @@ final class ModifierFacadeTest extends TestCase $applicator->shouldReceive('apply') ->once() ->with($image, $pathInfo, $modifierValues, $config) - ->andReturn(null); + ->andReturn([]); $facade = $this->createModifierFacade(config: $config, codec: $codec, modifierCollection: $modifierCollection); @@ -370,6 +376,8 @@ final class ModifierFacadeTest extends TestCase new ModifyResult( image: $image, modified: false, + encodeFormat: null, + encodeQuality: null, ), $facade->modifyImage($image, $pathInfo, $preset), ); diff --git a/tests/Persistence/ImagePersisterTest.phpt b/tests/Persistence/ImagePersisterTest.phpt index 98bef71..3f78a0a 100644 --- a/tests/Persistence/ImagePersisterTest.phpt +++ b/tests/Persistence/ImagePersisterTest.phpt @@ -225,6 +225,16 @@ final class ImagePersisterTest extends TestCase ->withNoArgs() ->andReturn($image); + $resource->shouldReceive('getEncodeQuality') + ->once() + ->withNoArgs() + ->andReturn(null); + + $resource->shouldReceive('getEncodeFormat') + ->once() + ->withNoArgs() + ->andReturn(null); + $pathInfo->shouldReceive('getModifiers') ->withNoArgs() ->andReturn(null); @@ -232,6 +242,11 @@ final class ImagePersisterTest extends TestCase $pathInfo->shouldReceive('getPath') ->andReturn('path/image'); + $config->shouldReceive('offsetExists') + ->once() + ->with(Config::ENCODE_QUALITY) + ->andReturn(true); + $config->shouldReceive('offsetGet') ->once() ->with(Config::ENCODE_QUALITY) @@ -318,6 +333,16 @@ final class ImagePersisterTest extends TestCase ->withNoArgs() ->andReturn(true); + $resource->shouldReceive('getEncodeQuality') + ->once() + ->withNoArgs() + ->andReturn(null); + + $resource->shouldReceive('getEncodeFormat') + ->once() + ->withNoArgs() + ->andReturn(null); + $pathInfo->shouldReceive('getModifiers') ->withNoArgs() ->andReturn(null); @@ -334,6 +359,11 @@ final class ImagePersisterTest extends TestCase ->withNoArgs() ->andReturn('image'); + $config->shouldReceive('offsetExists') + ->once() + ->with(Config::ENCODE_QUALITY) + ->andReturn(true); + $config->shouldReceive('offsetGet') ->once() ->with(Config::ENCODE_QUALITY) @@ -373,6 +403,16 @@ final class ImagePersisterTest extends TestCase ->withNoArgs() ->andReturn(true); + $resource->shouldReceive('getEncodeQuality') + ->once() + ->withNoArgs() + ->andReturn(null); + + $resource->shouldReceive('getEncodeFormat') + ->once() + ->withNoArgs() + ->andReturn(null); + $resource->shouldReceive('unlink') ->once() ->withNoArgs() @@ -385,6 +425,11 @@ final class ImagePersisterTest extends TestCase $pathInfo->shouldReceive('getPath') ->andReturn('path/image'); + $config->shouldReceive('offsetExists') + ->once() + ->with(Config::ENCODE_QUALITY) + ->andReturn(true); + $config->shouldReceive('offsetGet') ->once() ->with(Config::ENCODE_QUALITY) @@ -431,9 +476,24 @@ final class ImagePersisterTest extends TestCase $resource->shouldReceive('modifyImage') ->once() - ->with(['w' => 100, 'h' => 200]) + ->with(['w' => 100, 'h' => 200], true) ->andReturnSelf(); + $resource->shouldReceive('getEncodeQuality') + ->once() + ->withNoArgs() + ->andReturn(null); + + $resource->shouldReceive('getEncodeFormat') + ->once() + ->withNoArgs() + ->andReturn(null); + + $config->shouldReceive('offsetExists') + ->once() + ->with(Config::ENCODE_QUALITY) + ->andReturn(true); + $config->shouldReceive('offsetGet') ->once() ->with(Config::ENCODE_QUALITY) @@ -471,6 +531,16 @@ final class ImagePersisterTest extends TestCase ->withNoArgs() ->andReturn(true); + $resource->shouldReceive('getEncodeQuality') + ->once() + ->withNoArgs() + ->andReturn(null); + + $resource->shouldReceive('getEncodeFormat') + ->once() + ->withNoArgs() + ->andReturn(null); + $pathInfo->shouldReceive('getModifiers') ->withNoArgs() ->andReturn(null); @@ -478,6 +548,11 @@ final class ImagePersisterTest extends TestCase $pathInfo->shouldReceive('getPath') ->andReturn('path/image'); + $config->shouldReceive('offsetExists') + ->once() + ->with(Config::ENCODE_QUALITY) + ->andReturn(true); + $config->shouldReceive('offsetGet') ->once() ->with(Config::ENCODE_QUALITY) @@ -522,6 +597,16 @@ final class ImagePersisterTest extends TestCase ->withNoArgs() ->andReturn(true); + $resource->shouldReceive('getEncodeQuality') + ->once() + ->withNoArgs() + ->andReturn(null); + + $resource->shouldReceive('getEncodeFormat') + ->once() + ->withNoArgs() + ->andReturn(null); + $pathInfo->shouldReceive('getModifiers') ->withNoArgs() ->andReturn(null); @@ -529,6 +614,11 @@ final class ImagePersisterTest extends TestCase $pathInfo->shouldReceive('getPath') ->andReturn('path/image'); + $config->shouldReceive('offsetExists') + ->once() + ->with(Config::ENCODE_QUALITY) + ->andReturn(true); + $config->shouldReceive('offsetGet') ->once() ->with(Config::ENCODE_QUALITY) @@ -904,23 +994,12 @@ final class ImagePersisterTest extends TestCase private function setupImageSaveExpectations(Image|MockInterface $image, string $content = '... image content ...'): void { - $isEncodedCalled = $encodeCalled = false; - - $image->shouldReceive('isEncoded') - ->once() - ->withNoArgs() - ->andReturnUsing(static function () use (&$isEncodedCalled): bool { - $isEncodedCalled = true; - - return false; - }); + $encodeCalled = false; $image->shouldReceive('encode') ->once() - ->with(null, 90) - ->andReturnUsing(static function () use ($image, &$isEncodedCalled, &$encodeCalled): Image { - Assert::true($isEncodedCalled); - + ->with('', 90) + ->andReturnUsing(static function () use ($image, &$encodeCalled): Image { $encodeCalled = true; return $image; diff --git a/tests/Resource/ImageResourceTest.phpt b/tests/Resource/ImageResourceTest.phpt index bea4517..0b87c6b 100644 --- a/tests/Resource/ImageResourceTest.phpt +++ b/tests/Resource/ImageResourceTest.phpt @@ -44,11 +44,13 @@ final class ImageResourceTest extends TestCase $modifyResult = new ModifyResult( image: $modifiedImage, modified: true, + encodeFormat: null, + encodeQuality: null, ); $modifierFacade->shouldReceive('modifyImage') ->once() - ->with($image, $pathInfo, ['w' => 300]) + ->with($image, $pathInfo, ['w' => 300], false) ->andReturn($modifyResult); $resource1 = new ImageResource($pathInfo, $image, '/tmp/image.png', $modifierFacade); diff --git a/tests/Resource/TmpFileImageResourceTest.phpt b/tests/Resource/TmpFileImageResourceTest.phpt index abc2893..53ec1c6 100644 --- a/tests/Resource/TmpFileImageResourceTest.phpt +++ b/tests/Resource/TmpFileImageResourceTest.phpt @@ -49,11 +49,13 @@ final class TmpFileImageResourceTest extends TestCase $modifyResult = new ModifyResult( image: $modifiedImage, modified: true, + encodeFormat: null, + encodeQuality: null, ); $modifierFacade->shouldReceive('modifyImage') ->once() - ->with($image, $pathInfo, ['w' => 300]) + ->with($image, $pathInfo, ['w' => 300], false) ->andReturn($modifyResult); $resource1 = new TmpFileImageResource($pathInfo, $image, $modifierFacade, new TmpFile('/tmp/fake'));