Image Editing API

WordPress provides a built-in image editor for cropping, rotating, flipping, and scaling images. The API can be used programmatically and integrates with the media library.

WP_Image_Editor Class

The base class for image manipulation. WordPress uses GD or Imagick depending on server configuration.

Getting an Editor Instance

$editor = wp_get_image_editor( $file_path );

if ( is_wp_error( $editor ) ) {
    // Handle error - no editor available
    echo $editor->get_error_message();
} else {
    // Use the editor
}

Checking Editor Support

// Check if specific features are supported
$supports = wp_image_editor_supports( array(
    'mime_type' => 'image/png',
    'methods'   => array( 'resize', 'rotate' ),
) );

if ( $supports ) {
    // Proceed with operation
}

Core Image Operations

Resize

$editor = wp_get_image_editor( '/path/to/image.jpg' );

if ( ! is_wp_error( $editor ) ) {
    // Resize to max 800x600, maintaining aspect ratio
    $result = $editor->resize( 800, 600, false );
    
    if ( ! is_wp_error( $result ) ) {
        $saved = $editor->save( '/path/to/resized.jpg' );
    }
}

Parameters:

$editor->resize(
    int  $max_w,      // Maximum width
    int  $max_h,      // Maximum height
    bool $crop = false // Whether to crop (true) or resize (false)
);

Crop

$editor = wp_get_image_editor( '/path/to/image.jpg' );

if ( ! is_wp_error( $editor ) ) {
    // Crop 200x200 from position 50,50
    $result = $editor->crop( 50, 50, 200, 200 );
    
    if ( ! is_wp_error( $result ) ) {
        $saved = $editor->save( '/path/to/cropped.jpg' );
    }
}

Parameters:

$editor->crop(
    int $src_x,        // Source X position
    int $src_y,        // Source Y position
    int $src_w,        // Source width
    int $src_h,        // Source height
    int $dst_w = null, // Destination width (optional)
    int $dst_h = null  // Destination height (optional)
);

Rotate

$editor = wp_get_image_editor( '/path/to/image.jpg' );

if ( ! is_wp_error( $editor ) ) {
    // Rotate 90 degrees clockwise
    $result = $editor->rotate( -90 );
    
    if ( ! is_wp_error( $result ) ) {
        $saved = $editor->save( '/path/to/rotated.jpg' );
    }
}

Flip

$editor = wp_get_image_editor( '/path/to/image.jpg' );

if ( ! is_wp_error( $editor ) ) {
    // Flip horizontally
    $result = $editor->flip( true, false );
    
    // Flip vertically
    $result = $editor->flip( false, true );
    
    // Both
    $result = $editor->flip( true, true );
}

Multi-Size Generation

$editor = wp_get_image_editor( '/path/to/image.jpg' );

if ( ! is_wp_error( $editor ) ) {
    $sizes = array(
        'thumbnail' => array(
            'width'  => 150,
            'height' => 150,
            'crop'   => true,
        ),
        'medium' => array(
            'width'  => 300,
            'height' => 300,
            'crop'   => false,
        ),
        'large' => array(
            'width'  => 1024,
            'height' => 1024,
            'crop'   => false,
        ),
    );
    
    $result = $editor->multi_resize( $sizes );
    
    // Returns array of generated files
    /*
    array(
        'thumbnail' => array(
            'file'      => 'image-150x150.jpg',
            'width'     => 150,
            'height'    => 150,
            'mime-type' => 'image/jpeg',
        ),
        ...
    )
    */
}

Image Quality

$editor = wp_get_image_editor( '/path/to/image.jpg' );

if ( ! is_wp_error( $editor ) ) {
    // Set quality (1-100)
    $editor->set_quality( 85 );
    
    // Save with new quality
    $editor->save( '/path/to/output.jpg' );
}

Filter Default Quality

add_filter( 'jpeg_quality', function( $quality ) {
    return 90;
} );

add_filter( 'wp_editor_set_quality', function( $quality, $mime_type ) {
    if ( 'image/webp' === $mime_type ) {
        return 85;
    }
    return $quality;
}, 10, 2 );

Attachment Image Editing

wp_save_image()

Save edited image from the media library editor.

// Called via AJAX in media library
$result = wp_save_image( $attachment_id );

wp_image_editor()

Render the image editor interface.

wp_image_editor( $post_id, $msg = false );

Image Edit Actions (AJAX)

WordPress handles these via AJAX:

Action Function
image-editor wp_ajax_image_editor()
imgedit-preview Preview edited image
edit-attachment Apply edits

Programmatic Attachment Editing

Update Attachment After Editing

function programmatic_image_edit( $attachment_id ) {
    $file = get_attached_file( $attachment_id );
    
    if ( ! $file ) {
        return new WP_Error( 'no_file', 'Attachment file not found' );
    }
    
    $editor = wp_get_image_editor( $file );
    
    if ( is_wp_error( $editor ) ) {
        return $editor;
    }
    
    // Make edits
    $editor->rotate( 90 );
    $editor->resize( 800, 800 );
    
    // Save back to original file
    $saved = $editor->save( $file );
    
    if ( is_wp_error( $saved ) ) {
        return $saved;
    }
    
    // Regenerate metadata (thumbnails, etc.)
    $metadata = wp_generate_attachment_metadata( $attachment_id, $file );
    wp_update_attachment_metadata( $attachment_id, $metadata );
    
    return true;
}

Regenerate Thumbnails

function regenerate_thumbnails( $attachment_id ) {
    $file = get_attached_file( $attachment_id );
    
    if ( ! $file ) {
        return false;
    }
    
    // Delete old sizes
    $old_metadata = wp_get_attachment_metadata( $attachment_id );
    
    if ( isset( $old_metadata['sizes'] ) ) {
        $upload_dir = wp_upload_dir();
        $base_dir   = trailingslashit( dirname( $file ) );
        
        foreach ( $old_metadata['sizes'] as $size ) {
            @unlink( $base_dir . $size['file'] );
        }
    }
    
    // Generate new sizes
    require_once ABSPATH . 'wp-admin/includes/image.php';
    
    $metadata = wp_generate_attachment_metadata( $attachment_id, $file );
    wp_update_attachment_metadata( $attachment_id, $metadata );
    
    return true;
}

Image Metadata Functions

wp_read_image_metadata()

Extract EXIF/IPTC data from an image.

$meta = wp_read_image_metadata( '/path/to/image.jpg' );

/*
array(
    'aperture'          => '2.8',
    'credit'            => 'Photographer Name',
    'camera'            => 'Canon EOS 5D',
    'caption'           => 'Image caption',
    'created_timestamp' => 1234567890,
    'copyright'         => '© 2024',
    'focal_length'      => '50',
    'iso'               => '100',
    'shutter_speed'     => '0.004',
    'title'             => 'Image Title',
    'orientation'       => '1',
    'keywords'          => array( 'tag1', 'tag2' ),
)
*/

wp_get_attachment_metadata()

Get attachment metadata including sizes.

$metadata = wp_get_attachment_metadata( $attachment_id );

/*
array(
    'width'      => 1920,
    'height'     => 1080,
    'file'       => '2024/01/image.jpg',
    'filesize'   => 245678,
    'sizes'      => array(
        'thumbnail' => array(
            'file'      => 'image-150x150.jpg',
            'width'     => 150,
            'height'    => 150,
            'mime-type' => 'image/jpeg',
            'filesize'  => 12345,
        ),
        // ... other sizes
    ),
    'image_meta' => array( ... ),
)
*/

Image Size Registration

add_image_size()

Register custom image sizes.

add_action( 'after_setup_theme', 'register_custom_sizes' );

function register_custom_sizes() {
    // Hard crop (centered)
    add_image_size( 'custom-square', 400, 400, true );
    
    // Soft resize (proportional)
    add_image_size( 'custom-large', 800, 600, false );
    
    // Custom crop position (left, top)
    add_image_size( 'custom-crop', 400, 300, array( 'left', 'top' ) );
}

get_intermediate_image_sizes()

Get all registered image sizes.

$sizes = get_intermediate_image_sizes();
// array( 'thumbnail', 'medium', 'medium_large', 'large', 'custom-square', ... )

Editor Implementation Classes

WP_Image_Editor_GD

GD library implementation (default fallback).

WP_Image_Editor_Imagick

ImageMagick implementation (preferred when available).

Checking Available Editors

// Get list of available editors
$editors = _wp_image_editor_choose();
// Returns class name like 'WP_Image_Editor_Imagick'

// Check what the image will use
$editor = wp_get_image_editor( $file );
echo get_class( $editor ); // WP_Image_Editor_Imagick or WP_Image_Editor_GD

Hooks

Hook Description
wp_image_editors Filter available editor classes
image_editor_save_pre Before saving edited image
wp_save_image_editor_file Override image save
wp_save_image_file After saving image file
jpeg_quality Filter JPEG quality
wp_editor_set_quality Filter any image quality
image_make_intermediate_size After making intermediate size

Error Handling

$editor = wp_get_image_editor( $file );

if ( is_wp_error( $editor ) ) {
    switch ( $editor->get_error_code() ) {
        case 'error_loading_image':
            // Could not load image
            break;
        case 'image_size_unsupported':
            // Image too large
            break;
        case 'error_getting_dimensions':
            // Could not read dimensions
            break;
    }
}

Complete Example: Image Processing Pipeline

function process_uploaded_image( $attachment_id ) {
    $file = get_attached_file( $attachment_id );
    
    if ( ! $file || ! file_exists( $file ) ) {
        return new WP_Error( 'no_file', 'File not found' );
    }
    
    $editor = wp_get_image_editor( $file );
    
    if ( is_wp_error( $editor ) ) {
        return $editor;
    }
    
    // Get original dimensions
    $size = $editor->get_size();
    
    // Auto-rotate based on EXIF
    $meta = wp_read_image_metadata( $file );
    if ( isset( $meta['orientation'] ) ) {
        switch ( $meta['orientation'] ) {
            case 3:
                $editor->rotate( 180 );
                break;
            case 6:
                $editor->rotate( -90 );
                break;
            case 8:
                $editor->rotate( 90 );
                break;
        }
    }
    
    // Limit max dimensions
    $max_dim = 2048;
    if ( $size['width'] > $max_dim || $size['height'] > $max_dim ) {
        $editor->resize( $max_dim, $max_dim, false );
    }
    
    // Set quality
    $editor->set_quality( 85 );
    
    // Save
    $saved = $editor->save( $file );
    
    if ( is_wp_error( $saved ) ) {
        return $saved;
    }
    
    // Regenerate metadata
    require_once ABSPATH . 'wp-admin/includes/image.php';
    
    $metadata = wp_generate_attachment_metadata( $attachment_id, $file );
    wp_update_attachment_metadata( $attachment_id, $metadata );
    
    return true;
}