Proportionally constrain image based on fixed width or height using PHP
The following post isn’t WordPress specific, however I’m using this in a WordPress environment, so you’ll find several references to it.
Array containing the file details
I have a custom meta box that allows a user to upload an image (or any file, really) to a post, but it doesn’t store the image as an attachment in the built-in media library. Instead, it stores the image in a predetermined location in the uploads folder and associates the image to the post using a post meta field.
The file can then be retrieved by supplying a post ID and field name to the WordPress get_post_meta()
function. The post meta field contains a serialized array that looks something like this:
Array
(
[post_id] => 84612
[name] => test-post-1362694023.jpg
[old_name] => Apple Glow.jpg
[relative_path] => meta/post-thumbnail/2013/test-post-1362694023.jpg
[created_date] => 2013-03-07 16:07:03
[mime_type] => image/jpeg
[file_size] => 14214
[image_width] => 180
[image_height] => 113
)
As a side note, the image is renamed during the upload based on the post title, if one exists. This was to ensure that all images uploaded via this meta box would be SEO friendly.
Once I have the array, I pass it to another function that will convert it to the desired output.
function convert_post_meta_file($file, $mode = 'auto', $args = array());
If you just pass it the image file, it automatically determines that an image tag would be best based on the mime type, however you can overwrite that to be any one of the other predefined modes. You can also pass in arguments to overwrite any defaults for that mode.
For example, let’s say your image is 180×113 pixels, but you wanted to force the height to be fixed to 60 pixels. You could pass that image attribute in via the third parameter of the function:
$args = array(
'attributes' => array(
'height' => 60
)
);
$img = convert_post_meta_file($file, 'image', $args);
If you printed out the value of $img
, it would look like this:
<img width="96" height="60" src="http://url.com/wp-content/uploads/meta/post-thumbnail/2013/test-post-1362694023.jpg" alt="Test Post" />
As you can see, the image was forced into the new dimensions. It’s important to note that it doesn’t actually create a new image of that size, but it simply constrains it. I use this for displaying images like this in different areas and context, but they are usually very similar to the original size.
Let’s look at how the convert function works.
Convert the file array to HTML
First we’re going to create a variable called $output
to hold all of our output. Then we’ll do a quick check to make sure that there is a file and that it’s in the form of an array.
Check for a valid file
function convert_post_meta_file($file, $mode = 'auto', $args = array()) {
$output = '';
if(empty($file)) {
return $output;
}
if(!is_array($file)) {
return $output;
}
}
Determine the file type
Next we’ll check whether the $mode
variable is set to auto
, which means that we need to check for what mode we need to display the file in based on the mime type.
function convert_post_meta_file($file, $mode = 'auto', $args = array()) {
<!-- other stuff here -->
if(isset($file['mime_type']) && $mode == 'auto') {
$mode = convert_mime_type_to_group_name($file['mime_type']);
}
}
At this point we have a mode that we can use. The convert_mime_type_to_group_name()
function simply contains an array of mime types that are mapped to an appropriate mode that will be returned accordingly.
Determine how to present the file
Now that we have the mode, we can process the file with another, more specific function.
function convert_post_meta_file($file, $mode = 'auto', $args = array()) {
<!-- other stuff here -->
switch($mode) {
case 'audio':
$output = convert_post_meta_file_to_audio($file, $args);
break;
case 'image':
$output = convert_post_meta_file_to_image($file, $args);
break;
case 'link':
$output = convert_post_meta_file_to_link($file, $args);
break;
case 'url':
$output = convert_post_meta_file_to_url($file);
break;
case 'video':
$output = convert_post_meta_file_to_video($file, $args);
break;
default:
$output = convert_post_meta_file_to_link($file, $args);
break;
}
return $output;
}
Since we supplied the convert function with an image, it’s going to pass the file and its arguments to the convert_post_meta_file_to_image()
function.
Convert the image array to HTML output
Once there, we’ll start with creating an array that’s going to hold all essential values that we’ll need for processing. If I know I need more than one or two variables and they’re all related, I prefer an array over creating many different variables.
function convert_post_meta_file_to_image($file, $args) {
$image = array(
'actual_width' => 0,
'actual_height' => 0,
'ratio' => 0,
'target_width' => 0,
'target_height' => 0
);
}
Retrieve the image URL
Next I’m going convert the $file
to a URL, as I’ll need that for the image tag later and to determine the image size if it wasn’t stored in the $file
array for some reason.
function convert_post_meta_file_to_image($file, $args) {
<!-- other stuff here -->
$file_url = convert_post_meta_file_to_url($file);
}
Retrieve the actual image size
Let’s figure out what the actual image size is. We’ll check to see if either the width or height is missing in our array, and if so, use PHP’s getimagesize()
function to obtain those values. Then we’ll store them in our temporary array as actual_width
and actual_height
.
function convert_post_meta_file_to_image($file, $args) {
<!-- other stuff here -->
if(!isset($file['image_width']) || !isset($file['image_height'])) {
$image_size = getimagesize($file_url);
if(isset($image_size[0])) {
$image['actual_width'] = $image_size[0];
}
if(isset($image_size[1])) {
$image['actual_height'] = $image_size[1];
}
} else {
$image['actual_width'] = $file['image_width'];
$image['actual_height'] = $file['image_height'];
}
}
Calculate the image width to height ratio
Now we can calculate the ratio between the image width and height, so we can use this value to determine the new size.
function convert_post_meta_file_to_image($file, $args) {
<!-- other stuff here -->
$image['ratio'] = $image['actual_width'] / $image['actual_height'];
}
Retrieve the target image size
With the ratio stored, we can check to see if there is a width and/or height provided in the arguments. We’ll also use this opportunity to check and make sure that the new size isn’t greater than the size of our actual image.
function convert_post_meta_file_to_image($file, $args) {
<!-- other stuff here -->
if(isset($args['attributes']['width'])
&& $args['attributes']['width'] < $image['actual_width']) {
$image['target_width'] = $args['attributes']['width'];
}
if(isset($args['attributes']['height'])
&& $args['attributes']['height'] < $image['actual_height']) {
$image['target_height'] = $args['attributes']['height'];
}
}
Calculate the new image size
Loaded with our actual and target image size, we can proceed with saving the image attributes we’re going to use to set the image size.
The different scenarios are as follows:
- If we have a target width and height, simply use what was provided, assuming that the person who provided them knows what they’re doing.
- If only a width was provided, we need to calculate the height using our ratio.
- If only a height was provided, we need to calculate the width using our ratio.
- If all else fails, simply display the image using the actual size.
function convert_post_meta_file_to_image($file, $args) {
<!-- other stuff here -->
if($image['target_width'] && $image['target_height']) {
$args['attributes']['width'] = $image['target_width'];
$args['attributes']['height'] = $image['target_height'];
} else if($image['target_width']) {
$args['attributes']['width'] = $image['target_width'];
$args['attributes']['height'] = round($image['target_width'] / $image['ratio']);
} else if($image['target_height']) {
$args['attributes']['width'] = round($image['target_height'] * $image['ratio']);
$args['attributes']['height'] = $image['target_height'];
} else {
$args['attributes']['width'] = $image['actual_width'];
$args['attributes']['height'] = $image['actual_height'];
}
}
Add the alt attribute to the image
I’m also going to set the alt
attribute of the image using the post title of the page, provided no other alt
was provided in $args
.
function convert_post_meta_file_to_image($file, $args) {
<!-- other stuff here -->
if(!isset($args['attributes']['alt']) && isset($file['post_id'])) {
$args['attributes']['alt'] = get_the_title($file['post_id']);
}
}
Add the src attribute to the image
Last, but not least, I’m going to set the src
attribute of the image and build my image tag.
function convert_post_meta_file_to_image($file, $args) {
<!-- other stuff here -->
$args['attributes']['src'] = $file_url;
$attributes = convert_array_to_attribute_string($args['attributes']);
return '<img ' . $attributes . ' />';
}
Hopefully this post provided some useful information on how images can be constrained via a function that can provide a fixed width or height.
Featured image by Jessica Ruscello.