7

Up to now, I have had no problems with the background-image thumbnails in my theme. However, now that I'm trying to use an SVG as the featured image, something is breaking. The problem seems to related to the width of the SVGs being returned as zero by wp_get_attachment_image_src(). So what I am I trying to do is figure out how to extract the width and height information from the SVG then set them to the appropriate values in the SVGs database fields, isn't proving easy.

Note: there is not a general problem uploading or rendering svgs, they display fine in my logo.

The error and code: evidence of zero-width

This is the error that Wordpress throws on the page: Warning: Division by zero in /wp-content/themes/tesseract/functions.php on line 771

This is the code in functions.php before the error (on line 771).

    /*759*/ function tesseract_output_featimg_blog() {
    /*760*/
    /*761*/ global $post;
    /*762*/
    /*763*/ $thumbnail = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'full' );
    /*764*/ $featImg_display = get_theme_mod('tesseract_blog_display_featimg');
    /*765*/ $featImg_pos = get_theme_mod('tesseract_blog_featimg_pos');
    /*766*/
    /*767*/ $w = $thumbnail[1]; /* supposed to return thumbnail width */
    /*768*/ $h = $thumbnail[2];  /* supposed to return thumbnail height */
    /*769*/ $bw = 720;
    /*770*/ $wr = $w/$bw;  
    /*771*/ $hr = $h/$wr; /**** this is the line that throws the error ****/

You can see there is a division with $wr as the denominator. Its seems that $wr is being computed as zero because its only non-constant factor, $w, is also zero (the other factor is 720, so it can't be to blame). $w's value is determined by $thumbnail[1]. $thumbnail[1]'s value is is set on line 763 with this code:

$thumbnail = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'full' );

And according to the the Codex, the second value in the returning array of the function wp_get_attachment_image_src(i.e. $thumbnail[1]) is indeed the width of the thumbnail. That value does appear to be zero, because it is the same value as $w, which is indeed denominator on line 771. :(

Important Note: My theme implements thumbnails as background-images

The parent theme I am using, "Tesseract", places featured images as background images of anchors/ <a> elements, instead of placing them as <img> elements. I do not believe this is the cause of the problem, but when offering solutions, they should be compatible with background-images, not <img> objects.

Couldn't adapt fix #1:

I did find this webpage which provides a fix for problems using SVGs as featured images. It suggests that the issue is related to the computation of the width. However, I can't apply that fix because it is for an img element that has an SVG as a src, i have an <a> element that has the SVG location set to the the background-image-url.

This is the fix

function custom_admin_head() {
  $css = '';
  $css = 'td.media-icon img[src$=".svg"] { width: 100% !important; height: auto !important; }';
  echo '<style type="text/css">'.$css.'</style>';
}
add_action('admin_head', 'custom_admin_head');

Fix #2 (min-width via CSS) didn't work

Based on the idea I got from the above page, that the width of the might be the issue, I tried setting a min-width of 50% using just CSS to the <a>. Here is the code:

a.entry-post-thumbnail.above {
    min-width: 50% !important;
} 

My developer tools showed that the CSS "took", that the min-width did get set to 50%. Still WP threw the same error the image did not display. Maybe the CSS doesn't set it before functions.php runs wp_get_attachment_image_src, so it doesn't matter?

Anyone have any clues about how to work around this zero computation? I'd really like to get this working with the background-images, and without having to overwrite too much of the parent theme.

Fix #3 (hooking a filter) didn't work.

With the help of @Milo, @NathanJohnson, and @prosti I was able to try a filter to alter wp_get_attachment_image_src(). The code doesn't produce an error but it doesn't remove the division error or get the SVG to display. This the snippet I placed into functions.php. Perhaps the priorities are wrong? :

add_filter( 'wp_get_attachment_image_src', 'fix_wp_get_attachment_image_svg', 10, 4 );  /* the hook */

     function fix_wp_get_attachment_image_svg($image, $attachment_id, $size, $icon) {
        if (is_array($image) && preg_match('/\.svg$/i', $image[0]) && $image[1] == 1) {
            if(is_array($size)) {
                $image[1] = $size[0];
                $image[2] = $size[1];
            } elseif(($xml = simplexml_load_file($image[0])) !== false) {
                $attr = $xml->attributes();
                $viewbox = explode(' ', $attr->viewBox);
                $image[1] = isset($attr->width) && preg_match('/\d+/', $attr->width, $value) ? (int) $value[0] : (count($viewbox) == 4 ? (int) $viewbox[2] : null);
                $image[2] = isset($attr->height) && preg_match('/\d+/', $attr->height, $value) ? (int) $value[0] : (count($viewbox) == 4 ? (int) $viewbox[3] : null);
            } else {
                $image[1] = $image[2] = null;
            }
        }
        return $image;
    } 

The bottom line:

I believe I need to figure out how to extract the width information from the SVG file itself and add it to the WP database before functions.php runs the computation on line 771. If you know how, your guidance would be really appreciated.

Some potentially helpful Resources

  • This question seems to have helpful information, and the snippet provided by @Josh there allowed me to finally view my SVGs in the media library, but the featured image is still broken.
  • This question seems to have some XML-based solutions but I don't know how to adapt it to WP.
  • Also one commenter below pointed me to This filter which seems to be relevant.

The SVG file's header

This is the SVG header:

<?xml version="1.0" encoding="utf-8"?> <!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="475.419px" height="406.005px" viewBox="0 0 475.419 406.005" enable-background="new 0 0 475.419 406.005" xml:space="preserve">
  • 1
    Look at wp_get_attachment_image_src in source code, there is a filter of the same name. Use the filter to return correct sizes for svg images. – Milo Feb 6 '17 at 1:48
  • 1
    SVGs are not actually "images" and WordPress has no native support for them. You need to look into whatever is adding SVG support to your site and address the issue in that. – Otto Feb 6 '17 at 1:50
  • 1
    Look at fix_wp_get_attachment_image_svg function here and where the filter is added here. Read add_filter in docs for an explanation of filters. – Milo Feb 6 '17 at 2:34
  • 1
    SVGs aren't images, so when WordPress computes the image size it doesn't recognize the file as an image, so it returns zero. Using the wp_get_attachment_image_src filter to "manually" return the size of the image seems like the correct approach. – Nathan Johnson Feb 8 '17 at 0:46
  • Comments are not for extended discussion; this conversation has been moved to chat. – Howdy_McGee Feb 8 '17 at 0:56
7

I solved it!!! The filter in fix #3 (above) wasn't working because of the third condition of this if statement that triggers the dimension extraction and attachment:

if (is_array($image) && preg_match('/\.svg$/i', $image[0]) && $image[1] == 1)

$image[1] in the third condition is the reported width of the SVG file as WP sees it before it enters the filter. Since I know that its value, the width, is 0 (from the "zero-division error" described above) I suspected that this value needed to be changed to match it. I changed the final 1 on that if condition to a 0 and..voila! The division error went away and the image is displaying, and beautifully so I might ad!

I was able to spot this because in many forums people complain that SVGs are incorrectly getting assigned widths of 1px. This was probably the case in some versions of WP, but in my WP 4.7.2 media library, no dimension for the SVG, not even 1px is posted, as a dimension. Instead there was simply no metadata regarding dimensions in my case.

I think a flexible version of the filter would allow it to be applied if the width is 1 or 0, for people who had the 1px problem and for people like me, who had a width of 0. Below I included the fix, using $image[1] <= 1as the third condition of the if statement instead of $image[1] == 1 but I suppose you could use this, too: ( ($image[1] == 0) || ($image[1] == 1) )

The working filter

 add_filter( 'wp_get_attachment_image_src', 'fix_wp_get_attachment_image_svg', 10, 4 );  /* the hook */

 function fix_wp_get_attachment_image_svg($image, $attachment_id, $size, $icon) {
    if (is_array($image) && preg_match('/\.svg$/i', $image[0]) && $image[1] <= 1) {
        if(is_array($size)) {
            $image[1] = $size[0];
            $image[2] = $size[1];
        } elseif(($xml = simplexml_load_file($image[0])) !== false) {
            $attr = $xml->attributes();
            $viewbox = explode(' ', $attr->viewBox);
            $image[1] = isset($attr->width) && preg_match('/\d+/', $attr->width, $value) ? (int) $value[0] : (count($viewbox) == 4 ? (int) $viewbox[2] : null);
            $image[2] = isset($attr->height) && preg_match('/\d+/', $attr->height, $value) ? (int) $value[0] : (count($viewbox) == 4 ? (int) $viewbox[3] : null);
        } else {
            $image[1] = $image[2] = null;
        }
    }
    return $image;
} 

Thank you everyone for your help it was really a team effort!

  • Doesn't work on WordPress 4.9.4 – Vadim H Mar 21 '18 at 15:58
2
+50

It is hard to guess exactly what may be the problem for the WordPress enthusiast like me but since you pinged me in the Loop...

Just to mention you haven't shared the theme files...

File: wp-includes/media.php
804: /**
805:  * Retrieve an image to represent an attachment.
806:  *
807:  * A mime icon for files, thumbnail or intermediate size for images.
808:  *
809:  * The returned array contains four values: the URL of the attachment image src,
810:  * the width of the image file, the height of the image file, and a boolean
811:  * representing whether the returned array describes an intermediate (generated)
812:  * image size or the original, full-sized upload.
813:  *
814:  * @since 2.5.0
815:  *
816:  * @param int          $attachment_id Image attachment ID.
817:  * @param string|array $size          Optional. Image size. Accepts any valid image size, or an array of width
818:  *                                    and height values in pixels (in that order). Default 'thumbnail'.
819:  * @param bool         $icon          Optional. Whether the image should be treated as an icon. Default false.
820:  * @return false|array Returns an array (url, width, height, is_intermediate), or false, if no image is available.
821:  */
822: function wp_get_attachment_image_src( $attachment_id, $size = 'thumbnail', $icon = false ) {
823:    // get a thumbnail or intermediate image if there is one
824:    $image = image_downsize( $attachment_id, $size );
825:    if ( ! $image ) {
826:        $src = false;
827: 
828:        if ( $icon && $src = wp_mime_type_icon( $attachment_id ) ) {
829:            /** This filter is documented in wp-includes/post.php */
830:            $icon_dir = apply_filters( 'icon_dir', ABSPATH . WPINC . '/images/media' );
831: 
832:            $src_file = $icon_dir . '/' . wp_basename( $src );
833:            @list( $width, $height ) = getimagesize( $src_file );
834:        }
835: 
836:        if ( $src && $width && $height ) {
837:            $image = array( $src, $width, $height );
838:        }
839:    }

This part with:

@list( $width, $height ) = getimagesize( $src_file );

Looks like the problem since SVG images don't have the size info. They just fit the container.

So you may create your own version of wp_get_attachment_image_src where you can somehow set the size.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.