XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

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

Big Lists in Small Spaces

SVG At the Movies

Mobile SVG

SVG and Typography: Animation

Going Mobile With SVG: Standards

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.



1 to 3 of 3
  1. Is there a Part Two?
    2004-04-19 09:13:01 efultyn
  2. Error Message
    2003-05-07 04:53:15 Doug Talbott
  3. Javascript error
    2002-09-24 03:16:48 Francisco Monteiro
1 to 3 of 3