9

I'd like to add an image size where the resulting image will be the largest possible size while retaining a 4:3 aspect ratio.

Suppose I've added an image size like so:

add_image_size( 'cover-image', 2048, 1536, true );

By default, WP will only create an image with that size if the full size image is larger than those dimensions.

But suppose my full size image is only 1000px wide. I would still like to have an image cropped to the largest possible 4:3 ratio, which in this case would be 1000x750.

Can this be done?

1 Answer 1

13

The Approach

I think the best approach is to create an image size "on the fly", just before the images is resized.

You can do that using 'intermediate_image_sizes_advanced' filter hook. That allows you to edit the size to be generated, but being aware of the current image size, that is stored in the array $metadata passed by the filter as second argument.

The Math

First of all let's write a class that returns the biggest sizes for a specific ratio.

class ImageRatio {

  private $ratio;

  function __construct($ratioW = 4, $ratioH = 3) {
    $this->ratio = array($ratioW, $ratioH);
  }

  function getLargestSize($imgW, $imgH) {
    $inverse = false;
    // let's try to keep width and calculate new height  
    $newSize = round(($this->ratio[1] * $imgW) / $this->ratio[0]);
    if ($newSize > $imgH) {
       $inverse = true;
       // if the calculated height is bigger than actual size
       // let's keep current height and calculate new width
       $newSize = round(($this->ratio[0] * $imgH) / $this->ratio[1]);
    }

    return $inverse ? array( $newSize, $imgH ) : array( $imgW, $newSize );
  }

}

Class usage

The usage of the class is pretty easy:

$ratio = new ImageRatio(4, 3)

$ratio->getLargestSize(1000, 500); // return: array(667, 500)
$ratio->getLargestSize(1000, 800); // return: array(1000, 750)

In Action

At this point, we can make use of the class to calculate on the fly an new images size, based on the image is being uploaded

add_filter( 'intermediate_image_sizes_advanced', function( $sizes, $metadata ) {

   if (! empty( $metadata['width'] ) && ! empty( $metadata['height'] ) ) {
      // calculate the max width and height for the ratio
      $ratio = new ImageRatio( 4, 3 );
      list($width, $height) = $ratio->getLargestSize( 
         $metadata['width'],
         $metadata['height']
      );
      // let's add our custom size
      $sizes['biggest-4-3'] = array(
        'width'  => $width,
        'height' => $height,
        'crop'   => true
      );
   }

   return $sizes;

}, 10, 2 );

Using the New Size

$image = wp_get_attachment_image( $attachment_id, 'biggest-4-3' );

Note

Of course, this works for all the images that you upload after the code is in place. For older images, you should re-generate the thumbnails, on the fly when they are used, or in bulk using one of the plugins available on the web.

2
  • This works great, thanks! One minor correction I would make: I think it's unnecessary to set a default ratio in the constructor, as I might use this for other ratios in the future. Makes sense to always supply the desired ratio on instantiation. Dec 24, 2015 at 14:45
  • The args in constructor are just default. You can pass any ratio you want, default are used only if you don't pass anything
    – gmazzap
    Dec 25, 2015 at 11:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.