[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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


Generated: Sat May 30 01:00:03 2020 Cross-referenced by PHPXref 0.7.1