11

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.

1

3 Answers 3

11

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);
1
  • This works beautifully, thanks @Josh. FYI, if you need to check if your PHP installation has support for SimpleXML, create a php file (eg called phpinfo.php) with the function phpinfo(); and place that in your site's root directory. Then load that page in your browser (ie, example.com/phpinfo.php) and look for SimpleXML. In my case, for PHP 7+, it was enabled by default on both my localhost installation and my Plesk VPN. Commented Dec 4, 2020 at 16:56
2

This isn't something you're going to easily be able to "hack in" with a plugin or some small set of code.

The short of it is that SVGs, by and large, are not "images" in the sense of all the images that have came before it. SVGs are vector based images, and the first ones to get any real traction on the web.

All images before that have been bitmap based. WordPress's image handling system was written specifically to deal with those, and this inherent design is located at every point in the system.

It's an underlying assumption that images have widths and heights, for example. SVGs have neither, they can be any size. There's a whole basic "editor" for images built into WordPress, none of the functionality of which can really apply to SVGs.

The multimedia system is slowly being redeveloped, with the emphasis here on "slowly". There is a whole lot of backward compatibility to be maintained and new designs to be implemented. Additionally, most people are far more interested in supporting video, audio, and playlists. As this redesign work is done, and sections of the library become more abstracted, then this sort of thing will become easier to support over time. But it's not there yet, and it won't be for a while. This is why the SVG mime type isn't supported, because adding that mime type until all the underlying pieces work would be a road to breakage.

For SVGs, the wp_attachment_is_image should return false, because wp_attachment_is_image is used to determine whether or not to show the editor button and whether or not image_downsize tries to resize the image into the thumbnails and such. Neither of which would work for SVGs anyway. To properly support SVGs, you would need to write a new system for adding metadata for those images entirely, and then add support for it in all the places that metadata might be used. As you can imagine, that isn't a small job.

1
  • 1
    SVGs does have size (viewport and view box), it's just more "virtual" than fixed pixel–dependent dimensions of bitmaps.
    – Rarst
    Commented Oct 20, 2014 at 13:22
1

Just from reading the source (not testing), I can see that the extension needs to match:

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

which reads as (pseudo code)

if image/ is the first 6 chars in the $post object post_mime_type property OR there is an extension OR import is the $post objects post_mime_type property AND the current file extension is one of (Array)

And that means that the last statement will always decide if the if turns out true or not.

From what I can read in get_attached_file(), there's a filter that would allow faking the extension:

return apply_filters( 'get_attached_file', $file, $attachment_id );

In other words, you could try to return the same file but with a different extension. It wouldn't conflict with other parts, as the wp_attachment_is_image() just returns bool.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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