WordPress.org

Make WordPress Core

Ticket #37140: 37140-3.patch

File 37140-3.patch, 12.8 KB (added by sanchothefat, 3 years ago)

Rewritten to only remove exif orientation data on user edit

  • src/wp-admin/includes/image-edit.php

        diff --git src/wp-admin/includes/image-edit.php src/wp-admin/includes/image-edit.php
    index db11b56..a998956 100644
    function wp_restore_image($post_id) { 
    643643                        if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) {
    644644
    645645                                // Delete only if it's an edited image.
    646                                 if ( preg_match('/-e[0-9]{13}\./', $parts['basename']) ) {
     646                                if ( wp_get_image_edit_hash( $parts['basename'] ) ) {
    647647                                        wp_delete_file( $file );
    648648                                }
    649649                        } elseif ( isset( $meta['width'], $meta['height'] ) ) {
    function wp_restore_image($post_id) { 
    666666                                if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) {
    667667
    668668                                        // Delete only if it's an edited image.
    669                                         if ( preg_match('/-e[0-9]{13}-/', $meta['sizes'][$default_size]['file']) ) {
     669                                        if ( wp_get_image_edit_hash( $meta['sizes'][$default_size]['file'] ) ) {
    670670                                                $delete_file = path_join( $parts['dirname'], $meta['sizes'][$default_size]['file'] );
    671671                                                wp_delete_file( $delete_file );
    672672                                        }
    function wp_save_image( $post_id ) { 
    837837         */
    838838        if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE && ! empty( $meta['sizes'] ) ) {
    839839                foreach ( $meta['sizes'] as $size ) {
    840                         if ( ! empty( $size['file'] ) && preg_match( '/-e[0-9]{13}-/', $size['file'] ) ) {
     840                        if ( ! empty( $size['file'] ) && wp_get_image_edit_hash( $size['file'] ) ) {
    841841                                $delete_file = path_join( $dirname, $size['file'] );
    842842                                wp_delete_file( $delete_file );
    843843                        }
  • src/wp-includes/class-wp-image-editor-imagick.php

     
    diff --git src/wp-includes/class-wp-image-editor-imagick.php src/wp-includes/class-wp-image-editor-imagick.php
    index fc6fc93..69e51db 100644
    class WP_Image_Editor_Imagick extends WP_Image_Editor { 
    628628                        $filename = $this->generate_filename( null, null, $extension );
    629629
    630630                try {
     631                        // Normalise exif orientation data to make it consistent across devices
     632                        if ( wp_get_image_edit_hash( $filename ) ) {
     633                                $this->image->setImageOrientation( Imagick::ORIENTATION_TOPLEFT );
     634                        }
     635
    631636                        // Store initial Format
    632637                        $orig_format = $this->image->getImageFormat();
    633638
  • src/wp-includes/media.php

    diff --git src/wp-includes/media.php src/wp-includes/media.php
    index ba52555..8ca68c4 100644
    function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac 
    11071107         * contain a unique hash. Look for that hash and use it later to filter
    11081108         * out images that are leftovers from previous versions.
    11091109         */
    1110         $image_edited = preg_match( '/-e[0-9]{13}/', wp_basename( $image_src ), $image_edit_hash );
     1110        $image_edit_hash = wp_get_image_edit_hash( wp_basename( $image_src ) );
    11111111
    11121112        /**
    11131113         * Filters the maximum image width to be included in a 'srcset' attribute.
    function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac 
    11471147                }
    11481148
    11491149                // Filter out images that are from previous edits.
    1150                 if ( $image_edited && ! strpos( $image['file'], $image_edit_hash[0] ) ) {
     1150                if ( $image_edit_hash && ! strpos( $image['file'], $image_edit_hash ) ) {
    11511151                        continue;
    11521152                }
    11531153
    function wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ) { 
    13771377        }
    13781378
    13791379        // Bail early if an image has been inserted and later edited.
    1380         if ( preg_match( '/-e[0-9]{13}/', $image_meta['file'], $img_edit_hash ) &&
    1381                 strpos( wp_basename( $image_src ), $img_edit_hash[0] ) === false ) {
    1382 
     1380        $img_edit_hash = wp_get_image_edit_hash( $image_meta['file'] );
     1381        if ( $img_edit_hash && strpos( wp_basename( $image_src ), $img_edit_hash ) === false ) {
    13831382                return $image;
    13841383        }
    13851384
    function wpview_media_sandbox_styles() { 
    38503849
    38513850        return array( $mediaelement, $wpmediaelement );
    38523851}
     3852
     3853/**
     3854 * Returns false or the edit hash if an image file has been edited in the media modal.
     3855 *
     3856 * @since 4.7.0
     3857 *
     3858 * @param string $filename
     3859 * @return string|false
     3860 */
     3861function wp_get_image_edit_hash( $filename ) {
     3862        $is_edited = preg_match( '/-(e[0-9]{13})[\.-]/', $filename, $image_edit_hash );
     3863        return $is_edited ? $image_edit_hash[1] : false;
     3864}
  • new file tests/phpunit/tests/image/orientation.php

    diff --git tests/phpunit/data/images/orientation-1.jpg tests/phpunit/data/images/orientation-1.jpg
    new file mode 100644
    index 0000000..015c5f1
    Binary files /dev/null and tests/phpunit/data/images/orientation-1.jpg differ
    diff --git tests/phpunit/data/images/orientation-2.jpg tests/phpunit/data/images/orientation-2.jpg
    new file mode 100644
    index 0000000..2e7fb57
    Binary files /dev/null and tests/phpunit/data/images/orientation-2.jpg differ
    diff --git tests/phpunit/data/images/orientation-3.jpg tests/phpunit/data/images/orientation-3.jpg
    new file mode 100644
    index 0000000..74505cf
    Binary files /dev/null and tests/phpunit/data/images/orientation-3.jpg differ
    diff --git tests/phpunit/data/images/orientation-4.jpg tests/phpunit/data/images/orientation-4.jpg
    new file mode 100644
    index 0000000..ea133fb
    Binary files /dev/null and tests/phpunit/data/images/orientation-4.jpg differ
    diff --git tests/phpunit/data/images/orientation-5.jpg tests/phpunit/data/images/orientation-5.jpg
    new file mode 100644
    index 0000000..81a8af6
    Binary files /dev/null and tests/phpunit/data/images/orientation-5.jpg differ
    diff --git tests/phpunit/data/images/orientation-6.jpg tests/phpunit/data/images/orientation-6.jpg
    new file mode 100644
    index 0000000..7426aba
    Binary files /dev/null and tests/phpunit/data/images/orientation-6.jpg differ
    diff --git tests/phpunit/data/images/orientation-7.jpg tests/phpunit/data/images/orientation-7.jpg
    new file mode 100644
    index 0000000..a541d95
    Binary files /dev/null and tests/phpunit/data/images/orientation-7.jpg differ
    diff --git tests/phpunit/data/images/orientation-8.jpg tests/phpunit/data/images/orientation-8.jpg
    new file mode 100644
    index 0000000..3f51d28
    Binary files /dev/null and tests/phpunit/data/images/orientation-8.jpg differ
    diff --git tests/phpunit/tests/image/orientation.php tests/phpunit/tests/image/orientation.php
    new file mode 100644
    index 0000000..a61a96d
    - +  
     1<?php
     2require_once dirname( __FILE__ ) . '/base.php';
     3
     4/**
     5 * Test the WP_Image_Editor base class
     6 *
     7 * @group image
     8 * @group media
     9 * @group orientation
     10 */
     11class Tests_Image_Editor_Orientation extends WP_Image_UnitTestCase {
     12
     13        public $editor_engine = 'WP_Image_Editor_Imagick';
     14
     15        public function setUp() {
     16                require_once( ABSPATH . WPINC . '/class-wp-image-editor.php' );
     17                require_once( ABSPATH . WPINC . '/class-wp-image-editor-imagick.php' );
     18
     19                parent::setUp();
     20        }
     21
     22        /**
     23         * Test removing orientation exif data on rotate
     24         *
     25         * @ticket       37140
     26         *
     27         * @dataProvider get_images
     28         */
     29        public function test_remove_orientation_data_on_rotate( $file ) {
     30
     31                preg_match( '/-(\d)\.(?:jpe?g|png|gif)$/', $file, $matches );
     32                $orientation_value = intval( $matches[1] );
     33
     34                // Make sure we trigger wp_is_edited_image()
     35                $file = $this->get_temp_edited_image( $file );
     36
     37                $data = wp_read_image_metadata( $file );
     38
     39                $this->assertEquals( $orientation_value, intval( $data['orientation'] ), 'Orientation value read from does not match image file exif data: ' . $file );
     40
     41                $temp_file = $this->get_temp_edited_image( $file, 'new' );
     42
     43                $image = wp_get_image_editor( $file );
     44                $image->rotate( 180 );
     45                $image->save( $temp_file );
     46
     47                $data = wp_read_image_metadata( $temp_file );
     48
     49                $this->assertEquals( 1, intval( $data['orientation'] ), 'Orientation exif data was not updated after rotating image: ' . $file );
     50
     51                unlink( $file );
     52                unlink( $temp_file );
     53        }
     54
     55        /**
     56         * Test removing orientation exif data on rotate
     57         *
     58         * @ticket       37140
     59         *
     60         * @dataProvider get_images
     61         */
     62        public function test_remove_orientation_data_on_flip( $file ) {
     63
     64                preg_match( '/-(\d)\.(?:jpe?g|png|gif)$/', $file, $matches );
     65                $orientation_value = intval( $matches[1] );
     66
     67                // Make sure we trigger wp_is_edited_image()
     68                $file = $this->get_temp_edited_image( $file );
     69
     70                $data = wp_read_image_metadata( $file );
     71
     72                $this->assertEquals( $orientation_value, intval( $data['orientation'] ), 'Orientation value read from does not match image file exif data: ' . $file );
     73
     74                $temp_file = $this->get_temp_edited_image( $file, 'new' );
     75
     76                $image = wp_get_image_editor( $file );
     77                $image->flip( true, true );
     78                $image->save( $temp_file );
     79
     80                $data = wp_read_image_metadata( $temp_file );
     81
     82                $this->assertEquals( 1, intval( $data['orientation'] ), 'Orientation exif data was not updated after flipping image: ' . $file );
     83
     84                unlink( $file );
     85                unlink( $temp_file );
     86        }
     87
     88        /**
     89         * Test removing orientation exif data on resize
     90         *
     91         * @ticket       37140
     92         *
     93         * @dataProvider get_images
     94         */
     95        public function test_remove_orientation_data_on_resize( $file ) {
     96
     97                preg_match( '/-(\d)\.(?:jpe?g|png|gif)$/', $file, $matches );
     98                $orientation_value = intval( $matches[1] );
     99
     100                // Make sure we trigger wp_is_edited_image()
     101                $file = $this->get_temp_edited_image( $file );
     102
     103                $data = wp_read_image_metadata( $file );
     104
     105                $this->assertEquals( $orientation_value, intval( $data['orientation'] ), 'Orientation value read from does not match image file exif data: ' . $file );
     106
     107                $temp_file = $this->get_temp_edited_image( $file, 'new' );
     108
     109                $image = wp_get_image_editor( $file );
     110                $image->resize( 100, 100, true );
     111                $image->save( $temp_file );
     112
     113                $data = wp_read_image_metadata( $temp_file );
     114
     115                $this->assertEquals( 1, intval( $data['orientation'] ), 'Orientation exif data was not updated after resizing image: ' . $file );
     116
     117                unlink( $file );
     118                unlink( $temp_file );
     119        }
     120
     121        /**
     122         * Test removing orientation exif data on crop
     123         *
     124         * @ticket       37140
     125         *
     126         * @dataProvider get_images
     127         */
     128        public function test_remove_orientation_data_on_crop( $file ) {
     129
     130                preg_match( '/-(\d)\.(?:jpe?g|png|gif)$/', $file, $matches );
     131                $orientation_value = intval( $matches[1] );
     132
     133                // Make sure we trigger wp_is_edited_image()
     134                $file = $this->get_temp_edited_image( $file );
     135
     136                $data = wp_read_image_metadata( $file );
     137
     138                $this->assertEquals( $orientation_value, intval( $data['orientation'] ), 'Orientation value read from does not match image file exif data: ' . $file );
     139
     140                $temp_file = $this->get_temp_edited_image( $file, 'new' );
     141
     142                $image = wp_get_image_editor( $file );
     143                $image->crop( 0, 0, 100, 100 );
     144                $image->save( $temp_file );
     145
     146                $data = wp_read_image_metadata( $temp_file );
     147
     148                $this->assertEquals( 1, intval( $data['orientation'] ), 'Orientation exif data was not updated after cropping image: ' . $file );
     149
     150                unlink( $file );
     151                unlink( $temp_file );
     152        }
     153
     154        /**
     155         * Test preserve orientation exif data on resize non edited image
     156         *
     157         * @ticket       37140
     158         *
     159         * @dataProvider get_images
     160         */
     161        public function test_preserve_orientation_data_on_resize( $file ) {
     162
     163                preg_match( '/-(\d)\.(?:jpe?g|png|gif)$/', $file, $matches );
     164                $orientation_value = intval( $matches[1] );
     165
     166                $data = wp_read_image_metadata( $file );
     167
     168                $this->assertEquals( $orientation_value, intval( $data['orientation'] ), 'Orientation value read from does not match image file exif data: ' . $file );
     169
     170                $temp_file = wp_tempnam( $file ) . '.jpg';
     171
     172                $image = wp_get_image_editor( $file );
     173                $image->resize( 100, 100, true );
     174                $image->save( $temp_file );
     175
     176                $data = wp_read_image_metadata( $temp_file );
     177
     178                $this->assertEquals( $orientation_value, intval( $data['orientation'] ), 'Orientation exif data was not preserved after resizing unedited image: ' . $file );
     179
     180                unlink( $temp_file );
     181        }
     182
     183        /**
     184         * Test rotating image on upload to normalise image orientation
     185         *
     186         * @ticket       14459
     187         *
     188         * @dataProvider get_images
     189         */
     190        public function test_rotate_on_upload( $file ) {
     191
     192                preg_match( '/-(\d)\.(?:jpe?g|png|gif)$/', $file, $matches );
     193                $orientation_value = intval( $matches[1] );
     194
     195                $image_id   = self::factory()->attachment->create_upload_object( $file );
     196                $image_file = get_attached_file( $image_id );
     197                $data       = wp_read_image_metadata( $image_file );
     198
     199                if ( in_array( $orientation_value, array( 3, 5, 8 ), true ) ) {
     200                        $this->assertEquals( 1, intval( $data['orientation'] ), 'Orientation exif data was not updated after uploading image: ' . $file );
     201                } else {
     202                        $this->assertEquals( $orientation_value, intval( $data['orientation'] ), 'Orientation exif data was incorrectly modified after uploading image: ' . $file );
     203                }
     204
     205        }
     206
     207        /**
     208         * dataProvider for images
     209         */
     210        public function get_images() {
     211                return array_map( function ( $num ) {
     212                        return array( DIR_TESTDATA . "/images/orientation-{$num}.jpg" );
     213                }, range( 1, 8 ) );
     214        }
     215
     216        /**
     217         * Make a temporary copy of the image with an edit hash.
     218         *
     219         * @param $file
     220         * @param $modifier
     221         * @return string
     222         */
     223        protected function get_temp_edited_image( $file, $modifier = '' ) {
     224                $tmp_file = wp_tempnam( basename( $file ) ) . '-e1234567890123.jpg';
     225                copy( $file, $tmp_file );
     226                return $tmp_file;
     227        }
     228
     229}