jreut

Monday, 26 June 2017 at 8:19 EDT

How do I use an SVG as an image?

Last weekend I spent a few hours tinkering with the index page of my Web site. I don't have much experience with the front end of Web development, and although it's something I like to do I sometimes find it really hard to figure out the "right" way to write Web pages. I do, however, like to read documentation, and for the Web you can't beat the MDN and the WHATWG. It seems I learn something new by accident every time I visit either of those sites!

Partly because I am sentimental toward standardization in industries and mostly because I am exhausted by the revolving door of front end technologies, I try to use as much of the Web standards that I can. For me, that means I like to write pages in plain HTML with ES5-compatible Javascript whenever I want to do something dynamic. While it is a little bit more tedious to write HTML directly, I don't have to think about which characters I have to escape in Markdown or how to configure a static site generator.

An underappreciated standard

I love SVG! I've used it in the past to make custom clipping paths around images, to filter images and to make line drawings for presentations. All without having to fire up a fancy, expensive program or make destructive edits to a file!

But it's complicated

SVG can be confusing, though. I ran into something that surprised me when I tried to use an SVG document as the src atrribute of an <img> element. In particular, I made an SVG that used a custom font via a CSS @import directive. For example, let's say the SVG was something like this:

Hello, SVG!
An inline SVG with a custom font
This tiny SVG displays some text with a fancy Web font. It uses CSS declarations inside an SVG <style> element:

    

So far this is all unsurprising to me. It get's interesting, though, once I try to put that SVG into a separate file and set it to be the src of an <img>:

An SVG embedded with
Hey, what happened! The spec has precious little to say about what's allowed in an <img>:
The src attribute must be present, and must contain a valid non-empty URL potentially surrounded by spaces referencing a non-interactive, optionally animated, image resource that is neither paged nor scripted.
Ok, but I don't think my document has anything that's paged or scripted in the first place.

It turns out Web browsers restrict the behavior of SVGs that have been embedded into an <img>. In particular, they can't load external resources like the @font-face stylesheet I want to import from Google Fonts. This is a bummer, but thankfully the same page of documentation that tells me I can't do what I want also tells me how I can fix it!

Documentation to the rescue!

There is a page on MDN that talks about using SVGs as images in HTML pages. It says that, while you will get a restricted version of the SVG if you embed it through an <img> element, you can use an <object> element instead:

An SVG embedded with
Success!

I'm glad I spent the time to read the documentation before trying to hack something together. My first idea of a solution was to fetch the SVG document with Javascript and append my HTML with the contents of the response body. I much prefer the notion of writing a declarative HTML element over writing imperative code! I don't have to worry about dancing around with XMLHttpRequest (blech) or worrying about who supports Fetch. The <object> element has been supported since HTML4!

There's probably a good security or privacy reason why an <img> isn't allowed to run scripts or load resource while an <object> is. Maybe it has something to do with how much browsers are supposed to trust image elements over "plugin" elements? I'm not sure.