Can’t extract and set SVG dimensions

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-imageurl.

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">

2 s
2

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!

Leave a Comment