[wp-trac] [WordPress Trac] #14459: Rotate Full Size Images on Upload
WordPress Trac
noreply at wordpress.org
Sun Jul 28 20:32:35 UTC 2019
#14459: Rotate Full Size Images on Upload
-------------------------------------------------+-------------------------
Reporter: mrroundhill | Owner:
| mikeschroder
Type: enhancement | Status: reviewing
Priority: normal | Milestone: Future
| Release
Component: Media | Version: 3.0
Severity: normal | Resolution:
Keywords: has-patch needs-testing needs-unit- | Focuses:
tests |
-------------------------------------------------+-------------------------
Changes (by pbiron):
* keywords: has-patch => has-patch needs-testing needs-unit-tests
Comment:
Replying to [comment:34 n7studios]:
> Patch:
> https://gist.github.com/n7studios/6a764d46bc1d515ba406
>
> 3. I'm a bit dubious about line 140, as I think it'd potentially replace
any 3, 6 or 8 value with 1 (potentially breaking other EXIF metadata -
although in my tests, this didn't happen) - I struggled with hexadecimals:
> {{{#!php
> $exif_data = str_replace( chr( dechex( $original_orientation ) ) , chr(
0x1 ), $exif_data );
> }}}
[https://core.trac.wordpress.org/attachment/ticket/14459/14459.3.diff
14459.3.diff] incorporates much of the plugin in the gist by @n7studios
into core. First, props to him for providing a great foundation on which
to base this patch!
Here's a high-level overview of how the patch achieves what it does:
1. hooks into `wp_handle_upload` and rotates uplaoded JPEG images (with
the image editor classes) if necessary based on the EXIF orientation
a. the function hooked is in `/wp-includes/media.php`, the hook is
added in `/wp-includes/default-filters.php`
b. adding the hook there (as opposed to `/wp-admin/includes/admin-
filters.php` where I had originally tried) allows it to work in the block
editor, as well as the Media Library and classic editor
c. see TODO below for one quirk about support in the block editor
2. adds methods to `WP_Image_Editor_GD` that:
a. when a JPEG image is saved by the editor, any JPEG application
segments in the originaly uploaded image are copied to the saved image
(this is necessary because GD strips all application segments from JPEGs)
i. note that `WP_Image_Editor_Imagick` handles this automatically,
because Imagick preserves those segments
ii. currently, only EXIF (APP1), ICC Profile (APP2) and IPTC (APP13)
segments are copied, but I //think// the code is general enough that it
can easily be extended to copy any segments...I just don't have any images
that have other segments to test with, so I limited the implementation to
those 3. Copying the ICC Profile (APP2) segment is not part of the gist
from @n7studios.
iii. the JPEG application segments are copied from the original
image to **any** image the editor saves, e.g., itermediate sizes, crops,
etc.
b. when a JPEG image has been rotated by the editor (whether in the
function this patch hooks to `wp_handle_upload`, or when done
interactively a user), the EXIF orientation tag value is updated to be
"normal" (when the rotated image is saved)
i. note that `WP_Image_Editor_Imagick` handles this automatically,
thanks to [https://core.trac.wordpress.org/changeset/40123 [40123]]
ii. if the original image did not contain the EXIF orientation tag
then this step is not done (see Questions below)
iii. the way this is accomplished addresses the part that @n7studios
was "a bit dubious about" in his gist, by finding the offset into the
binary EXIF data where the orientation value is stored and altering that
value in an "EXIF-compliant" way (instead of the `str_replace()` approach
used in the gist.. which, yes, could adversely affect other EXIF data)
iv. I also did **not** include the code from the gist that checks
whether the image being saved has a particular JPEG application segment
and only copies JPEG segments from the original image if the image being
saved doesn't. Why? Because the whole point of 2a is that GD doesn't
save application segments so that code was unnecessary
== Questions
1. If the original image does **not** contain the EXIF orientation tag,
should we add it when images are rotated and saved? I don't think so, but
thought I'd ask
2. If an uploaded image has an EXIF orientation of anything other than
"normal" (e.g., 2, which means "flipped"), should we modify the image
accordingly on upload?
a. all the previous patches on this ticket and the gist from @n7studios
are limited to rotation
b. the scope of this ticket is just about rotation, but I think we
should definately consider flipping on upload as well (see
[https://core.trac.wordpress.org/attachment/ticket/37140/orientation-2.jpg
orientation-2.jpg] for an example)
c. if we flip on upload, then we should also set the EXIF orientation
to normal when `WP_Image_Editor_GD::flip()` is used
3. If the original image contains an EXIF user comment tag (0x9286), the
value of that tag is **not** preserved. Why? Because GD overwrites it
with a string like "CREATOR: gd-jpeg v1.0 (using IJG JPEG v90), quality =
82." Should we worry about that?
== TODOs
1. it might be good to add more inline documentation explaining how the
binary EXIF manipulation is done
a. there is some, but more would be better.
b. for those that don't know, the binary EXIF data is actually TIFF
data. Yeah, I know, it's weird that TIFF data is embedded within JPEG
data. Even weirder is that TIFFs can contain embedded JPEGs (which can
contain embedded TIFF data, etc) :-)
c. TIFF is pretty complicated so more explanation would probably be
helpful for future maintanence of the code
2. the code that finds the EXIF orientation tag in the binary EXIF data is
"technically" incomplete. It currently only looks in the first TIFF IFD
(Image File Directory) for the orientation tag. TIFF IFDs can contain
"pointers" to other IFDs elsewhere in the data (sometimes called "sub
IFDs"). All of the JPEG images I have to test with have the orientation
tag within the first IFD (and I don't think I've seen any images in the
real world that have it in a "sub IFD", but they do exist). So, I didn't
write the code to follow those pointers to sub IFDs...since I wouldn't
have any way to test it. So, if anyone has any JPEGs that store the EXIF
orientation tag in a sub IFD, please add a few of them as attachments to
this ticket and I'll update the patch accordingly
3. as mentioned above, there is a slight "quirk" when a JPEG is uploaded
using the block editor (as opposed to in the Media Library or classic
editor "Add Media"). If the image has an EXIF orientation tag that
indicates the image is rotated, the image momentarily displays in the
rotated orientation in the block editor but then quickly corrects itself.
I //think// this quirk is a result of how the block editor uses
`WP_REST_Attachments_Controller` and someone who knows that REST API
endpoint (and how the block editor uses it) better than I will have to fix
that
4. ICC Profiles (APP2 JPEG segment) can span more than 1 application
segment (since segments are limited to 64K bytes and some profiles can be
longer than that). I've never seen that in the real world, so I don't
know how it is accomplished (e.g., does the APP2 segment contain a pointer
to where the additional segment where the rest of the profile is stored?)
and haven't accounted for it in this patch. See Annex B.4 of
[http://www.color.org/specification/ICC1v43_2010-12.pdf Specification
ICC.1:2010]
5. write unit tests
--
Ticket URL: <https://core.trac.wordpress.org/ticket/14459#comment:71>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list