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


Generated: Wed Aug 12 01:00:03 2020 Cross-referenced by PHPXref 0.7.1