[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ -> class-wp-image-editor.php (source)

   1  <?php
   2  /**
   3   * Base WordPress Image Editor
   4   *
   5   * @package WordPress
   6   * @subpackage Image_Editor
   7   */
   8  
   9  /**
  10   * Base image editor class from which implementations extend
  11   *
  12   * @since 3.5.0
  13   */
  14  abstract class WP_Image_Editor {
  15      protected $file              = null;
  16      protected $size              = null;
  17      protected $mime_type         = null;
  18      protected $default_mime_type = 'image/jpeg';
  19      protected $quality           = false;
  20      protected $default_quality   = 82;
  21  
  22      /**
  23       * Each instance handles a single file.
  24       *
  25       * @param string $file Path to the file to load.
  26       */
  27  	public function __construct( $file ) {
  28          $this->file = $file;
  29      }
  30  
  31      /**
  32       * Checks to see if current environment supports the editor chosen.
  33       * Must be overridden in a subclass.
  34       *
  35       * @since 3.5.0
  36       *
  37       * @abstract
  38       *
  39       * @param array $args
  40       * @return bool
  41       */
  42  	public static function test( $args = array() ) {
  43          return false;
  44      }
  45  
  46      /**
  47       * Checks to see if editor supports the mime-type specified.
  48       * Must be overridden in a subclass.
  49       *
  50       * @since 3.5.0
  51       *
  52       * @abstract
  53       *
  54       * @param string $mime_type
  55       * @return bool
  56       */
  57  	public static function supports_mime_type( $mime_type ) {
  58          return false;
  59      }
  60  
  61      /**
  62       * Loads image from $this->file into editor.
  63       *
  64       * @since 3.5.0
  65       * @abstract
  66       *
  67       * @return bool|WP_Error True if loaded; WP_Error on failure.
  68       */
  69      abstract public function load();
  70  
  71      /**
  72       * Saves current image to file.
  73       *
  74       * @since 3.5.0
  75       * @abstract
  76       *
  77       * @param string $destfilename
  78       * @param string $mime_type
  79       * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string}
  80       */
  81      abstract public function save( $destfilename = null, $mime_type = null );
  82  
  83      /**
  84       * Resizes current image.
  85       *
  86       * At minimum, either a height or width must be provided.
  87       * If one of the two is set to null, the resize will
  88       * maintain aspect ratio according to the provided dimension.
  89       *
  90       * @since 3.5.0
  91       * @abstract
  92       *
  93       * @param  int|null $max_w Image width.
  94       * @param  int|null $max_h Image height.
  95       * @param  bool     $crop
  96       * @return bool|WP_Error
  97       */
  98      abstract public function resize( $max_w, $max_h, $crop = false );
  99  
 100      /**
 101       * Resize multiple images from a single source.
 102       *
 103       * @since 3.5.0
 104       * @abstract
 105       *
 106       * @param array $sizes {
 107       *     An array of image size arrays. Default sizes are 'small', 'medium', 'large'.
 108       *
 109       *     @type array $size {
 110       *         @type int  $width  Image width.
 111       *         @type int  $height Image height.
 112       *         @type bool $crop   Optional. Whether to crop the image. Default false.
 113       *     }
 114       * }
 115       * @return array An array of resized images metadata by size.
 116       */
 117      abstract public function multi_resize( $sizes );
 118  
 119      /**
 120       * Crops Image.
 121       *
 122       * @since 3.5.0
 123       * @abstract
 124       *
 125       * @param int $src_x The start x position to crop from.
 126       * @param int $src_y The start y position to crop from.
 127       * @param int $src_w The width to crop.
 128       * @param int $src_h The height to crop.
 129       * @param int $dst_w Optional. The destination width.
 130       * @param int $dst_h Optional. The destination height.
 131       * @param bool $src_abs Optional. If the source crop points are absolute.
 132       * @return bool|WP_Error
 133       */
 134      abstract public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false );
 135  
 136      /**
 137       * Rotates current image counter-clockwise by $angle.
 138       *
 139       * @since 3.5.0
 140       * @abstract
 141       *
 142       * @param float $angle
 143       * @return bool|WP_Error
 144       */
 145      abstract public function rotate( $angle );
 146  
 147      /**
 148       * Flips current image.
 149       *
 150       * @since 3.5.0
 151       * @abstract
 152       *
 153       * @param bool $horz Flip along Horizontal Axis
 154       * @param bool $vert Flip along Vertical Axis
 155       * @return bool|WP_Error
 156       */
 157      abstract public function flip( $horz, $vert );
 158  
 159      /**
 160       * Streams current image to browser.
 161       *
 162       * @since 3.5.0
 163       * @abstract
 164       *
 165       * @param string $mime_type The mime type of the image.
 166       * @return bool|WP_Error True on success, WP_Error object or false on failure.
 167       */
 168      abstract public function stream( $mime_type = null );
 169  
 170      /**
 171       * Gets dimensions of image.
 172       *
 173       * @since 3.5.0
 174       *
 175       * @return array {
 176       *     Dimensions of the image.
 177       *
 178       *     @type int $width  The image width.
 179       *     @type int $height The image height.
 180       * }
 181       */
 182  	public function get_size() {
 183          return $this->size;
 184      }
 185  
 186      /**
 187       * Sets current image size.
 188       *
 189       * @since 3.5.0
 190       *
 191       * @param int $width
 192       * @param int $height
 193       * @return true
 194       */
 195  	protected function update_size( $width = null, $height = null ) {
 196          $this->size = array(
 197              'width'  => (int) $width,
 198              'height' => (int) $height,
 199          );
 200          return true;
 201      }
 202  
 203      /**
 204       * Gets the Image Compression quality on a 1-100% scale.
 205       *
 206       * @since 4.0.0
 207       *
 208       * @return int $quality Compression Quality. Range: [1,100]
 209       */
 210  	public function get_quality() {
 211          if ( ! $this->quality ) {
 212              $this->set_quality();
 213          }
 214  
 215          return $this->quality;
 216      }
 217  
 218      /**
 219       * Sets Image Compression quality on a 1-100% scale.
 220       *
 221       * @since 3.5.0
 222       *
 223       * @param int $quality Compression Quality. Range: [1,100]
 224       * @return true|WP_Error True if set successfully; WP_Error on failure.
 225       */
 226  	public function set_quality( $quality = null ) {
 227          if ( null === $quality ) {
 228              /**
 229               * Filters the default image compression quality setting.
 230               *
 231               * Applies only during initial editor instantiation, or when set_quality() is run
 232               * manually without the `$quality` argument.
 233               *
 234               * set_quality() has priority over the filter.
 235               *
 236               * @since 3.5.0
 237               *
 238               * @param int    $quality   Quality level between 1 (low) and 100 (high).
 239               * @param string $mime_type Image mime type.
 240               */
 241              $quality = apply_filters( 'wp_editor_set_quality', $this->default_quality, $this->mime_type );
 242  
 243              if ( 'image/jpeg' == $this->mime_type ) {
 244                  /**
 245                   * Filters the JPEG compression quality for backward-compatibility.
 246                   *
 247                   * Applies only during initial editor instantiation, or when set_quality() is run
 248                   * manually without the `$quality` argument.
 249                   *
 250                   * set_quality() has priority over the filter.
 251                   *
 252                   * The filter is evaluated under two contexts: 'image_resize', and 'edit_image',
 253                   * (when a JPEG image is saved to file).
 254                   *
 255                   * @since 2.5.0
 256                   *
 257                   * @param int    $quality Quality level between 0 (low) and 100 (high) of the JPEG.
 258                   * @param string $context Context of the filter.
 259                   */
 260                  $quality = apply_filters( 'jpeg_quality', $quality, 'image_resize' );
 261              }
 262  
 263              if ( $quality < 0 || $quality > 100 ) {
 264                  $quality = $this->default_quality;
 265              }
 266          }
 267  
 268          // Allow 0, but squash to 1 due to identical images in GD, and for backward compatibility.
 269          if ( 0 === $quality ) {
 270              $quality = 1;
 271          }
 272  
 273          if ( ( $quality >= 1 ) && ( $quality <= 100 ) ) {
 274              $this->quality = $quality;
 275              return true;
 276          } else {
 277              return new WP_Error( 'invalid_image_quality', __( 'Attempted to set image quality outside of the range [1,100].' ) );
 278          }
 279      }
 280  
 281      /**
 282       * Returns preferred mime-type and extension based on provided
 283       * file's extension and mime, or current file's extension and mime.
 284       *
 285       * Will default to $this->default_mime_type if requested is not supported.
 286       *
 287       * Provides corrected filename only if filename is provided.
 288       *
 289       * @since 3.5.0
 290       *
 291       * @param string $filename
 292       * @param string $mime_type
 293       * @return array { filename|null, extension, mime-type }
 294       */
 295  	protected function get_output_format( $filename = null, $mime_type = null ) {
 296          $new_ext = null;
 297  
 298          // By default, assume specified type takes priority.
 299          if ( $mime_type ) {
 300              $new_ext = $this->get_extension( $mime_type );
 301          }
 302  
 303          if ( $filename ) {
 304              $file_ext  = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
 305              $file_mime = $this->get_mime_type( $file_ext );
 306          } else {
 307              // If no file specified, grab editor's current extension and mime-type.
 308              $file_ext  = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) );
 309              $file_mime = $this->mime_type;
 310          }
 311  
 312          // Check to see if specified mime-type is the same as type implied by
 313          // file extension. If so, prefer extension from file.
 314          if ( ! $mime_type || ( $file_mime == $mime_type ) ) {
 315              $mime_type = $file_mime;
 316              $new_ext   = $file_ext;
 317          }
 318  
 319          // Double-check that the mime-type selected is supported by the editor.
 320          // If not, choose a default instead.
 321          if ( ! $this->supports_mime_type( $mime_type ) ) {
 322              /**
 323               * Filters default mime type prior to getting the file extension.
 324               *
 325               * @see wp_get_mime_types()
 326               *
 327               * @since 3.5.0
 328               *
 329               * @param string $mime_type Mime type string.
 330               */
 331              $mime_type = apply_filters( 'image_editor_default_mime_type', $this->default_mime_type );
 332              $new_ext   = $this->get_extension( $mime_type );
 333          }
 334  
 335          if ( $filename ) {
 336              $dir = pathinfo( $filename, PATHINFO_DIRNAME );
 337              $ext = pathinfo( $filename, PATHINFO_EXTENSION );
 338  
 339              $filename = trailingslashit( $dir ) . wp_basename( $filename, ".$ext" ) . ".{$new_ext}";
 340          }
 341  
 342          return array( $filename, $new_ext, $mime_type );
 343      }
 344  
 345      /**
 346       * Builds an output filename based on current file, and adding proper suffix
 347       *
 348       * @since 3.5.0
 349       *
 350       * @param string $suffix
 351       * @param string $dest_path
 352       * @param string $extension
 353       * @return string filename
 354       */
 355  	public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) {
 356          // $suffix will be appended to the destination filename, just before the extension.
 357          if ( ! $suffix ) {
 358              $suffix = $this->get_suffix();
 359          }
 360  
 361          $dir = pathinfo( $this->file, PATHINFO_DIRNAME );
 362          $ext = pathinfo( $this->file, PATHINFO_EXTENSION );
 363  
 364          $name    = wp_basename( $this->file, ".$ext" );
 365          $new_ext = strtolower( $extension ? $extension : $ext );
 366  
 367          if ( ! is_null( $dest_path ) ) {
 368              $_dest_path = realpath( $dest_path );
 369              if ( $_dest_path ) {
 370                  $dir = $_dest_path;
 371              }
 372          }
 373  
 374          return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}";
 375      }
 376  
 377      /**
 378       * Builds and returns proper suffix for file based on height and width.
 379       *
 380       * @since 3.5.0
 381       *
 382       * @return string|false suffix
 383       */
 384  	public function get_suffix() {
 385          if ( ! $this->get_size() ) {
 386              return false;
 387          }
 388  
 389          return "{$this->size['width']}x{$this->size['height']}";
 390      }
 391  
 392      /**
 393       * Check if a JPEG image has EXIF Orientation tag and rotate it if needed.
 394       *
 395       * @since 5.3.0
 396       *
 397       * @return bool|WP_Error True if the image was rotated. False if not rotated (no EXIF data or the image doesn't need to be rotated).
 398       *                       WP_Error if error while rotating.
 399       */
 400  	public function maybe_exif_rotate() {
 401          $orientation = null;
 402  
 403          if ( is_callable( 'exif_read_data' ) && 'image/jpeg' === $this->mime_type ) {
 404              $exif_data = @exif_read_data( $this->file );
 405  
 406              if ( ! empty( $exif_data['Orientation'] ) ) {
 407                  $orientation = (int) $exif_data['Orientation'];
 408              }
 409          }
 410  
 411          /**
 412           * Filters the `$orientation` value to correct it before rotating or to prevemnt rotating the image.
 413           *
 414           * @since 5.3.0
 415           *
 416           * @param int    $orientation EXIF Orientation value as retrieved from the image file.
 417           * @param string $file        Path to the image file.
 418           */
 419          $orientation = apply_filters( 'wp_image_maybe_exif_rotate', $orientation, $this->file );
 420  
 421          if ( ! $orientation || 1 === $orientation ) {
 422              return false;
 423          }
 424  
 425          switch ( $orientation ) {
 426              case 2:
 427                  // Flip horizontally.
 428                  $result = $this->flip( true, false );
 429                  break;
 430              case 3:
 431                  // Rotate 180 degrees or flip horizontally and vertically.
 432                  // Flipping seems faster and uses less resources.
 433                  $result = $this->flip( true, true );
 434                  break;
 435              case 4:
 436                  // Flip vertically.
 437                  $result = $this->flip( false, true );
 438                  break;
 439              case 5:
 440                  // Rotate 90 degrees counter-clockwise and flip vertically.
 441                  $result = $this->rotate( 90 );
 442  
 443                  if ( ! is_wp_error( $result ) ) {
 444                      $result = $this->flip( false, true );
 445                  }
 446  
 447                  break;
 448              case 6:
 449                  // Rotate 90 degrees clockwise (270 counter-clockwise).
 450                  $result = $this->rotate( 270 );
 451                  break;
 452              case 7:
 453                  // Rotate 90 degrees counter-clockwise and flip horizontally.
 454                  $result = $this->rotate( 90 );
 455  
 456                  if ( ! is_wp_error( $result ) ) {
 457                      $result = $this->flip( true, false );
 458                  }
 459  
 460                  break;
 461              case 8:
 462                  // Rotate 90 degrees counter-clockwise.
 463                  $result = $this->rotate( 90 );
 464                  break;
 465          }
 466  
 467          return $result;
 468      }
 469  
 470      /**
 471       * Either calls editor's save function or handles file as a stream.
 472       *
 473       * @since 3.5.0
 474       *
 475       * @param string|stream $filename
 476       * @param callable $function
 477       * @param array $arguments
 478       * @return bool
 479       */
 480  	protected function make_image( $filename, $function, $arguments ) {
 481          $stream = wp_is_stream( $filename );
 482          if ( $stream ) {
 483              ob_start();
 484          } else {
 485              // The directory containing the original file may no longer exist when using a replication plugin.
 486              wp_mkdir_p( dirname( $filename ) );
 487          }
 488  
 489          $result = call_user_func_array( $function, $arguments );
 490  
 491          if ( $result && $stream ) {
 492              $contents = ob_get_contents();
 493  
 494              $fp = fopen( $filename, 'w' );
 495  
 496              if ( ! $fp ) {
 497                  ob_end_clean();
 498                  return false;
 499              }
 500  
 501              fwrite( $fp, $contents );
 502              fclose( $fp );
 503          }
 504  
 505          if ( $stream ) {
 506              ob_end_clean();
 507          }
 508  
 509          return $result;
 510      }
 511  
 512      /**
 513       * Returns first matched mime-type from extension,
 514       * as mapped from wp_get_mime_types()
 515       *
 516       * @since 3.5.0
 517       *
 518       * @param string $extension
 519       * @return string|false
 520       */
 521  	protected static function get_mime_type( $extension = null ) {
 522          if ( ! $extension ) {
 523              return false;
 524          }
 525  
 526          $mime_types = wp_get_mime_types();
 527          $extensions = array_keys( $mime_types );
 528  
 529          foreach ( $extensions as $_extension ) {
 530              if ( preg_match( "/{$extension}/i", $_extension ) ) {
 531                  return $mime_types[ $_extension ];
 532              }
 533          }
 534  
 535          return false;
 536      }
 537  
 538      /**
 539       * Returns first matched extension from Mime-type,
 540       * as mapped from wp_get_mime_types()
 541       *
 542       * @since 3.5.0
 543       *
 544       * @param string $mime_type
 545       * @return string|false
 546       */
 547  	protected static function get_extension( $mime_type = null ) {
 548          $extensions = explode( '|', array_search( $mime_type, wp_get_mime_types() ) );
 549  
 550          if ( empty( $extensions[0] ) ) {
 551              return false;
 552          }
 553  
 554          return $extensions[0];
 555      }
 556  }
 557  


Generated: Fri Apr 3 01:00:03 2020 Cross-referenced by PHPXref 0.7.1