[ 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 sub-class.
  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 sub-class.
  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 {'width'=>int, 'height'=>int}
 176       */
 177  	public function get_size() {
 178          return $this->size;
 179      }
 180  
 181      /**
 182       * Sets current image size.
 183       *
 184       * @since 3.5.0
 185       *
 186       * @param int $width
 187       * @param int $height
 188       * @return true
 189       */
 190  	protected function update_size( $width = null, $height = null ) {
 191          $this->size = array(
 192              'width'  => (int) $width,
 193              'height' => (int) $height,
 194          );
 195          return true;
 196      }
 197  
 198      /**
 199       * Gets the Image Compression quality on a 1-100% scale.
 200       *
 201       * @since 4.0.0
 202       *
 203       * @return int $quality Compression Quality. Range: [1,100]
 204       */
 205  	public function get_quality() {
 206          if ( ! $this->quality ) {
 207              $this->set_quality();
 208          }
 209  
 210          return $this->quality;
 211      }
 212  
 213      /**
 214       * Sets Image Compression quality on a 1-100% scale.
 215       *
 216       * @since 3.5.0
 217       *
 218       * @param int $quality Compression Quality. Range: [1,100]
 219       * @return true|WP_Error True if set successfully; WP_Error on failure.
 220       */
 221  	public function set_quality( $quality = null ) {
 222          if ( null === $quality ) {
 223              /**
 224               * Filters the default image compression quality setting.
 225               *
 226               * Applies only during initial editor instantiation, or when set_quality() is run
 227               * manually without the `$quality` argument.
 228               *
 229               * set_quality() has priority over the filter.
 230               *
 231               * @since 3.5.0
 232               *
 233               * @param int    $quality   Quality level between 1 (low) and 100 (high).
 234               * @param string $mime_type Image mime type.
 235               */
 236              $quality = apply_filters( 'wp_editor_set_quality', $this->default_quality, $this->mime_type );
 237  
 238              if ( 'image/jpeg' == $this->mime_type ) {
 239                  /**
 240                   * Filters the JPEG compression quality for backward-compatibility.
 241                   *
 242                   * Applies only during initial editor instantiation, or when set_quality() is run
 243                   * manually without the `$quality` argument.
 244                   *
 245                   * set_quality() has priority over the filter.
 246                   *
 247                   * The filter is evaluated under two contexts: 'image_resize', and 'edit_image',
 248                   * (when a JPEG image is saved to file).
 249                   *
 250                   * @since 2.5.0
 251                   *
 252                   * @param int    $quality Quality level between 0 (low) and 100 (high) of the JPEG.
 253                   * @param string $context Context of the filter.
 254                   */
 255                  $quality = apply_filters( 'jpeg_quality', $quality, 'image_resize' );
 256              }
 257  
 258              if ( $quality < 0 || $quality > 100 ) {
 259                  $quality = $this->default_quality;
 260              }
 261          }
 262  
 263          // Allow 0, but squash to 1 due to identical images in GD, and for backward compatibility.
 264          if ( 0 === $quality ) {
 265              $quality = 1;
 266          }
 267  
 268          if ( ( $quality >= 1 ) && ( $quality <= 100 ) ) {
 269              $this->quality = $quality;
 270              return true;
 271          } else {
 272              return new WP_Error( 'invalid_image_quality', __( 'Attempted to set image quality outside of the range [1,100].' ) );
 273          }
 274      }
 275  
 276      /**
 277       * Returns preferred mime-type and extension based on provided
 278       * file's extension and mime, or current file's extension and mime.
 279       *
 280       * Will default to $this->default_mime_type if requested is not supported.
 281       *
 282       * Provides corrected filename only if filename is provided.
 283       *
 284       * @since 3.5.0
 285       *
 286       * @param string $filename
 287       * @param string $mime_type
 288       * @return array { filename|null, extension, mime-type }
 289       */
 290  	protected function get_output_format( $filename = null, $mime_type = null ) {
 291          $new_ext = null;
 292  
 293          // By default, assume specified type takes priority
 294          if ( $mime_type ) {
 295              $new_ext = $this->get_extension( $mime_type );
 296          }
 297  
 298          if ( $filename ) {
 299              $file_ext  = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
 300              $file_mime = $this->get_mime_type( $file_ext );
 301          } else {
 302              // If no file specified, grab editor's current extension and mime-type.
 303              $file_ext  = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) );
 304              $file_mime = $this->mime_type;
 305          }
 306  
 307          // Check to see if specified mime-type is the same as type implied by
 308          // file extension.  If so, prefer extension from file.
 309          if ( ! $mime_type || ( $file_mime == $mime_type ) ) {
 310              $mime_type = $file_mime;
 311              $new_ext   = $file_ext;
 312          }
 313  
 314          // Double-check that the mime-type selected is supported by the editor.
 315          // If not, choose a default instead.
 316          if ( ! $this->supports_mime_type( $mime_type ) ) {
 317              /**
 318               * Filters default mime type prior to getting the file extension.
 319               *
 320               * @see wp_get_mime_types()
 321               *
 322               * @since 3.5.0
 323               *
 324               * @param string $mime_type Mime type string.
 325               */
 326              $mime_type = apply_filters( 'image_editor_default_mime_type', $this->default_mime_type );
 327              $new_ext   = $this->get_extension( $mime_type );
 328          }
 329  
 330          if ( $filename ) {
 331              $dir = pathinfo( $filename, PATHINFO_DIRNAME );
 332              $ext = pathinfo( $filename, PATHINFO_EXTENSION );
 333  
 334              $filename = trailingslashit( $dir ) . wp_basename( $filename, ".$ext" ) . ".{$new_ext}";
 335          }
 336  
 337          return array( $filename, $new_ext, $mime_type );
 338      }
 339  
 340      /**
 341       * Builds an output filename based on current file, and adding proper suffix
 342       *
 343       * @since 3.5.0
 344       *
 345       * @param string $suffix
 346       * @param string $dest_path
 347       * @param string $extension
 348       * @return string filename
 349       */
 350  	public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) {
 351          // $suffix will be appended to the destination filename, just before the extension
 352          if ( ! $suffix ) {
 353              $suffix = $this->get_suffix();
 354          }
 355  
 356          $dir = pathinfo( $this->file, PATHINFO_DIRNAME );
 357          $ext = pathinfo( $this->file, PATHINFO_EXTENSION );
 358  
 359          $name    = wp_basename( $this->file, ".$ext" );
 360          $new_ext = strtolower( $extension ? $extension : $ext );
 361  
 362          if ( ! is_null( $dest_path ) ) {
 363              $_dest_path = realpath( $dest_path );
 364              if ( $_dest_path ) {
 365                  $dir = $_dest_path;
 366              }
 367          }
 368  
 369          return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}";
 370      }
 371  
 372      /**
 373       * Builds and returns proper suffix for file based on height and width.
 374       *
 375       * @since 3.5.0
 376       *
 377       * @return false|string suffix
 378       */
 379  	public function get_suffix() {
 380          if ( ! $this->get_size() ) {
 381              return false;
 382          }
 383  
 384          return "{$this->size['width']}x{$this->size['height']}";
 385      }
 386  
 387      /**
 388       * Check if a JPEG image has EXIF Orientation tag and rotate it if needed.
 389       *
 390       * @since 5.3.0
 391       *
 392       * @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).
 393       *                       WP_Error if error while rotating.
 394       */
 395  	public function maybe_exif_rotate() {
 396          $orientation = null;
 397  
 398          if ( is_callable( 'exif_read_data' ) && 'image/jpeg' === $this->mime_type ) {
 399              $exif_data = @exif_read_data( $this->file );
 400  
 401              if ( ! empty( $exif_data['Orientation'] ) ) {
 402                  $orientation = (int) $exif_data['Orientation'];
 403              }
 404          }
 405  
 406          /**
 407           * Filters the `$orientation` value to correct it before rotating or to prevemnt rotating the image.
 408           *
 409           * @since 5.3.0
 410           *
 411           * @param int    $orientation EXIF Orientation value as retrieved from the image file.
 412           * @param string $file        Path to the image file.
 413           */
 414          $orientation = apply_filters( 'wp_image_maybe_exif_rotate', $orientation, $this->file );
 415  
 416          if ( ! $orientation || $orientation === 1 ) {
 417              return false;
 418          }
 419  
 420          switch ( $orientation ) {
 421              case 2:
 422                  // Flip horizontally.
 423                  $result = $this->flip( true, false );
 424                  break;
 425              case 3:
 426                  // Rotate 180 degrees or flip horizontally and vertically.
 427                  // Flipping seems faster/uses less resources.
 428                  $result = $this->flip( true, true );
 429                  break;
 430              case 4:
 431                  // Flip vertically.
 432                  $result = $this->flip( false, true );
 433                  break;
 434              case 5:
 435                  // Rotate 90 degrees counter-clockwise and flip vertically.
 436                  $result = $this->rotate( 90 );
 437  
 438                  if ( ! is_wp_error( $result ) ) {
 439                      $result = $this->flip( false, true );
 440                  }
 441  
 442                  break;
 443              case 6:
 444                  // Rotate 90 degrees clockwise (270 counter-clockwise).
 445                  $result = $this->rotate( 270 );
 446                  break;
 447              case 7:
 448                  // Rotate 90 degrees counter-clockwise and flip horizontally.
 449                  $result = $this->rotate( 90 );
 450  
 451                  if ( ! is_wp_error( $result ) ) {
 452                      $result = $this->flip( true, false );
 453                  }
 454  
 455                  break;
 456              case 8:
 457                  // Rotate 90 degrees counter-clockwise.
 458                  $result = $this->rotate( 90 );
 459                  break;
 460          }
 461  
 462          return $result;
 463      }
 464  
 465      /**
 466       * Either calls editor's save function or handles file as a stream.
 467       *
 468       * @since 3.5.0
 469       *
 470       * @param string|stream $filename
 471       * @param callable $function
 472       * @param array $arguments
 473       * @return bool
 474       */
 475  	protected function make_image( $filename, $function, $arguments ) {
 476          $stream = wp_is_stream( $filename );
 477          if ( $stream ) {
 478              ob_start();
 479          } else {
 480              // The directory containing the original file may no longer exist when using a replication plugin.
 481              wp_mkdir_p( dirname( $filename ) );
 482          }
 483  
 484          $result = call_user_func_array( $function, $arguments );
 485  
 486          if ( $result && $stream ) {
 487              $contents = ob_get_contents();
 488  
 489              $fp = fopen( $filename, 'w' );
 490  
 491              if ( ! $fp ) {
 492                  ob_end_clean();
 493                  return false;
 494              }
 495  
 496              fwrite( $fp, $contents );
 497              fclose( $fp );
 498          }
 499  
 500          if ( $stream ) {
 501              ob_end_clean();
 502          }
 503  
 504          return $result;
 505      }
 506  
 507      /**
 508       * Returns first matched mime-type from extension,
 509       * as mapped from wp_get_mime_types()
 510       *
 511       * @since 3.5.0
 512       *
 513       * @param string $extension
 514       * @return string|false
 515       */
 516  	protected static function get_mime_type( $extension = null ) {
 517          if ( ! $extension ) {
 518              return false;
 519          }
 520  
 521          $mime_types = wp_get_mime_types();
 522          $extensions = array_keys( $mime_types );
 523  
 524          foreach ( $extensions as $_extension ) {
 525              if ( preg_match( "/{$extension}/i", $_extension ) ) {
 526                  return $mime_types[ $_extension ];
 527              }
 528          }
 529  
 530          return false;
 531      }
 532  
 533      /**
 534       * Returns first matched extension from Mime-type,
 535       * as mapped from wp_get_mime_types()
 536       *
 537       * @since 3.5.0
 538       *
 539       * @param string $mime_type
 540       * @return string|false
 541       */
 542  	protected static function get_extension( $mime_type = null ) {
 543          $extensions = explode( '|', array_search( $mime_type, wp_get_mime_types() ) );
 544  
 545          if ( empty( $extensions[0] ) ) {
 546              return false;
 547          }
 548  
 549          return $extensions[0];
 550      }
 551  }
 552  


Generated: Sun Sep 22 01:00:03 2019 Cross-referenced by PHPXref 0.7.1