Extending SVG for XForms
by Antoine Quint
|
Pages: 1, 2
Parsing CSS strings with the DOM
Locating and making sense of the actual XML elements in the
XForms namespace was a good start. But our
<forms:button> elements also have some CSS
properties that we need to parse since SVG does not support
the CSS 2 Box Model as-is. When reading CSS from SVG elements,
we use the style field of SVG DOM objects, which
returns a nice CSSStyleDeclaration
CSS DOM object. The SVG Viewer knows from the DTD that the
style attribute in the SVG world maps to CSS DOM
objects. Alas, the style attribute is just a
random attribute in XForms as far as the SVG Viewer is
concerned, so it's up to us to parse the CSS data.
I read from the <forms:button>'s
style attributes and we build a
CSSStyleDeclaration object from it. The CSS DOM
does not have functionality to do this, so I implemented it
in Button.generateSVGStyle():
var style = this.element.getAttribute('style');
var dummy = document.createElementNS(SVG.ns, 'rect');
dummy.setAttribute('style', style);
The trick is to create a dummy SVG element, which will never
get rendered, in order use its ability to return a
CSSStyleDeclaration from its style
attribute. So we read the style attribute from
our <forms:button> element, fed it to our
dummy SVG element, and now we will be able to get its
corresponding CSSStyleDeclaration by calling
dummy.style. Now that we have an easy access to
the CSS data, we can construct a new CSS string for the SVG
representation of our button:
var fill = dummy.style.getPropertyValue('background-color');
var stroke = dummy.style.getPropertyValue('border-color');
var strokeWidth = dummy.style.getPropertyValue('border-width');
if (fill == '') {
fill = '#d4d0c8';
}
if (stroke == '') {
stroke = '#404040';
}
if (strokeWidth == '') {
strokeWidth = '1px';
}
style = '';
style += 'shape-rendering: optimizeSpeed; ';
style += 'fill: ' + fill + '; ';
style += 'stroke: ' + stroke + '; ';
style += 'stroke-width: ' + strokeWidth + '; ';
I also took the liberty to specify default values for the
XForms CSS properties. Having a
<forms:button> element with no style
specified will result in an unattractive grayish color. It's
always nice to allow for some flexibility. Here we could also
have created another CSSStyleDeclaration to start
building our new CSS string. It would have been less
error-prone, but here just creating a simple string proved to
be just fine. Another thing that would have been smart here
would have been to create an EcmaScript
CSSStyleDeclaration object that would have
allowed both constructing from a plain string containing CSS
data and printing the object to a plain string. This would
have hidden the SVG dummy element's logic, but I tried to
keep the code as simple as possible.
Drawing the Button
Now that we have figured out the XML and CSS parsing, it would
be the right time to start drawing something on the
screen. Back in the initialization method you noticed the call
button._draw(). Taking a closer look at that
method, we see it starts by asking for the SVG to be generated
through a call to this.generateSVG(). Let's check
out the Button.generateSVG() method:
Button.prototype.generateSVG = function () {
this.caption.generateSVG();
this.SVGElement = document.createElementNS(SVG.ns, 'rect');
var width = Math.round(this.caption.getSize().width + this.marginLeft * 2);
var height = Math.round(this.caption.getSize().height + this.marginTop * 2);
this.SVGElement.setAttribute('width', width);
this.SVGElement.setAttribute('height', height);
this.SVGElement.setAttribute('style', this.style);
}
We start by asking our button's caption to generate its SVG code:
Caption.prototype.generateSVG = function () {
this.SVGElement = document.createElementNS(SVG.ns, 'text');
var textNode = document.createTextNode(this.getText());
this.SVGElement.appendChild(textNode);
var x = this.button.marginLeft;
var y = this.getSize().height + this.button.marginTop;
this.SVGElement.setAttribute('x', x);
this.SVGElement.setAttribute('y', y);
this.SVGElement.setAttribute('style', this.SVGStyle);
}
Both methods go through approximately the same process. The
Button creates an SVG <rect>
element, while the Caption generates a
<text> element. Both also apply the CSS
style computed with their respective
generateSVGStyle() to their style
attributes. The tricky part in drawing a button is to get the
size of the SVG <text> element we will
create in order to compute its location as well as its
wrapping rectangle's size. In effect, I've implemented a
getSize() method on the Caption
object:
Caption.prototype.getSize = function () {
return this.SVGElement.getBBox();
}
|
Also in Sacré SVG |
And who's showing up? It's our old friend the
SVGLocatable::getBBox() method from the SVG
DOM. Not only does it apply to SVG shapes, but it also applies
to text elements. So all we need to do is take into account
the margin settings we have specified on our
Button object (the marginLeft and
marginTop properties) to figure out the correct
layout. Now the last thing to worry about is where we are
going to put that new SVG code in our existing DOM tree.
Looking back at the Button._draw() method, we can
see that the new graphics (a <rect> and a
<text>) have been appended as siblings to
the XForms button element. Doing so offers two main
advantages. First, it kept the XForms XML and SVG
representation at the same hierarchical levels, retaining the
structural meaning of the original XForms element. Second, our
SVG element's will now inherit transformations which will
allow explicit positioning that's completely painless since
the SVG Viewer will take care of it. So in the end, we managed
to combine comfort with value, a good deal all in all.
Wrapping the First Part Up
Though this was a bit of a trek, all we've ended up with is a static button, though it can be style and positioned. For now it was important to be able to make sense of a document with multiple namespaces through DOM scripting. In next month's column we'll add SVG interactivity.
- Is there a Part Two?
2004-04-19 09:13:01 efultyn - Error Message
2003-05-07 04:53:15 Doug Talbott - Error Message
2003-09-16 20:13:33 devi devi - Error Message
2003-05-07 05:01:39 Doug Talbott - Javascript error
2002-09-24 03:16:48 Francisco Monteiro