diff --git a/core/modules/image/src/Tests/ImageDimensionsTest.php b/core/modules/image/src/Tests/ImageDimensionsTest.php index 136b0f1..d81c04d 100644 --- a/core/modules/image/src/Tests/ImageDimensionsTest.php +++ b/core/modules/image/src/Tests/ImageDimensionsTest.php @@ -207,18 +207,14 @@ public function testImageDimensions() { $effect_id = $style->addImageEffect($effect); $style->save(); - // @todo Uncomment this once - // https://www.drupal.org/project/drupal/issues/2670966 is resolved. - // $this->assertEqual($this->getImageTag($variables), ''); + $this->assertEqual($this->getImageTag($variables), ''); $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.'); $this->drupalGet($this->getAbsoluteUrl($url)); $this->assertResponse(200, 'Image was generated at the URL.'); $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.'); $image_file = $image_factory->get($generated_uri); - // @todo Uncomment this once - // https://www.drupal.org/project/drupal/issues/2670966 is resolved. - // $this->assertEqual($image_file->getWidth(), 41); - // $this->assertEqual($image_file->getHeight(), 41); + $this->assertEqual($image_file->getWidth(), 41); + $this->assertEqual($image_file->getHeight(), 41); $effect_plugin = $style->getEffect($effect_id); $style->deleteImageEffect($effect_plugin); diff --git a/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Rotate.php b/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Rotate.php index e567ea4..af6bb6d 100644 --- a/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Rotate.php +++ b/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Rotate.php @@ -3,6 +3,7 @@ namespace Drupal\system\Plugin\ImageToolkit\Operation\gd; use Drupal\Component\Utility\Color; +use Drupal\Component\Utility\Rectangle; /** * Defines GD2 rotate operation. @@ -96,6 +97,13 @@ protected function execute(array $arguments) { // Stores the original GD resource. $original_res = $this->getToolkit()->getResource(); + // Get expected width and height resulting from the rotation. + $rect = new Rectangle($this->getToolkit()->getWidth(), $this->getToolkit()->getHeight()); + $rect = $rect->rotate($arguments['degrees']); + $expected_width = $rect->getBoundingWidth(); + $expected_height = $rect->getBoundingHeight(); + + // Rotate the image. if ($new_res = imagerotate($this->getToolkit()->getResource(), 360 - $arguments['degrees'], $arguments['background_idx'])) { $this->getToolkit()->setResource($new_res); imagedestroy($original_res); @@ -108,6 +116,67 @@ protected function execute(array $arguments) { imagecolortransparent($this->getToolkit()->getResource(), $transparent_idx); } + // Resizes the image if width and height are not as expected. + // PHP 7.0.26+ and 7.1.12+ GD rotates differently then it did in PHP 5.5 + // and above, resulting in different dimensions. Here we are harmonizing + // final size across PHP versions by resizing the image to the dimensions + // we expect. + // @see https://bugs.php.net/bug.php?id=65148 + if ($this->getToolkit()->getWidth() != $expected_width || $this->getToolkit()->getHeight() != $expected_height) { + // If either dimension of the current image is bigger than expected, + // crop the image. + if ($this->getToolkit()->getWidth() > $expected_width || $this->getToolkit()->getHeight() > $expected_height) { + $crop_width = min($expected_width, $this->getToolkit()->getWidth()); + $crop_height = min($expected_height, $this->getToolkit()->getHeight()); + if (!$this->getToolkit()->apply('crop', [ + 'x' => $this->getToolkit()->getWidth() / 2 - $crop_width / 2, + 'y' => $this->getToolkit()->getHeight() / 2 - $crop_height /2, + 'width' => $crop_width, + 'height' => $crop_height, + ])) { + return FALSE; + } + } + // If the image at this point is smaller than expected, place it above + // a canvas of the expected dimensions. + if ($this->getToolkit()->getWidth() < $expected_width || $this->getToolkit()->getHeight() < $expected_height) { + // Store the current GD resource. + $temp_res = $this->getToolkit()->getResource(); + + // Prepare the canvas. + $data = [ + 'width' => $expected_width, + 'height' => $expected_height, + 'extension' => image_type_to_extension($this->getToolkit()->getType(), FALSE), + 'transparent_color' => $this->getToolkit()->getTransparentColor(), + 'is_temp' => TRUE, + ]; + if (!$this->getToolkit()->apply('create_new', $data)) { + return FALSE; + } + + // Fill the canvas with the required background color. + imagefill($this->getToolkit()->getResource(), 0, 0, $arguments['background_idx']); + + // Overlay the current image on the canvas. + imagealphablending($temp_res, TRUE); + imagesavealpha($temp_res, TRUE); + imagealphablending($this->getToolkit()->getResource(), TRUE); + imagesavealpha($this->getToolkit()->getResource(), TRUE); + $x_pos = (int) ($expected_width / 2 - imagesx($temp_res) / 2); + $y_pos = (int) ($expected_height / 2 - imagesy($temp_res) / 2); + if (imagecopy($this->getToolkit()->getResource(), $temp_res, $x_pos, $y_pos, 0, 0, imagesx($temp_res), imagesy($temp_res))) { + imagedestroy($temp_res); + } + else { + // In case of failure, destroy the temporary resource and restore + // the original one. + imagedestroy($this->getToolkit()->getResource()); + $this->getToolkit()->setResource($temp_res); + return FALSE; + } + } + } return TRUE; } diff --git a/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php index 96ce7e0..8148a89 100644 --- a/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php +++ b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php @@ -214,9 +214,7 @@ public function testManipulations() { ]; // Systems using non-bundled GD2 don't have imagerotate. Test if available. - // @todo Remove the version check once - // https://www.drupal.org/project/drupal/issues/2670966 is resolved. - if (function_exists('imagerotate') && (version_compare(phpversion(), '7.0.26') < 0)) { + if (function_exists('imagerotate')) { $operations += [ 'rotate_5' => [ 'function' => 'rotate',