Articles

  • Rendering variable-sized SVGs

    As I tweeted earlier, I've been playing with SVG recently. But not in the way most people use SVG - I've been working on a website that generates SVGs on the fly with variable sets of data. This means coding the SVG manually, as opposed to drawing it visually in something like Inkscape. 

    As the set of data is variable, the dimensions of the resultant SVG is also variable. This means it is easier to just leave off the height and width attributes on the head SVG element, because it can't really be determined until it is rendered (and even then, there could be differences in the renderer or settings that may affect it). You would expect it to be determined implicitly, and it does kind of happen, but not as well as you'd expect.

    The 4 major browsers all offer differing behaviour. Here's an SVG that demonstrates the problems. Try opening it in different SVG-supporting browsers, Opera, Safari, Chrome or IE 9.

    To start off with, for some silly reason, no browser has the ability to pan or zoom SVG out of the box, not even if you are opening the raw SVG in the browser. And SVG without explicit dimensions don't get scrollbars, so anything that doesn't fit on the screen might as well not exist, even though there is obviously an implicit size for it to work with. Who knows why.

    Anyway, everything looks good when you open it. In that SVG, there is a script that scales the image when you scroll your mouse wheel. Try it. Alternatively, you can adjust the zoom level in your browser using the menus or CTRL and - or +.

    Personally, the expected behaviour is that the entire image is scaled and the browser window should be filled with the image. Scrollbars should appear if the image is larger than the window. This means that the default SVG viewport should be the size of the image, and if the dimensions are not explicitly specified, they should be implied based on the rendering's bounding box. The SVG specs aren't very explicit about what should happen in this situation as far as I can see.

    Here's what happens in the browsers I have installed:

    Opera 10.63 - this one isn't bad. The scaling works as expected. And performance is pretty good. The only issue are the grey vertical lines at hourly intervals. Those lines should stretch the height of the viewport (x1=100 y1=0 x2=100 y2=100%), i.e. the image. In this case Opera seems to have decided the viewport size is the browser window size due to the absence of explicit image dimensions. Ok, and looks good at the default zoom level, but as soon as you zoom out, you can see that they end at the original zoom level's edge, instead of extending to the new viewport edge as expected. To be fair, I can't find anything in the SVG specs that explicitly say what should happen, but this behaviour doesn't seem useful.

    Safari 5.0.3 / Chrome 8.0.552.210 beta - the WebKit family of browsers don't cope with this very well. As you zoom, it scales the image's dimensions as well as the SVG elements. This means that when you zoom out, the image appears clipped, making zooming out pointless. You can see the same behaviour when zooming in, as the scrollbars appear. Looking around at the SVG DOM, it seems that the root SVG element is given a value of 100% for both the width and height if they are not specified. On load, this is calculated from the implicit viewport, the window. When scaling though, these values are recalculated using the new scale factor instead, causing the problem.

    Firefox 4 Beta 7 - first off, the performance is horrible compared to every other browser. Stutters and lags, and it isn't even particularly complex, though it is large. When scaling, it exhibits the same issues as Opera. Hope they fix it before 4 goes gold.

    Internet Explorer 9 Beta - surprisingly, this is the only browser that rendered the SVG as expected (although it didn't have scrollbars either). The image scaled as expected, and the grey vertical lines worked as well. Performance was impressive too. Maybe they had the advantage of hindsight.

    The solution

    There are two. The first one is obvious - explicitly specify dimensions on the SVG image. All these problems go away if you do this.

    The second is a bit of a hack - if your SVG is going to be viewed in something that can execute JavaScript, you can use the following snippet to add the missing width and height attributes based on the bounding box of the rendered content, i.e. the width and height as rendered. The problem with this is that it doesn't cause scrollbars to appear in Opera. In Firefox 4 Beta 7, the scrollbars only appear as you start scaling. It works fine in Firefox 3.6.10 though.

    <script type="text/javascript">
        <![CDATA[
            function fixDimensions() {
                // fix bug with Opera, Firefox and Chrome when scaling. Surprisingly,
                // only IE9 works as expected.
                var svgEl = document.documentElement;
                var bBox = svgEl.getBBox();
                if (svgEl.width.baseVal.valueAsString == "100%")
                        svgEl.width.baseVal.valueAsString = bBox.width + "px";
                if (svgEl.height.baseVal.valueAsString == "100%")
                        svgEl.height.baseVal.valueAsString = bBox.height + "px";
            }
        ]]>
    </script>

    You then need to add the following attribute to your root SVG element -

    onload="fixDimensions()"

    That is needed as it doesn't seem to reliably work in all browsers if you add that handler using addEventListener, presumably because it isn't added before the load event fires.

    Here is the same SVG, but with the above script, and without the scaling script. Use the browser menus or keyboard shortcuts to test.

     

Comments

No comments have been posted yet.

Leave a comment