WordPress.org

Make WordPress Core

Ticket #14459: 14459.4.diff

File 14459.4.diff, 8.5 KB (added by azaozz, 7 days ago)
  • src/wp-admin/includes/image.php

     
    159159}
    160160
    161161/**
     162 * Updates the attached file and image meta data when the original image was edited.
     163 *
     164 * @since 5.3.0
     165 * @access private
     166 *
     167 * @param array  $saved_data    The data retirned from WP_Image_Editor after successfully saving an image.
     168 * @param string $original_file Path to the original file.
     169 * @param array  $image_meta    The image meta data.
     170 * @param int    $attachment_id The attachment post ID.
     171 * @return array The updated image meta data.
     172 */
     173function _wp_image_meta_replace_original( $saved_data, $original_file, $image_meta, $attachment_id ) {
     174        $new_file = $saved_data['path'];
     175
     176        // Update the attached file meta.
     177        update_attached_file( $attachment_id, $new_file );
     178
     179        // Width and height of the new image.
     180        $image_meta['width']  = $saved_data['width'];
     181        $image_meta['height'] = $saved_data['height'];
     182
     183        // Make the file path relative to the upload dir.
     184        $image_meta['file'] = _wp_relative_upload_path( $new_file );
     185
     186        // Store the original image file name in image_meta.
     187        $image_meta['original_image'] = wp_basename( $original_file );
     188       
     189        return $image_meta;
     190}
     191
     192/**
    162193 * Creates image sub-sizes, adds the new data to the image meta `sizes` array, and updates the image metadata.
    163194 *
    164195 * Intended for use after an image is uploaded. Saves/updates the image metadata after each
     
    223254                // Resize the image
    224255                $resized = $editor->resize( $threshold, $threshold );
    225256
     257                // Rotate JPEGs according to EXIF Orientation.
     258                if ( ! is_wp_error( $resized ) && ! empty( $exif_meta['orientation'] ) && (int) $exif_meta['orientation'] !== 1 ) {
     259                        $resized = $editor->maybe_exif_rotate();
     260                }
     261
    226262                if ( ! is_wp_error( $resized ) ) {
    227                         // TODO: EXIF rotate here.
    228                         // By default the editor will append `{width}x{height}` to the file name of the resized image.
    229                         // Better to append the threshold size instead so the image file name would be like "my-image-2560.jpg"
    230                         // and not look like a "regular" sub-size.
     263                        // Append the threshold size to the image file name. It would be like "my-image-2560.jpg".
    231264                        // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality).
    232265                        $saved = $editor->save( $editor->generate_filename( $threshold ) );
    233266
    234267                        if ( ! is_wp_error( $saved ) ) {
    235                                 $new_file = $saved['path'];
     268                                $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
     269                        } else {
     270                                // TODO: handle errors.
     271                        }
     272                } else {
     273                        // TODO: handle errors.
     274                }
     275        } elseif ( ! empty( $exif_meta['orientation'] ) && (int) $exif_meta['orientation'] !== 1 ) {
     276                $editor = wp_get_image_editor( $file );
    236277
    237                                 // Update the attached file meta.
    238                                 update_attached_file( $attachment_id, $new_file );
     278                if ( is_wp_error( $editor ) ) {
     279                        // This image cannot be edited.
     280                        return $image_meta;
     281                }
    239282
    240                                 // Width and height of the new image.
    241                                 $image_meta['width']  = $saved['width'];
    242                                 $image_meta['height'] = $saved['height'];
     283                // Rotate the image
     284                $rotated = $editor->maybe_exif_rotate();
    243285
    244                                 // Make the file path relative to the upload dir.
    245                                 $image_meta['file'] = _wp_relative_upload_path( $new_file );
     286                if ( true === $rotated ) {
     287                        // Append `-rotated` to the image file name. It would be like "my-image-rotated.jpg".
     288                        $saved = $editor->save( $editor->generate_filename( 'rotated' ) );
    246289
    247                                 // Store the original image file name in image_meta.
    248                                 $image_meta['original_image'] = wp_basename( $file );
     290                        if ( ! is_wp_error( $saved ) ) {
     291                                $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
     292                        } else {
     293                                // TODO: handle errors.
    249294                        }
    250295                }
    251296        }
     
    327372                return $image_meta;
    328373        }
    329374
     375        // If JPEG and EXIF data exists, rotate the source image before creating sub-sizes.
     376        if ( ! empty( $image_meta['image_meta']['orientation'] ) && (int) $image_meta['image_meta']['orientation'] !== 1 ) {
     377                $rotated = $editor->maybe_exif_rotate();
     378
     379                if ( is_wp_error( $rotated ) ) {
     380                        // TODO: handle errors.
     381                }
     382        }
     383
    330384        if ( method_exists( $editor, 'make_subsize' ) ) {
    331385                foreach ( $new_sizes as $new_size_name => $new_size_data ) {
    332386                        $new_size_meta = $editor->make_subsize( $new_size_data );
  • src/wp-includes/class-wp-image-editor-imagick.php

     
    566566                try {
    567567                        $this->image->rotateImage( new ImagickPixel( 'none' ), 360 - $angle );
    568568
    569                         // Normalise Exif orientation data so that display is consistent across devices.
     569                        // Normalise EXIF orientation data so that display is consistent across devices.
    570570                        if ( is_callable( array( $this->image, 'setImageOrientation' ) ) && defined( 'Imagick::ORIENTATION_TOPLEFT' ) ) {
    571571                                $this->image->setImageOrientation( Imagick::ORIENTATION_TOPLEFT );
    572572                        }
     
    602602                        if ( $vert ) {
    603603                                $this->image->flopImage();
    604604                        }
     605
     606                        // Normalise EXIF orientation data so that display is consistent across devices.
     607                        if ( is_callable( array( $this->image, 'setImageOrientation' ) ) && defined( 'Imagick::ORIENTATION_TOPLEFT' ) ) {
     608                                $this->image->setImageOrientation( Imagick::ORIENTATION_TOPLEFT );
     609                        }
    605610                } catch ( Exception $e ) {
    606611                        return new WP_Error( 'image_flip_error', $e->getMessage() );
    607612                }
     613
    608614                return true;
    609615        }
    610616
    611617        /**
     618         * Check if a JPEG image has EXIF Orientation tag and rotate it if needed.
     619         *
     620         * As Imagick copies the EXIF data to the flipped/rotated image, proceed only
     621         * if the EXIF Orientation tag can be reset afterwards.
     622         *
     623         * @since 5.3.0
     624         *
     625         * @return bool|WP_Error True if the image was rotated. False if no EXIF data or if the image doesn't need rotation.
     626         *                       WP_Error if error while rotating.
     627         */
     628        public function maybe_exif_rotate() {
     629                if ( is_callable( array( $this->image, 'setImageOrientation' ) ) && defined( 'Imagick::ORIENTATION_TOPLEFT' ) ) {
     630                        return parent::maybe_exif_rotate();
     631                } else {
     632                        return new WP_Error( 'write_exif_error', __( 'The image cannot be rotated because the embedded meta data cannot be updated.' ) );
     633                }
     634        }
     635
     636        /**
    612637         * Saves current image to file.
    613638         *
    614639         * @since 3.5.0
  • src/wp-includes/class-wp-image-editor.php

     
    385385        }
    386386
    387387        /**
     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 no EXIF data or if the image doesn't need rotation.
     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 = @exif_read_data( $this->file );
     400
     401                        if ( ! empty( $exif['Orientation'] ) ) {
     402                                $orientation = (int) $exif['Orientation'];
     403                        }
     404                }
     405
     406                if ( ! $orientation || $orientation === 1 ) {
     407                        return false;
     408                }
     409
     410                switch ( $orientation ) {
     411                        case 2:
     412                                // Flip horizontally.
     413                                $result = $this->flip( true, false );
     414                                break;
     415                        case 3:
     416                                // Rotate 180 degrees or flip horizontally and vertically.
     417                                // Flipping seems to be faster/use less resources.
     418                                $result = $this->flip( true, true );
     419                                break;
     420                        case 4:
     421                                // Flip vertically.
     422                                $result = $this->flip( false, true );
     423                                break;
     424                        case 5:
     425                                // Rotate 90 degrees counter-clockwise and flip vertically.
     426                                $result = $this->rotate( 90 );
     427
     428                                if ( ! is_wp_error( $result ) ) {
     429                                        $result = $this->flip( false, true );
     430                                }
     431
     432                                break;
     433                        case 6:
     434                                // Rotate 90 degrees clockwise (270 counter-clockwise).
     435                                $result = $this->rotate( 270 );
     436                                break;
     437                        case 7:
     438                                // Rotate 90 degrees counter-clockwise and flip horizontally.
     439                                $result = $this->rotate( 90 );
     440
     441                                if ( ! is_wp_error( $result ) ) {
     442                                        $result = $this->flip( true, false );
     443                                }
     444
     445                                break;
     446                        case 8:
     447                                // Rotate 90 degrees counter-clockwise.
     448                                $result = $this->rotate( 90 );
     449                                break;
     450                }
     451
     452                return $result;
     453        }
     454
     455        /**
    388456         * Either calls editor's save function or handles file as a stream.
    389457         *
    390458         * @since 3.5.0