[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WordPress Image Editor 4 * 5 * @package WordPress 6 * @subpackage Administration 7 */ 8 9 /** 10 * Loads the WP image-editing interface. 11 * 12 * @since 2.9.0 13 * 14 * @param int $post_id Attachment post ID. 15 * @param false|object $msg Optional. Message to display for image editor updates or errors. 16 * Default false. 17 */ 18 function wp_image_editor( $post_id, $msg = false ) { 19 $nonce = wp_create_nonce( "image_editor-$post_id" ); 20 $meta = wp_get_attachment_metadata( $post_id ); 21 $thumb = image_get_intermediate_size( $post_id, 'thumbnail' ); 22 $sub_sizes = isset( $meta['sizes'] ) && is_array( $meta['sizes'] ); 23 $note = ''; 24 25 if ( isset( $meta['width'], $meta['height'] ) ) { 26 $big = max( $meta['width'], $meta['height'] ); 27 } else { 28 die( __( 'Image data does not exist. Please re-upload the image.' ) ); 29 } 30 31 $sizer = $big > 400 ? 400 / $big : 1; 32 33 $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true ); 34 $can_restore = false; 35 if ( ! empty( $backup_sizes ) && isset( $backup_sizes['full-orig'], $meta['file'] ) ) { 36 $can_restore = wp_basename( $meta['file'] ) !== $backup_sizes['full-orig']['file']; 37 } 38 39 if ( $msg ) { 40 if ( isset( $msg->error ) ) { 41 $note = "<div class='notice notice-error' tabindex='-1' role='alert'><p>$msg->error</p></div>"; 42 } elseif ( isset( $msg->msg ) ) { 43 $note = "<div class='notice notice-success' tabindex='-1' role='alert'><p>$msg->msg</p></div>"; 44 } 45 } 46 $edit_custom_sizes = false; 47 /** 48 * Filters whether custom sizes are available options for image editing. 49 * 50 * @since 6.0.0 51 * 52 * @param bool|string[] $edit_custom_sizes True if custom sizes can be edited or array of custom size names. 53 */ 54 $edit_custom_sizes = apply_filters( 'edit_custom_thumbnail_sizes', $edit_custom_sizes ); 55 ?> 56 <div class="imgedit-wrap wp-clearfix"> 57 <div id="imgedit-panel-<?php echo $post_id; ?>"> 58 59 <div class="imgedit-panel-content wp-clearfix"> 60 <?php echo $note; ?> 61 <div class="imgedit-menu wp-clearfix"> 62 <button type="button" onclick="imageEdit.handleCropToolClick( <?php echo "$post_id, '$nonce'"; ?>, this )" class="imgedit-crop button disabled" disabled><?php esc_html_e( 'Crop' ); ?></button> 63 <?php 64 65 // On some setups GD library does not provide imagerotate() - Ticket #11536. 66 if ( wp_image_editor_supports( 67 array( 68 'mime_type' => get_post_mime_type( $post_id ), 69 'methods' => array( 'rotate' ), 70 ) 71 ) ) { 72 $note_no_rotate = ''; 73 ?> 74 <button type="button" class="imgedit-rleft button" onclick="imageEdit.rotate( 90, <?php echo "$post_id, '$nonce'"; ?>, this)"><?php esc_html_e( 'Rotate left' ); ?></button> 75 <button type="button" class="imgedit-rright button" onclick="imageEdit.rotate(-90, <?php echo "$post_id, '$nonce'"; ?>, this)"><?php esc_html_e( 'Rotate right' ); ?></button> 76 <?php 77 } else { 78 $note_no_rotate = '<p class="note-no-rotate"><em>' . __( 'Image rotation is not supported by your web host.' ) . '</em></p>'; 79 ?> 80 <button type="button" class="imgedit-rleft button disabled" disabled></button> 81 <button type="button" class="imgedit-rright button disabled" disabled></button> 82 <?php } ?> 83 84 <button type="button" onclick="imageEdit.flip(1, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-flipv button"><?php esc_html_e( 'Flip vertical' ); ?></button> 85 <button type="button" onclick="imageEdit.flip(2, <?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-fliph button"><?php esc_html_e( 'Flip horizontal' ); ?></button> 86 87 <br class="imgedit-undo-redo-separator" /> 88 <button type="button" id="image-undo-<?php echo $post_id; ?>" onclick="imageEdit.undo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-undo button disabled" disabled><?php esc_html_e( 'Undo' ); ?></button> 89 <button type="button" id="image-redo-<?php echo $post_id; ?>" onclick="imageEdit.redo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-redo button disabled" disabled><?php esc_html_e( 'Redo' ); ?></button> 90 <?php echo $note_no_rotate; ?> 91 </div> 92 93 <input type="hidden" id="imgedit-sizer-<?php echo $post_id; ?>" value="<?php echo $sizer; ?>" /> 94 <input type="hidden" id="imgedit-history-<?php echo $post_id; ?>" value="" /> 95 <input type="hidden" id="imgedit-undone-<?php echo $post_id; ?>" value="0" /> 96 <input type="hidden" id="imgedit-selection-<?php echo $post_id; ?>" value="" /> 97 <input type="hidden" id="imgedit-x-<?php echo $post_id; ?>" value="<?php echo isset( $meta['width'] ) ? $meta['width'] : 0; ?>" /> 98 <input type="hidden" id="imgedit-y-<?php echo $post_id; ?>" value="<?php echo isset( $meta['height'] ) ? $meta['height'] : 0; ?>" /> 99 100 <div id="imgedit-crop-<?php echo $post_id; ?>" class="imgedit-crop-wrap"> 101 <img id="image-preview-<?php echo $post_id; ?>" onload="imageEdit.imgLoaded('<?php echo $post_id; ?>')" 102 src="<?php echo esc_url( admin_url( 'admin-ajax.php', 'relative' ) ) . '?action=imgedit-preview&_ajax_nonce=' . $nonce . '&postid=' . $post_id . '&rand=' . rand( 1, 99999 ); ?>" alt="" /> 103 </div> 104 105 <div class="imgedit-submit"> 106 <input type="button" onclick="imageEdit.close(<?php echo $post_id; ?>, 1)" class="button imgedit-cancel-btn" value="<?php esc_attr_e( 'Cancel' ); ?>" /> 107 <input type="button" onclick="imageEdit.save(<?php echo "$post_id, '$nonce'"; ?>)" disabled="disabled" class="button button-primary imgedit-submit-btn" value="<?php esc_attr_e( 'Save' ); ?>" /> 108 </div> 109 </div> 110 111 <div class="imgedit-settings"> 112 <div class="imgedit-group"> 113 <div class="imgedit-group-top"> 114 <h2><?php _e( 'Scale Image' ); ?></h2> 115 <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Scale Image Help' ); ?></span></button> 116 <div class="imgedit-help"> 117 <p><?php _e( 'You can proportionally scale the original image. For best results, scaling should be done before you crop, flip, or rotate. Images can only be scaled down, not up.' ); ?></p> 118 </div> 119 <?php if ( isset( $meta['width'], $meta['height'] ) ) : ?> 120 <p> 121 <?php 122 printf( 123 /* translators: %s: Image width and height in pixels. */ 124 __( 'Original dimensions %s' ), 125 '<span class="imgedit-original-dimensions">' . $meta['width'] . ' × ' . $meta['height'] . '</span>' 126 ); 127 ?> 128 </p> 129 <?php endif; ?> 130 <div class="imgedit-submit"> 131 132 <fieldset class="imgedit-scale"> 133 <legend><?php _e( 'New dimensions:' ); ?></legend> 134 <div class="nowrap"> 135 <label for="imgedit-scale-width-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'scale width' ); ?></label> 136 <input type="text" id="imgedit-scale-width-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1, this)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1, this)" value="<?php echo isset( $meta['width'] ) ? $meta['width'] : 0; ?>" /> 137 <span class="imgedit-separator" aria-hidden="true">×</span> 138 <label for="imgedit-scale-height-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'scale height' ); ?></label> 139 <input type="text" id="imgedit-scale-height-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0, this)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0, this)" value="<?php echo isset( $meta['height'] ) ? $meta['height'] : 0; ?>" /> 140 <span class="imgedit-scale-warn" id="imgedit-scale-warn-<?php echo $post_id; ?>">!</span> 141 <div class="imgedit-scale-button-wrapper"><input id="imgedit-scale-button" type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'scale')" class="button button-primary" value="<?php esc_attr_e( 'Scale' ); ?>" /></div> 142 </div> 143 </fieldset> 144 145 </div> 146 </div> 147 </div> 148 149 <?php if ( $can_restore ) { ?> 150 151 <div class="imgedit-group"> 152 <div class="imgedit-group-top"> 153 <h2><button type="button" onclick="imageEdit.toggleHelp(this);" class="button-link" aria-expanded="false"><?php _e( 'Restore original image' ); ?> <span class="dashicons dashicons-arrow-down imgedit-help-toggle"></span></button></h2> 154 <div class="imgedit-help imgedit-restore"> 155 <p> 156 <?php 157 _e( 'Discard any changes and restore the original image.' ); 158 159 if ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) { 160 echo ' ' . __( 'Previously edited copies of the image will not be deleted.' ); 161 } 162 ?> 163 </p> 164 <div class="imgedit-submit"> 165 <input type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'restore')" class="button button-primary" value="<?php esc_attr_e( 'Restore image' ); ?>" <?php echo $can_restore; ?> /> 166 </div> 167 </div> 168 </div> 169 </div> 170 171 <?php } ?> 172 173 <div class="imgedit-group"> 174 <div class="imgedit-group-top"> 175 <h2><?php _e( 'Image Crop' ); ?></h2> 176 <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Image Crop Help' ); ?></span></button> 177 178 <div class="imgedit-help"> 179 <p><?php _e( 'To crop the image, click on it and drag to make your selection.' ); ?></p> 180 181 <p><strong><?php _e( 'Crop Aspect Ratio' ); ?></strong><br /> 182 <?php _e( 'The aspect ratio is the relationship between the width and height. You can preserve the aspect ratio by holding down the shift key while resizing your selection. Use the input box to specify the aspect ratio, e.g. 1:1 (square), 4:3, 16:9, etc.' ); ?></p> 183 184 <p><strong><?php _e( 'Crop Selection' ); ?></strong><br /> 185 <?php _e( 'Once you have made your selection, you can adjust it by entering the size in pixels. The minimum selection size is the thumbnail size as set in the Media settings.' ); ?></p> 186 </div> 187 </div> 188 189 <fieldset class="imgedit-crop-ratio"> 190 <legend><?php _e( 'Aspect ratio:' ); ?></legend> 191 <div class="nowrap"> 192 <label for="imgedit-crop-width-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'crop ratio width' ); ?></label> 193 <input type="text" id="imgedit-crop-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 0, this)" onblur="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 0, this)" /> 194 <span class="imgedit-separator" aria-hidden="true">:</span> 195 <label for="imgedit-crop-height-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'crop ratio height' ); ?></label> 196 <input type="text" id="imgedit-crop-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 1, this)" onblur="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 1, this)" /> 197 </div> 198 </fieldset> 199 200 <fieldset id="imgedit-crop-sel-<?php echo $post_id; ?>" class="imgedit-crop-sel"> 201 <legend><?php _e( 'Selection:' ); ?></legend> 202 <div class="nowrap"> 203 <label for="imgedit-sel-width-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'selection width' ); ?></label> 204 <input type="text" id="imgedit-sel-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" onblur="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" /> 205 <span class="imgedit-separator" aria-hidden="true">×</span> 206 <label for="imgedit-sel-height-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'selection height' ); ?></label> 207 <input type="text" id="imgedit-sel-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" onblur="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" /> 208 </div> 209 </fieldset> 210 211 </div> 212 213 <?php 214 if ( $thumb && $sub_sizes ) { 215 $thumb_img = wp_constrain_dimensions( $thumb['width'], $thumb['height'], 160, 120 ); 216 ?> 217 218 <div class="imgedit-group imgedit-applyto"> 219 <div class="imgedit-group-top"> 220 <h2><?php _e( 'Thumbnail Settings' ); ?></h2> 221 <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"><?php esc_html_e( 'Thumbnail Settings Help' ); ?></span></button> 222 <div class="imgedit-help"> 223 <p><?php _e( 'You can edit the image while preserving the thumbnail. For example, you may wish to have a square thumbnail that displays just a section of the image.' ); ?></p> 224 </div> 225 </div> 226 227 <figure class="imgedit-thumbnail-preview"> 228 <img src="<?php echo $thumb['url']; ?>" width="<?php echo $thumb_img[0]; ?>" height="<?php echo $thumb_img[1]; ?>" class="imgedit-size-preview" alt="" draggable="false" /> 229 <figcaption class="imgedit-thumbnail-preview-caption"><?php _e( 'Current thumbnail' ); ?></figcaption> 230 </figure> 231 232 <div id="imgedit-save-target-<?php echo $post_id; ?>" class="imgedit-save-target"> 233 <fieldset> 234 <legend><?php _e( 'Apply changes to:' ); ?></legend> 235 236 <span class="imgedit-label"> 237 <input type="radio" id="imgedit-target-all" name="imgedit-target-<?php echo $post_id; ?>" value="all" checked="checked" /> 238 <label for="imgedit-target-all"><?php _e( 'All image sizes' ); ?></label> 239 </span> 240 241 <span class="imgedit-label"> 242 <input type="radio" id="imgedit-target-thumbnail" name="imgedit-target-<?php echo $post_id; ?>" value="thumbnail" /> 243 <label for="imgedit-target-thumbnail"><?php _e( 'Thumbnail' ); ?></label> 244 </span> 245 246 <span class="imgedit-label"> 247 <input type="radio" id="imgedit-target-nothumb" name="imgedit-target-<?php echo $post_id; ?>" value="nothumb" /> 248 <label for="imgedit-target-nothumb"><?php _e( 'All sizes except thumbnail' ); ?></label> 249 </span> 250 <?php 251 if ( $edit_custom_sizes ) { 252 if ( ! is_array( $edit_custom_sizes ) ) { 253 $edit_custom_sizes = get_intermediate_image_sizes(); 254 } 255 foreach ( array_unique( $edit_custom_sizes ) as $key => $size ) { 256 if ( array_key_exists( $size, $meta['sizes'] ) ) { 257 if ( 'thumbnail' === $size ) { 258 continue; 259 } 260 ?> 261 <span class="imgedit-label"> 262 <input type="radio" id="imgedit-target-custom<?php echo esc_attr( $key ); ?>" name="imgedit-target-<?php echo $post_id; ?>" value="<?php echo esc_attr( $size ); ?>" /> 263 <label for="imgedit-target-custom<?php echo esc_attr( $key ); ?>"><?php echo esc_html( $size ); ?></label> 264 </span> 265 <?php 266 } 267 } 268 } 269 ?> 270 </fieldset> 271 </div> 272 </div> 273 274 <?php } ?> 275 276 </div> 277 278 </div> 279 <div class="imgedit-wait" id="imgedit-wait-<?php echo $post_id; ?>"></div> 280 <div class="hidden" id="imgedit-leaving-<?php echo $post_id; ?>"><?php _e( "There are unsaved changes that will be lost. 'OK' to continue, 'Cancel' to return to the Image Editor." ); ?></div> 281 </div> 282 <?php 283 } 284 285 /** 286 * Streams image in WP_Image_Editor to browser. 287 * 288 * @since 2.9.0 289 * 290 * @param WP_Image_Editor $image The image editor instance. 291 * @param string $mime_type The mime type of the image. 292 * @param int $attachment_id The image's attachment post ID. 293 * @return bool True on success, false on failure. 294 */ 295 function wp_stream_image( $image, $mime_type, $attachment_id ) { 296 if ( $image instanceof WP_Image_Editor ) { 297 298 /** 299 * Filters the WP_Image_Editor instance for the image to be streamed to the browser. 300 * 301 * @since 3.5.0 302 * 303 * @param WP_Image_Editor $image The image editor instance. 304 * @param int $attachment_id The attachment post ID. 305 */ 306 $image = apply_filters( 'image_editor_save_pre', $image, $attachment_id ); 307 308 if ( is_wp_error( $image->stream( $mime_type ) ) ) { 309 return false; 310 } 311 312 return true; 313 } else { 314 /* translators: 1: $image, 2: WP_Image_Editor */ 315 _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) ); 316 317 /** 318 * Filters the GD image resource to be streamed to the browser. 319 * 320 * @since 2.9.0 321 * @deprecated 3.5.0 Use {@see 'image_editor_save_pre'} instead. 322 * 323 * @param resource|GdImage $image Image resource to be streamed. 324 * @param int $attachment_id The attachment post ID. 325 */ 326 $image = apply_filters_deprecated( 'image_save_pre', array( $image, $attachment_id ), '3.5.0', 'image_editor_save_pre' ); 327 328 switch ( $mime_type ) { 329 case 'image/jpeg': 330 header( 'Content-Type: image/jpeg' ); 331 return imagejpeg( $image, null, 90 ); 332 case 'image/png': 333 header( 'Content-Type: image/png' ); 334 return imagepng( $image ); 335 case 'image/gif': 336 header( 'Content-Type: image/gif' ); 337 return imagegif( $image ); 338 case 'image/webp': 339 if ( function_exists( 'imagewebp' ) ) { 340 header( 'Content-Type: image/webp' ); 341 return imagewebp( $image, null, 90 ); 342 } 343 return false; 344 default: 345 return false; 346 } 347 } 348 } 349 350 /** 351 * Saves image to file. 352 * 353 * @since 2.9.0 354 * 355 * @param string $filename Name of the file to be saved. 356 * @param WP_Image_Editor $image The image editor instance. 357 * @param string $mime_type The mime type of the image. 358 * @param int $post_id Attachment post ID. 359 * @return bool True on success, false on failure. 360 */ 361 function wp_save_image_file( $filename, $image, $mime_type, $post_id ) { 362 if ( $image instanceof WP_Image_Editor ) { 363 364 /** This filter is documented in wp-admin/includes/image-edit.php */ 365 $image = apply_filters( 'image_editor_save_pre', $image, $post_id ); 366 367 /** 368 * Filters whether to skip saving the image file. 369 * 370 * Returning a non-null value will short-circuit the save method, 371 * returning that value instead. 372 * 373 * @since 3.5.0 374 * 375 * @param bool|null $override Value to return instead of saving. Default null. 376 * @param string $filename Name of the file to be saved. 377 * @param WP_Image_Editor $image The image editor instance. 378 * @param string $mime_type The mime type of the image. 379 * @param int $post_id Attachment post ID. 380 */ 381 $saved = apply_filters( 'wp_save_image_editor_file', null, $filename, $image, $mime_type, $post_id ); 382 383 if ( null !== $saved ) { 384 return $saved; 385 } 386 387 return $image->save( $filename, $mime_type ); 388 } else { 389 /* translators: 1: $image, 2: WP_Image_Editor */ 390 _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) ); 391 392 /** This filter is documented in wp-admin/includes/image-edit.php */ 393 $image = apply_filters_deprecated( 'image_save_pre', array( $image, $post_id ), '3.5.0', 'image_editor_save_pre' ); 394 395 /** 396 * Filters whether to skip saving the image file. 397 * 398 * Returning a non-null value will short-circuit the save method, 399 * returning that value instead. 400 * 401 * @since 2.9.0 402 * @deprecated 3.5.0 Use {@see 'wp_save_image_editor_file'} instead. 403 * 404 * @param bool|null $override Value to return instead of saving. Default null. 405 * @param string $filename Name of the file to be saved. 406 * @param resource|GdImage $image Image resource or GdImage instance. 407 * @param string $mime_type The mime type of the image. 408 * @param int $post_id Attachment post ID. 409 */ 410 $saved = apply_filters_deprecated( 411 'wp_save_image_file', 412 array( null, $filename, $image, $mime_type, $post_id ), 413 '3.5.0', 414 'wp_save_image_editor_file' 415 ); 416 417 if ( null !== $saved ) { 418 return $saved; 419 } 420 421 switch ( $mime_type ) { 422 case 'image/jpeg': 423 /** This filter is documented in wp-includes/class-wp-image-editor.php */ 424 return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) ); 425 case 'image/png': 426 return imagepng( $image, $filename ); 427 case 'image/gif': 428 return imagegif( $image, $filename ); 429 case 'image/webp': 430 if ( function_exists( 'imagewebp' ) ) { 431 return imagewebp( $image, $filename ); 432 } 433 return false; 434 default: 435 return false; 436 } 437 } 438 } 439 440 /** 441 * Image preview ratio. Internal use only. 442 * 443 * @since 2.9.0 444 * 445 * @ignore 446 * @param int $w Image width in pixels. 447 * @param int $h Image height in pixels. 448 * @return float|int Image preview ratio. 449 */ 450 function _image_get_preview_ratio( $w, $h ) { 451 $max = max( $w, $h ); 452 return $max > 400 ? ( 400 / $max ) : 1; 453 } 454 455 /** 456 * Returns an image resource. Internal use only. 457 * 458 * @since 2.9.0 459 * @deprecated 3.5.0 Use WP_Image_Editor::rotate() 460 * @see WP_Image_Editor::rotate() 461 * 462 * @ignore 463 * @param resource|GdImage $img Image resource. 464 * @param float|int $angle Image rotation angle, in degrees. 465 * @return resource|GdImage|false GD image resource or GdImage instance, false otherwise. 466 */ 467 function _rotate_image_resource( $img, $angle ) { 468 _deprecated_function( __FUNCTION__, '3.5.0', 'WP_Image_Editor::rotate()' ); 469 470 if ( function_exists( 'imagerotate' ) ) { 471 $rotated = imagerotate( $img, $angle, 0 ); 472 473 if ( is_gd_image( $rotated ) ) { 474 imagedestroy( $img ); 475 $img = $rotated; 476 } 477 } 478 479 return $img; 480 } 481 482 /** 483 * Flips an image resource. Internal use only. 484 * 485 * @since 2.9.0 486 * @deprecated 3.5.0 Use WP_Image_Editor::flip() 487 * @see WP_Image_Editor::flip() 488 * 489 * @ignore 490 * @param resource|GdImage $img Image resource or GdImage instance. 491 * @param bool $horz Whether to flip horizontally. 492 * @param bool $vert Whether to flip vertically. 493 * @return resource|GdImage (maybe) flipped image resource or GdImage instance. 494 */ 495 function _flip_image_resource( $img, $horz, $vert ) { 496 _deprecated_function( __FUNCTION__, '3.5.0', 'WP_Image_Editor::flip()' ); 497 498 $w = imagesx( $img ); 499 $h = imagesy( $img ); 500 $dst = wp_imagecreatetruecolor( $w, $h ); 501 502 if ( is_gd_image( $dst ) ) { 503 $sx = $vert ? ( $w - 1 ) : 0; 504 $sy = $horz ? ( $h - 1 ) : 0; 505 $sw = $vert ? -$w : $w; 506 $sh = $horz ? -$h : $h; 507 508 if ( imagecopyresampled( $dst, $img, 0, 0, $sx, $sy, $w, $h, $sw, $sh ) ) { 509 imagedestroy( $img ); 510 $img = $dst; 511 } 512 } 513 514 return $img; 515 } 516 517 /** 518 * Crops an image resource. Internal use only. 519 * 520 * @since 2.9.0 521 * 522 * @ignore 523 * @param resource|GdImage $img Image resource or GdImage instance. 524 * @param float $x Source point x-coordinate. 525 * @param float $y Source point y-coordinate. 526 * @param float $w Source width. 527 * @param float $h Source height. 528 * @return resource|GdImage (maybe) cropped image resource or GdImage instance. 529 */ 530 function _crop_image_resource( $img, $x, $y, $w, $h ) { 531 $dst = wp_imagecreatetruecolor( $w, $h ); 532 533 if ( is_gd_image( $dst ) ) { 534 if ( imagecopy( $dst, $img, 0, 0, $x, $y, $w, $h ) ) { 535 imagedestroy( $img ); 536 $img = $dst; 537 } 538 } 539 540 return $img; 541 } 542 543 /** 544 * Performs group of changes on Editor specified. 545 * 546 * @since 2.9.0 547 * 548 * @param WP_Image_Editor $image WP_Image_Editor instance. 549 * @param array $changes Array of change operations. 550 * @return WP_Image_Editor WP_Image_Editor instance with changes applied. 551 */ 552 function image_edit_apply_changes( $image, $changes ) { 553 if ( is_gd_image( $image ) ) { 554 /* translators: 1: $image, 2: WP_Image_Editor */ 555 _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) ); 556 } 557 558 if ( ! is_array( $changes ) ) { 559 return $image; 560 } 561 562 // Expand change operations. 563 foreach ( $changes as $key => $obj ) { 564 if ( isset( $obj->r ) ) { 565 $obj->type = 'rotate'; 566 $obj->angle = $obj->r; 567 unset( $obj->r ); 568 } elseif ( isset( $obj->f ) ) { 569 $obj->type = 'flip'; 570 $obj->axis = $obj->f; 571 unset( $obj->f ); 572 } elseif ( isset( $obj->c ) ) { 573 $obj->type = 'crop'; 574 $obj->sel = $obj->c; 575 unset( $obj->c ); 576 } 577 $changes[ $key ] = $obj; 578 } 579 580 // Combine operations. 581 if ( count( $changes ) > 1 ) { 582 $filtered = array( $changes[0] ); 583 for ( $i = 0, $j = 1, $c = count( $changes ); $j < $c; $j++ ) { 584 $combined = false; 585 if ( $filtered[ $i ]->type == $changes[ $j ]->type ) { 586 switch ( $filtered[ $i ]->type ) { 587 case 'rotate': 588 $filtered[ $i ]->angle += $changes[ $j ]->angle; 589 $combined = true; 590 break; 591 case 'flip': 592 $filtered[ $i ]->axis ^= $changes[ $j ]->axis; 593 $combined = true; 594 break; 595 } 596 } 597 if ( ! $combined ) { 598 $filtered[ ++$i ] = $changes[ $j ]; 599 } 600 } 601 $changes = $filtered; 602 unset( $filtered ); 603 } 604 605 // Image resource before applying the changes. 606 if ( $image instanceof WP_Image_Editor ) { 607 608 /** 609 * Filters the WP_Image_Editor instance before applying changes to the image. 610 * 611 * @since 3.5.0 612 * 613 * @param WP_Image_Editor $image WP_Image_Editor instance. 614 * @param array $changes Array of change operations. 615 */ 616 $image = apply_filters( 'wp_image_editor_before_change', $image, $changes ); 617 } elseif ( is_gd_image( $image ) ) { 618 619 /** 620 * Filters the GD image resource before applying changes to the image. 621 * 622 * @since 2.9.0 623 * @deprecated 3.5.0 Use {@see 'wp_image_editor_before_change'} instead. 624 * 625 * @param resource|GdImage $image GD image resource or GdImage instance. 626 * @param array $changes Array of change operations. 627 */ 628 $image = apply_filters_deprecated( 'image_edit_before_change', array( $image, $changes ), '3.5.0', 'wp_image_editor_before_change' ); 629 } 630 631 foreach ( $changes as $operation ) { 632 switch ( $operation->type ) { 633 case 'rotate': 634 if ( 0 != $operation->angle ) { 635 if ( $image instanceof WP_Image_Editor ) { 636 $image->rotate( $operation->angle ); 637 } else { 638 $image = _rotate_image_resource( $image, $operation->angle ); 639 } 640 } 641 break; 642 case 'flip': 643 if ( 0 != $operation->axis ) { 644 if ( $image instanceof WP_Image_Editor ) { 645 $image->flip( ( $operation->axis & 1 ) != 0, ( $operation->axis & 2 ) != 0 ); 646 } else { 647 $image = _flip_image_resource( $image, ( $operation->axis & 1 ) != 0, ( $operation->axis & 2 ) != 0 ); 648 } 649 } 650 break; 651 case 'crop': 652 $sel = $operation->sel; 653 654 if ( $image instanceof WP_Image_Editor ) { 655 $size = $image->get_size(); 656 $w = $size['width']; 657 $h = $size['height']; 658 659 $scale = 1 / _image_get_preview_ratio( $w, $h ); // Discard preview scaling. 660 $image->crop( $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale ); 661 } else { 662 $scale = 1 / _image_get_preview_ratio( imagesx( $image ), imagesy( $image ) ); // Discard preview scaling. 663 $image = _crop_image_resource( $image, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale ); 664 } 665 break; 666 } 667 } 668 669 return $image; 670 } 671 672 673 /** 674 * Streams image in post to browser, along with enqueued changes 675 * in `$_REQUEST['history']`. 676 * 677 * @since 2.9.0 678 * 679 * @param int $post_id Attachment post ID. 680 * @return bool True on success, false on failure. 681 */ 682 function stream_preview_image( $post_id ) { 683 $post = get_post( $post_id ); 684 685 wp_raise_memory_limit( 'admin' ); 686 687 $img = wp_get_image_editor( _load_image_to_edit_path( $post_id ) ); 688 689 if ( is_wp_error( $img ) ) { 690 return false; 691 } 692 693 $changes = ! empty( $_REQUEST['history'] ) ? json_decode( wp_unslash( $_REQUEST['history'] ) ) : null; 694 if ( $changes ) { 695 $img = image_edit_apply_changes( $img, $changes ); 696 } 697 698 // Scale the image. 699 $size = $img->get_size(); 700 $w = $size['width']; 701 $h = $size['height']; 702 703 $ratio = _image_get_preview_ratio( $w, $h ); 704 $w2 = max( 1, $w * $ratio ); 705 $h2 = max( 1, $h * $ratio ); 706 707 if ( is_wp_error( $img->resize( $w2, $h2 ) ) ) { 708 return false; 709 } 710 711 return wp_stream_image( $img, $post->post_mime_type, $post_id ); 712 } 713 714 /** 715 * Restores the metadata for a given attachment. 716 * 717 * @since 2.9.0 718 * 719 * @param int $post_id Attachment post ID. 720 * @return stdClass Image restoration message object. 721 */ 722 function wp_restore_image( $post_id ) { 723 $meta = wp_get_attachment_metadata( $post_id ); 724 $file = get_attached_file( $post_id ); 725 $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true ); 726 $old_backup_sizes = $backup_sizes; 727 $restored = false; 728 $msg = new stdClass; 729 730 if ( ! is_array( $backup_sizes ) ) { 731 $msg->error = __( 'Cannot load image metadata.' ); 732 return $msg; 733 } 734 735 $parts = pathinfo( $file ); 736 $suffix = time() . rand( 100, 999 ); 737 $default_sizes = get_intermediate_image_sizes(); 738 739 if ( isset( $backup_sizes['full-orig'] ) && is_array( $backup_sizes['full-orig'] ) ) { 740 $data = $backup_sizes['full-orig']; 741 742 if ( $parts['basename'] != $data['file'] ) { 743 if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) { 744 745 // Delete only if it's an edited image. 746 if ( preg_match( '/-e[0-9]{13}\./', $parts['basename'] ) ) { 747 wp_delete_file( $file ); 748 } 749 } elseif ( isset( $meta['width'], $meta['height'] ) ) { 750 $backup_sizes[ "full-$suffix" ] = array( 751 'width' => $meta['width'], 752 'height' => $meta['height'], 753 'file' => $parts['basename'], 754 ); 755 } 756 } 757 758 $restored_file = path_join( $parts['dirname'], $data['file'] ); 759 $restored = update_attached_file( $post_id, $restored_file ); 760 761 $meta['file'] = _wp_relative_upload_path( $restored_file ); 762 $meta['width'] = $data['width']; 763 $meta['height'] = $data['height']; 764 } 765 766 foreach ( $default_sizes as $default_size ) { 767 if ( isset( $backup_sizes[ "$default_size-orig" ] ) ) { 768 $data = $backup_sizes[ "$default_size-orig" ]; 769 if ( isset( $meta['sizes'][ $default_size ] ) && $meta['sizes'][ $default_size ]['file'] != $data['file'] ) { 770 if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) { 771 772 // Delete only if it's an edited image. 773 if ( preg_match( '/-e[0-9]{13}-/', $meta['sizes'][ $default_size ]['file'] ) ) { 774 $delete_file = path_join( $parts['dirname'], $meta['sizes'][ $default_size ]['file'] ); 775 wp_delete_file( $delete_file ); 776 } 777 } else { 778 $backup_sizes[ "$default_size-{$suffix}" ] = $meta['sizes'][ $default_size ]; 779 } 780 } 781 782 $meta['sizes'][ $default_size ] = $data; 783 } else { 784 unset( $meta['sizes'][ $default_size ] ); 785 } 786 } 787 788 if ( ! wp_update_attachment_metadata( $post_id, $meta ) || 789 ( $old_backup_sizes !== $backup_sizes && ! update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes ) ) ) { 790 791 $msg->error = __( 'Cannot save image metadata.' ); 792 return $msg; 793 } 794 795 if ( ! $restored ) { 796 $msg->error = __( 'Image metadata is inconsistent.' ); 797 } else { 798 $msg->msg = __( 'Image restored successfully.' ); 799 } 800 801 return $msg; 802 } 803 804 /** 805 * Saves image to post, along with enqueued changes 806 * in `$_REQUEST['history']`. 807 * 808 * @since 2.9.0 809 * 810 * @param int $post_id Attachment post ID. 811 * @return stdClass 812 */ 813 function wp_save_image( $post_id ) { 814 $_wp_additional_image_sizes = wp_get_additional_image_sizes(); 815 816 $return = new stdClass; 817 $success = false; 818 $delete = false; 819 $scaled = false; 820 $nocrop = false; 821 $post = get_post( $post_id ); 822 823 $img = wp_get_image_editor( _load_image_to_edit_path( $post_id, 'full' ) ); 824 if ( is_wp_error( $img ) ) { 825 $return->error = esc_js( __( 'Unable to create new image.' ) ); 826 return $return; 827 } 828 829 $fwidth = ! empty( $_REQUEST['fwidth'] ) ? (int) $_REQUEST['fwidth'] : 0; 830 $fheight = ! empty( $_REQUEST['fheight'] ) ? (int) $_REQUEST['fheight'] : 0; 831 $target = ! empty( $_REQUEST['target'] ) ? preg_replace( '/[^a-z0-9_-]+/i', '', $_REQUEST['target'] ) : ''; 832 $scale = ! empty( $_REQUEST['do'] ) && 'scale' === $_REQUEST['do']; 833 834 if ( $scale && $fwidth > 0 && $fheight > 0 ) { 835 $size = $img->get_size(); 836 $sX = $size['width']; 837 $sY = $size['height']; 838 839 // Check if it has roughly the same w / h ratio. 840 $diff = round( $sX / $sY, 2 ) - round( $fwidth / $fheight, 2 ); 841 if ( -0.1 < $diff && $diff < 0.1 ) { 842 // Scale the full size image. 843 if ( $img->resize( $fwidth, $fheight ) ) { 844 $scaled = true; 845 } 846 } 847 848 if ( ! $scaled ) { 849 $return->error = esc_js( __( 'Error while saving the scaled image. Please reload the page and try again.' ) ); 850 return $return; 851 } 852 } elseif ( ! empty( $_REQUEST['history'] ) ) { 853 $changes = json_decode( wp_unslash( $_REQUEST['history'] ) ); 854 if ( $changes ) { 855 $img = image_edit_apply_changes( $img, $changes ); 856 } 857 } else { 858 $return->error = esc_js( __( 'Nothing to save, the image has not changed.' ) ); 859 return $return; 860 } 861 862 $meta = wp_get_attachment_metadata( $post_id ); 863 $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true ); 864 865 if ( ! is_array( $meta ) ) { 866 $return->error = esc_js( __( 'Image data does not exist. Please re-upload the image.' ) ); 867 return $return; 868 } 869 870 if ( ! is_array( $backup_sizes ) ) { 871 $backup_sizes = array(); 872 } 873 874 // Generate new filename. 875 $path = get_attached_file( $post_id ); 876 877 $basename = pathinfo( $path, PATHINFO_BASENAME ); 878 $dirname = pathinfo( $path, PATHINFO_DIRNAME ); 879 $ext = pathinfo( $path, PATHINFO_EXTENSION ); 880 $filename = pathinfo( $path, PATHINFO_FILENAME ); 881 $suffix = time() . rand( 100, 999 ); 882 883 if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE && 884 isset( $backup_sizes['full-orig'] ) && $backup_sizes['full-orig']['file'] != $basename ) { 885 886 if ( 'thumbnail' === $target ) { 887 $new_path = "{$dirname}/{$filename}-temp.{$ext}"; 888 } else { 889 $new_path = $path; 890 } 891 } else { 892 while ( true ) { 893 $filename = preg_replace( '/-e([0-9]+)$/', '', $filename ); 894 $filename .= "-e{$suffix}"; 895 $new_filename = "{$filename}.{$ext}"; 896 $new_path = "{$dirname}/$new_filename"; 897 if ( file_exists( $new_path ) ) { 898 $suffix++; 899 } else { 900 break; 901 } 902 } 903 } 904 905 // Save the full-size file, also needed to create sub-sizes. 906 if ( ! wp_save_image_file( $new_path, $img, $post->post_mime_type, $post_id ) ) { 907 $return->error = esc_js( __( 'Unable to save the image.' ) ); 908 return $return; 909 } 910 911 if ( 'nothumb' === $target || 'all' === $target || 'full' === $target || $scaled ) { 912 $tag = false; 913 if ( isset( $backup_sizes['full-orig'] ) ) { 914 if ( ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) && $backup_sizes['full-orig']['file'] !== $basename ) { 915 $tag = "full-$suffix"; 916 } 917 } else { 918 $tag = 'full-orig'; 919 } 920 921 if ( $tag ) { 922 $backup_sizes[ $tag ] = array( 923 'width' => $meta['width'], 924 'height' => $meta['height'], 925 'file' => $basename, 926 ); 927 } 928 $success = ( $path === $new_path ) || update_attached_file( $post_id, $new_path ); 929 930 $meta['file'] = _wp_relative_upload_path( $new_path ); 931 932 $size = $img->get_size(); 933 $meta['width'] = $size['width']; 934 $meta['height'] = $size['height']; 935 936 if ( $success ) { 937 $sizes = get_intermediate_image_sizes(); 938 if ( 'nothumb' === $target || 'all' === $target ) { 939 if ( 'nothumb' === $target ) { 940 $sizes = array_diff( $sizes, array( 'thumbnail' ) ); 941 } 942 } elseif ( 'thumbnail' !== $target ) { 943 $sizes = array_diff( $sizes, array( $target ) ); 944 } 945 } 946 947 $return->fw = $meta['width']; 948 $return->fh = $meta['height']; 949 } elseif ( 'thumbnail' === $target ) { 950 $sizes = array( 'thumbnail' ); 951 $success = true; 952 $delete = true; 953 $nocrop = true; 954 } else { 955 $sizes = array( $target ); 956 $success = true; 957 $delete = true; 958 $nocrop = $_wp_additional_image_sizes[ $size ]['crop']; 959 } 960 961 /* 962 * We need to remove any existing resized image files because 963 * a new crop or rotate could generate different sizes (and hence, filenames), 964 * keeping the new resized images from overwriting the existing image files. 965 * https://core.trac.wordpress.org/ticket/32171 966 */ 967 if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE && ! empty( $meta['sizes'] ) ) { 968 foreach ( $meta['sizes'] as $size ) { 969 if ( ! empty( $size['file'] ) && preg_match( '/-e[0-9]{13}-/', $size['file'] ) ) { 970 $delete_file = path_join( $dirname, $size['file'] ); 971 wp_delete_file( $delete_file ); 972 } 973 } 974 } 975 976 if ( isset( $sizes ) ) { 977 $_sizes = array(); 978 979 foreach ( $sizes as $size ) { 980 $tag = false; 981 if ( isset( $meta['sizes'][ $size ] ) ) { 982 if ( isset( $backup_sizes[ "$size-orig" ] ) ) { 983 if ( ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) && $backup_sizes[ "$size-orig" ]['file'] != $meta['sizes'][ $size ]['file'] ) { 984 $tag = "$size-$suffix"; 985 } 986 } else { 987 $tag = "$size-orig"; 988 } 989 990 if ( $tag ) { 991 $backup_sizes[ $tag ] = $meta['sizes'][ $size ]; 992 } 993 } 994 995 if ( isset( $_wp_additional_image_sizes[ $size ] ) ) { 996 $width = (int) $_wp_additional_image_sizes[ $size ]['width']; 997 $height = (int) $_wp_additional_image_sizes[ $size ]['height']; 998 $crop = ( $nocrop ) ? false : $_wp_additional_image_sizes[ $size ]['crop']; 999 } else { 1000 $height = get_option( "{$size}_size_h" ); 1001 $width = get_option( "{$size}_size_w" ); 1002 $crop = ( $nocrop ) ? false : get_option( "{$size}_crop" ); 1003 } 1004 1005 $_sizes[ $size ] = array( 1006 'width' => $width, 1007 'height' => $height, 1008 'crop' => $crop, 1009 ); 1010 } 1011 1012 $meta['sizes'] = array_merge( $meta['sizes'], $img->multi_resize( $_sizes ) ); 1013 } 1014 1015 unset( $img ); 1016 1017 if ( $success ) { 1018 wp_update_attachment_metadata( $post_id, $meta ); 1019 update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes ); 1020 1021 if ( 'thumbnail' === $target || 'all' === $target || 'full' === $target ) { 1022 // Check if it's an image edit from attachment edit screen. 1023 if ( ! empty( $_REQUEST['context'] ) && 'edit-attachment' === $_REQUEST['context'] ) { 1024 $thumb_url = wp_get_attachment_image_src( $post_id, array( 900, 600 ), true ); 1025 $return->thumbnail = $thumb_url[0]; 1026 } else { 1027 $file_url = wp_get_attachment_url( $post_id ); 1028 if ( ! empty( $meta['sizes']['thumbnail'] ) ) { 1029 $thumb = $meta['sizes']['thumbnail']; 1030 $return->thumbnail = path_join( dirname( $file_url ), $thumb['file'] ); 1031 } else { 1032 $return->thumbnail = "$file_url?w=128&h=128"; 1033 } 1034 } 1035 } 1036 } else { 1037 $delete = true; 1038 } 1039 1040 if ( $delete ) { 1041 wp_delete_file( $new_path ); 1042 } 1043 1044 $return->msg = esc_js( __( 'Image saved' ) ); 1045 return $return; 1046 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Dec 24 01:00:02 2024 | Cross-referenced by PHPXref 0.7.1 |