From b98a997f100a8b5388a1cdfff640ad8d1b281eff Mon Sep 17 00:00:00 2001 From: Dave Long <dave@longwaveconsulting.com> Date: Sat, 2 Nov 2024 22:46:14 +0000 Subject: [PATCH] Issue #2350849 by mondrake, fietserwin, ankithashetty, jhedstrom: Deprecate image_filter_keyword() (cherry picked from commit fa4cdaf3f26a829d4a403946948b42b727c3f5f7) --- core/lib/Drupal/Component/Utility/Image.php | 25 ++++++ core/modules/image/image.module | 6 ++ .../Plugin/ImageEffect/CropImageEffect.php | 5 +- .../ImageEffect/ScaleAndCropImageEffect.php | 9 ++- .../src/Functional/ImageFieldTestBase.php | 1 - .../tests/src/Kernel/ImageEffectsTest.php | 10 +-- .../tests/src/Unit/ImageDeprecationTest.php | 24 ++++++ .../Tests/Component/Utility/ImageTest.php | 80 +++++++++++++++++++ 8 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 core/modules/image/tests/src/Unit/ImageDeprecationTest.php diff --git a/core/lib/Drupal/Component/Utility/Image.php b/core/lib/Drupal/Component/Utility/Image.php index f1368c1b8390..d955644b5c99 100644 --- a/core/lib/Drupal/Component/Utility/Image.php +++ b/core/lib/Drupal/Component/Utility/Image.php @@ -56,4 +56,29 @@ public static function scaleDimensions(array &$dimensions, $width = NULL, $heigh return TRUE; } + /** + * Returns the offset in pixels from the anchor. + * + * @param string $anchor + * The anchor ('top', 'left', 'bottom', 'right', 'center'). + * @param int $current_size + * The current size, in pixels. + * @param int $new_size + * The new size, in pixels. + * + * @return int + * The offset from the anchor, in pixels. + * + * @throws \InvalidArgumentException + * When the $anchor argument is not valid. + */ + public static function getKeywordOffset(string $anchor, int $current_size, int $new_size): int { + return match ($anchor) { + 'bottom', 'right' => $current_size - $new_size, + 'center' => (int) round($current_size / 2 - $new_size / 2), + 'top', 'left' => 0, + default => throw new \InvalidArgumentException("Invalid anchor '{$anchor}' provided to getKeywordOffset()"), + }; + } + } diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 6807f2c02b76..864be56cf41a 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -336,8 +336,14 @@ function template_preprocess_image_style(&$variables) { * @return int|string * The offset from the anchor, in pixels, or the anchor itself, if its value * isn't one of the accepted values. + * + * @deprecated in drupal:11.1.0 and is removed from drupal:12.0.0. Use + * \Drupal\Component\Utility\Image::getKeywordOffset() instead. + * + * @see https://www.drupal.org/node/3268441 */ function image_filter_keyword($anchor, $current_size, $new_size) { + @trigger_error('image_filter_keyword() is deprecated in drupal:11.1.0 and is removed from drupal:12.0.0. Use \Drupal\Component\Utility\Image::getKeywordOffset() instead. See https://www.drupal.org/node/3268441', E_USER_DEPRECATED); switch ($anchor) { case 'top': case 'left': diff --git a/core/modules/image/src/Plugin/ImageEffect/CropImageEffect.php b/core/modules/image/src/Plugin/ImageEffect/CropImageEffect.php index accdfe17b88f..7e31430e9c9e 100644 --- a/core/modules/image/src/Plugin/ImageEffect/CropImageEffect.php +++ b/core/modules/image/src/Plugin/ImageEffect/CropImageEffect.php @@ -2,6 +2,7 @@ namespace Drupal\image\Plugin\ImageEffect; +use Drupal\Component\Utility\Image; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Image\ImageInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; @@ -22,8 +23,8 @@ class CropImageEffect extends ResizeImageEffect { */ public function applyEffect(ImageInterface $image) { [$x, $y] = explode('-', $this->configuration['anchor']); - $x = image_filter_keyword($x, $image->getWidth(), $this->configuration['width']); - $y = image_filter_keyword($y, $image->getHeight(), $this->configuration['height']); + $x = Image::getKeywordOffset($x, $image->getWidth(), (int) $this->configuration['width']); + $y = Image::getKeywordOffset($y, $image->getHeight(), (int) $this->configuration['height']); if (!$image->crop($x, $y, $this->configuration['width'], $this->configuration['height'])) { $this->logger->error('Image crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', ['%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()]); return FALSE; diff --git a/core/modules/image/src/Plugin/ImageEffect/ScaleAndCropImageEffect.php b/core/modules/image/src/Plugin/ImageEffect/ScaleAndCropImageEffect.php index 73489d7aceca..7ce4a2038859 100644 --- a/core/modules/image/src/Plugin/ImageEffect/ScaleAndCropImageEffect.php +++ b/core/modules/image/src/Plugin/ImageEffect/ScaleAndCropImageEffect.php @@ -2,6 +2,7 @@ namespace Drupal\image\Plugin\ImageEffect; +use Drupal\Component\Utility\Image; use Drupal\Core\Image\ImageInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\image\Attribute\ImageEffect; @@ -20,13 +21,13 @@ class ScaleAndCropImageEffect extends CropImageEffect { * {@inheritdoc} */ public function applyEffect(ImageInterface $image) { - $width = $this->configuration['width']; - $height = $this->configuration['height']; + $width = (int) $this->configuration['width']; + $height = (int) $this->configuration['height']; $scale = max($width / $image->getWidth(), $height / $image->getHeight()); [$x, $y] = explode('-', $this->configuration['anchor']); - $x = image_filter_keyword($x, $image->getWidth() * $scale, $width); - $y = image_filter_keyword($y, $image->getHeight() * $scale, $height); + $x = Image::getKeywordOffset($x, (int) round($image->getWidth() * $scale), $width); + $y = Image::getKeywordOffset($y, (int) round($image->getHeight() * $scale), $height); if (!$image->apply('scale_and_crop', ['x' => $x, 'y' => $y, 'width' => $width, 'height' => $height])) { $this->logger->error('Image scale and crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', ['%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()]); diff --git a/core/modules/image/tests/src/Functional/ImageFieldTestBase.php b/core/modules/image/tests/src/Functional/ImageFieldTestBase.php index afbf29003fe5..da98d02866a2 100644 --- a/core/modules/image/tests/src/Functional/ImageFieldTestBase.php +++ b/core/modules/image/tests/src/Functional/ImageFieldTestBase.php @@ -18,7 +18,6 @@ * - image.module: * image_style_options() * \Drupal\image\ImageStyleInterface::flush() - * image_filter_keyword() */ /** diff --git a/core/modules/image/tests/src/Kernel/ImageEffectsTest.php b/core/modules/image/tests/src/Kernel/ImageEffectsTest.php index 6b7662e33e43..1e5c75339225 100644 --- a/core/modules/image/tests/src/Kernel/ImageEffectsTest.php +++ b/core/modules/image/tests/src/Kernel/ImageEffectsTest.php @@ -87,7 +87,7 @@ public function testCropEffect(): void { // @todo Test also keyword offsets in #3040887. // @see https://www.drupal.org/project/drupal/issues/3040887 $this->assertImageEffect(['crop'], 'image_crop', [ - 'anchor' => 'top-1', + 'anchor' => 'top-left', 'width' => 3, 'height' => 4, ]); @@ -97,7 +97,7 @@ public function testCropEffect(): void { // X was passed correctly. $this->assertEquals(0, $calls['crop'][0][0]); // Y was passed correctly. - $this->assertEquals(1, $calls['crop'][0][1]); + $this->assertEquals(0, $calls['crop'][0][1]); // Width was passed correctly. $this->assertEquals(3, $calls['crop'][0][2]); // Height was passed correctly. @@ -131,7 +131,7 @@ public function testScaleAndCropEffect(): void { // Check the parameters. $calls = $this->imageTestGetAllCalls(); // X was computed and passed correctly. - $this->assertEquals(7.5, $calls['scale_and_crop'][0][0]); + $this->assertEquals(8, $calls['scale_and_crop'][0][0]); // Y was computed and passed correctly. $this->assertEquals(0, $calls['scale_and_crop'][0][1]); // Width was computed and passed correctly. @@ -145,7 +145,7 @@ public function testScaleAndCropEffect(): void { */ public function testScaleAndCropEffectWithAnchor(): void { $this->assertImageEffect(['scale_and_crop'], 'image_scale_and_crop', [ - 'anchor' => 'top-1', + 'anchor' => 'top-left', 'width' => 5, 'height' => 10, ]); @@ -155,7 +155,7 @@ public function testScaleAndCropEffectWithAnchor(): void { // X was computed and passed correctly. $this->assertEquals(0, $calls['scale_and_crop'][0][0]); // Y was computed and passed correctly. - $this->assertEquals(1, $calls['scale_and_crop'][0][1]); + $this->assertEquals(0, $calls['scale_and_crop'][0][1]); // Width was computed and passed correctly. $this->assertEquals(5, $calls['scale_and_crop'][0][2]); // Height was computed and passed correctly. diff --git a/core/modules/image/tests/src/Unit/ImageDeprecationTest.php b/core/modules/image/tests/src/Unit/ImageDeprecationTest.php new file mode 100644 index 000000000000..255f34284351 --- /dev/null +++ b/core/modules/image/tests/src/Unit/ImageDeprecationTest.php @@ -0,0 +1,24 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\image\Unit; + +use Drupal\Tests\UnitTestCase; + +/** + * @group Image + * @group legacy + */ +class ImageDeprecationTest extends UnitTestCase { + + /** + * Tests deprecation of image_filter_keyword. + */ + public function testImageFilterKeywordDeprecation(): void { + include_once __DIR__ . '/../../../image.module'; + $this->expectDeprecation('image_filter_keyword() is deprecated in drupal:11.1.0 and is removed from drupal:12.0.0. Use \Drupal\Component\Utility\Image::getKeywordOffset() instead. See https://www.drupal.org/node/3268441'); + $this->assertSame('miss', image_filter_keyword('miss', 0, 0)); + } + +} diff --git a/core/tests/Drupal/Tests/Component/Utility/ImageTest.php b/core/tests/Drupal/Tests/Component/Utility/ImageTest.php index 7d179e3145b3..e67eb9a4d163 100644 --- a/core/tests/Drupal/Tests/Component/Utility/ImageTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/ImageTest.php @@ -157,4 +157,84 @@ public static function providerTestScaleDimensions() { return $tests; } + /** + * @covers ::getKeywordOffset + */ + public function testInvalidGetKeywordOffset(): void { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid anchor \'foo\' provided to getKeywordOffset()'); + Image::getKeywordOffset('foo', 0, 0); + } + + /** + * @covers ::getKeywordOffset + * + * @dataProvider providerTestGetKeywordOffset + */ + public function testGetKeywordOffset(array $input, int $expected): void { + $this->assertSame($expected, Image::getKeywordOffset($input['anchor'], $input['current'], $input['new'])); + } + + /** + * Provides data for testGetKeywordOffset(). + * + * @return \Generator + * Test scenarios. + * + * @see testGetKeywordOffset() + */ + public static function providerTestGetKeywordOffset(): \Generator { + yield "'left' => return 0" => [ + 'input' => [ + 'anchor' => 'left', + 'current' => 100, + 'new' => 20, + ], + 'expected' => 0, + ]; + yield "'top' => return 0" => [ + 'input' => [ + 'anchor' => 'top', + 'current' => 100, + 'new' => 20, + ], + 'expected' => 0, + ]; + + yield "'right' => return (current - new)" => [ + 'input' => [ + 'anchor' => 'right', + 'current' => 100, + 'new' => 20, + ], + 'expected' => 80, + ]; + yield "'bottom' => return (current - new)" => [ + 'input' => [ + 'anchor' => 'bottom', + 'current' => 100, + 'new' => 30, + ], + 'expected' => 70, + ]; + + yield "a) 'center' => return (current - new)/2" => [ + 'input' => [ + 'anchor' => 'center', + 'current' => 100, + 'new' => 20, + ], + 'expected' => 40, + ]; + yield "b) 'center' => return (current - new)/2" => [ + 'input' => [ + 'anchor' => 'center', + 'current' => 100, + 'new' => 91, + ], + 'expected' => 5, + ]; + + } + } -- GitLab