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


Generated: Thu Oct 21 01:00:03 2021 Cross-referenced by PHPXref 0.7.1