Cross-Browser SVG Issues

While getting this page ready to display good-looking math, graphs, and figures, I ran into a few problems with how the current browsers handle SVG. I was somewhat surprised considering the age of some of the associated bugs. To give a little bit of background on what I am talking about, let’s first consider the problem I am trying to solve (and how one could go about it). I will choose a simple term as an example : the sum of all cubes from 1 to 100. While there is a markup-language called MathML which could be used to render it, the browser-support is very shaky — and the output is often not pretty. The alternative I’d like to pursue is to embed a rendering of the term as a graphics-file. The easiest way to do that is to just use a rasterized image like so (this works in any browser, including backward ones from Redmond) :

<img src="term.png" alt="sum_{i=1}^{100}i^3" />

This solution is not very elegant however. The author has to guess the resolution and screensize of the reader (so as to provide a properly-sized image), and zooming or printing makes the term really ugly :

<img src="term.png" alt="sum_{i=1}^{100}i^3" width="78" height="96" />

You may have noticed that the width- and height-attributed are given here; this is not necessary if you want to use the intrinsic size of the image (as in the first example), and no browser requires it for proper display; in the second example, these attributes are used to illustrate the effect of scaling of a rasterized image.

To avoid such problems when the originating image is clearly scalable/vectorized, I want to use Scalable Vector Graphics instead. All modern browsers support this open standard, at least in name. The handling of that support, however, differs.

There are several ways to include SVG-content in a webpage, some of them standards-conformant. The conformant ones would be (and I provide examples here; if your browser rocks, they’ll be displayed correctly) :

<img src="test.svg" alt="sum_{i=1}^{100}i^3" />

<object type="image/svg+xml" data="test.svg"></object>

It is possible to specify alternate content to be displayed for objects the browser does not understand. For instance, it might be a good idea to use

<object type="image/svg+xml" data="test.svg"> <img src="test.png" alt="sum_{i=1}^{100}i^3" /> </object>

in order to make sure that even backwards browsers from Redmond display something sensible (Internet Explorer, no matter which version, does not handle SVG natively).

You’ll notice that neither of these specify width- and height-attributes. The browser has all it needs to do “the right thing” — The SVG-file specifies a default width- and height in pixels (technically speaking these values are defined as coordinates in userspace, but failing a unit being named they are to be interpreted as pixels by default). Both the <img>- and the <object>-Element should thus occupy 39×48 pixels. Ideally, this would display something sensible on all the major browsers and fail gracefully on older ones. Let’s see how that works out.

Opera 9.64

Opera handles all of the above examples just fine. When using the SVG in an <img> without explicit width- and height, it uses the intrinsic size. Same for <object>. When resizing the webpage (CTRL+Mousewheel), the term scales smoothly without artifacts or clipping. SVG support in this browser is excellent, no complaints, well done.

Firefox 3.0.8

Mozilla Firefox and other browsers based on its engine claim to have SVG support. Well, they do, but only if you use <object> to embed the image; you cannot use <img> (in this case, Firefox will simply display the text specified in the alt-attribute of the element). This is rather unfortunate, as we will soon see. This bug has been open in Firefox for over 4 years (276431); apparently, the developers cannot figure out how to display a SVG-file in an img-tag “securely” — SVG is potentially scriptable and can potentially embed external files; this seems to be unsurmountable for them). They are also still working on support for SVG fonts (Bug 119490).

So while

<img src="test.svg" alt="sum_{i=1}^{100}i^3" />

will not work, Firefox does fine with the <object>-version most of the time and obtains its size correctly (most of the time). Scaling works fine (CTRL+Mousewheel) in that case. I say “most of the time” because sometimes, correct object dimensions are only applies after reloading the page (when this bug happens, some SVGs will be vastly bigger than they should be, and others a lot smaller).

WebKit, Chrome, Safari

The WebKit-based Browsers such as Google Chrome 2, Apple Safari 4, Konqueror, etc. also claim SVG support. Unfortunately, it is not quite as decent as one would hope.

In WebKit,

<img src="test.svg" alt="sum_{i=1}^{100}i^3" />

will work. It will obtain the dimensions of its area from the intrinsic dimensions of the SVG file, as it should. However zooming the page will not also resize that area; it will remain constant. As such, support of SVG in Chrome et al. is lacking one of the features that make SVG so useful : decent-quality scaling. The WebKit-renderer is able to scale SVGs if given a hint to do so, though :

<img src="test.svg" alt="sum_{i=1}^{100}i^3" width="78" height="96" />

Is double the intrinsic size of the image; this variant does not scale either, though. If you were to want to take advantage of scaling in WebKit, you would have to define the width and height of the image in non-absolute terms such as percentages of its container :

<img src="test.svg" alt="sum_{i=1}^{100}i^3" width="5%" />

In this case, the height will scale as much as needed to accomodate the width, as you would expect (the way the scaling is done can be specified in the SVG-file in the preserveAspectRatio-attribute of the root-element). One might want to attempt to use the the em-units here to get some relation to text size going, but that does not work with WebKit. This scaling is also not very robust (for instance, the SVG’s height in this page’s header is not scaled correctly).

While not perfect, the <img>-support at least somewhat works as expected, except for zooming. Not so for the <object>-support. In fact, WebKit will not use any intrinsic dimension-information of the SVG when the element is not given a height- or width-attribute. It will simply use the default size of objects, 300×150 pixels. This size will also not scale when zooming but, and here is the kicker, the embedded SVG content will (and grow out of the containing rectangle pretty fast). You can give the <object> an explicit width and height, but that will not scale either when zooming the page :

<object type="image/svg+xml" data="test.svg" width="39" height="48"></object>

Internet Explorer

Microsoft’s Internet Explorer simply does not handle SVG at all. You can install the unsupported and no longer updated Adobe SVG Viewer to get some form of support — but at least for me it fails to work well.

Where to go from here ?

The best option to at least fail gracefully with IE is the aforementioned

<object type="image/svg+xml" data="test.svg"> <img src="test.png" alt="sum_{i=1}^{100}i^3" /> </object>

Variant. This would be grand if WebKit were able to handle <object>-embedded SVGs gracefully, but it is not. Another way would be to use

<img src="test.svg" alt="sum_{i=1}^{100}i^3" />

and replace the SVG with rasterized images on the fly via JavaScript when Internet Explorer is loaded — but that does not work for Firefox.

With the help of a CSS Browser Selector JavaScript snippet, a workable solution that both delivers SVG-display in the supporting browsers as best as they are able and fails somewhat gracefully on IE or without JavaScript enabled would be

<head> <script type="text/javascript" src="/js/css_browser_selector.js" /> <style type="text/css"> div.svgobject { display: none } .ie div.svgobject { display: inline } .ie img.svg { display: none } .gecko div.svgobject { display: inline } .gecko img.svg { display: none } </style> </head> ... <img src="test.svg" alt="sum_{i=1}^{100}i^3" class="svg" /> <div class="svgobject"><object type="image/svg+xml" data="test.svg"> <img src="test.png" alt="sum_{i=1}^{100}i^3"/> </object></div>

Here, I simply embed the image twice (once as <img>, once as <object> with a PNG-fallback), with the <object>-display being disabled by default via CSS and the <img>-display enabled. If the browser is IE or Firefox, the <object>-version is used instead. If JavaScript is disabled, the default is to display the <img>-version of the SVG — which seems a sensible default for future browsers.
In case you were wondering why the <object>-version is wrapped in a <div>-tag : Safari up to 3.x (and associated WebKit versions) simply do not understand the CSS style attribute of display: none; on <object>-elements.

Conclusion

Opera got it right. Firefox does not support SVG in places where you would expect it to. WebKit has buggy behavior w.r.t. SVG. Internet Explorer simply fails. Hopefully, Firefox and WebKit will achieve parity with Opera on these issues soon so as to bring SVG adoption forward — and avoid ugly hacks to make it work everywhere.

1. Philip Kahn says:

As I referenced your site in writing the code to handle this, I thought you’d appreciate this:

http://blog.revealedsingularity.net/post/2009/11/05/svgs-and-browsers

I wrote up a bit of container code to display SVG graphics in the big five browsers, albeit with some limitations in IE. I’d be interested as to your opinion on it! A direct link to the library is here:

http://filehost.revealedsingularity.net/universal_svg.7z

2. [...] I had to use the object tag because Firefox can’t render SVG with the img tag. Read more about cross browsing SVG issues. [...]

• rupps says:

discovered something interesting:

I just pasted a svg, instead of using img… etc, just pasted the whole svg.

exploring with firebug, I saw an interesting CSS rule:

html|* > svg { -webkit-transform-origin: 50% 50% }

it certainly opens another place to mingle…

besides, that gave me a clue to implement a (temporary) solution to address the scaling problem on chrome (only scales maintaining aspect ratio).

What I do is:

ok so now you have the img properly scaled in X, Y’s gonna be bigger or smaller than your desired height, so you just scale-transform height manually:

var scale=$(“#svgstuff”).get(0).naturalHeight / MY_HEIGHT;$(“#svgstuff”).css(“-webkit-transform:scaleY(“+scale+”)”;

it’s quick n dirty but it works

still interested in all this, please drop me a message if you find better ways to do it.

3. Noah Diewald says:

Thanks for this article. I found it very useful. I was pulling my hair out trying to figure out how to get a scaling SVG object in Chrome.

A down side of this approach, although I haven’t found anything better, is that Opera, Chrome and probably other webkit browsers ignore JavaScript in the SVG file when using the img tag. So if you were planning on having animations or interactive features that work in all SVG supporting browsers and also wanted to make your SVG scale, this won’t solve the problem.

It looks like there are a lot of open bugs related to intrinsic object height and width in the WebKit issue tracker but nothing seems to have been done. I hope that someone finds the time to work on it.

4. Tosh says:

Have you had a look at http://www.mathjax.org/

renders latex in the browser using JavaScript, It works well for me.

Thanks for the article

5. rupps says:

hi! been fighting with svg scaling,

situation seems improved now,

in my tests, in firefox 12.0 svg’s inside img’s scale beautifully to provided width & height as you can expect

safari, 100%

chrome: big deception, it scales, but only mantaining aspect ratio. this is driving me crazy, do you have an idea what to try? i tried all your methods, even the object thing, no results.

android stock browser 3.2 (webkit) 100 % !!

internet explorer … not very interested really

6. rupps says:

the definitive solution for fixing webkit scaling and allow anisotropic scaling: thanks to ur article i managed to fix it. the idea is, img.naturalheight&img.naturalwidth contain valuable information about the original SVG aspect ratio. As chrome only scales isotropic, we scale the image 100%width, height auto, and later we apply a transform that shrinks/expands only scaling Y. something like:

var img=new Image();