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


Generated: Mon Jun 17 08:20:02 2019 Cross-referenced by PHPXref 0.7