[ 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 true|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 true|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 true|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 true|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 true|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 true|WP_Error True on success, WP_Error object 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 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               * The WP_Image_Editor::set_quality() method 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                   * The WP_Image_Editor::set_quality() method 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          /**
 320           * Filters the image editor output format mapping.
 321           *
 322           * Enables filtering the mime type used to save images. By default,
 323           * the mapping array is empty, so the mime type matches the source image.
 324           *
 325           * This should be considered experimental as there are a few edge cases that
 326           * still need to be solved.
 327           *
 328           * When this filter is used in combination with the {@see 'wp_editor_set_quality'}
 329           * filter, the image quality value used will be that of the original mime type,
 330           * not the mapped one. This could result in unexpected image quality for generated
 331           * images. See https://core.trac.wordpress.org/ticket/53667 for more details.
 332           *
 333           * When a mime type is mapped to another, and two images with the same name are
 334           * uploaded (image.jpg and image.webp, for example), the generated images for one
 335           * will potentially overwrite the other's.
 336           * See https://core.trac.wordpress.org/ticket/53668 for more details.
 337           *
 338           * @see WP_Image_Editor::get_output_format()
 339           *
 340           * @since 5.8.0
 341           *
 342           * @param string[] $output_format {
 343           *     An array of mime type mappings. Maps a source mime type to a new
 344           *     destination mime type. Default empty array.
 345           *
 346           *     @type string ...$0 The new mime type.
 347           * }
 348           * @param string $filename  Path to the image.
 349           * @param string $mime_type The source image mime type.
 350           * }
 351           */
 352          $output_format = apply_filters( 'image_editor_output_format', array(), $filename, $mime_type );
 353  
 354          if ( isset( $output_format[ $mime_type ] )
 355              && $this->supports_mime_type( $output_format[ $mime_type ] )
 356          ) {
 357              $mime_type = $output_format[ $mime_type ];
 358              $new_ext   = $this->get_extension( $mime_type );
 359          }
 360  
 361          // Double-check that the mime-type selected is supported by the editor.
 362          // If not, choose a default instead.
 363          if ( ! $this->supports_mime_type( $mime_type ) ) {
 364              /**
 365               * Filters default mime type prior to getting the file extension.
 366               *
 367               * @see wp_get_mime_types()
 368               *
 369               * @since 3.5.0
 370               *
 371               * @param string $mime_type Mime type string.
 372               */
 373              $mime_type = apply_filters( 'image_editor_default_mime_type', $this->default_mime_type );
 374              $new_ext   = $this->get_extension( $mime_type );
 375          }
 376  
 377          if ( $filename ) {
 378              $dir = pathinfo( $filename, PATHINFO_DIRNAME );
 379              $ext = pathinfo( $filename, PATHINFO_EXTENSION );
 380  
 381              $filename = trailingslashit( $dir ) . wp_basename( $filename, ".$ext" ) . ".{$new_ext}";
 382          }
 383  
 384          return array( $filename, $new_ext, $mime_type );
 385      }
 386  
 387      /**
 388       * Builds an output filename based on current file, and adding proper suffix
 389       *
 390       * @since 3.5.0
 391       *
 392       * @param string $suffix
 393       * @param string $dest_path
 394       * @param string $extension
 395       * @return string filename
 396       */
 397  	public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) {
 398          // $suffix will be appended to the destination filename, just before the extension.
 399          if ( ! $suffix ) {
 400              $suffix = $this->get_suffix();
 401          }
 402  
 403          $dir = pathinfo( $this->file, PATHINFO_DIRNAME );
 404          $ext = pathinfo( $this->file, PATHINFO_EXTENSION );
 405  
 406          $name    = wp_basename( $this->file, ".$ext" );
 407          $new_ext = strtolower( $extension ? $extension : $ext );
 408  
 409          if ( ! is_null( $dest_path ) ) {
 410              if ( ! wp_is_stream( $dest_path ) ) {
 411                  $_dest_path = realpath( $dest_path );
 412                  if ( $_dest_path ) {
 413                      $dir = $_dest_path;
 414                  }
 415              } else {
 416                  $dir = $dest_path;
 417              }
 418          }
 419  
 420          return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}";
 421      }
 422  
 423      /**
 424       * Builds and returns proper suffix for file based on height and width.
 425       *
 426       * @since 3.5.0
 427       *
 428       * @return string|false suffix
 429       */
 430  	public function get_suffix() {
 431          if ( ! $this->get_size() ) {
 432              return false;
 433          }
 434  
 435          return "{$this->size['width']}x{$this->size['height']}";
 436      }
 437  
 438      /**
 439       * Check if a JPEG image has EXIF Orientation tag and rotate it if needed.
 440       *
 441       * @since 5.3.0
 442       *
 443       * @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).
 444       *                       WP_Error if error while rotating.
 445       */
 446  	public function maybe_exif_rotate() {
 447          $orientation = null;
 448  
 449          if ( is_callable( 'exif_read_data' ) && 'image/jpeg' === $this->mime_type ) {
 450              $exif_data = @exif_read_data( $this->file );
 451  
 452              if ( ! empty( $exif_data['Orientation'] ) ) {
 453                  $orientation = (int) $exif_data['Orientation'];
 454              }
 455          }
 456  
 457          /**
 458           * Filters the `$orientation` value to correct it before rotating or to prevemnt rotating the image.
 459           *
 460           * @since 5.3.0
 461           *
 462           * @param int    $orientation EXIF Orientation value as retrieved from the image file.
 463           * @param string $file        Path to the image file.
 464           */
 465          $orientation = apply_filters( 'wp_image_maybe_exif_rotate', $orientation, $this->file );
 466  
 467          if ( ! $orientation || 1 === $orientation ) {
 468              return false;
 469          }
 470  
 471          switch ( $orientation ) {
 472              case 2:
 473                  // Flip horizontally.
 474                  $result = $this->flip( true, false );
 475                  break;
 476              case 3:
 477                  // Rotate 180 degrees or flip horizontally and vertically.
 478                  // Flipping seems faster and uses less resources.
 479                  $result = $this->flip( true, true );
 480                  break;
 481              case 4:
 482                  // Flip vertically.
 483                  $result = $this->flip( false, true );
 484                  break;
 485              case 5:
 486                  // Rotate 90 degrees counter-clockwise and flip vertically.
 487                  $result = $this->rotate( 90 );
 488  
 489                  if ( ! is_wp_error( $result ) ) {
 490                      $result = $this->flip( false, true );
 491                  }
 492  
 493                  break;
 494              case 6:
 495                  // Rotate 90 degrees clockwise (270 counter-clockwise).
 496                  $result = $this->rotate( 270 );
 497                  break;
 498              case 7:
 499                  // Rotate 90 degrees counter-clockwise and flip horizontally.
 500                  $result = $this->rotate( 90 );
 501  
 502                  if ( ! is_wp_error( $result ) ) {
 503                      $result = $this->flip( true, false );
 504                  }
 505  
 506                  break;
 507              case 8:
 508                  // Rotate 90 degrees counter-clockwise.
 509                  $result = $this->rotate( 90 );
 510                  break;
 511          }
 512  
 513          return $result;
 514      }
 515  
 516      /**
 517       * Either calls editor's save function or handles file as a stream.
 518       *
 519       * @since 3.5.0
 520       *
 521       * @param string   $filename
 522       * @param callable $function
 523       * @param array    $arguments
 524       * @return bool
 525       */
 526  	protected function make_image( $filename, $function, $arguments ) {
 527          $stream = wp_is_stream( $filename );
 528          if ( $stream ) {
 529              ob_start();
 530          } else {
 531              // The directory containing the original file may no longer exist when using a replication plugin.
 532              wp_mkdir_p( dirname( $filename ) );
 533          }
 534  
 535          $result = call_user_func_array( $function, $arguments );
 536  
 537          if ( $result && $stream ) {
 538              $contents = ob_get_contents();
 539  
 540              $fp = fopen( $filename, 'w' );
 541  
 542              if ( ! $fp ) {
 543                  ob_end_clean();
 544                  return false;
 545              }
 546  
 547              fwrite( $fp, $contents );
 548              fclose( $fp );
 549          }
 550  
 551          if ( $stream ) {
 552              ob_end_clean();
 553          }
 554  
 555          return $result;
 556      }
 557  
 558      /**
 559       * Returns first matched mime-type from extension,
 560       * as mapped from wp_get_mime_types()
 561       *
 562       * @since 3.5.0
 563       *
 564       * @param string $extension
 565       * @return string|false
 566       */
 567  	protected static function get_mime_type( $extension = null ) {
 568          if ( ! $extension ) {
 569              return false;
 570          }
 571  
 572          $mime_types = wp_get_mime_types();
 573          $extensions = array_keys( $mime_types );
 574  
 575          foreach ( $extensions as $_extension ) {
 576              if ( preg_match( "/{$extension}/i", $_extension ) ) {
 577                  return $mime_types[ $_extension ];
 578              }
 579          }
 580  
 581          return false;
 582      }
 583  
 584      /**
 585       * Returns first matched extension from Mime-type,
 586       * as mapped from wp_get_mime_types()
 587       *
 588       * @since 3.5.0
 589       *
 590       * @param string $mime_type
 591       * @return string|false
 592       */
 593  	protected static function get_extension( $mime_type = null ) {
 594          $extensions = explode( '|', array_search( $mime_type, wp_get_mime_types(), true ) );
 595  
 596          if ( empty( $extensions[0] ) ) {
 597              return false;
 598          }
 599  
 600          return $extensions[0];
 601      }
 602  }
 603  


Generated: Wed Aug 4 01:00:05 2021 Cross-referenced by PHPXref 0.7.1