From 93912664449ceba6af320a43f3142fb078d64a78 Mon Sep 17 00:00:00 2001 From: michalsn Date: Tue, 22 Apr 2025 12:20:05 +0200 Subject: [PATCH 1/6] feat: add clearMetadata() method to provide privacy options when using imagick handler --- system/Images/Handlers/BaseHandler.php | 15 ++++ system/Images/Handlers/ImageMagickHandler.php | 47 ++++++++++ tests/system/Images/GDHandlerTest.php | 9 ++ .../system/Images/ImageMagickHandlerTest.php | 88 +++++++++++++++++++ user_guide_src/source/changelogs/v4.7.0.rst | 1 + user_guide_src/source/libraries/images.rst | 27 ++++++ .../source/libraries/images/015.php | 6 ++ .../source/libraries/images/016.php | 8 ++ .../source/libraries/images/017.php | 10 +++ utils/phpstan-baseline/loader.neon | 2 +- utils/phpstan-baseline/varTag.type.neon | 7 +- 11 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 user_guide_src/source/libraries/images/015.php create mode 100644 user_guide_src/source/libraries/images/016.php create mode 100644 user_guide_src/source/libraries/images/017.php diff --git a/system/Images/Handlers/BaseHandler.php b/system/Images/Handlers/BaseHandler.php index de3e15974a1b..33bb5a97aafd 100644 --- a/system/Images/Handlers/BaseHandler.php +++ b/system/Images/Handlers/BaseHandler.php @@ -769,4 +769,19 @@ public function getHeight() { return ($this->resource !== null) ? $this->_getHeight() : $this->height; } + + /** + * Clears image metadata. + * + * This method has no use in the GDHandler, + * since all the data are cleared automatically. + * + * GDHandler can't preserve the image metadata. + * + * @param array|string> $data + */ + public function clearMetadata(array $data = []): static + { + return $this; + } } diff --git a/system/Images/Handlers/ImageMagickHandler.php b/system/Images/Handlers/ImageMagickHandler.php index 08886937a4b1..dd78e51bc8ba 100644 --- a/system/Images/Handlers/ImageMagickHandler.php +++ b/system/Images/Handlers/ImageMagickHandler.php @@ -537,4 +537,51 @@ public function reorient(bool $silent = false) default => $this, }; } + + /** + * Clears metadata from the image based on specified parameters. + * + * Configuration for metadata clearing: + * - If empty, all metadata is stripped + * - If contains 'except' key, keeps only those properties + * - Otherwise, deletes only the specified keys + * + * @param array|string> $data + * + * @return $this + * + * @throws ImagickException + */ + public function clearMetadata(array $data = []): static + { + $this->ensureResource(); + + // Strip all metadata when no parameters are provided + if ($data === []) { + $this->resource->stripImage(); + + return $this; + } + + // Keep only properties specified in 'except' array + if (isset($data['except'])) { + $propertiesToKeep = (array) $data['except']; + $allPropertyNames = $this->resource->getImageProperties('*', false); + + foreach ($allPropertyNames as $property) { + if (! in_array($property, $propertiesToKeep, true)) { + $this->resource->deleteImageProperty($property); + } + } + + return $this; + } + + // Delete only specific properties + foreach ($data as $property) { + $this->resource->deleteImageProperty($property); + } + + return $this; + } } diff --git a/tests/system/Images/GDHandlerTest.php b/tests/system/Images/GDHandlerTest.php index 121ce040904a..d85d1a142ffd 100644 --- a/tests/system/Images/GDHandlerTest.php +++ b/tests/system/Images/GDHandlerTest.php @@ -454,4 +454,13 @@ public function testImageReorientPortrait(): void $this->assertSame(['red' => 62, 'green' => 62, 'blue' => 62, 'alpha' => 0], $rgb); } } + + public function testClearMetadataReturnsSelf(): void + { + $this->handler->withFile($this->path); + + $result = $this->handler->clearMetadata(); + + $this->assertSame($this->handler, $result); + } } diff --git a/tests/system/Images/ImageMagickHandlerTest.php b/tests/system/Images/ImageMagickHandlerTest.php index 924095253e48..4de8d46dee14 100644 --- a/tests/system/Images/ImageMagickHandlerTest.php +++ b/tests/system/Images/ImageMagickHandlerTest.php @@ -443,4 +443,92 @@ public function testImageReorientPortrait(): void $this->assertSame(['red' => 62, 'green' => 62, 'blue' => 62, 'alpha' => 0], $rgb); } } + + public function testClearMetadataEnsuresResource(): void + { + $this->expectException(ImageException::class); + $this->handler->clearMetadata(); + } + + public function testClearMetadataReturnsSelf(): void + { + $this->handler->withFile($this->path); + + $result = $this->handler->clearMetadata(); + + $this->assertSame($this->handler, $result); + } + + public function testClearMetadataAll(): void + { + $this->handler->withFile($this->path); + /** @var Imagick $imagick */ + $imagick = $this->handler->getResource(); + $before = $imagick->getImageProperties(); + $this->assertCount(14, $before); + + $this->handler + ->clearMetadata() + ->save($this->root . 'ci-logo-no-metadata.png'); + + $this->handler->withFile($this->root . 'ci-logo-no-metadata.png'); + /** @var Imagick $imagick */ + $imagick = $this->handler->getResource(); + $after = $imagick->getImageProperties(); + + $this->assertCount(9, $after); + } + + public function testClearMetadataExcept(): void + { + $this->handler->withFile($this->path); + /** @var Imagick $imagick */ + $imagick = $this->handler->getResource(); + $before = $imagick->getImageProperties(); + $this->assertCount(14, $before); + + // Keep 2 properties + $this->handler + ->clearMetadata(['except' => ['png:bKGD', 'png:cHRM']]) + ->save($this->root . 'ci-logo-no-metadata.png'); + + $this->handler->withFile($this->root . 'ci-logo-no-metadata.png'); + /** @var Imagick $imagick */ + $imagick = $this->handler->getResource(); + $after = $imagick->getImageProperties(); + + $this->assertArrayHasKey('png:bKGD', $after); + $this->assertArrayHasKey('png:cHRM', $after); + $this->assertArrayNotHasKey('png:gAMA', $after); + + $this->assertCount(12, $after); + } + + public function testClearMetadataSpecific(): void + { + $this->handler->withFile($this->path); + /** @var Imagick $imagick */ + $imagick = $this->handler->getResource(); + $before = $imagick->getImageProperties(); + + $this->assertArrayNotHasKey('png:tIME', $before); + $this->assertCount(14, $before); + + // Delete only 1 + $this->handler + ->clearMetadata(['png:gAMA']) + ->save($this->root . 'ci-logo-no-metadata.png'); + + $this->handler->withFile($this->root . 'ci-logo-no-metadata.png'); + /** @var Imagick $imagick */ + $imagick = $this->handler->getResource(); + $after = $imagick->getImageProperties(); + + $this->assertArrayHasKey('png:bKGD', $after); + $this->assertArrayHasKey('png:cHRM', $after); + $this->assertArrayHasKey('png:tIME', $after); + $this->assertArrayNotHasKey('png:gAMA', $after); + + $this->assertCount(14, $after); + } } diff --git a/user_guide_src/source/changelogs/v4.7.0.rst b/user_guide_src/source/changelogs/v4.7.0.rst index 13017b93db93..c6dfde8cd61e 100644 --- a/user_guide_src/source/changelogs/v4.7.0.rst +++ b/user_guide_src/source/changelogs/v4.7.0.rst @@ -64,6 +64,7 @@ Libraries **Email:** Added support for choosing the SMTP authorization method. You can change it via ``Config\Email::$SMTPAuthMethod`` option. **Image:** The ``ImageMagickHandler`` has been rewritten to rely solely on the PHP ``imagick`` extension. +**Image:** Added ``ImageMagickHandler::clearMetadata()`` method to remove image metadata for privacy protection. Helpers and Functions ===================== diff --git a/user_guide_src/source/libraries/images.rst b/user_guide_src/source/libraries/images.rst index f263eb173d06..12b66a0f8075 100644 --- a/user_guide_src/source/libraries/images.rst +++ b/user_guide_src/source/libraries/images.rst @@ -260,3 +260,30 @@ The possible options that are recognized are as follows: - ``vOffset`` Additional offset on the y axis, in pixels - ``fontPath`` The full server path to the TTF font you wish to use. System font will be used if none is given. - ``fontSize`` The font size to use. When using the GD handler with the system font, valid values are between ``1`` to ``5``. + +Clearing Image Metadata +======================= + +This method provides control over which metadata is preserved or removed from an image. + +.. important:: The GD image library automatically strips all metadata during processing, + so this method has no additional effect when using the GD handler. + This behavior is built into GD itself and cannot be modified. + +.. note:: Some essential technical metadata (dimensions, color depth) will be regenerated + during save operations as they're required for image display. However, all privacy-sensitive + information such as GPS location, camera details, and timestamps can be completely removed. + +The method supports three different operations depending on the provided parameters: + +**Clear all metadata** - When an empty array is passed, all metadata is stripped from the image. + +.. literalinclude:: images/015.php + +**Keep only specific properties** - When using the 'except' key, only the specified properties are preserved. + +.. literalinclude:: images/016.php + +**Delete specific properties** - When providing a list of property names, only those properties are removed. + +.. literalinclude:: images/017.php diff --git a/user_guide_src/source/libraries/images/015.php b/user_guide_src/source/libraries/images/015.php new file mode 100644 index 000000000000..446deab2d35f --- /dev/null +++ b/user_guide_src/source/libraries/images/015.php @@ -0,0 +1,6 @@ +withFile('/path/to/image/mypic.jpg') + ->clearMetadata() + ->save('/path/to/new/image.jpg'); diff --git a/user_guide_src/source/libraries/images/016.php b/user_guide_src/source/libraries/images/016.php new file mode 100644 index 000000000000..9e780b1b8994 --- /dev/null +++ b/user_guide_src/source/libraries/images/016.php @@ -0,0 +1,8 @@ +withFile('/path/to/image/mypic.jpg') + ->clearMetadata([ + 'except' => ['exif:Copyright', 'exif:Author'], + ]) + ->save('/path/to/new/image.jpg'); diff --git a/user_guide_src/source/libraries/images/017.php b/user_guide_src/source/libraries/images/017.php new file mode 100644 index 000000000000..a69330720e96 --- /dev/null +++ b/user_guide_src/source/libraries/images/017.php @@ -0,0 +1,10 @@ +withFile('/path/to/image/mypic.jpg') + ->clearMetadata([ + 'exif:GPSLatitude', + 'exif:GPSLongitude', + 'exif:GPSAltitude', + ]) + ->save('/path/to/new/image.jpg'); diff --git a/utils/phpstan-baseline/loader.neon b/utils/phpstan-baseline/loader.neon index e4506414128e..18506a4ff302 100644 --- a/utils/phpstan-baseline/loader.neon +++ b/utils/phpstan-baseline/loader.neon @@ -1,4 +1,4 @@ -# total 3253 errors +# total 3259 errors includes: - argument.type.neon - assign.propertyType.neon diff --git a/utils/phpstan-baseline/varTag.type.neon b/utils/phpstan-baseline/varTag.type.neon index 4be1089ace00..5ad72847e223 100644 --- a/utils/phpstan-baseline/varTag.type.neon +++ b/utils/phpstan-baseline/varTag.type.neon @@ -1,7 +1,12 @@ -# total 2 errors +# total 8 errors parameters: ignoreErrors: + - + message: '#^PHPDoc tag @var with type Imagick is not subtype of type resource\.$#' + count: 6 + path: ../../tests/system/Images/ImageMagickHandlerTest.php + - message: '#^PHPDoc tag @var with type Tests\\Support\\Entity\\UserWithCasts is not subtype of type list\\|null\.$#' count: 1 From 56eb92eadf868fd647a376ece291237bd513ab7a Mon Sep 17 00:00:00 2001 From: michalsn Date: Tue, 22 Apr 2025 12:50:33 +0200 Subject: [PATCH 2/6] simplify tests --- tests/system/Images/ImageMagickHandlerTest.php | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/system/Images/ImageMagickHandlerTest.php b/tests/system/Images/ImageMagickHandlerTest.php index 4de8d46dee14..1bed10318579 100644 --- a/tests/system/Images/ImageMagickHandlerTest.php +++ b/tests/system/Images/ImageMagickHandlerTest.php @@ -465,7 +465,7 @@ public function testClearMetadataAll(): void /** @var Imagick $imagick */ $imagick = $this->handler->getResource(); $before = $imagick->getImageProperties(); - $this->assertCount(14, $before); + $this->assertGreaterThan(10, $before); $this->handler ->clearMetadata() @@ -476,7 +476,7 @@ public function testClearMetadataAll(): void $imagick = $this->handler->getResource(); $after = $imagick->getImageProperties(); - $this->assertCount(9, $after); + $this->assertLessThan(10, count($after)); } public function testClearMetadataExcept(): void @@ -485,7 +485,7 @@ public function testClearMetadataExcept(): void /** @var Imagick $imagick */ $imagick = $this->handler->getResource(); $before = $imagick->getImageProperties(); - $this->assertCount(14, $before); + $this->assertArrayHasKey('png:gAMA', $before); // Keep 2 properties $this->handler @@ -500,8 +500,6 @@ public function testClearMetadataExcept(): void $this->assertArrayHasKey('png:bKGD', $after); $this->assertArrayHasKey('png:cHRM', $after); $this->assertArrayNotHasKey('png:gAMA', $after); - - $this->assertCount(12, $after); } public function testClearMetadataSpecific(): void @@ -511,8 +509,9 @@ public function testClearMetadataSpecific(): void $imagick = $this->handler->getResource(); $before = $imagick->getImageProperties(); - $this->assertArrayNotHasKey('png:tIME', $before); - $this->assertCount(14, $before); + $this->assertArrayHasKey('png:bKGD', $before); + $this->assertArrayHasKey('png:cHRM', $before); + $this->assertArrayHasKey('png:gAMA', $before); // Delete only 1 $this->handler @@ -526,9 +525,6 @@ public function testClearMetadataSpecific(): void $this->assertArrayHasKey('png:bKGD', $after); $this->assertArrayHasKey('png:cHRM', $after); - $this->assertArrayHasKey('png:tIME', $after); $this->assertArrayNotHasKey('png:gAMA', $after); - - $this->assertCount(14, $after); } } From dd56f1e520da5b7b646efb469afb1580f84e2107 Mon Sep 17 00:00:00 2001 From: michalsn Date: Wed, 23 Apr 2025 08:14:04 +0200 Subject: [PATCH 3/6] update clearMetadata() --- system/Images/Handlers/BaseHandler.php | 4 +- system/Images/Handlers/ImageMagickHandler.php | 37 +---------- .../system/Images/ImageMagickHandlerTest.php | 62 +++---------------- user_guide_src/source/libraries/images.rst | 20 ++---- .../source/libraries/images/016.php | 8 --- .../source/libraries/images/017.php | 10 --- utils/phpstan-baseline/loader.neon | 2 +- utils/phpstan-baseline/varTag.type.neon | 4 +- 8 files changed, 18 insertions(+), 129 deletions(-) delete mode 100644 user_guide_src/source/libraries/images/016.php delete mode 100644 user_guide_src/source/libraries/images/017.php diff --git a/system/Images/Handlers/BaseHandler.php b/system/Images/Handlers/BaseHandler.php index 33bb5a97aafd..5996be0b52ba 100644 --- a/system/Images/Handlers/BaseHandler.php +++ b/system/Images/Handlers/BaseHandler.php @@ -777,10 +777,8 @@ public function getHeight() * since all the data are cleared automatically. * * GDHandler can't preserve the image metadata. - * - * @param array|string> $data */ - public function clearMetadata(array $data = []): static + public function clearMetadata(): static { return $this; } diff --git a/system/Images/Handlers/ImageMagickHandler.php b/system/Images/Handlers/ImageMagickHandler.php index dd78e51bc8ba..2e8b56acd858 100644 --- a/system/Images/Handlers/ImageMagickHandler.php +++ b/system/Images/Handlers/ImageMagickHandler.php @@ -539,48 +539,17 @@ public function reorient(bool $silent = false) } /** - * Clears metadata from the image based on specified parameters. - * - * Configuration for metadata clearing: - * - If empty, all metadata is stripped - * - If contains 'except' key, keeps only those properties - * - Otherwise, deletes only the specified keys - * - * @param array|string> $data + * Clears metadata from the image. * * @return $this * * @throws ImagickException */ - public function clearMetadata(array $data = []): static + public function clearMetadata(): static { $this->ensureResource(); - // Strip all metadata when no parameters are provided - if ($data === []) { - $this->resource->stripImage(); - - return $this; - } - - // Keep only properties specified in 'except' array - if (isset($data['except'])) { - $propertiesToKeep = (array) $data['except']; - $allPropertyNames = $this->resource->getImageProperties('*', false); - - foreach ($allPropertyNames as $property) { - if (! in_array($property, $propertiesToKeep, true)) { - $this->resource->deleteImageProperty($property); - } - } - - return $this; - } - - // Delete only specific properties - foreach ($data as $property) { - $this->resource->deleteImageProperty($property); - } + $this->resource->stripImage(); return $this; } diff --git a/tests/system/Images/ImageMagickHandlerTest.php b/tests/system/Images/ImageMagickHandlerTest.php index 1bed10318579..8fc45f220676 100644 --- a/tests/system/Images/ImageMagickHandlerTest.php +++ b/tests/system/Images/ImageMagickHandlerTest.php @@ -459,72 +459,24 @@ public function testClearMetadataReturnsSelf(): void $this->assertSame($this->handler, $result); } - public function testClearMetadataAll(): void + public function testClearMetadata(): void { - $this->handler->withFile($this->path); + $this->handler->withFile($this->origin . 'Steveston_dusk.JPG'); /** @var Imagick $imagick */ $imagick = $this->handler->getResource(); $before = $imagick->getImageProperties(); - $this->assertGreaterThan(10, $before); - - $this->handler - ->clearMetadata() - ->save($this->root . 'ci-logo-no-metadata.png'); - - $this->handler->withFile($this->root . 'ci-logo-no-metadata.png'); - /** @var Imagick $imagick */ - $imagick = $this->handler->getResource(); - $after = $imagick->getImageProperties(); - $this->assertLessThan(10, count($after)); - } - - public function testClearMetadataExcept(): void - { - $this->handler->withFile($this->path); - /** @var Imagick $imagick */ - $imagick = $this->handler->getResource(); - $before = $imagick->getImageProperties(); - $this->assertArrayHasKey('png:gAMA', $before); + $this->assertCount(44, $before); - // Keep 2 properties $this->handler - ->clearMetadata(['except' => ['png:bKGD', 'png:cHRM']]) - ->save($this->root . 'ci-logo-no-metadata.png'); - - $this->handler->withFile($this->root . 'ci-logo-no-metadata.png'); - /** @var Imagick $imagick */ - $imagick = $this->handler->getResource(); - $after = $imagick->getImageProperties(); - - $this->assertArrayHasKey('png:bKGD', $after); - $this->assertArrayHasKey('png:cHRM', $after); - $this->assertArrayNotHasKey('png:gAMA', $after); - } - - public function testClearMetadataSpecific(): void - { - $this->handler->withFile($this->path); - /** @var Imagick $imagick */ - $imagick = $this->handler->getResource(); - $before = $imagick->getImageProperties(); - - $this->assertArrayHasKey('png:bKGD', $before); - $this->assertArrayHasKey('png:cHRM', $before); - $this->assertArrayHasKey('png:gAMA', $before); - - // Delete only 1 - $this->handler - ->clearMetadata(['png:gAMA']) - ->save($this->root . 'ci-logo-no-metadata.png'); + ->clearMetadata() + ->save($this->root . 'exif-info-no-metadata.jpg'); - $this->handler->withFile($this->root . 'ci-logo-no-metadata.png'); + $this->handler->withFile($this->root . 'exif-info-no-metadata.jpg'); /** @var Imagick $imagick */ $imagick = $this->handler->getResource(); $after = $imagick->getImageProperties(); - $this->assertArrayHasKey('png:bKGD', $after); - $this->assertArrayHasKey('png:cHRM', $after); - $this->assertArrayNotHasKey('png:gAMA', $after); + $this->assertCount(5, $after); } } diff --git a/user_guide_src/source/libraries/images.rst b/user_guide_src/source/libraries/images.rst index 12b66a0f8075..b2b6b0238942 100644 --- a/user_guide_src/source/libraries/images.rst +++ b/user_guide_src/source/libraries/images.rst @@ -264,26 +264,14 @@ The possible options that are recognized are as follows: Clearing Image Metadata ======================= -This method provides control over which metadata is preserved or removed from an image. +This method removes metadata (EXIF, XMP, ICC, IPTC, comments, etc.) from an image. .. important:: The GD image library automatically strips all metadata during processing, so this method has no additional effect when using the GD handler. This behavior is built into GD itself and cannot be modified. -.. note:: Some essential technical metadata (dimensions, color depth) will be regenerated - during save operations as they're required for image display. However, all privacy-sensitive - information such as GPS location, camera details, and timestamps can be completely removed. - -The method supports three different operations depending on the provided parameters: - -**Clear all metadata** - When an empty array is passed, all metadata is stripped from the image. +Some essential technical metadata (dimensions, color depth) will be regenerated during save operations +as they're required for image display. However, all privacy-sensitive information such as GPS location, +camera details, and timestamps will be completely removed. .. literalinclude:: images/015.php - -**Keep only specific properties** - When using the 'except' key, only the specified properties are preserved. - -.. literalinclude:: images/016.php - -**Delete specific properties** - When providing a list of property names, only those properties are removed. - -.. literalinclude:: images/017.php diff --git a/user_guide_src/source/libraries/images/016.php b/user_guide_src/source/libraries/images/016.php deleted file mode 100644 index 9e780b1b8994..000000000000 --- a/user_guide_src/source/libraries/images/016.php +++ /dev/null @@ -1,8 +0,0 @@ -withFile('/path/to/image/mypic.jpg') - ->clearMetadata([ - 'except' => ['exif:Copyright', 'exif:Author'], - ]) - ->save('/path/to/new/image.jpg'); diff --git a/user_guide_src/source/libraries/images/017.php b/user_guide_src/source/libraries/images/017.php deleted file mode 100644 index a69330720e96..000000000000 --- a/user_guide_src/source/libraries/images/017.php +++ /dev/null @@ -1,10 +0,0 @@ -withFile('/path/to/image/mypic.jpg') - ->clearMetadata([ - 'exif:GPSLatitude', - 'exif:GPSLongitude', - 'exif:GPSAltitude', - ]) - ->save('/path/to/new/image.jpg'); diff --git a/utils/phpstan-baseline/loader.neon b/utils/phpstan-baseline/loader.neon index 18506a4ff302..f93eac55feb4 100644 --- a/utils/phpstan-baseline/loader.neon +++ b/utils/phpstan-baseline/loader.neon @@ -1,4 +1,4 @@ -# total 3259 errors +# total 3255 errors includes: - argument.type.neon - assign.propertyType.neon diff --git a/utils/phpstan-baseline/varTag.type.neon b/utils/phpstan-baseline/varTag.type.neon index 5ad72847e223..099fce1db55c 100644 --- a/utils/phpstan-baseline/varTag.type.neon +++ b/utils/phpstan-baseline/varTag.type.neon @@ -1,10 +1,10 @@ -# total 8 errors +# total 4 errors parameters: ignoreErrors: - message: '#^PHPDoc tag @var with type Imagick is not subtype of type resource\.$#' - count: 6 + count: 2 path: ../../tests/system/Images/ImageMagickHandlerTest.php - From afe903f62a0f2bc840525ce25a8c9e4ee64a8a64 Mon Sep 17 00:00:00 2001 From: michalsn Date: Wed, 23 Apr 2025 08:20:06 +0200 Subject: [PATCH 4/6] fix test --- tests/system/Images/ImageMagickHandlerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/Images/ImageMagickHandlerTest.php b/tests/system/Images/ImageMagickHandlerTest.php index 8fc45f220676..be6bee2cf798 100644 --- a/tests/system/Images/ImageMagickHandlerTest.php +++ b/tests/system/Images/ImageMagickHandlerTest.php @@ -466,7 +466,7 @@ public function testClearMetadata(): void $imagick = $this->handler->getResource(); $before = $imagick->getImageProperties(); - $this->assertCount(44, $before); + $this->assertGreaterThan(40, count($before)); $this->handler ->clearMetadata() @@ -477,6 +477,6 @@ public function testClearMetadata(): void $imagick = $this->handler->getResource(); $after = $imagick->getImageProperties(); - $this->assertCount(5, $after); + $this->assertLessThanOrEqual(5, count($after)); } } From 812703a9f62b096f5be62f53c1d5eb676eede9e1 Mon Sep 17 00:00:00 2001 From: michalsn Date: Sat, 3 May 2025 18:00:52 +0200 Subject: [PATCH 5/6] add clearMetadata to the interface --- system/Images/ImageHandlerInterface.php | 7 +++++++ user_guide_src/source/changelogs/v4.7.0.rst | 2 ++ 2 files changed, 9 insertions(+) diff --git a/system/Images/ImageHandlerInterface.php b/system/Images/ImageHandlerInterface.php index 93b2009d6a93..a96977214ec8 100644 --- a/system/Images/ImageHandlerInterface.php +++ b/system/Images/ImageHandlerInterface.php @@ -149,4 +149,11 @@ public function text(string $text, array $options = []); * @return bool */ public function save(?string $target = null, int $quality = 90); + + /** + * Clear metadata before saving image as a new file. + * + * @return $this + */ + public function clearMetadata(): static; } diff --git a/user_guide_src/source/changelogs/v4.7.0.rst b/user_guide_src/source/changelogs/v4.7.0.rst index c6dfde8cd61e..2f48a1007161 100644 --- a/user_guide_src/source/changelogs/v4.7.0.rst +++ b/user_guide_src/source/changelogs/v4.7.0.rst @@ -26,6 +26,8 @@ Behavior Changes Interface Changes ================= +- **Images:** The ``ImageHandlerInterface`` now includes a new method: ``clearMetadata()``. If you've implemented your own handler from scratch, you will need to provide an implementation for this method to ensure compatibility. + Method Signature Changes ======================== From ffa314aeeb959eda06018a706064173fa7324843 Mon Sep 17 00:00:00 2001 From: michalsn Date: Sat, 3 May 2025 18:01:09 +0200 Subject: [PATCH 6/6] update method description --- system/Images/Handlers/BaseHandler.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/system/Images/Handlers/BaseHandler.php b/system/Images/Handlers/BaseHandler.php index 5996be0b52ba..e38c5836ccca 100644 --- a/system/Images/Handlers/BaseHandler.php +++ b/system/Images/Handlers/BaseHandler.php @@ -771,12 +771,9 @@ public function getHeight() } /** - * Clears image metadata. + * Placeholder method for implementing metadata clearing logic. * - * This method has no use in the GDHandler, - * since all the data are cleared automatically. - * - * GDHandler can't preserve the image metadata. + * This method should be implemented to remove or reset metadata as needed. */ public function clearMetadata(): static {