diff --git a/src/View/ImageViewBuilder.php b/src/View/ImageViewBuilder.php
index de1d6da..72761cf 100644
--- a/src/View/ImageViewBuilder.php
+++ b/src/View/ImageViewBuilder.php
@@ -4,6 +4,7 @@ namespace Drupal\twig_tweak\View;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Image\ImageFactory;
use Drupal\file\FileInterface;
/**
@@ -11,6 +12,20 @@ use Drupal\file\FileInterface;
*/
class ImageViewBuilder {
+ /**
+ * The provider image factory.
+ *
+ * @var \Drupal\Core\Image\ImageFactory
+ */
+ protected $imageFactory;
+
+ /**
+ * Constructs an ImageViewBuilder object.
+ */
+ public function __construct(ImageFactory $imageFactory) {
+ $this->imageFactory = $imageFactory;
+ }
+
/**
* Builds an image.
*
@@ -37,6 +52,20 @@ class ImageViewBuilder {
$build['#uri'] = $file->getFileUri();
$build['#attributes'] = $attributes;
if ($style) {
+ // If an image style is given, image module needs the original
+ // image dimensions to calculate image style's
+ // width and height and set the attributes.
+ // See https://www.drupal.org/project/twig_tweak/issues/3356042
+ $uri = $file->getFileUri();
+ $image = $this->imageFactory->get($uri);
+ if ($image->isValid()) {
+ $build['#width'] = $image->getWidth();
+ $build['#height'] = $image->getHeight();
+ }
+ else {
+ $build['#width'] = $build['#height'] = NULL;
+ }
+
if ($responsive) {
$build['#type'] = 'responsive_image';
$build['#responsive_image_style_id'] = $style;
diff --git a/tests/src/Kernel/ImageViewBuilderTest.php b/tests/src/Kernel/ImageViewBuilderTest.php
index 53aa42b..3dab2ee 100644
--- a/tests/src/Kernel/ImageViewBuilderTest.php
+++ b/tests/src/Kernel/ImageViewBuilderTest.php
@@ -4,14 +4,14 @@ namespace Drupal\Tests\twig_tweak\Kernel;
use Drupal\Core\Cache\Cache;
use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\DrupalKernel;
+use Drupal\Core\File\FileSystemInterface;
use Drupal\file\Entity\File;
+use Drupal\file\FileInterface;
use Drupal\image\Entity\ImageStyle;
use Drupal\responsive_image\Entity\ResponsiveImageStyle;
-use Symfony\Component\HttpFoundation\Request;
/**
- * A test for ImageViewBuilderTest.
+ * A test class for testing the image view builder.
*
* @group twig_tweak
*/
@@ -31,21 +31,110 @@ final class ImageViewBuilderTest extends AbstractTestCase {
'breakpoint',
];
+ /**
+ * The ImageStyle.
+ *
+ * @var \Drupal\image\Entity\ImageStyle
+ */
+ protected $imageStyle;
+
+ /**
+ * The ResponsiveImageStyle.
+ *
+ * @var \Drupal\responsive_image\Entity\ResponsiveImageStyle
+ */
+ protected $responsiveImageStyle;
+
+ /**
+ * The public image uri.
+ *
+ * @var string
+ */
+ protected string $publicImageUri;
+
+ /**
+ * The private image uri.
+ *
+ * @var string
+ */
+ protected string $privateImageUri;
+
+ /**
+ * The public image file.
+ *
+ * @var \Drupal\file\FileInterface
+ */
+ protected $publicImage;
+
+ /**
+ * The private image file.
+ *
+ * @var \Drupal\file\FileInterface
+ */
+ protected $privateImage;
+
/**
* {@inheritdoc}
*/
public function setUp(): void {
parent::setUp();
- // Add file_private_path setting.
- $request = Request::create('/');
- $site_path = DrupalKernel::findSitePath($request);
- $this->setSetting('file_private_path', $site_path . '/private');
-
$this->installEntitySchema('file');
$this->installSchema('file', 'file_usage');
- ImageStyle::create(['name' => 'large'])->save();
- ResponsiveImageStyle::create(['id' => 'wide'])->save();
+
+ $fileSystemService = \Drupal::service('file_system');
+
+ $fileSystemService->prepareDirectory($this->siteDirectory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
+ $privateFilesDirectory = $this->siteDirectory . '/private';
+ $fileSystemService->prepareDirectory($privateFilesDirectory, FileSystemInterface::CREATE_DIRECTORY);
+ $this->setSetting('file_private_path', $privateFilesDirectory);
+
+ $this->imageStyle = ImageStyle::create([
+ 'name' => 'small',
+ 'label' => 'Small',
+ ]);
+ $this->imageStyle->save();
+ // Add a crop effect:
+ $this->imageStyle->addImageEffect([
+ 'id' => 'image_resize',
+ 'data' => [
+ 'width' => 10,
+ 'height' => 10,
+ ],
+ 'weight' => 0,
+ ]);
+ $this->imageStyle->save();
+
+ $this->responsiveImageStyle = ResponsiveImageStyle::create([
+ 'id' => 'wide',
+ 'label' => 'Wide',
+ 'breakpoint_group' => 'twig_tweak_image_view_builder',
+ 'fallback_image_style' => 'small',
+ ]);
+ $this->responsiveImageStyle->save();
+
+ // Create a copy of a test image file in root.
+ // Original sizes: 40x20px.
+ $this->publicImageUri = 'public://image-test-do.jpg';
+ $fileSystemService->copy('core/tests/fixtures/files/image-test.jpg', $this->publicImageUri, FileSystemInterface::EXISTS_REPLACE);
+ $this->assertFileExists($this->publicImageUri);
+ $this->publicImage = File::create([
+ 'uri' => $this->publicImageUri,
+ 'status' => FileInterface::STATUS_PERMANENT,
+ ]);
+ $this->publicImage->save();
+
+ // Create a copy of a test image file in root.
+ // Original sizes: 40x20px.
+ $this->privateImageUri = 'private://image-test-do.png';
+ $fileSystemService
+ ->copy('core/tests/fixtures/files/image-test.png', $this->privateImageUri, FileSystemInterface::EXISTS_REPLACE);
+ $this->assertFileExists($this->privateImageUri);
+ $this->privateImage = File::create([
+ 'uri' => $this->privateImageUri,
+ 'status' => FileInterface::STATUS_PERMANENT,
+ ]);
+ $this->privateImage->save();
}
/**
@@ -61,21 +150,20 @@ final class ImageViewBuilderTest extends AbstractTestCase {
* Test callback.
*/
public function testImageViewBuilder(): void {
-
$view_builder = $this->container->get('twig_tweak.image_view_builder');
- /** @var \Drupal\file\FileInterface $public_image */
- $public_image = File::create(['uri' => 'public://ocean.jpg']);
- $public_image->save();
-
- /** @var \Drupal\file\FileInterface $private_image */
- $private_image = File::create(['uri' => 'private://sea.jpg']);
- $private_image->save();
+ $uri = $this->publicImage->getFileUri();
+ $image = \Drupal::service('image.factory')->get($uri);
+ $imageOriginalWidth = $image->getWidth();
+ $imageOriginalHeight = $image->getHeight();
+ self::assertTrue($image->isValid());
+ self::assertEquals(40, $imageOriginalWidth);
+ self::assertEquals(20, $imageOriginalHeight);
// -- Without style.
- $build = $view_builder->build($public_image);
+ $build = $view_builder->build($this->publicImage);
$expected_build = [
- '#uri' => 'public://ocean.jpg',
+ '#uri' => $this->publicImageUri,
'#attributes' => [],
'#theme' => 'image',
'#cache' => [
@@ -85,21 +173,23 @@ final class ImageViewBuilderTest extends AbstractTestCase {
],
'tags' => [
'file:1',
- 'tag_for_public://ocean.jpg',
+ 'tag_for_' . $this->publicImageUri,
],
'max-age' => 70,
],
];
self::assertRenderArray($expected_build, $build);
- self::assertSame('', $this->renderPlain($build));
+ self::assertSame('', $this->renderPlain($build));
// -- With style.
- $build = $view_builder->build($public_image, 'large', ['alt' => 'Ocean']);
+ $build = $view_builder->build($this->publicImage, 'small', ['alt' => 'Image Test Do']);
$expected_build = [
- '#uri' => 'public://ocean.jpg',
- '#attributes' => ['alt' => 'Ocean'],
+ '#uri' => $this->publicImageUri,
+ '#attributes' => ['alt' => 'Image Test Do'],
+ '#width' => $imageOriginalWidth,
+ '#height' => $imageOriginalHeight,
'#theme' => 'image_style',
- '#style_name' => 'large',
+ '#style_name' => 'small',
'#cache' => [
'contexts' => [
'user',
@@ -107,19 +197,21 @@ final class ImageViewBuilderTest extends AbstractTestCase {
],
'tags' => [
'file:1',
- 'tag_for_public://ocean.jpg',
+ 'tag_for_' . $this->publicImageUri,
],
'max-age' => 70,
],
];
self::assertRenderArray($expected_build, $build);
- self::assertSame('', $this->renderPlain($build));
+ self::assertSame('', $this->renderPlain($build));
// -- With responsive style.
- $build = $view_builder->build($public_image, 'wide', ['alt' => 'Ocean'], TRUE);
+ $build = $view_builder->build($this->publicImage, 'wide', ['alt' => 'Image Test Do'], TRUE);
$expected_build = [
- '#uri' => 'public://ocean.jpg',
- '#attributes' => ['alt' => 'Ocean'],
+ '#uri' => $this->publicImageUri,
+ '#attributes' => ['alt' => 'Image Test Do'],
+ '#width' => $imageOriginalWidth,
+ '#height' => $imageOriginalHeight,
'#type' => 'responsive_image',
'#responsive_image_style_id' => 'wide',
'#cache' => [
@@ -129,22 +221,22 @@ final class ImageViewBuilderTest extends AbstractTestCase {
],
'tags' => [
'file:1',
- 'tag_for_public://ocean.jpg',
+ 'tag_for_' . $this->publicImageUri,
],
'max-age' => 70,
],
];
self::assertRenderArray($expected_build, $build);
- self::assertSame('', $this->renderPlain($build));
+ self::assertSame('', $this->renderPlain($build));
// -- Private image with access check.
- $build = $view_builder->build($private_image);
+ $build = $view_builder->build($this->privateImage);
$expected_build = [
'#cache' => [
'contexts' => ['user'],
'tags' => [
'file:2',
- 'tag_for_private://sea.jpg',
+ 'tag_for_' . $this->privateImageUri,
],
'max-age' => 70,
],
@@ -153,9 +245,9 @@ final class ImageViewBuilderTest extends AbstractTestCase {
self::assertSame('', $this->renderPlain($build));
// -- Private image without access check.
- $build = $view_builder->build($private_image, NULL, [], FALSE, FALSE);
+ $build = $view_builder->build($this->privateImage, NULL, [], FALSE, FALSE);
$expected_build = [
- '#uri' => 'private://sea.jpg',
+ '#uri' => $this->privateImageUri,
'#attributes' => [],
'#theme' => 'image',
'#cache' => [
@@ -165,7 +257,7 @@ final class ImageViewBuilderTest extends AbstractTestCase {
],
];
self::assertRenderArray($expected_build, $build);
- self::assertSame('', $this->renderPlain($build));
+ self::assertSame('', $this->renderPlain($build));
}
/**
diff --git a/twig_tweak.services.yml b/twig_tweak.services.yml
index 6e33f41..b224788 100644
--- a/twig_tweak.services.yml
+++ b/twig_tweak.services.yml
@@ -31,6 +31,7 @@ services:
twig_tweak.image_view_builder:
class: Drupal\twig_tweak\View\ImageViewBuilder
+ arguments: ['@image.factory']
twig_tweak.url_extractor:
class: Drupal\twig_tweak\UrlExtractor