Last week someone I know asked me how to do always a top center crop on thumbnails and I knew it was possible because of WP_Image_Editor. But you always wondered what it was before 3.5 and it always seemed you needed to hack code till I founded a post from George Stephanis. His post shows that in 3.4 you where able to do that with the filter ‘image_resize_dimensions’. But if you read the last code example you would see it would apply it to all sizes. Maybe there are ways to use globals/filters to request the size but it ain’t easy.
In 3.5 that code didn’t changed and maybe it should since putting the logic inside WP_Image_Editor means you must know what you are doing. This is because other plugins can also add their implementations and can overwrite the logic for changing the crop position. Probably a filter inside wp_get_image_editor() makes sense to have or outside if the context is import to know.
Understanding the code
The method that handles all sizes inside a WP_Image_Editor is multi_resize(). It receives an array with all the sizes as keys and each containing an array with the width, height and crop variable. The following code example I copy/paste the function wp_generate_attachment_metadata(). As you see it builds a sizes array and passes it to multi_resize(). That will return an array with all the image metadata.
function wp_generate_attachment_metadata( $attachment_id, $file ) {
$attachment = get_post( $attachment_id );
$metadata = array();
if ( preg_match('!^image/!', get_post_mime_type( $attachment )) && file_is_displayable_image($file) ) {
$imagesize = getimagesize( $file );
$metadata['width'] = $imagesize[0];
$metadata['height'] = $imagesize[1];
// Make the file path relative to the upload dir
$metadata['file'] = _wp_relative_upload_path($file);
// make thumbnails and other intermediate sizes
global $_wp_additional_image_sizes;
foreach ( get_intermediate_image_sizes() as $s ) {
$sizes[$s] = array( 'width' => '', 'height' => '', 'crop' => false );
if ( isset( $_wp_additional_image_sizes[$s]['width'] ) )
$sizes[$s]['width'] = intval( $_wp_additional_image_sizes[$s]['width'] ); // For theme-added sizes
else
$sizes[$s]['width'] = get_option( "{$s}_size_w" ); // For default sizes set in options
if ( isset( $_wp_additional_image_sizes[$s]['height'] ) )
$sizes[$s]['height'] = intval( $_wp_additional_image_sizes[$s]['height'] ); // For theme-added sizes
else
$sizes[$s]['height'] = get_option( "{$s}_size_h" ); // For default sizes set in options
if ( isset( $_wp_additional_image_sizes[$s]['crop'] ) )
$sizes[$s]['crop'] = intval( $_wp_additional_image_sizes[$s]['crop'] ); // For theme-added sizes
else
$sizes[$s]['crop'] = get_option( "{$s}_crop" ); // For default sizes set in options
}
$sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes );
if ( $sizes ) {
$editor = wp_get_image_editor( $file );
if ( ! is_wp_error( $editor ) )
$metadata['sizes'] = $editor->multi_resize( $sizes );
} else {
$metadata['sizes'] = array();
}
// fetch additional metadata from exif/iptc
$image_meta = wp_read_image_metadata( $file );
if ( $image_meta )
$metadata['image_meta'] = $image_meta;
}
return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id );
}
multi_resize()
public function multi_resize( $sizes ) {
$metadata = array();
$orig_size = $this->size;
foreach ( $sizes as $size => $size_data ) {
$image = $this->_resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
if( ! is_wp_error( $image ) ) {
$resized = $this->_save( $image );
imagedestroy( $image );
if ( ! is_wp_error( $resized ) && $resized ) {
unset( $resized['path'] );
$metadata[$size] = $resized;
}
}
$this->size = $orig_size;
}
return $metadata;
}
So above you see how multi_resize() works. For our purpose we only need to make and if/else on calling _resize(). We do need something like the method _resize() but then with our own coordinates. And our customized _resize() would overwrite the variable $src_y to the value of 0. Most likely in a real world example you would integrate all crop positions to the new created method so you can easily switch.
End result
https://gist.github.com/markoheijnen/5134202#file-unknown-crop-top-center.php
https://gist.github.com/markoheijnen/5134202#file-editor-gd.php
In case you wondered why two files are needed, please read the post about how to include a custom image editor