diff --git a/readme.md b/readme.md index 15c4fa8..3ef2562 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,11 @@ +##WordPress 4.4 and Native Responsive Images + +As of WordPress 4.4, images are responsive by default. If you are on WordPress 4.4 or plan to update, you will not need to install this plugin. + +If you have had this plugin installed since before version 2.5 but are running version 4.4 of WordPress, it is important that you leave the plugin installed. This is because all versions of the plugin before version 2.5 relied on a `data-sizes` attribute being present on an image in order to provide the responsive markup needed. If the plugin in this case is removed, then images in posts will be left with invalid markup. We are working to address this issue, and you can [keep track of our progress here](https://github.com/ResponsiveImagesCG/wp-tevko-responsive-images/issues/178). + +You can still use the plugin for advanced image compression support or as a simple way to include the picturefill script. The plugin will fall back to WordPress default functions if responsive image support is detected in your installation. + RICG-responsive-images --- @@ -9,7 +17,7 @@ This plugin works by including all available image sizes for each image upload. ## Contribution Guidelines -Please submit pull requests to our dev branch. If your contribution requires such, please aim to include appropriate tests with your pr as well. +Please submit pull requests to our dev branch. If your contribution requires such, please aim to include appropriate tests with your PR as well. ## Documentation @@ -19,7 +27,7 @@ No configuration is needed! Just install the plugin and enjoy automatic responsi ### For Theme Developers -This plugin includes several functions that can be used by theme and plugin developers in templates. +This plugin includes several functions that can be used by theme and plugin developers in templates, as well as hooks to filter their output. ### Advanced Image Compression @@ -153,6 +161,26 @@ Array of width and height values in pixels (in that order). `wp_calculate_image_srcset()` +##### Usage Example + +``` + 800 ) { + $max_width = 2048; + } + + return $max_width; +} +add_filter( 'max_srcset_image_width', 'custom_max_srcset_image_width', 10, 2 ); + +?> +``` + --- #### Hook wp_calculate_image_srcset @@ -179,6 +207,9 @@ $width (array) { **$size_array** (array) Array of width and height values in pixels (in that order). +**$image_src** (string) +The `src` of the image. + **$image_meta** (array) The image meta data as returned by `wp_get_attachment_metadata()`. @@ -189,6 +220,33 @@ Image attachment ID or 0. `wp_calculate_image_srcset()` +##### Usage Example + +``` + $source ) { + if ( $source['value'] > 800 ) { + unset( $sources[ $key ] ); + } + } + } + } + + return $sources; +} +add_filter( 'wp_calculate_image_srcset', 'custom_wp_calculate_image_srcset', 10, 5 ); + +?> +``` + --- #### Function wp_get_attachment_image_sizes @@ -313,6 +371,36 @@ Image attachment ID of the original image or 0. `wp_calculate_image_sizes()` +##### Usage Example + +``` + $content_width ) { + $upload_dir = wp_upload_dir(); + $upload_baseurl = $upload_dir['baseurl']; + $fullsize_file = $image_meta['file']; + $fullsize_url = trailingslashit( $upload_baseurl ) . $fullsize_file; + + if ( $image_src === $fullsize_url ) { + $sizes = '(max-width: ' . $content_width . 'px) 100vw, ' . $content_width . 'px'; + } + } + } + + return $sizes; +} +add_filter( 'wp_calculate_image_sizes', 'custom_wp_calculate_image_sizes', 10, 5 ); + +?> +``` + --- ### Backward Compatibility @@ -360,10 +448,20 @@ We use a hook because if you attempt to dequeue a script before it's enqueued, w ## Version -3.1.0 +3.1.1 ## Changelog +**3.1.1** + +- Fixes a bug where the srcset of images in imported content was missing or broken (issue #263). +- Improved calculation of ratio difference for images to be included in the srcset. (issue #260). +- Fixes a bug where `img` tags without ending slash don't get responsive images (issue #259). +- Deprecates the helper function `tevkori_get_media_embedded_in_content()` which is no longer used. +- Makes sure that the setup of default themes doesn't break the tests (issue #261). +- Adds more examples to the Hook Reference in readme.md. +- Corrections and improvements to inline documentation. + **3.1.0** - Adds special handling of GIFs in srcset attributes to preserve animation (issue #223). diff --git a/readme.txt b/readme.txt index ae98f85..77a4142 100644 --- a/readme.txt +++ b/readme.txt @@ -3,8 +3,8 @@ Contributors: tevko, wilto, joemcgill, jaspermdegroot, chriscoyier, Michael McGi Donate link: https://app.etapestry.com/hosted/BoweryResidentsCommittee/OnlineDonation.html Tags: Responsive, Images, Responsive Images, SRCSET, Picturefill Requires at least: 4.0 -Tested up to: 4.3 -Stable tag: 3.1.0 +Tested up to: 4.4 +Stable tag: 3.1.1 License: GPLv2 License URI: http://www.gnu.org/licenses/gpl-2.0.txt @@ -18,6 +18,13 @@ This plugin works by including all available image sizes for each image upload. **Important notes** +* As of WordPress 4.4, images are responsive by default. If you are on WordPress 4.4 or plan to update, you will not need to install this plugin. + +If you have had this plugin installed since before version 2.5 but are running version 4.4 of WordPress, it is important that you leave the plugin installed. This is because all versions of the plugin before version 2.5 relied on a `data-sizes` attribute being present on an image in order to provide the responsive markup needed. If the plugin in this case is removed, then images in posts will be left with invalid markup. We are working to address this issue, and you can keep track of our progress here at https://github.com/ResponsiveImagesCG/wp-tevko-responsive-images/issues/178. + +You can still use the plugin for advanced image compression support or as a simple way to include the picturefill script. The plugin will fall back to WordPress default functions if responsive image support is detected in your installation. + + * Version 3.1.0 includes important changes that make this plugin compatible with WordPress version 4.4. Upgrading is highly recommended. * As of version 2.5.0, the plugin adds `srcset` and `sizes` attributes to images on the front end instead of adding them to the image markup saved in posts. @@ -32,6 +39,15 @@ This plugin works by including all available image sizes for each image upload. == Changelog == += 3.1.1 = +* Fixes a bug where the srcset of images in imported content was missing or broken. +* Improved calculation of ratio difference for images to be included in the srcset. +* Fixes a bug where `img` tags without ending slash don't get responsive images. +* Deprecates the helper function `tevkori_get_media_embedded_in_content()` which is no longer used. +* Makes sure that the setup of default themes doesn't break the tests. +* Adds more examples to the Hook Reference in readme.md. +* Corrections and improvements to inline documentation. + = 3.1.0 = * Adds special handling of GIFs in srcset attributes to preserve animation. * Makes internal srcset/sizes functions more consistent. diff --git a/tests/test-suite.php b/tests/test-suite.php index b7575dc..f1eb436 100644 --- a/tests/test-suite.php +++ b/tests/test-suite.php @@ -9,6 +9,13 @@ class RICG_Responsive_Images_Tests extends WP_UnitTestCase { public static function setUpBeforeClass() { self::$test_file_name = dirname(__FILE__) . '/data/test-large.png'; self::$large_id = self::create_upload_object( self::$test_file_name ); + + // Keep default themes from ruining things. + // remove_action( 'after_setup_theme', 'twentyfifteen_setup' ); + // remove_action( 'after_setup_theme', 'twentysixteen_setup' ); + + // Remove Twenty Sixteen sizes filter for now. + remove_filter( 'wp_calculate_image_sizes', 'twentysixteen_content_image_sizes_attr' ); } public static function tearDownAfterClass() { @@ -50,22 +57,33 @@ public static function create_upload_object( $filename, $parent = 0 ) { * @expectedDeprecated tevkori_get_srcset_array */ function test_tevkori_get_srcset_array() { - // make an image + global $_wp_additional_image_sizes; + + // Make an image. $id = self::$large_id; $srcset = tevkori_get_srcset_array( $id, 'medium' ); $year_month = date('Y/m'); - $image = wp_get_attachment_metadata( $id ); + $image_meta = wp_get_attachment_metadata( $id ); - foreach( $image['sizes'] as $name => $size ) { + $intermediates = array( 'medium', 'medium_large', 'large', 'full' ); + + // Add any soft crop intermediate sizes. + foreach ( $_wp_additional_image_sizes as $name => $additional_size ) { + if ( ! $_wp_additional_image_sizes[$name]['crop'] || 0 === $_wp_additional_image_sizes[$name]['height'] ) { + $intermediates[] = $name; + } + } + + foreach( $image_meta['sizes'] as $name => $size ) { // Whitelist the sizes that should be included so we pick up 'medium_large' in 4.4. - if ( in_array( $name, array( 'medium', 'medium_large', 'large' ) ) ) { + if ( in_array( $name, $intermediates ) ) { $expected[$size['width']] = 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' . $size['file'] . ' ' . $size['width'] . 'w'; } } // Add the full size width at the end. - $expected[$image['width']] = 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w'; + $expected[$image_meta['width']] = 'http://example.org/wp-content/uploads/' . $image_meta['file'] . ' ' . $image_meta['width'] .'w'; $this->assertSame( $expected, $srcset ); } @@ -120,22 +138,33 @@ function test_tevkori_get_srcset_array_false() { * @expectedDeprecated tevkori_get_srcset_array */ function test_tevkori_get_srcset_array_random_size_name() { + global $_wp_additional_image_sizes; + // Make an image. $id = self::$large_id; $srcset = tevkori_get_srcset_array( $id, 'foo' ); $year_month = date('Y/m'); - $image = wp_get_attachment_metadata( $id ); + $image_meta = wp_get_attachment_metadata( $id ); + + $intermediates = array( 'medium', 'medium_large', 'large', 'full' ); + + // Add any soft crop intermediate sizes. + foreach ( $_wp_additional_image_sizes as $name => $additional_size ) { + if ( ! $_wp_additional_image_sizes[$name]['crop'] || 0 === $_wp_additional_image_sizes[$name]['height'] ) { + $intermediates[] = $name; + } + } - foreach( $image['sizes'] as $name => $size ) { + foreach( $image_meta['sizes'] as $name => $size ) { // Whitelist the sizes that should be included so we pick up 'medium_large' in 4.4. - if ( in_array( $name, array( 'medium', 'medium_large', 'large' ) ) ) { + if ( in_array( $name, $intermediates ) ) { $expected[$size['width']] = 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' . $size['file'] . ' ' . $size['width'] . 'w'; } } // Add the full size width at the end. - $expected[$image['width']] = 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w'; + $expected[$image_meta['width']] = 'http://example.org/wp-content/uploads/' . $image_meta['file'] . ' ' . $image_meta['width'] .'w'; $this->assertSame( $expected, $srcset ); } @@ -144,6 +173,8 @@ function test_tevkori_get_srcset_array_random_size_name() { * @expectedDeprecated tevkori_get_srcset_array */ function test_tevkori_get_srcset_array_no_date_upoads() { + global $_wp_additional_image_sizes; + // Save the current setting for uploads folders. $uploads_use_yearmonth_folders = get_option( 'uploads_use_yearmonth_folders' ); @@ -153,17 +184,26 @@ function test_tevkori_get_srcset_array_no_date_upoads() { // Make an image. $id = self::create_upload_object( self::$test_file_name ); $srcset = tevkori_get_srcset_array( $id, 'medium' ); - $image = wp_get_attachment_metadata( $id ); + $image_meta = wp_get_attachment_metadata( $id ); + + $intermediates = array( 'medium', 'medium_large', 'large', 'full' ); - foreach( $image['sizes'] as $name => $size ) { + // Add any soft crop intermediate sizes. + foreach ( $_wp_additional_image_sizes as $name => $additional_size ) { + if ( ! $_wp_additional_image_sizes[$name]['crop'] || 0 === $_wp_additional_image_sizes[$name]['height'] ) { + $intermediates[] = $name; + } + } + + foreach( $image_meta['sizes'] as $name => $size ) { // Whitelist the sizes that should be included so we pick up 'medium_large' in 4.4. - if ( in_array( $name, array( 'medium', 'medium_large', 'large' ) ) ) { + if ( in_array( $name, $intermediates ) ) { $expected[$size['width']] = 'http://example.org/wp-content/uploads/' . $size['file'] . ' ' . $size['width'] . 'w'; } } // Add the full size width at the end. - $expected[$image['width']] = 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w'; + $expected[$image_meta['width']] = 'http://example.org/wp-content/uploads/' . $image_meta['file'] . ' ' . $image_meta['width'] .'w'; $this->assertSame( $expected, $srcset ); @@ -243,33 +283,86 @@ function test_tevkori_get_srcset_array_no_width() { $id = self::create_upload_object( self::$test_file_name ); $srcset = tevkori_get_srcset_array( $id, 'medium' ); - // The srcset should be false + // The srcset should be false. $this->assertFalse( $srcset ); + // Remove filter. remove_filter( 'image_downsize', array( $this, '_filter_image_downsize' ) ); } + function test_wp_calculate_image_srcset_ratio_variance() { + // Mock data for this test. + $size_array = array( 218, 300); + $image_src = 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/2015/12/test-768x1055-218x300.png'; + $image_meta = array( + 'width' => 768, + 'height' => 1055, + 'file' => '2015/12/test-768x1055.png', + 'sizes' => array( + 'thumbnail' => array( + 'file' => 'test-768x1055-150x150.png', + 'width' => 150, + 'height' => 150, + 'mime-type' => 'image/png', + ), + 'medium' => array( + 'file' => 'test-768x1055-218x300.png', + 'width' => 218, + 'height' => 300, + 'mime-type' => 'image/png', + ), + 'custom-600' => array( + 'file' => 'test-768x1055-600x824.png', + 'width' => 600, + 'height' => 824, + 'mime-type' => 'image/png', + ), + 'post-thumbnail' => array( + 'file' => 'test-768x1055-768x510.png', + 'width' => 768, + 'height' => 510, + 'mime-type' => 'image/png', + ), + ), + ); + + $expected_srcset = 'http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/2015/12/test-768x1055-218x300.png 218w, http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/2015/12/test-768x1055-600x824.png 600w, http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/2015/12/test-768x1055.png 768w'; + + $this->assertSame( $expected_srcset, wp_calculate_image_srcset( $size_array, $image_src, $image_meta ) ); +} + /** * @expectedDeprecated tevkori_get_srcset_string */ function test_tevkori_get_srcset_string() { + global $_wp_additional_image_sizes; + // Make an image. $id = self::$large_id; $srcset = tevkori_get_srcset_string( $id, 'full' ); - $image = wp_get_attachment_metadata( $id ); + $image_meta = wp_get_attachment_metadata( $id ); $year_month = date('Y/m'); + $intermediates = array( 'medium', 'medium_large', 'large', 'full' ); + + // Add any soft crop intermediate sizes. + foreach ( $_wp_additional_image_sizes as $name => $additional_size ) { + if ( ! $_wp_additional_image_sizes[$name]['crop'] || 0 === $_wp_additional_image_sizes[$name]['height'] ) { + $intermediates[] = $name; + } + } + $expected = ''; - foreach( $image['sizes'] as $name => $size ) { + foreach( $image_meta['sizes'] as $name => $size ) { // Whitelist the sizes that should be included so we pick up 'medium_large' in 4.4. - if ( in_array( $name, array( 'medium', 'medium_large', 'large' ) ) ) { + if ( in_array( $name, $intermediates ) ) { $expected .= 'http://example.org/wp-content/uploads/' . $year_month = date('Y/m') . '/' . $size['file'] . ' ' . $size['width'] . 'w, '; } } // Add the full size width at the end. - $expected .= 'http://example.org/wp-content/uploads/' . $image['file'] . ' ' . $image['width'] .'w'; + $expected .= 'http://example.org/wp-content/uploads/' . $image_meta['file'] . ' ' . $image_meta['width'] .'w'; $expected = sprintf( 'srcset="%s"', $expected ); @@ -288,6 +381,9 @@ function test_tevkori_get_sizes() { // Test sizes against the default WP sizes. $intermediates = array( 'thumbnail', 'medium', 'large' ); + // Make sure themes aren't filtering the sizes array. + remove_all_filters( 'wp_calculate_image_sizes' ); + foreach( $intermediates as $int ) { $width = get_option( $int . '_size_w' ); @@ -395,62 +491,76 @@ function test_tevkori_get_sizes_string() { /** * @group 170 - * @expectedDeprecated tevkori_get_srcset_string - * @expectedDeprecated tevkori_get_sizes_string - * @expectedDeprecated tevkori_filter_content_images */ - function test_tevkori_filter_content_images() { + function test_wp_make_content_images_responsive() { // Make an image. - $id = self::$large_id; + $image_meta = wp_get_attachment_metadata( self::$large_id ); + $size_array = array( $image_meta['sizes']['medium']['width'], $image_meta['sizes']['medium']['height'] ); - $srcset = tevkori_get_srcset_string( $id, 'medium' ); - $sizes = tevkori_get_sizes_string( $id, 'medium' ); + $srcset = sprintf( 'srcset="%s"', esc_attr( wp_get_attachment_image_srcset( self::$large_id, 'medium', $image_meta ) ) ); + $sizes = sprintf( 'sizes="%s"', esc_attr( wp_get_attachment_image_sizes( self::$large_id, 'medium', $image_meta ) ) ); // Function used to build HTML for the editor. - $img = get_image_tag( $id, '', '', '', 'medium' ); - $img_no_size = str_replace( 'size-', '', $img ); - $img_no_size_id = str_replace( 'wp-image-', 'id-', $img_no_size ); - $img_with_sizes = str_replace( '', '/>', $img ); + $img_html5 = str_replace( ' />', '>', $img ); // Manually add srcset and sizes to the markup from get_image_tag(). - $respimg = preg_replace('|]+) />|', '', $img ); - $respimg_no_size = preg_replace('|]+) />|', '', $img_no_size ); - $respimg_with_sizes = preg_replace('|]+) />|', '', $img_with_sizes ); + $respimg = preg_replace( '|]+) />|', '', $img ); + $respimg_no_size_in_class = preg_replace( '|]+) />|', '', $img_no_size_in_class ); + $respimg_no_width_height = preg_replace( '|]+) />|', '', $img_no_width_height ); + $respimg_with_sizes_attr = preg_replace('|]+) />|', '', $img_with_sizes_attr ); + $respimg_xhtml = preg_replace( '|]+)/>|', '', $img_xhtml ); + $respimg_html5 = preg_replace( '|]+)>|', '', $img_html5 ); + + $content = ' +

Image, standard. Should have srcset and sizes.

+ %1$s - $content = '

Welcome to WordPress! This post contains important information. After you read it, you can make it private to hide it from visitors but still have the information handy for future reference.

-

First things first:

+

Image, no size class. Should have srcset and sizes.

+ %2$s - %1$s +

Image, no width and height attributes. Should have srcset and sizes (from matching the file name).

+ %3$s - +

Image, no attachment ID class. Should NOT have srcset and sizes.

+ %4$s - %2$s +

Image, with sizes attribute. Should NOT have two sizes attributes.

+ %5$s -

As a subscriber, you will receive an email every time an update is available (and only then). This will make it easier to keep your site up to date, and secure from evildoers.
- When a new version is released, log in to the Dashboard and follow the instructions.
- Upgrading is a couple of clicks!

+

Image, XHTML 1.0 style (no space before the closing slash). Should have srcset and sizes.

+ %6$s - %3$s +

Image, HTML 5.0 style. Should have srcset and sizes.

+ %7$s'; + + $content_unfiltered = sprintf( $content, $img, $img_no_size_in_class, $img_no_width_height, $img_no_size_id, $img_with_sizes_attr, $img_xhtml, $img_html5 ); + $content_filtered = sprintf( $content, $respimg, $respimg_no_size_in_class, $respimg_no_width_height, $img_no_size_id, $respimg_with_sizes_attr, $respimg_xhtml, $respimg_html5 ); -

Then you can start enjoying the WordPress experience:

- + $this->assertSame( $content_filtered, wp_make_content_images_responsive( $content_unfiltered ) ); + } - %4$s'; + /** + * When rendering attributes for responsive images, + * we rely on the 'wp-image-*' class to find the image by ID. + * The class name may not be consistent with attachment IDs in DB when + * working with imported content or when a user has edited + * the 'src' attribute manually. To avoid incorrect images + * being displayed, ensure we don't add attributes in this case. + */ + function test_wp_make_content_images_responsive_wrong() { + $image = get_image_tag( self::$large_id, '', '', '', 'medium' ); - $content_unfiltered = sprintf( $content, $img, $img_no_size, $img_no_size_id, $img_with_sizes ); - $content_filtered = sprintf( $content, $respimg, $respimg_no_size, $img_no_size_id, $respimg_with_sizes ); + // Replace the src URL. + $image_wrong_src = preg_replace( '|src="[^"]+"|', 'src="http://' . WP_TESTS_DOMAIN . '/wp-content/uploads/foo.jpg"', $image ); - $this->assertSame( $content_filtered, tevkori_filter_content_images( $content_unfiltered ) ); + $this->assertSame( $image_wrong_src, wp_make_content_images_responsive( $image_wrong_src ) ); } /** @@ -566,4 +676,37 @@ function test_wp_calculate_image_srcset_animated_gifs() { $this->assertFalse( strpos( wp_calculate_image_srcset( $large_src, $size_array, $image_meta ), $full_src ) ); } + function test_wp_make_content_images_responsive_schemes() { + $image_meta = wp_get_attachment_metadata( self::$large_id ); + $size_array = array( (int) $image_meta['sizes']['medium']['width'], (int) $image_meta['sizes']['medium']['height'] ); + + $srcset = sprintf( 'srcset="%s"', wp_get_attachment_image_srcset( self::$large_id, $size_array, $image_meta ) ); + $sizes = sprintf( 'sizes="%s"', wp_get_attachment_image_sizes( self::$large_id, $size_array, $image_meta ) ); + + // Build HTML for the editor. + $img = get_image_tag( self::$large_id, '', '', '', 'medium' ); + $img_https = str_replace( 'http://', 'https://', $img ); + $img_relative = str_replace( 'http://', '//', $img ); + + // Manually add srcset and sizes to the markup from get_image_tag(); + $respimg = preg_replace( '|]+) />|', '', $img ); + $respimg_https = preg_replace( '|]+) />|', '', $img_https ); + $respimg_relative = preg_replace( '|]+) />|', '', $img_relative ); + + $content = ' +

Image, http: protocol. Should have srcset and sizes.

+ %1$s + +

Image, http: protocol. Should have srcset and sizes.

+ %2$s + +

Image, protocol-relative. Should have srcset and sizes.

+ %3$s'; + + $unfiltered = sprintf( $content, $img, $img_https, $img_relative ); + $expected = sprintf( $content, $respimg, $respimg_https, $respimg_relative ); + $actual = wp_make_content_images_responsive( $unfiltered ); + + $this->assertSame( $expected, $actual ); + } } diff --git a/wp-tevko-core-functions.php b/wp-tevko-core-functions.php index 2d13301..a0e90f2 100644 --- a/wp-tevko-core-functions.php +++ b/wp-tevko-core-functions.php @@ -54,6 +54,8 @@ function _wp_get_image_size_from_meta( $size_name, $image_meta ) { * * @since 3.0.0 * + * @see wp_calculate_image_srcset() + * * @param int $attachment_id Image attachment ID. * @param array|string $size Optional. Image size. Accepts any valid image size, or an array of * width and height values in pixels (in that order). Default 'medium'. @@ -135,9 +137,6 @@ function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac $image_baseurl = trailingslashit( $image_baseurl ); - // Calculate the image aspect ratio. - $image_ratio = $image_height / $image_width; - /* * Images that have been edited in WordPress after being uploaded will * contain a unique hash. Look for that hash and use it later to filter @@ -174,15 +173,21 @@ function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac continue; } - // Calculate the new image ratio. - if ( $image['width'] ) { - $image_ratio_compare = $image['height'] / $image['width']; + /** + * To check for varying crops, we calculate the expected size of the smaller + * image if the larger were contstrained by the width of the smaller and then + * see if it matches what we're expecting. + */ + if ( $image_width > $image['width'] ) { + $constrained_size = wp_constrain_dimensions( $image_width, $image_height, $image['width'] ); + $expected_size = array( $image['width'], $image['height'] ); } else { - $image_ratio_compare = 0; + $constrained_size = wp_constrain_dimensions( $image['width'], $image['height'], $image_width ); + $expected_size = array( $image_width, $image_height ); } - // If the new ratio differs by less than 0.01, use it. - if ( abs( $image_ratio - $image_ratio_compare ) < 0.01 ) { + // If the image dimensions are within 1px of the expected size, use it. + if ( abs( $constrained_size[0] - $expected_size[0] ) <= 1 && abs( $constrained_size[1] - $expected_size[1] ) <= 1 ) { // Add the URL, descriptor, and value to the sources array to be returned. $sources[ $image['width'] ] = array( 'url' => $image_baseurl . $image['file'], @@ -200,12 +205,12 @@ function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac * @param array $sources { * One or more arrays of source data to include in the 'srcset'. * - * @type type array $width { - * @type type string $url The URL of an image source. - * @type type string $descriptor The descriptor type used in the image candidate string, - * either 'w' or 'x'. - * @type type int $value The source width, if paired with a 'w' descriptor or a - * pixel density value if paired with an 'x' descriptor. + * @type array $width { + * @type string $url The URL of an image source. + * @type string $descriptor The descriptor type used in the image candidate string, + * either 'w' or 'x'. + * @type int $value The source width if paired with a 'w' descriptor, or a + * pixel density value if paired with an 'x' descriptor. * } * } * @param array $size_array Array of width and height values in pixels (in that order). @@ -234,6 +239,8 @@ function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attac * * @since 3.0.0 * + * @see wp_calculate_image_sizes() + * * @param int $attachment_id Image attachment ID. * @param array|string $size Optional. Image size. Accepts any valid image size, or an array of width * and height values in pixels (in that order). Default 'medium'. @@ -325,11 +332,13 @@ function wp_calculate_image_sizes( $size, $image_src = null, $image_meta = null, * @return string Converted content with 'srcset' and 'sizes' attributes added to images. */ function wp_make_content_images_responsive( $content ) { - $images = tevkori_get_media_embedded_in_content( $content, 'img' ); + if ( ! preg_match_all( '/]+>/', $content, $matches ) ) { + return $content; + } $selected_images = $attachment_ids = array(); - foreach( $images as $image ) { + foreach( $matches[0] as $image ) { if ( false === strpos( $image, ' srcset=' ) && preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) && ( $attachment_id = absint( $class_id[1] ) ) ) { @@ -362,47 +371,6 @@ function wp_make_content_images_responsive( $content ) { } add_filter( 'the_content', 'wp_make_content_images_responsive', 5, 1 ); -/** - * Check the content blob for an audio, video, object, embed, or iframe tags. - * This is a copy of `get_media_embedded_in_content()` in WP 4.4 in order to provide - * back compatibility to older versions of WordPress. - * - * @since 3.0.0 - * - * @param string $content A string which might contain media data. - * @param array $types An array of media types: 'audio', 'video', 'object', 'embed', or 'iframe'. - * @return array A list of found HTML media embeds. - */ -function tevkori_get_media_embedded_in_content( $content, $types = null ) { - $html = array(); - - /** - * Filter the embedded media types that are allowed to be returned from the content blob. - * - * @param array $allowed_media_types An array of allowed media types. Default media types are - * 'audio', 'video', 'object', 'embed', 'iframe', and 'img'. - */ - $allowed_media_types = apply_filters( 'media_embedded_in_content_allowed_types', array( 'audio', 'video', 'object', 'embed', 'iframe', 'img' ) ); - - if ( ! empty( $types ) ) { - if ( ! is_array( $types ) ) { - $types = array( $types ); - } - - $allowed_media_types = array_intersect( $allowed_media_types, $types ); - } - - $tags = implode( '|', $allowed_media_types ); - - if ( preg_match_all( '#<(?P' . $tags . ')[^<]*?(?:>[\s\S]*?<\/(?P=tag)>|\s*\/>)#', $content, $matches ) ) { - foreach ( $matches[0] as $match ) { - $html[] = $match; - } - } - - return $html; -} - /** * Adds 'srcset' and 'sizes' attributes to an existing 'img' element. * @@ -437,6 +405,29 @@ function wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ) { return $image; } + /** + * To make sure that our ID and image src match, we loop through all the sizes + * in our attachment metadata and bail early if our src file isn't included. + */ + $file_name = wp_basename( $image_src ); + + $all_sizes = wp_list_pluck( $image_meta['sizes'], 'file' ); + $all_sizes[] = $image_meta['file']; + + $matched = false; + + foreach( $all_sizes as $size ) { + if ( false !== strpos( $size, $file_name ) ) { + $matched = true; + break; + } + } + + // Bail early if the image src doesn't match any of the known image sizes. + if ( ! $matched ) { + return $image; + } + $width = preg_match( '/ width="([0-9]+)"/', $image, $match_width ) ? (int) $match_width[1] : 0; $height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0; diff --git a/wp-tevko-deprecated-functions.php b/wp-tevko-deprecated-functions.php index f2bbc35..44a08f5 100644 --- a/wp-tevko-deprecated-functions.php +++ b/wp-tevko-deprecated-functions.php @@ -412,3 +412,46 @@ function _wp_get_attachment_image_sizes_filter_shim( $sizes, $size, $image_src, } } add_filter( 'wp_calculate_image_sizes', '_wp_get_attachment_image_sizes_filter_shim', 10, 5 ); + +/** + * Check the content blob for an audio, video, object, embed, iframe, or img tags. + * This is a modified version of `get_media_embedded_in_content()` that was modified + * during the WP 4.4 cycle to return images in content, before being reverted. + * + * @since 3.0.0 + * @deprecated 3.1.1 Use 'get_media_embedded_in_content()' + * + * @param string $content A string which might contain media data. + * @param array $types An array of media types: 'audio', 'video', 'object', 'embed', 'iframe', or 'img'. + * @return array A list of found HTML media embeds. + */ +function tevkori_get_media_embedded_in_content( $content, $types = null ) { + _deprecated_function( __FUNCTION__, '3.1.1', 'get_media_embedded_in_content()' ); + $html = array(); + + /** + * Filter the embedded media types that are allowed to be returned from the content blob. + * + * @param array $allowed_media_types An array of allowed media types. Default media types are + * 'audio', 'video', 'object', 'embed', 'iframe', and 'img'. + */ + $allowed_media_types = apply_filters( 'media_embedded_in_content_allowed_types', array( 'audio', 'video', 'object', 'embed', 'iframe', 'img' ) ); + + if ( ! empty( $types ) ) { + if ( ! is_array( $types ) ) { + $types = array( $types ); + } + + $allowed_media_types = array_intersect( $allowed_media_types, $types ); + } + + $tags = implode( '|', $allowed_media_types ); + + if ( preg_match_all( '#<(?P' . $tags . ')[^<]*?(?:>[\s\S]*?<\/(?P=tag)>|\s*\/>)#', $content, $matches ) ) { + foreach ( $matches[0] as $match ) { + $html[] = $match; + } + } + + return $html; +} diff --git a/wp-tevko-responsive-images.php b/wp-tevko-responsive-images.php index cebf741..59a3305 100644 --- a/wp-tevko-responsive-images.php +++ b/wp-tevko-responsive-images.php @@ -8,7 +8,7 @@ * Plugin Name: RICG Responsive Images * Plugin URI: https://github.com/ResponsiveImagesCG/wp-tevko-responsive-images * Description: Bringing automatic default responsive images to WordPress - * Version: 3.1.0 + * Version: 3.1.1 * Author: The RICG * Author URI: http://responsiveimages.org/ * License: GPL-2.0+