Image width and height attributes swapped when portrait format

Created on 29 March 2024, 8 months ago
Updated 4 April 2024, 8 months ago

Problem/Motivation

  • Drupal core 10.2.4
  • ImageMagick 8.x-3.7
  • GraphQL 8.x-4.7
  • PHP 8.3

The bug:

  • the width and height attributes of images are swapped,
  • only affects portrait format -- height is greater than width
  • but not landscape format -- width is greater than height
  • in both cases the images themselves are correct, it is only the width and height attributes that are affected
  • Original images (all jpeg) and all image styles using "Scale" effect are affected. (Possibly others which I have not tried)
  • see annotated screenshot and code example below
  • perhaps Drupal::service('image.factory') is always setting width as the larger of the two dimensions, regardless of what they are

Additional info

I'm developing a site using Claro as my backend/admin theme with a react + graphql frontend using drupal/graphql v4
Initially I thought it could be a bug with the graphql module, but core (theme Claro) shows the same incorrect behaviour, see annotated screenshot below. My knowledge of Drupal 10 and PHP 8.3 are limited ;-), so unfortunately I have not been able to output (vardump, debug etc) within the code to track it down.

Steps to reproduce

  1. create a content type with an image field
  2. ensure in "Manage display" for the contentType Image is not hidden and set to "Image" (not "Url to Image")
  3. create 2 examples, one with a jpeg image in landscape format and another with an image in portrait format.
  4. view each of these and using the browser developer tools inspect the DOM img tag for the image
  5. what you should see:
    • see screenshot below
    • For Portrait format <img ... width="imageHeight" height="imageWidth" />
    • Landscape format is as expected
  6. you can repeat this for other image styles created using the "Scale" effect, be sure to alter "Manage display" accordingly, the result will be the same

Screenshot demonstrating the bug

Code from drupal/graphql DataProducer where I first noticed the error

In the code below, obviously $entity->width/height or $image->getWidth()/getHeight() are already incorrect.
Hence my guess is the bug is somewhere in Drupal::service('image.factory') or similar
My knowledge of Drupal 10 and PHP 8 are limited ;-)

public function resolve(FileInterface $entity = NULL, $style, RefinableCacheableDependencyInterface $metadata) {
    // Return if we don't have an entity.
    if (!$entity) {
      return NULL;
    }

    $access = $entity->access('view', NULL, TRUE);
    $metadata->addCacheableDependency($access);
    if ($access->isAllowed() && $image_style = ImageStyle::load($style)) {

      $width = $entity->width;
      $height = $entity->height;

      if (empty($width) || empty($height)) {
        /** @var \Drupal\Core\Image\ImageInterface $image */
        $image = \Drupal::service('image.factory')->get($entity->getFileUri());
        if ($image->isValid()) {
          $width = $image->getWidth();
          $height = $image->getHeight();
        }
      }

      // Determine the dimensions of the styled image.
      $dimensions = [
        'width' => $width,
        'height' => $height,
      ];

      $image_style->transformDimensions($dimensions, $entity->getFileUri());
      $metadata->addCacheableDependency($image_style);

      // The underlying URL generator that will be invoked will leak cache
      // metadata, resulting in an exception. By wrapping within a new render
      // context, we can capture the leaked metadata and make sure it gets
      // incorporated into the response.
      $context = new RenderContext();
      $url = $this->renderer->executeInRenderContext($context, function () use ($image_style, $entity) {
        return $image_style->buildUrl($entity->getFileUri());
      });

      if (!$context->isEmpty()) {
        $metadata->addCacheableDependency($context->pop());
      }

      return [
        'url' => $url,
        'width' => $dimensions['width'],
        'height' => $dimensions['height'],
      ];
    }

    return NULL;
  }

Proposed resolution

The image attributes should be correct for both image formates.

🐛 Bug report
Status

Closed: duplicate

Version

10.2

Component
Image system 

Last updated 3 days ago

Created by

🇩🇪Germany bird-cage

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @bird-cage
  • Could you please add direct and simple steps to reproduce on a freshly-installed Drupal site? There is quite a lot of information here and if I understand correctly, graphql is not really involved.

  • 🇩🇪Germany bird-cage

    @cilefen
    graphql is not involved, but that is where I first became aware of the issue.

    Steps to reproduce

    On a fresh drupal install v10.2.3

    1. create a content type with an image field
    2. ensure in "Manage display" for the contentType Image is not hidden and set to "Image" (not "Url to Image")
    3. create 2 examples of this content, one with a jpeg image in landscape format and another with an image in portrait format.
      • Portrait is an image where its height is greater then its width
      • Landscape is an image where its width is greater then its height
    4. go to "view" page for each of these (portrait and landscape) and using the browser developer tools inspect the DOM img tag for the image
    5. what you should see:
    6. For images with Portrait format, the img tag's width and height attributes are wrong, height is where width should be and visa versa: <img ... width="imageHeight" height="imageWidth" />
      • My screenshot in my original post shows what you will see in the Browser DOM inspector
      • You can only see the error with the browser DOM inspector, but it is a major problem for me in my react graphql implementation
    7. The example with a Landscape format image is correct and as expected
    8. you can repeat this for other image styles created using the "Scale" effect, be sure to alter "Manage display" accordingly, the result will be the same. Images with a portrait format will have the width and height values in the img tag swapped :-(

    My guess is something like Drupal::service('image.factory') is incorrectly always setting width and height as if width is always the greater of the 2 values, which it is for landscape but not portrait formates.

  • 🇮🇳India Jaykumar95 Ahmedabad

    I can confirm that this is coming in Drupal 10.2.4 and PHP 8.3.

    My finding is when we manually change the image orientation from landscape to portrait or portrait to landscape then only this issue is occurs.
    Say I have an original image of size 500x1000 and manually changes it to 1000x500 using Rotate right or Rotate left feature of Windows system, Drupal is saving it's width as 500 and height as 1000 as original image size, but when I rotate that same image using the MS Paint the image width and height saved correctly in Drupal.

  • 🇮🇳India priyanka277

    This has nothing to do with Drupal as it's using PHP's default getimagesize() function which itself is giving the wrong info about height and width in above mentioned scenario.

    I have use below code snippet to test the issue.

    1. The First image ls1.jpeg is a portrait image(500*1000)
    2. The second image ls2.jpeg is a manually rotated to 1000*500 which is returning width=500 and height=1000.
    3. The Third image ls3.jpeg is rotated using Ms Paint to 1000*500 which is returning width=1000 and height=500.
    list($width, $height, $type, $attr) = getimagesize("\images\ls1.jpeg");
    echo "<img src=\"\images\ls1.jpeg\" $attr alt=\"getimagesize() example\" />";
    
    list($width, $height, $type, $attr) = getimagesize("\images\ls2.jpeg");
    echo "<img src=\"\images\ls2.jpeg\" $attr alt=\"getimagesize() example\" />";
    
    list($width, $height, $type, $attr) = getimagesize("\images\ls3.jpeg");
    echo "<img src=\"\images\ls3.jpeg\" $attr alt=\"getimagesize() example\" />";
    
  • 🇮🇳India priyanka277

    This has nothing to do with Drupal as it's using PHP's default getimagesize() function which itself is giving the wrong info about height and width in above mentioned scenario.

    I have use below code snippet to test the issue.

    1. The First image ls1.jpeg is a portrait image(500*1000)
    2. The second image ls2.jpeg is a manually rotated to 1000*500 which is returning width=500 and height=1000.
    3. The Third image ls3.jpeg is rotated using Ms Paint to 1000*500 which is returning width=1000 and height=500.
    list($width, $height, $type, $attr) = getimagesize("\images\ls1.jpeg");
    echo "<img src=\"\images\ls1.jpeg\" $attr alt=\"getimagesize() example\" />";
    
    list($width, $height, $type, $attr) = getimagesize("\images\ls2.jpeg");
    echo "<img src=\"\images\ls2.jpeg\" $attr alt=\"getimagesize() example\" />";
    
    list($width, $height, $type, $attr) = getimagesize("\images\ls3.jpeg");
    echo "<img src=\"\images\ls3.jpeg\" $attr alt=\"getimagesize() example\" />";
    
  • 🇮🇳India priyanka277

    This has nothing to do with Drupal as it's using PHP's default getimagesize() function which itself is giving the wrong info about height and width in above mentioned scenario.

    I have use below code snippet to test the issue.

    1. The First image ls1.jpeg is a portrait image(500*1000)
    2. The second image ls2.jpeg is a manually rotated to 1000*500 which is returning width=500 and height=1000.
    3. The Third image ls3.jpeg is rotated using Ms Paint to 1000*500 which is returning width=1000 and height=500.
    list($width, $height, $type, $attr) = getimagesize("\images\ls1.jpeg");
    echo "<img src=\"\images\ls1.jpeg\" $attr alt=\"getimagesize() example\" />";
    
    list($width, $height, $type, $attr) = getimagesize("\images\ls2.jpeg");
    echo "<img src=\"\images\ls2.jpeg\" $attr alt=\"getimagesize() example\" />";
    
    list($width, $height, $type, $attr) = getimagesize("\images\ls3.jpeg");
    echo "<img src=\"\images\ls3.jpeg\" $attr alt=\"getimagesize() example\" />";
    
  • Status changed to Postponed: needs info 8 months ago
  • Is this because of phone cameras setting the orientation metadata? If so there is a fix for that: image effects module.

  • Status changed to Needs review 8 months ago
  • 🇮🇳India Jaykumar95 Ahmedabad

    Thank you @priyanka277 for pointing out.

    I have dig deeper and found that in core\modules\system\src\Plugin\ImageToolkit\GDToolkit.php file we are using getimagesize() function which is creating this issue.
    Please find attached patch which fixes the issue using below reference link.

    PHP getimagesize() mixes up width and height

    Image Orientation has Strange Behavior

  • Status changed to Closed: duplicate 8 months ago
  • I think this is a duplicate. Please confirm.

  • 🇩🇪Germany bird-cage

    @cilefen Thanks for your help, I think this can be closed
    It may be related to "#2664632: Add Auto orientation image effect"

    What I have discovered, particularly after reading all of the comments

    • My guess is it is related to iphone images or something like that
    • I'm still in an early dev phase and am using images from various random sources to test and develop
    • I now see many are correct, and many show the error I reported
    • Often (but not always) in MS-Word and Libra, when inserting images taken from my iphone, they are turned 90 , 180 or 270 degrees, eventhough they appear correct in mac's Preview app
    • To fix this in those cases, I have to open them again in mac's Preview app, turn them left and back right again, and save the change. Then when inserting in Word or Libra they will be correct (strange!!)
    • So I guess the real bug is buried deep inside iphone images somewhere :-|
  • It’s not buried deep. EXIF data in phone images can encode the phone orientation. It’s easy to see in most image viewers.

Production build 0.71.5 2024