<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[58849] trunk: Media: Automatically convert HEIC images to JPEG</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/58849">58849</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/58849","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>noisysocks</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2024-08-05 04:11:40 +0000 (Mon, 05 Aug 2024)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Media: Automatically convert HEIC images to JPEG

Automatically create a JPEG version of uploaded HEIC images if the server has
a version of Imagick that supports HEIC. Conversion is done silently through
the existing `WP_Image_Editor` infrastructure that creates multiple sizes of
uploaded images.

This allows users to view HEIC images in WP Admin and use them in their posts
and pages regardless of whether their browser supports HEIC. Browser support
for HEIC is relatively low (only Safari) while the occurrence of HEIC images is
relatively common. The original HEIC image can be downloaded via a link on
the attachment page.

Props adamsilverstein, noisysocks, swissspidy, spacedmonkey, peterwilsoncc.
Fixes <a href="https://core.trac.wordpress.org/ticket/53645">#53645</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcjsmediacontrollerslibraryjs">trunk/src/js/media/controllers/library.js</a></li>
<li><a href="#trunksrcwpadminincludesimagephp">trunk/src/wp-admin/includes/image.php</a></li>
<li><a href="#trunksrcwpincludesclasswpimageeditorimagickphp">trunk/src/wp-includes/class-wp-image-editor-imagick.php</a></li>
<li><a href="#trunksrcwpincludesclasswpimageeditorphp">trunk/src/wp-includes/class-wp-image-editor.php</a></li>
<li><a href="#trunksrcwpincludescompatphp">trunk/src/wp-includes/compat.php</a></li>
<li><a href="#trunksrcwpincludesfunctionsphp">trunk/src/wp-includes/functions.php</a></li>
<li><a href="#trunksrcwpincludesmediaphp">trunk/src/wp-includes/media.php</a></li>
<li><a href="#trunksrcwpincludespostphp">trunk/src/wp-includes/post.php</a></li>
<li><a href="#trunksrcwpincludesrestapiendpointsclasswprestattachmentscontrollerphp">trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php</a></li>
<li><a href="#trunktestsphpunittestsfunctionsphp">trunk/tests/phpunit/tests/functions.php</a></li>
<li><a href="#trunktestsphpunittestsimagefunctionsphp">trunk/tests/phpunit/tests/image/functions.php</a></li>
<li><a href="#trunktestsphpunittestsimageresizephp">trunk/tests/phpunit/tests/image/resize.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsphpunitdataimagestestimageheic">trunk/tests/phpunit/data/images/test-image.heic</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcjsmediacontrollerslibraryjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/js/media/controllers/library.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/js/media/controllers/library.js 2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/src/js/media/controllers/library.js   2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -196,7 +196,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        isImageAttachment: function( attachment ) {
</span><span class="cx" style="display: block; padding: 0 10px">                // If uploading, we know the filename but not the mime type.
</span><span class="cx" style="display: block; padding: 0 10px">                if ( attachment.get('uploading') ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        return /\.(jpe?g|png|gif|webp|avif)$/i.test( attachment.get('filename') );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 return /\.(jpe?g|png|gif|webp|avif|heic)$/i.test( attachment.get('filename') );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                return attachment.get('type') === 'image';
</span></span></pre></div>
<a id="trunksrcwpadminincludesimagephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/image.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/image.php     2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/src/wp-admin/includes/image.php       2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -543,6 +543,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 2.1.0
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 6.0.0 The `$filesize` value was added to the returned array.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 6.7.0 The 'image/heic' mime type is supported.
</ins><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @param int    $attachment_id Attachment ID to process.
</span><span class="cx" style="display: block; padding: 0 10px">  * @param string $file          Filepath of the attached image.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -555,7 +556,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        $support   = false;
</span><span class="cx" style="display: block; padding: 0 10px">        $mime_type = get_post_mime_type( $attachment );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        if ( preg_match( '!^image/!', $mime_type ) && file_is_displayable_image( $file ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( 'image/heic' === $mime_type || ( preg_match( '!^image/!', $mime_type ) && file_is_displayable_image( $file ) ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Make thumbnails and other intermediate sizes.
</span><span class="cx" style="display: block; padding: 0 10px">                $metadata = wp_create_image_subsizes( $file, $attachment_id );
</span><span class="cx" style="display: block; padding: 0 10px">        } elseif ( wp_attachment_is( 'video', $attachment ) ) {
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpimageeditorimagickphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-image-editor-imagick.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-image-editor-imagick.php   2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/src/wp-includes/class-wp-image-editor-imagick.php     2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -219,7 +219,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                $this->image->setImageCompressionQuality( $quality );
</span><span class="cx" style="display: block; padding: 0 10px">                                        }
</span><span class="cx" style="display: block; padding: 0 10px">                                        break;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                case 'image/avif':
</del><span class="cx" style="display: block; padding: 0 10px">                                 default:
</span><span class="cx" style="display: block; padding: 0 10px">                                        $this->image->setImageCompressionQuality( $quality );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -258,10 +257,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /*
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * If we still don't have the image size, fall back to `wp_getimagesize`. This ensures AVIF images
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+          * If we still don't have the image size, fall back to `wp_getimagesize`. This ensures AVIF and HEIC images
</ins><span class="cx" style="display: block; padding: 0 10px">                  * are properly sized without affecting previous `getImageGeometry` behavior.
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( ( ! $width || ! $height ) && 'image/avif' === $this->mime_type ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( ( ! $width || ! $height ) && ( 'image/avif' === $this->mime_type || 'image/heic' === $this->mime_type ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         $size   = wp_getimagesize( $this->file );
</span><span class="cx" style="display: block; padding: 0 10px">                        $width  = $size[0];
</span><span class="cx" style="display: block; padding: 0 10px">                        $height = $size[1];
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpimageeditorphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-image-editor.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-image-editor.php   2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/src/wp-includes/class-wp-image-editor.php     2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -318,7 +318,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                $quality = 86;
</span><span class="cx" style="display: block; padding: 0 10px">                                break;
</span><span class="cx" style="display: block; padding: 0 10px">                        case 'image/jpeg':
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        case 'image/avif':
</del><span class="cx" style="display: block; padding: 0 10px">                         default:
</span><span class="cx" style="display: block; padding: 0 10px">                                $quality = $this->default_quality;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -366,26 +365,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $new_ext   = $file_ext;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                /**
-                * Filters the image editor output format mapping.
-                *
-                * Enables filtering the mime type used to save images. By default,
-                * the mapping array is empty, so the mime type matches the source image.
-                *
-                * @see WP_Image_Editor::get_output_format()
-                *
-                * @since 5.8.0
-                *
-                * @param string[] $output_format {
-                *     An array of mime type mappings. Maps a source mime type to a new
-                *     destination mime type. Default empty array.
-                *
-                *     @type string ...$0 The new mime type.
-                * }
-                * @param string $filename  Path to the image.
-                * @param string $mime_type The source image mime type.
-                */
-               $output_format = apply_filters( 'image_editor_output_format', array(), $filename, $mime_type );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $output_format = wp_get_image_editor_output_format( $filename, $mime_type );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( isset( $output_format[ $mime_type ] )
</span><span class="cx" style="display: block; padding: 0 10px">                        && $this->supports_mime_type( $output_format[ $mime_type ] )
</span></span></pre></div>
<a id="trunksrcwpincludescompatphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/compat.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/compat.php  2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/src/wp-includes/compat.php    2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -549,3 +549,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> if ( ! defined( 'IMG_AVIF' ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">        define( 'IMG_AVIF', IMAGETYPE_AVIF );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+// IMAGETYPE_HEIC constant is not yet defined in PHP as of PHP 8.3.
+if ( ! defined( 'IMAGETYPE_HEIC' ) ) {
+       define( 'IMAGETYPE_HEIC', 99 );
+}
</ins></span></pre></div>
<a id="trunksrcwpincludesfunctionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/functions.php       2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/src/wp-includes/functions.php 2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2706,8 +2706,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * when regenerated. If yes, ensure the new file name will be unique and will produce unique sub-sizes.
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $is_image ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        /** This filter is documented in wp-includes/class-wp-image-editor.php */
-                       $output_formats = apply_filters( 'image_editor_output_format', array(), $_dir . $filename, $mime_type );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $output_formats = wp_get_image_editor_output_format( $_dir . $filename, $mime_type );
</ins><span class="cx" style="display: block; padding: 0 10px">                         $alt_types      = array();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! empty( $output_formats[ $mime_type ] ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3120,6 +3119,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        'image/tiff' => 'tif',
</span><span class="cx" style="display: block; padding: 0 10px">                                        'image/webp' => 'webp',
</span><span class="cx" style="display: block; padding: 0 10px">                                        'image/avif' => 'avif',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        'image/heic' => 'heic',
</ins><span class="cx" style="display: block; padding: 0 10px">                                 )
</span><span class="cx" style="display: block; padding: 0 10px">                        );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3299,6 +3299,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 4.7.1
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 5.8.0 Added support for WebP images.
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 6.5.0 Added support for AVIF images.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 6.7.0 Added support for HEIC images.
</ins><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @param string $file Full path to the file.
</span><span class="cx" style="display: block; padding: 0 10px">  * @return string|false The actual mime type or false if the type cannot be determined.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3372,6 +3373,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $mime = 'image/avif';
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               if (
+                       isset( $magic[1] ) &&
+                       isset( $magic[2] ) &&
+                       'ftyp' === hex2bin( $magic[1] ) &&
+                       ( 'heic' === hex2bin( $magic[2] ) || 'heif' === hex2bin( $magic[2] ) )
+               ) {
+                       $mime = 'image/heic';
+               }
</ins><span class="cx" style="display: block; padding: 0 10px">         } catch ( Exception $e ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $mime = false;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span></span></pre></div>
<a id="trunksrcwpincludesmediaphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/media.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/media.php   2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/src/wp-includes/media.php     2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4064,8 +4064,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        // Check and set the output mime type mapped to the input type.
</span><span class="cx" style="display: block; padding: 0 10px">        if ( isset( $args['mime_type'] ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                /** This filter is documented in wp-includes/class-wp-image-editor.php */
-               $output_format = apply_filters( 'image_editor_output_format', array(), $path, $args['mime_type'] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $output_format = wp_get_image_editor_output_format( $path, $args['mime_type'] );
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( isset( $output_format[ $args['mime_type'] ] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $args['output_mime_type'] = $output_format[ $args['mime_type'] ];
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4224,6 +4223,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $defaults['avif_upload_error'] = true;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        // Check if HEIC images can be edited.
+       if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/heic' ) ) ) {
+               $defaults['heic_upload_error'] = true;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Filters the Plupload default settings.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5483,12 +5487,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">  * Callback to enable showing of the user error when uploading .heic images.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @since 5.5.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 6.7.0 The default behavior is to enable heic uplooads as long as the server
+ *              supports the format. The uploads are converted to JPEG's by default.
</ins><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * @param array[] $plupload_settings The settings for Plupload.js.
</span><span class="cx" style="display: block; padding: 0 10px">  * @return array[] Modified settings for Plupload.js.
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function wp_show_heic_upload_error( $plupload_settings ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        $plupload_settings['heic_upload_error'] = true;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Check if HEIC images can be edited.
+       if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/heic' ) ) ) {
+               $plupload_init['heic_upload_error'] = true;
+       }
</ins><span class="cx" style="display: block; padding: 0 10px">         return $plupload_settings;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5586,6 +5595,29 @@
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        // For PHP versions that don't support HEIC images, extract the size info using Imagick when available.
+       if ( 'image/heic' === wp_get_image_mime( $filename ) ) {
+               $editor = wp_get_image_editor( $filename );
+               if ( is_wp_error( $editor ) ) {
+                       return false;
+               }
+               // If the editor for HEICs is Imagick, use it to get the image size.
+               if ( $editor instanceof WP_Image_Editor_Imagick ) {
+                       $size = $editor->get_size();
+                       return array(
+                               $size['width'],
+                               $size['height'],
+                               IMAGETYPE_HEIC,
+                               sprintf(
+                                       'width="%d" height="%d"',
+                                       $size['width'],
+                                       $size['height']
+                               ),
+                               'mime' => 'image/heic',
+                       );
+               }
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         // The image could not be parsed.
</span><span class="cx" style="display: block; padding: 0 10px">        return false;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6069,3 +6101,37 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        return $high_priority_element;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+/**
+ * Determines the output format for the image editor.
+ *
+ * @since 6.7.0
+ * @access private
+ *
+ * @param string $filename  Path to the image.
+ * @param string $mime_type The source image mime type.
+ * @return string[] An array of mime type mappings.
+ */
+function wp_get_image_editor_output_format( $filename, $mime_type ) {
+       /**
+        * Filters the image editor output format mapping.
+        *
+        * Enables filtering the mime type used to save images. By default,
+        * the mapping array is empty, so the mime type matches the source image.
+        *
+        * @see WP_Image_Editor::get_output_format()
+        *
+        * @since 5.8.0
+        * @since 6.7.0 The default was changed from array() to array( 'image/heic' => 'image/jpeg' ).
+        *
+        * @param string[] $output_format {
+        *     An array of mime type mappings. Maps a source mime type to a new
+        *     destination mime type. Default maps uploaded HEIC images to JPEG output.
+        *
+        *     @type string ...$0 The new mime type.
+        * }
+        * @param string $filename  Path to the image.
+        * @param string $mime_type The source image mime type.
+        */
+       return apply_filters( 'image_editor_output_format', array( 'image/heic' => 'image/jpeg' ), $filename, $mime_type );
+}
</ins></span></pre></div>
<a id="trunksrcwpincludespostphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/post.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/post.php    2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/src/wp-includes/post.php      2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6829,7 +6829,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        switch ( $type ) {
</span><span class="cx" style="display: block; padding: 0 10px">                case 'image':
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'webp', 'avif' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'webp', 'avif', 'heic' );
</ins><span class="cx" style="display: block; padding: 0 10px">                         return in_array( $ext, $image_exts, true );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                case 'audio':
</span></span></pre></div>
<a id="trunksrcwpincludesrestapiendpointsclasswprestattachmentscontrollerphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php 2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php   2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -531,7 +531,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $supported_types = array( 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $supported_types = array( 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif', 'image/heic' );
</ins><span class="cx" style="display: block; padding: 0 10px">                 $mime_type       = get_post_mime_type( $attachment_id );
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! in_array( $mime_type, $supported_types, true ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        return new WP_Error(
</span></span></pre></div>
<a id="trunktestsphpunitdataimagestestimageheic"></a>
<div class="binary"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/data/images/test-image.heic</h4>
<pre class="diff"><span>
<span class="cx">(Binary files differ)
</span></span></pre></div>
<span class="cx" style="display: block; padding: 0 10px">Index: trunk/tests/phpunit/data/images/test-image.heic
</span><span class="cx" style="display: block; padding: 0 10px">===================================================================
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">--- trunk/tests/phpunit/data/images/test-image.heic      2024-08-04 22:07:06 UTC (rev 58848)
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+++ trunk/tests/phpunit/data/images/test-image.heic       2024-08-05 04:11:40 UTC (rev 58849)
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: trunk/tests/phpunit/data/images/test-image.heic
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span><a id="svnmimetype"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:mime-type</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+application/octet-stream
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="trunktestsphpunittestsfunctionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/functions.php   2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/tests/phpunit/tests/functions.php     2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1355,6 +1355,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                DIR_TESTDATA . '/images/avif-transparent.avif',
</span><span class="cx" style="display: block; padding: 0 10px">                                'image/avif',
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        // HEIC.
+                       array(
+                               DIR_TESTDATA . '/images/test-image.heic',
+                               'image/heic',
+                       ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                return $data;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1384,7 +1389,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Data profider for test_wp_getimagesize().
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Data provider for test_wp_getimagesize().
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function data_wp_getimagesize() {
</span><span class="cx" style="display: block; padding: 0 10px">                $data = array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1542,6 +1547,35 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Tests that wp_getimagesize() correctly handles HEIC image files.
+        *
+        * @ticket 53645
+        */
+       public function test_wp_getimagesize_heic() {
+               if ( ! is_callable( 'exif_imagetype' ) && ! function_exists( 'getimagesize' ) ) {
+                       $this->markTestSkipped( 'The exif PHP extension is not loaded.' );
+               }
+
+               $file = DIR_TESTDATA . '/images/test-image.heic';
+
+               $editor = wp_get_image_editor( $file );
+               if ( is_wp_error( $editor ) || ! $editor->supports_mime_type( 'image/heic' ) ) {
+                       $this->markTestSkipped( 'No HEIC support in the editor engine on this system.' );
+               }
+
+               $expected = array(
+                       50,
+                       50,
+                       IMAGETYPE_HEIC,
+                       'width="50" height="50"',
+                       'mime' => 'image/heic',
+               );
+               $result   = wp_getimagesize( $file );
+               $this->assertSame( $expected, $result );
+       }
+
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * @ticket 39550
</span><span class="cx" style="display: block; padding: 0 10px">         * @dataProvider data_wp_check_filetype_and_ext
</span><span class="cx" style="display: block; padding: 0 10px">         * @requires extension fileinfo
</span></span></pre></div>
<a id="trunktestsphpunittestsimagefunctionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/image/functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/image/functions.php     2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/tests/phpunit/tests/image/functions.php       2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -237,6 +237,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'test-image.jp2',
</span><span class="cx" style="display: block; padding: 0 10px">                        'test-image.psd',
</span><span class="cx" style="display: block; padding: 0 10px">                        'test-image-zip.tiff',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'test-image.heic',
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                return $this->text_array_to_dataprovider( $files );
</span></span></pre></div>
<a id="trunktestsphpunittestsimageresizephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/image/resize.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/image/resize.php        2024-08-04 22:07:06 UTC (rev 58848)
+++ trunk/tests/phpunit/tests/image/resize.php  2024-08-05 04:11:40 UTC (rev 58849)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -114,6 +114,32 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertSame( IMAGETYPE_AVIF, $type );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * Test resizing HEIC image.
+        *
+        * @ticket 53645
+        */
+       public function test_resize_heic() {
+               $file   = DIR_TESTDATA . '/images/test-image.heic';
+               $editor = wp_get_image_editor( $file );
+
+               // Check if the editor supports the HEIC mime type.
+               if ( is_wp_error( $editor ) || ! $editor->supports_mime_type( 'image/heic' ) ) {
+                       $this->markTestSkipped( 'No HEIC support in the editor engine on this system.' );
+               }
+
+               $image = $this->resize_helper( $file, 25, 25 );
+
+               list( $w, $h, $type ) = wp_getimagesize( $image );
+
+               unlink( $image );
+
+               $this->assertSame( 'test-image-25x25.jpg', wp_basename( $image ) );
+               $this->assertSame( 25, $w );
+               $this->assertSame( 25, $h );
+               $this->assertSame( IMAGETYPE_JPEG, $type );
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         public function test_resize_larger() {
</span><span class="cx" style="display: block; padding: 0 10px">                // image_resize() should refuse to make an image larger.
</span><span class="cx" style="display: block; padding: 0 10px">                $image = $this->resize_helper( DIR_TESTDATA . '/images/test-image.jpg', 100, 100 );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -235,6 +261,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return $saved;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return $dest_file;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return $saved['path'];
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre>
</div>
</div>

</body>
</html>