Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Bridge/Nette/DI/ImageStorageExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
],
);

Expand Down
12 changes: 8 additions & 4 deletions src/Modifier/Applicator/Format.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -29,7 +28,7 @@ public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues
}

if (!$needEncode) {
return null;
return;
}

if (in_array($extension, ['jpg', 'pjpg'], true)) {
Expand All @@ -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
Expand Down
15 changes: 13 additions & 2 deletions src/Modifier/Applicator/ModifierApplicatorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, mixed>
*/
public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): ?Image;
public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): iterable;
}
10 changes: 5 additions & 5 deletions src/Modifier/Applicator/Orientation.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
40 changes: 18 additions & 22 deletions src/Modifier/Applicator/Resize.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
),
};
}
}
41 changes: 41 additions & 0 deletions src/Modifier/Applicator/StripMeta.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace SixtyEightPublishers\ImageStorage\Modifier\Applicator;

use Imagick;
use ImagickException;
use Intervention\Image\Image;
use SixtyEightPublishers\FileStorage\Config\ConfigInterface;
use SixtyEightPublishers\FileStorage\PathInfoInterface;
use SixtyEightPublishers\ImageStorage\Modifier\Collection\ModifierValues;

final class StripMeta implements ModifierApplicatorInterface
{
/**
* @throws ImagickException
*/
public function apply(Image $image, PathInfoInterface $pathInfo, ModifierValues $values, ConfigInterface $config): iterable
{
if (true !== $values->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 [];
}
}
2 changes: 1 addition & 1 deletion src/Modifier/Collection/ModifierValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
32 changes: 27 additions & 5 deletions src/Modifier/Facade/ModifierFacade.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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,
);
}
}
2 changes: 1 addition & 1 deletion src/Modifier/Facade/ModifierFacadeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ public function getCodec(): CodecInterface;
/**
* @param string|array<string, string|numeric|bool> $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;
}
2 changes: 2 additions & 0 deletions src/Modifier/Facade/ModifyResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
) {}
}
8 changes: 4 additions & 4 deletions src/Persistence/ImagePersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
}
Expand Down
31 changes: 28 additions & 3 deletions src/Resource/ImageResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
}
6 changes: 5 additions & 1 deletion src/Resource/ResourceInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,9 @@ public function hasBeenModified(): bool;
/**
* @param string|array<string, string|numeric|bool> $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;
}
2 changes: 2 additions & 0 deletions tests/Bridge/Nette/DI/ImageStorageExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ public function testExtensionShouldBeIntegratedWithMinimalConfiguration(): void
Applicator\Orientation::class,
Applicator\Resize::class,
Applicator\Format::class,
Applicator\StripMeta::class,
],
validatorTypes: [
Validator\AllowedResolutionValidator::class,
Expand Down Expand Up @@ -250,6 +251,7 @@ public function testExtensionShouldBeIntegratedWithCustomModifiersAndApplicators
Applicator\Orientation::class,
Applicator\Resize::class,
Applicator\Format::class,
Applicator\StripMeta::class,
],
validatorTypes: [
TestValidator::class,
Expand Down
Loading
Loading