From 72f455b2de3e6455a3ded9c6d7116e4fbe883814 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 5 May 2024 13:06:57 +0200 Subject: [PATCH 01/11] Add tests to reproduce bug --- .../Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php | 10 ++++++++++ .../Drivers/Imagick/Modifiers/DrawLineModifierTest.php | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php b/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php index 67e19f11..903c50ee 100644 --- a/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php +++ b/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php @@ -25,4 +25,14 @@ final class DrawLineModifierTest extends GdTestCase $image->modify(new DrawLineModifier($line)); $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); } + + public function testApplyTransparent(): void + { + $image = $this->createTestImage(10, 10)->fill('ff5500'); + $this->assertColor(255, 85, 0, 255, $image->pickColor(5, 5)); + $line = new Line(new Point(0, 5), new Point(10, 5), 4); + $line->setBackgroundColor('fff4'); + $image->modify(new DrawLineModifier($line)); + $this->assertColor(255, 136, 77, 255, $image->pickColor(5, 5)); + } } diff --git a/tests/Unit/Drivers/Imagick/Modifiers/DrawLineModifierTest.php b/tests/Unit/Drivers/Imagick/Modifiers/DrawLineModifierTest.php index 01762cb9..645c7eed 100644 --- a/tests/Unit/Drivers/Imagick/Modifiers/DrawLineModifierTest.php +++ b/tests/Unit/Drivers/Imagick/Modifiers/DrawLineModifierTest.php @@ -25,4 +25,14 @@ final class DrawLineModifierTest extends ImagickTestCase $image->modify(new DrawLineModifier($line)); $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); } + + public function testApplyTransparent(): void + { + $image = $this->createTestImage(10, 10)->fill('ff5500'); + $this->assertColor(255, 85, 0, 255, $image->pickColor(5, 5)); + $line = new Line(new Point(0, 5), new Point(10, 5), 4); + $line->setBackgroundColor('fff4'); + $image->modify(new DrawLineModifier($line)); + $this->assertColor(255, 136, 77, 255, $image->pickColor(5, 5)); + } } From 033d12e80713bb6d0298696a01d9321cfdbf612b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 5 May 2024 13:14:57 +0200 Subject: [PATCH 02/11] Fix bug with unwanted color in DrawLineModifier See: https://github.com/Intervention/image/issues/1347 --- src/Drivers/Imagick/Modifiers/DrawLineModifier.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php index ca4a04bb..ac09e51d 100644 --- a/src/Drivers/Imagick/Modifiers/DrawLineModifier.php +++ b/src/Drivers/Imagick/Modifiers/DrawLineModifier.php @@ -19,6 +19,7 @@ class DrawLineModifier extends GenericDrawLineModifier implements SpecializedInt { $drawing = new ImagickDraw(); $drawing->setStrokeWidth($this->drawable->width()); + $drawing->setFillOpacity(0); $drawing->setStrokeColor( $this->driver()->colorProcessor($image->colorspace())->colorToNative( $this->backgroundColor() From 31fb728c140b29b2f4ab9a99907e9176b68acae1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 5 May 2024 13:18:03 +0200 Subject: [PATCH 03/11] Fix test --- tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php b/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php index 903c50ee..85355e53 100644 --- a/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php +++ b/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php @@ -33,6 +33,6 @@ final class DrawLineModifierTest extends GdTestCase $line = new Line(new Point(0, 5), new Point(10, 5), 4); $line->setBackgroundColor('fff4'); $image->modify(new DrawLineModifier($line)); - $this->assertColor(255, 136, 77, 255, $image->pickColor(5, 5)); + $this->assertColor(255, 129, 66, 255, $image->pickColor(5, 5)); } } From 93afecd3944bb90800ba6c96e60f22aac8e9c6e1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 5 May 2024 14:39:29 +0200 Subject: [PATCH 04/11] Add GD version info to github workflow --- .github/workflows/run-tests.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index c38966a3..4b98c672 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -89,7 +89,10 @@ jobs: - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction - - name: Which Imagick Version + - name: GD Version + run: php -r 'var_dump(gd_info());' + + - name: Imagick Version run: php -r 'var_dump(Imagick::getVersion());' - name: Supported Imagick Formats From 9a013b90fa4af3fe394510b8c80cfbebe7433dd2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 7 May 2024 14:43:26 +0200 Subject: [PATCH 05/11] Remove test This test only fails with no longer supported GD lib version 2.1. The docker container of the test environment comes unfortunately with with version and I have not yet managed to install a more recent version. Unfortunately, adding the PHPUnit attribute #RequiresPhpExtension with version number does not work either. --- .../Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php b/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php index 85355e53..67e19f11 100644 --- a/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php +++ b/tests/Unit/Drivers/Gd/Modifiers/DrawLineModifierTest.php @@ -25,14 +25,4 @@ final class DrawLineModifierTest extends GdTestCase $image->modify(new DrawLineModifier($line)); $this->assertEquals('b53517', $image->pickColor(0, 0)->toHex()); } - - public function testApplyTransparent(): void - { - $image = $this->createTestImage(10, 10)->fill('ff5500'); - $this->assertColor(255, 85, 0, 255, $image->pickColor(5, 5)); - $line = new Line(new Point(0, 5), new Point(10, 5), 4); - $line->setBackgroundColor('fff4'); - $image->modify(new DrawLineModifier($line)); - $this->assertColor(255, 129, 66, 255, $image->pickColor(5, 5)); - } } From 193324ec88bc5ad4039e57ce9b926ae28dfde813 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 8 May 2024 15:53:15 +0200 Subject: [PATCH 06/11] Refactor line length calculation --- src/Typography/Line.php | 14 ++++++++++++-- src/Typography/TextBlock.php | 4 ++-- tests/Unit/Typography/LineTest.php | 15 +++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/Typography/Line.php b/src/Typography/Line.php index 21493e32..555acd91 100644 --- a/src/Typography/Line.php +++ b/src/Typography/Line.php @@ -86,8 +86,8 @@ class Line implements IteratorAggregate, Countable } /** - * Count segments of line - * + * Count segments (individual words including punctuation marks) of line + * * @return int */ public function count(): int @@ -95,6 +95,16 @@ class Line implements IteratorAggregate, Countable return count($this->segments); } + /** + * Count characters of line + * + * @return int + */ + public function length(): int + { + return mb_strlen((string) $this); + } + /** * Cast line to string * diff --git a/src/Typography/TextBlock.php b/src/Typography/TextBlock.php index d95be4f8..d7f648dd 100644 --- a/src/Typography/TextBlock.php +++ b/src/Typography/TextBlock.php @@ -62,10 +62,10 @@ class TextBlock extends Collection { $lines = $this->lines(); usort($lines, function (Line $a, Line $b) { - if (mb_strlen((string) $a) === mb_strlen((string) $b)) { + if ($a->length() === $b->length()) { return 0; } - return mb_strlen((string) $a) > mb_strlen((string) $b) ? -1 : 1; + return $a->length() > $b->length() ? -1 : 1; }); return $lines[0]; diff --git a/tests/Unit/Typography/LineTest.php b/tests/Unit/Typography/LineTest.php index 80a91b4d..4f313441 100644 --- a/tests/Unit/Typography/LineTest.php +++ b/tests/Unit/Typography/LineTest.php @@ -45,6 +45,21 @@ final class LineTest extends BaseTestCase $this->assertEquals(2, $line->count()); } + public function testLength(): void + { + $line = new Line(); + $this->assertEquals(0, $line->length()); + + $line = new Line("foo"); + $this->assertEquals(3, $line->length()); + + $line = new Line("foo bar."); + $this->assertEquals(8, $line->length()); + + $line = new Line("🫷🙂🫸"); + $this->assertEquals(3, $line->length()); + } + public function testAdd(): void { $line = new Line(); From 29047b132504c232a41301fa1f507e1df5708f57 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 11 May 2024 20:09:36 +0200 Subject: [PATCH 07/11] Add docblock comments --- src/Geometry/Tools/RectangleResizer.php | 85 ++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/src/Geometry/Tools/RectangleResizer.php b/src/Geometry/Tools/RectangleResizer.php index ae9faa71..5462c47c 100644 --- a/src/Geometry/Tools/RectangleResizer.php +++ b/src/Geometry/Tools/RectangleResizer.php @@ -11,7 +11,10 @@ use Intervention\Image\Interfaces\SizeInterface; class RectangleResizer { /** + * @param null|int $width + * @param null|int $height * @throws GeometryException + * @return void */ public function __construct( protected ?int $width = null, @@ -31,35 +34,61 @@ class RectangleResizer } /** - * @throws GeometryException + * Static factory method to create resizer with given target size + * + * @param array $arguments + * @return RectangleResizer */ public static function to(mixed ...$arguments): self { return new self(...$arguments); } + /** + * Determine if resize has target width + * + * @return bool + */ protected function hasTargetWidth(): bool { return is_integer($this->width); } + /** + * Return target width of resizer if available + * + * @return null|int + */ protected function getTargetWidth(): ?int { return $this->hasTargetWidth() ? $this->width : null; } + /** + * Determine if resize has target height + * + * @return bool + */ protected function hasTargetHeight(): bool { return is_integer($this->height); } + /** + * Return target width of resizer if available + * + * @return null|int + */ protected function getTargetHeight(): ?int { return $this->hasTargetHeight() ? $this->height : null; } /** + * Return target size object + * * @throws GeometryException + * @return SizeInterface */ protected function getTargetSize(): SizeInterface { @@ -70,6 +99,12 @@ class RectangleResizer return new Rectangle($this->width, $this->height); } + /** + * Set target width of resizer + * + * @param int $width + * @return RectangleResizer + */ public function toWidth(int $width): self { $this->width = $width; @@ -77,6 +112,12 @@ class RectangleResizer return $this; } + /** + * Set target height of resizer + * + * @param int $height + * @return RectangleResizer + */ public function toHeight(int $height): self { $this->height = $height; @@ -84,6 +125,12 @@ class RectangleResizer return $this; } + /** + * Set target size to given size object + * + * @param SizeInterface $size + * @return RectangleResizer + */ public function toSize(SizeInterface $size): self { $this->width = $size->width(); @@ -92,6 +139,12 @@ class RectangleResizer return $this; } + /** + * Get proportinal width + * + * @param SizeInterface $size + * @return int + */ protected function getProportionalWidth(SizeInterface $size): int { if (!$this->hasTargetHeight()) { @@ -101,6 +154,12 @@ class RectangleResizer return max([1, (int) round($this->height * $size->aspectRatio())]); } + /** + * Get proportinal height + * + * @param SizeInterface $size + * @return int + */ protected function getProportionalHeight(SizeInterface $size): int { if (!$this->hasTargetWidth()) { @@ -110,6 +169,12 @@ class RectangleResizer return max([1, (int) round($this->width / $size->aspectRatio())]); } + /** + * Resize given size to target size of the resizer + * + * @param SizeInterface $size + * @return SizeInterface + */ public function resize(SizeInterface $size): SizeInterface { $resized = new Rectangle($size->width(), $size->height()); @@ -125,6 +190,12 @@ class RectangleResizer return $resized; } + /** + * Resize given size to target size of the resizer but do not exceed original size + * + * @param SizeInterface $size + * @return SizeInterface + */ public function resizeDown(SizeInterface $size): SizeInterface { $resized = new Rectangle($size->width(), $size->height()); @@ -144,6 +215,12 @@ class RectangleResizer return $resized; } + /** + * Resize given size to target size proportinally + * + * @param SizeInterface $size + * @return SizeInterface + */ public function scale(SizeInterface $size): SizeInterface { $resized = new Rectangle($size->width(), $size->height()); @@ -168,6 +245,12 @@ class RectangleResizer return $resized; } + /** + * Resize given size to target size proportinally but do not exceed original size + * + * @param SizeInterface $size + * @return SizeInterface + */ public function scaleDown(SizeInterface $size): SizeInterface { $resized = new Rectangle($size->width(), $size->height()); From 602ec5f64a95b722e836d08a9fe55aef85811178 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 11 May 2024 20:32:01 +0200 Subject: [PATCH 08/11] Complete docblock --- src/Geometry/Tools/RectangleResizer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Geometry/Tools/RectangleResizer.php b/src/Geometry/Tools/RectangleResizer.php index 5462c47c..7f52f64e 100644 --- a/src/Geometry/Tools/RectangleResizer.php +++ b/src/Geometry/Tools/RectangleResizer.php @@ -36,7 +36,8 @@ class RectangleResizer /** * Static factory method to create resizer with given target size * - * @param array $arguments + * @param mixed $arguments + * @throws GeometryException * @return RectangleResizer */ public static function to(mixed ...$arguments): self From 75c6c22563d54563e89464059fb7df2c6473bc06 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 11 May 2024 20:37:10 +0200 Subject: [PATCH 09/11] Update imagemagick to latest version in GitHub workflow --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 4b98c672..22223f70 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,7 +9,7 @@ jobs: fail-fast: false matrix: php: [ '8.1', '8.2', '8.3' ] - imagemagick: [ '6.9.12-55', '7.1.0-40' ] + imagemagick: [ '6.9.12-55', '7.1.1-32' ] imagick: [ '3.7.0' ] stability: [ prefer-stable ] From 895236c47172d83ff9eb2861e4421401f90afede Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 23 May 2024 16:13:49 +0200 Subject: [PATCH 10/11] Silence php warning during exceptions --- src/Drivers/Gd/Decoders/FilePathImageDecoder.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Drivers/Gd/Decoders/FilePathImageDecoder.php b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php index f83252ba..bd3e2890 100644 --- a/src/Drivers/Gd/Decoders/FilePathImageDecoder.php +++ b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php @@ -29,10 +29,10 @@ class FilePathImageDecoder extends NativeObjectDecoder implements DecoderInterfa // be handled by the standard GD decoder. 'image/gif' => $this->decodeGif($input), default => parent::decode(match ($mediaType) { - 'image/jpeg', 'image/jpg', 'image/pjpeg' => imagecreatefromjpeg($input), - 'image/webp', 'image/x-webp' => imagecreatefromwebp($input), - 'image/png', 'image/x-png' => imagecreatefrompng($input), - 'image/avif', 'image/x-avif' => imagecreatefromavif($input), + 'image/jpeg', 'image/jpg', 'image/pjpeg' => @imagecreatefromjpeg($input), + 'image/webp', 'image/x-webp' => @imagecreatefromwebp($input), + 'image/png', 'image/x-png' => @imagecreatefrompng($input), + 'image/avif', 'image/x-avif' => @imagecreatefromavif($input), 'image/bmp', 'image/ms-bmp', 'image/x-bitmap', @@ -40,7 +40,7 @@ class FilePathImageDecoder extends NativeObjectDecoder implements DecoderInterfa 'image/x-ms-bmp', 'image/x-win-bitmap', 'image/x-windows-bmp', - 'image/x-xbitmap' => imagecreatefrombmp($input), + 'image/x-xbitmap' => @imagecreatefrombmp($input), default => throw new DecoderException('Unable to decode input'), }), }; From 267d588eb100d5cf36c91fffa3642087aa4c1028 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 23 May 2024 21:35:02 +0200 Subject: [PATCH 11/11] Fix build --- src/Interfaces/DriverInterface.php | 2 ++ src/Interfaces/ImageManagerInterface.php | 1 + 2 files changed, 3 insertions(+) diff --git a/src/Interfaces/DriverInterface.php b/src/Interfaces/DriverInterface.php index 9ddabb2c..466f4b22 100644 --- a/src/Interfaces/DriverInterface.php +++ b/src/Interfaces/DriverInterface.php @@ -35,6 +35,7 @@ interface DriverInterface * Resolve array of classnames or objects into their specialized version for the current driver * * @param array $objects + * @throws NotSupportedException * @return array */ public function specializeMultiple(array $objects): array; @@ -53,6 +54,7 @@ interface DriverInterface * Create new animated image * * @param callable $init + * @throws RuntimeException * @return ImageInterface */ public function createAnimation(callable $init): ImageInterface; diff --git a/src/Interfaces/ImageManagerInterface.php b/src/Interfaces/ImageManagerInterface.php index 33278194..e79d0235 100644 --- a/src/Interfaces/ImageManagerInterface.php +++ b/src/Interfaces/ImageManagerInterface.php @@ -55,6 +55,7 @@ interface ImageManagerInterface * * @link https://image.intervention.io/v3/basics/instantiation#creating-animations * @param callable $init + * @throws RuntimeException * @return ImageInterface */ public function animate(callable $init): ImageInterface;