In app development, the most common thing people do with an SVG is “render it to fit a specific area on screen”. This is very wise – it’s making use of the core feature of Scalable Vector Graphics: resolution independence.
Unfortunately, achieving that aim is a lot less obvious than you would expect. Most of the SVG Spec is extremely well written, but for this aspect the authors “had a bad day”, and wrote some rubbish prose that’s not technically possible. This leads to a lot of confusion…
I’m trying to make SVGKit for iOS/Mac be 100% standards compliant, and the lack of specification in this area has made it very difficult. Worse … people using the library are often confused by simple items like “how do I make the SVG file fit into a small area on screen?”.
It’s been difficult to work through, and I’ve decided to document what I *think* the authors intended – and how I’m implementing it in our open-source library.
W3C’s failure: the spec is WRONG
Before we start, I’m going to document exactly where the spec is incorrect, so that if they ever fix the spec, you (or I) can easily see if any of my assumptions have been overruled (by the better, newer spec).
- The “svg width” attribute is not defined
- The default value for “svg width” is illegal
- The “viewport” definition is INCOMPATIBLE with W3C’s own example of usage (given at: http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute)
NB: we are specifically talking ONLY about “the width of the outermost SVG element”. The “width” attribute for EVERYTHING ELSE IN SVG is *fully* defined.
The SVG Spec says this about “svg width”:
“If the attribute is not specified, the effect is as if a value of ‘100%’ were specified.”
…but, according to the spec, “%” is an illegal character in the “svg width” – only “presentation attributes” are allowed percentages, and “svg width” is explicitly NOT a presentation attribute (the full list is defined in SVG Spec Appendix N).
Even if were legal, the W3C committee fails to define what “percentage” actually means on an outermost SVG element (the definitions used elsewhere don’t make sense here). I’m sure there are “valid” implementations out there that use incompatible assumptions.
The viewport is described:
The SVG user agent negotiates with its parent user agent to determine the viewport
The ‘width’ attribute on the outermost svg element establishes the viewport’s width, unless the following conditions are met:
the SVG content is a separately stored resource that is embedded by reference (such as the ‘object’ element in XHTML [XHTML]), or the SVG content is embedded inline within a containing document;
and the referencing element or containing document is styled using CSS [CSS2] or XSL [XSL];
and there are CSS-compatible positioning properties ([CSS2], section 9.3) specified on the referencing element (e.g., the ‘object’ element) or on the containing document’s outermost svg element that are sufficient to establish the width of the viewport.
Under these conditions, the positioning properties establish the viewport’s width.
Firstly: that is some of the worst English writing I’ve seen in a long time. It’s way below the standard of writing used throughout the rest of the SVG Spec. I don’t know what happened here, but you get the impression the W3C committee wasn’t really paying attention when they signed-off on that :(.
Secondly: this is in direct conflict with the SVG Spec’s example usage of viewports. In the image below, the SVG Spec states that BOTH IMAGES have the same SVG source, that SPECIFIES an SVG width. According to the docs above, they would have to render identically!
Why did they do this anyway?
The first question when fixing a bug in a Spec is to ask: why did they add these features in the first place?
For viewport and width, we need to start with the viewbox. This is because the viewbox is WELL DEFINED, and the viewport and width are defined relative to it.
Also, it’s very easy to understand why it exists!
…why does SVG have a viewbox?
Easy: so that artists can have a “canvas” with many things on it, but tell apps/programmers “only use the bits I want you to see; ignore the stuff off the edges”. E.g. in Inkscape, the default setup is an A4 sheet of paper as your viewbox. Whatever you draw “outside the paper” is saved in your SVG – but won’t appear in any user-facing apps that use your SVG.
…why does SVG have an “svg width”?
Now it begins to make sense: if an artist can declare which parts of the image are “intended” to be rendered, what else do they want control over?
…Size, of course.
A normal bitmap has an explicit size in pixels. The artist decides how big the output image will be WHEN THEY CREATE THE FILE. But SVG has vector – size is meaningless.
That’s fine. And an app that merely “displays SVG files” will have no problem scaling every image to take up the full size of the app-window.
But many apps use images and RELY UPON the size of the image to do other things. e.g. when developing iPhone apps, we often use the “size” of an image to dictate how much space on-screen a widget should occupy. If the SVG file specified no size … how many pixels should we use to render it?
(this is where programmers get confused: the SVG spec states clearly that the ASSUMPTION is always that “1 unit = 1 pixel”)
But my long years of working with graphic artists show that many professional artists wilfully ignore simple facts like “1 unit is 1 pixel”, using arguments like “but it’s a vector, you can render it any size, so render it the size I told you to”. So, I suspect … the W3C committee that defined SVG decided to add a practical feature that would allow artists and programmers to BOTH get an easy life.
In that case, the correct definition of SVG width would be:
SVG width is the number of pixels wide this image would be if it had been created as a bitmap;
SVG height is the number of pixels tall this image would be if it had been created as a bitmap
…why does SVG have a “viewport”?
Ah, but it doesn’t! If you look closely, there is nothing in SVG spec that allows an SVG file to say anything about this mythical “viewport”.
In the real world, there are only three ways to render an image file, and one of those is a sub-type of the others:
- Take an image, scale it to fit the pre-determined “window” area on screen, and draw it inside that area
- e.g. in an image-viewer, there is a fixed area available (the main panel of the app), and you want to show each image COMPLETELY and EXACTLY fitting inside that area
- Take an image, draw it on screen, and resize the rest of the screen contents based on the image’s size
- e.g. in a list, if you have an icon for each row, make the height of each row be “the height of the icon image, plus 5 pixels above and 5 pixels below”
- Draw something which contains an embedded SVG image (e.g. draw a webpage that contains an SVG inside it)
- …this is actually either 1 or 2 above, depending on context.
For option 1, we only need to know “the total extent of the SVG image”, so that we can decide how much to scale it. This is COMPLETELY SOLVED by the SVG containing a “viewbox” attribute. Nothing else is needed, and nothing else would make sense.
For option 2, the viewbox is useless, but the “svg width” and “svg height” attributes tell us what the original artist intended.
For option 3 … choose one of 1 or 2 above depending on context.
…which leaves no need for a “viewport” concept. Except … it is useful when defining the SVG Spec to be able to describe things inside the SVG relative to the actual on-screen area where the SVG is being drawn.
And so: my *guess* is that the definition of “viewport” should be:
The area on screen, in pixels, where the SVG will be rendered.
This is decided by the APPLICATION, and can be any size, independent of the SVG file
In some cases, the APPLICATION will CHOOSE to use the “svg width” and “svg height” info, or the CSS styling info, to decide the size and shape of the viewport
In other cases, the APPLICATION will CHOOSE a viewport based on external information, and FORCE the SVG to render into that viewport, scaling the SVG’s viewable area to PRECISELY fill the viewport.
If the APPLICATION chooses to ignore the “svg width” and “svg height”, and the SVG file has a viewbox, it should COMPLETELY IGNORE the “svg width” and “svg height” and scale the SVG contents so that the area covered by the viewbox precisely fills the area of the viewport. Alternatively, if the APPLICATION is ignoring the “svg width” and “svg height” and the SVG file *does not* have a viewbox, it should scale the SVG contents by the ratio of “viewport width” to “svg width” (e.g. if viewport is 100px, and “svg width” is 200px, then all units inside the SVG should be scaled by 1/2).