[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

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


Generated: Tue Oct 22 08:20:01 2019 Cross-referenced by PHPXref 0.7