From 87d4d31e37f252bc5f08c94a81ba3468ac28d060 Mon Sep 17 00:00:00 2001 From: Pavel Linhart Date: Mon, 26 Jun 2023 09:31:21 +0200 Subject: [PATCH] quality range conversion, more tests, better regex --- src/ResizerConfig.php | 57 +++++++++++++-- src/ResizerParams.php | 4 +- src/ResizerParamsParser.php | 10 +-- tests/ResizerParamsParserTest.php | 116 ++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+), 12 deletions(-) diff --git a/src/ResizerConfig.php b/src/ResizerConfig.php index 4d74841..b8bd1ca 100644 --- a/src/ResizerConfig.php +++ b/src/ResizerConfig.php @@ -3,6 +3,7 @@ namespace Nelson\Resizer; +use Exception; use Nelson\Resizer\DI\ResizerConfigDTO; use Nette\SmartObject; @@ -148,11 +149,8 @@ public function getOptions(?int $quality = null): array { if ($quality !== null) { - $qualityPng = (int) round($quality / 10); - - if ($qualityPng > 9) { - $qualityPng = 9; - } + /** @var int<0, 9> $qualityPng */ + $qualityPng = (int) round($this->remapRange($quality, 0, 100, 0, 9)); } return [ @@ -170,4 +168,53 @@ public function getSupportedFormats(): array return $this->supportedFormats; } + + /** + * @see https://stackoverflow.com/a/36244586/2458557 + */ + private function remapRange( + int $intValue, + int $oMin, + int $oMax, + int $nMin, + int $nMax + ): float { + // Range check + if ($oMin === $oMax) { + throw new Exception('Warning: Zero input range'); + } + + if ($nMin === $nMax) { + throw new Exception('Warning: Zero output range'); + } + + // Check reversed input range + $bReverseInput = false; + $intOldMin = min($oMin, $oMax); + $intOldMax = max($oMin, $oMax); + if ($intOldMin !== $oMin) { + $bReverseInput = true; + } + + // Check reversed output range + $bReverseOutput = false; + $intNewMin = min($nMin, $nMax); + $intNewMax = max($nMin, $nMax); + if ($intNewMin !== $nMin) { + $bReverseOutput = true; + } + + $fRatio = ($intValue - $intOldMin) * ($intNewMax - $intNewMin) / ($intOldMax - $intOldMin); + if ($bReverseInput) { + $fRatio = ($intOldMax - $intValue) * ($intNewMax - $intNewMin) / ($intOldMax - $intOldMin); + } + + $fResult = $fRatio + $intNewMin; + if ($bReverseOutput) { + $fResult = $intNewMax - $fRatio; + } + + return $fResult; + } + } diff --git a/src/ResizerParams.php b/src/ResizerParams.php index a6ac59a..dfdf9ec 100755 --- a/src/ResizerParams.php +++ b/src/ResizerParams.php @@ -74,7 +74,7 @@ public function hasWidth(): bool } - /** @return int<0,100>|null */ + /** @return positive-int|null */ public function getHeight(): ?int { return $this->height; @@ -105,7 +105,7 @@ public function hasNoDimensions(): bool } - /** @return positive-int|null */ + /** @return int<0, 100>|null */ public function getQuality(): ?int { return $this->quality; diff --git a/src/ResizerParamsParser.php b/src/ResizerParamsParser.php index cf403b7..15aa608 100755 --- a/src/ResizerParamsParser.php +++ b/src/ResizerParamsParser.php @@ -13,12 +13,12 @@ class ResizerParamsParser private const PATTERN = '~ ^(?:auto| # use the image as-is, only compress/convert (?:(ifresize)-)? # ifresize modifier - ([lcr]?)(\d*) # width modifer and dimension + ([lcr]?)([1-9][0-9]*)? # width modifer and dimension x # divider - ([tcb]?)(\d*) # height modifier and dimension + ([tcb]?)([1-9][0-9]*)? # height modifier and dimension (!?) # force dimensions, disregard aspect ratio - ([+-]?[0-9]*) # horizontal margin, unused - ([+-]?[0-9]*) # vertical margin, unused + (?:-hm([+-]?[0-9]*))? # horizontal margin, unused + (?:-vm([+-]?[0-9]*))? # vertical margin, unused (?:-q([0-9][0-9]?|100))? # quality, 0-100 )$ ~x'; @@ -82,7 +82,7 @@ private function parseNumericValueToIntOrNull(string $value): ?int if (is_numeric($value)) { $int = (int) $value; - if ($int >= 0) { + if ($int > 0) { return $int; } } diff --git a/tests/ResizerParamsParserTest.php b/tests/ResizerParamsParserTest.php index 73ee70e..390bad2 100644 --- a/tests/ResizerParamsParserTest.php +++ b/tests/ResizerParamsParserTest.php @@ -316,6 +316,101 @@ public function testQuality4(): void } + public function testMargin1(): void + { + $expected = new ResizerParams( + false, + null, + null, + false, + '-100', + null, + 100, + 200, + null, + ); + + $actual = $this->parse('100x200-hm-100'); + $this->assertEquals($expected, $actual); + } + + + public function testMargin2(): void + { + $expected = new ResizerParams( + false, + null, + null, + false, + '+100', + null, + 100, + 200, + null, + ); + + $actual = $this->parse('100x200-hm+100'); + $this->assertEquals($expected, $actual); + } + + + public function testMargin3(): void + { + $expected = new ResizerParams( + false, + null, + null, + false, + null, + '-50', + 100, + 200, + null, + ); + + $actual = $this->parse('100x200-vm-50'); + $this->assertEquals($expected, $actual); + } + + + public function testMargin4(): void + { + $expected = new ResizerParams( + false, + null, + null, + false, + null, + '+50', + 100, + 200, + null, + ); + + $actual = $this->parse('100x200-vm+50'); + $this->assertEquals($expected, $actual); + } + + + public function testMargin5(): void + { + $expected = new ResizerParams( + false, + null, + null, + false, + '-100', + '+50', + 100, + 200, + null, + ); + + $actual = $this->parse('100x200-hm-100-vm+50'); + $this->assertEquals($expected, $actual); + } + + public function testWrongKeyword1(): void { $this->expectException(CouldNotParseResizerParamsException::class); @@ -344,6 +439,27 @@ public function testEmptyString(): void } + public function testEmptyZeroDimensions1(): void + { + $this->expectException(CouldNotParseResizerParamsException::class); + $this->parse('0x'); + } + + + public function testEmptyZeroDimensions2(): void + { + $this->expectException(CouldNotParseResizerParamsException::class); + $this->parse('x0'); + } + + + public function testEmptyZeroDimensions3(): void + { + $this->expectException(CouldNotParseResizerParamsException::class); + $this->parse('0x0'); + } + + public function testEmptyNull(): void { $this->expectException(CouldNotParseResizerParamsException::class);