diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 7f8314a..fe47168 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -290,12 +290,22 @@ function template_preprocess_image_style(&$variables) { $style->transformDimensions($dimensions, $variables['uri']); + // If the current image toolkit supports this file type, prepare the URI for + // the derivative image. If not, just use the original image resized to the + // dimensions specified by the style. + if (in_array(pathinfo($variables['uri'], PATHINFO_EXTENSION), \Drupal::service('image.factory')->getSupportedExtensions())) { + $uri = $style->buildUrl($variables['uri']); + } + else { + $uri = $variables['uri']; + } + $variables['image'] = array( '#theme' => 'image', '#width' => $dimensions['width'], '#height' => $dimensions['height'], '#attributes' => $variables['attributes'], - '#uri' => $style->buildUrl($variables['uri']), + '#uri' => $uri, '#style_name' => $variables['style_name'], ); diff --git a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php index e3ade5e..4c3a27d 100644 --- a/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php +++ b/core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php @@ -5,13 +5,11 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldDefinitionInterface; -use Drupal\Core\Image\ImageFactory; use Drupal\Core\Link; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Drupal\image\Entity\ImageStyle; -use Drupal\file\FileInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Cache\Cache; @@ -44,13 +42,6 @@ class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPlugi protected $imageStyleStorage; /** - * The image factory service. - * - * @var \Drupal\Core\Image\ImageFactory - */ - protected $imageFactory; - - /** * Constructs an ImageFormatter object. * * @param string $plugin_id @@ -70,11 +61,10 @@ class ImageFormatter extends ImageFormatterBase implements ContainerFactoryPlugi * @param \Drupal\Core\Session\AccountInterface $current_user * The current user. */ - public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, AccountInterface $current_user, EntityStorageInterface $image_style_storage, ImageFactory $image_factory) { + public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, AccountInterface $current_user, EntityStorageInterface $image_style_storage) { parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings); $this->currentUser = $current_user; $this->imageStyleStorage = $image_style_storage; - $this->imageFactory = $image_factory; } /** @@ -90,8 +80,7 @@ public static function create(ContainerInterface $container, array $configuratio $configuration['view_mode'], $configuration['third_party_settings'], $container->get('current_user'), - $container->get('entity.manager')->getStorage('image_style'), - $container->get('image.factory') + $container->get('entity.manager')->getStorage('image_style') ); } @@ -196,12 +185,15 @@ public function viewElements(FieldItemListInterface $items, $langcode) { $link_file = TRUE; } - $image_style_id = $this->getSetting('image_style'); - if ($image_style_id) { - $image_style = $this->imageStyleStorage->load($image_style_id); + $image_style_setting = $this->getSetting('image_style'); + + // Collect cache tags to be added for each item in the field. + $base_cache_tags = []; + if (!empty($image_style_setting)) { + $image_style = $this->imageStyleStorage->load($image_style_setting); + $base_cache_tags = $image_style->getCacheTags(); } - /** @var FileInterface $file */ foreach ($files as $delta => $file) { $cache_contexts = []; if (isset($link_file)) { @@ -214,6 +206,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { $url = Url::fromUri(file_create_url($image_uri)); $cache_contexts[] = 'url.site'; } + $cache_tags = Cache::mergeTags($base_cache_tags, $file->getCacheTags()); // Extract field item attributes for the theme function, and unset them // from the $item so that the field template does not re-render them. @@ -225,20 +218,13 @@ public function viewElements(FieldItemListInterface $items, $langcode) { '#theme' => 'image_formatter', '#item' => $item, '#item_attributes' => $item_attributes, + '#image_style' => $image_style_setting, '#url' => $url, '#cache' => array( - // Always use the file's cache tags. - 'tags' => $file->getCacheTags(), + 'tags' => $cache_tags, 'contexts' => $cache_contexts, ), ); - // If we're using an image style, and the current image toolkit supports - // this file type, apply the image style's cache tags to the rendered - // item. - if (isset($image_style) && $this->imageFactory->get($file->getFileUri())->isValid()) { - $elements[$delta]['#image_style'] = $image_style->id(); - $elements[$delta]['#cache']['tags'] = Cache::mergeTags($elements[$delta]['#cache']['tags'], $image_style->getCacheTags()); - } } return $elements; diff --git a/core/modules/image/tests/src/Kernel/ImageFormatterTest.php b/core/modules/image/tests/src/Kernel/ImageFormatterTest.php index ebb856b..67f3f27 100644 --- a/core/modules/image/tests/src/Kernel/ImageFormatterTest.php +++ b/core/modules/image/tests/src/Kernel/ImageFormatterTest.php @@ -111,19 +111,22 @@ public function testImageFormatterSvg() { // Install the default image styles. $this->installConfig(['image']); + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = $this->container->get('renderer'); + $png = File::create([ - 'uri' => 'temporary://' . $this->randomMachineName() . '.png', + 'uri' => 'public://test-image.png', ]); $png->save(); // We need to create an actual empty PNG, or the GD toolkit will not // consider the image valid. - $png_resource = imagecreate(100, 100); + $png_resource = imagecreate(300, 300); imagefill($png_resource, 0, 0, imagecolorallocate($png_resource, 0, 0, 0)); imagepng($png_resource, $png->getFileUri()); $svg = File::create([ - 'uri' => 'temporary://' . $this->randomMachineName() . '.svg', + 'uri' => 'public://test-image.svg', ]); $svg->save(); // We don't have to put any real SVG data in here, because the GD toolkit @@ -144,14 +147,31 @@ public function testImageFormatterSvg() { $build = $this->display->build($entity); // The first image is a PNG, so it is supported by the GD image toolkit. - // Which means that the image style should be applied, cache tags and all. - $this->assertEquals('medium', $build[$this->fieldName][0]['#image_style']); + // The image style should be applied with its cache tags, image derivative + // computed with its URI and dimensions. $this->assertCacheTags($build[$this->fieldName][0], ImageStyle::load('medium')->getCacheTags()); + $renderer->renderRoot($build[$this->fieldName][0]); + $this->assertEquals('medium', $build[$this->fieldName][0]['#image_style']); + // We check that the image URL contains the expected style directory + // structure. + $this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'styles/medium/public/test-image.png') !== FALSE); + $this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'width="220"') !== FALSE); + $this->assertTrue(strpos($build[$this->fieldName][0]['#markup'], 'height="220"') !== FALSE); // The second image is an SVG, which is not supported by the GD toolkit. - // So it should NOT have the image style (or its cache tags) applied. - $this->assertArrayNotHasKey('#image_style', $build[$this->fieldName][1]); - $this->assertSame($svg->getCacheTags(), $build[$this->fieldName][1]['#cache']['tags']); + // The image style should still be applied with its cache tags, but image + // derivative will not be available so tag will point to the original + // image. + $this->assertCacheTags($build[$this->fieldName][1], ImageStyle::load('medium')->getCacheTags()); + $renderer->renderRoot($build[$this->fieldName][1]); + $this->assertEquals('medium', $build[$this->fieldName][1]['#image_style']); + // We check that the image URL does not contain the style directory + // structure. + $this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'styles/medium/public/test-image.svg')); + // Since we did not store original image dimensions, width and height + // HTML attributes will not be present. + $this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'width')); + $this->assertFalse(strpos($build[$this->fieldName][1]['#markup'], 'height')); } /**