Ways to handle SVG rendering in wordpress?

With the advancement of internet browsers, I find myself more and more comfortable using SVGS when coding websites… especially for icons, and simple graphics that can be replaced on the fly by pngs.

It looks like wordpress almost supports SVGS. I say almost because:

  1. It’s not by default an allowed file type in wordpress. So you need to add that before uploading SVGs

  2. You can’t see a SVG thumbnail in the Media gallery. (see image below)

  3. Sometimes when you add it to the editor (via add media button) the editor doesn’t know the svg size, so although it adds the svg as an image it has a width and height of zero.

  4. When you click in “edit image” from within the media upload popup you get a message saying “image does not exist”. See image below.

I am fine with item 1 in this list, but has anyone figure out how a fix item 2 3, and 4?

enter image description here
enter image description here

Update about item 1:

To allow a new mime type (such as SVG) you can just add a hook in functions.php

function allow_new_mime_type($mimes) {

    $mimes['svg'] = 'image/svg+xml';

    return $mimes;
}
add_filter( 'mime_types', 'allow_new_mime_type' );

Now you should be able to upload SVGs. You can find further information in this tutorial. This only solves item 1, which as I mentioned before, it’s not an issue for me (although I think it should be a allowed by default).

Update about item 2:

I did some digging and tracked down the function that decides whether an attachment is an image or not. It seems that it all comes down to this function in wp-includes/post.php

/**
 * Check if the attachment is an image.
 *
 * @since 2.1.0
 *
 * @param int $post_id Attachment ID
 * @return bool
 */
function wp_attachment_is_image( $post_id = 0 ) {
    $post_id = (int) $post_id;
    if ( !$post = get_post( $post_id ) )
        return false;

    if ( !$file = get_attached_file( $post->ID ) )
        return false;

    $ext = preg_match('/\.([^.]+)$/', $file, $matches) ? strtolower($matches[1]) : false;

    $image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png' );

    if ( 'image/' == substr($post->post_mime_type, 0, 6) || $ext && 'import' == $post->post_mime_type && in_array($ext, $image_exts) )
        return true;
    return false;
}

As you can see there’s an array of valid image extensions defined in this function. I don’t see any filters that could be used to modified that array. But that’s a start…

I am not sure why the last if statement returns false for svgs though. Even if I don’t add the svg extension to the array $image_exts, the first condition should return true, shouldn’t it?

if ( 'image/' == substr($post->post_mime_type, 0, 6)

That checks if ‘image/’ is enqual to the first six character in the mime type, which for svg is image/svg+xml (first six is “image/”).

UPDATE

Upon further investigation, it seems that the problem is not with wp_attachment_is_image at all, but because the image size (width and height) are not being added to the attachment metadata when the SVG is uploaded. That’s because the function to calculate the image used is the php function getimagesize(), which does not return an image size for SVG. I found an answer on stackoverflow about the getimagesize function and about how svgs behave. See it here.

3 s
3

Take a look at wp_prepare_attachment_for_js(), which is what gathers attachment metadata for use on the Media pages. The eponymous filter lets us add or alter metadata.

The following example can be dropped into functions.php. Note: this requires SimpleXML support in PHP.

function common_svg_media_thumbnails($response, $attachment, $meta){
    if($response['type'] === 'image' && $response['subtype'] === 'svg+xml' && class_exists('SimpleXMLElement'))
    {
        try {
            $path = get_attached_file($attachment->ID);
            if(@file_exists($path))
            {
                $svg = new SimpleXMLElement(@file_get_contents($path));
                $src = $response['url'];
                $width = (int) $svg['width'];
                $height = (int) $svg['height'];

                //media gallery
                $response['image'] = compact( 'src', 'width', 'height' );
                $response['thumb'] = compact( 'src', 'width', 'height' );

                //media single
                $response['sizes']['full'] = array(
                    'height'        => $height,
                    'width'         => $width,
                    'url'           => $src,
                    'orientation'   => $height > $width ? 'portrait' : 'landscape',
                );
            }
        }
        catch(Exception $e){}
    }

    return $response;
}
add_filter('wp_prepare_attachment_for_js', 'common_svg_media_thumbnails', 10, 3);

Leave a Comment