[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-admin/includes/ -> image-edit.php (source)

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


Generated: Thu Oct 21 01:00:03 2021 Cross-referenced by PHPXref 0.7.1