Implementation

A preliminary implementation of the system has been developed as an extension to Batik, a Java SVG browser. Batik, aside from animation, provided a quite complete implementation of SVG to be built upon. For the XPath expression evaluation support, the XPath package from the Xalan project has been used.

Presented here is a description of how the implementation works, how much code was needed to get it to the current level of functionality, and which features are currently supported.

Constraint solver

The heart of the system is the constraint solver. Each expression in the document is a one-way dataflow constraint, which basically means it is a simple assignment of an expression to a particular attribute. The expressions can not form a recursive loop. The implementation currently does not check for circular constraints and will throw a stack overflow exception if it attempts to evaluate such a constraint.

An object called the constraint manager keeps track of all of the constraints in the document. When a document is loaded and the XML document tree has been created, the constraint manager walks through the tree adding each expression to a hash table in which the key is the element and attribute in which the expression was found. The XPath expressions are parsed but not yet evaluated. Batik then proceeds to build the graphics tree which is essentially a tree of Java2D objects used to render the document. Once the graphics tree has been constructed the constraint manager evaluates each of the constraints.

Each constraint object has a dirty property which indicates whether this constraint's expression must be re-evaluated due to any dependencies having changed. Whenever a constraint's value is changed, it sets the dirty flag on all of its reverse dependencies (those constraints which depend on it) and then schedule those constraints to be reevaluated. With this system, whenever an element's property is requested, be it for some script in the document or by other parts of Batik for the purposes of rendering, no unnecessary calculations are performed.

A constraint updating can ultimately come from one of two sources. Firstly, it could be because some script in the document has modified the expression stored in the attribute. This modification would cause the new expression to be parsed, its dependencies and reverse dependencies determined, and then evaluated. Secondly it could be because a built-in function which returns information about the environment has changed. There are currently four such functions in the implementation, namely the c:viewport, c:currentScale and c:currentTranslate and c:time functions.

Size of code

Batik is a medium sized project. The Batik CVS sources have 295795 lines of code in total (counting the files in the xml-batik/sources/org directory). The addition of constraint handling needed just an additional 7362 lines of code. This is only a 2.5% increase in code. Xalan's XPath implementation, though, takes a fair amount of code, coming in at around 46000 lines of code (not counting localisation files). Taking this into account, the amount of code added to the base Batik distribution is around 18%; still not unreasonable.

Current implementation status

The following sections detail the CSVG syntax that is currently supported. The c: prefix denotes elements and XPath functions in the CSVG namespace, http://mcc.id.au/2004/csvg.

Elements

These new elements are supported:

<c:constraint attributeName="attribute-name" value="expression"/>

The c:constraint element defines a constraint for one of the parent element's XML attributes or CSS properties.

<c:variable name="variable-name" value="expression"/>

The c:variable element defines a variable and assigns an expression to it. The variable can be referenced in XPath expressions with $variable-name.

<c:property name="property-name" value="expression"/>

Properties work just like variables, but when used inside an element's shadow tree (as generated by XSLT templates under RCC) their values can be retrieved from outside the shadow tree with the c:property function.

Expressions

All standard SVG animatable properties may have constraints applied to them. Non-animatable properties may still be used as lvalues inside expressions. Any attribute that is not defined by SVG can also be defined by a constraint. The constraint solver knows the type of each attribute and CSS property, so that the results of expressions can be coerced (if possible) to the right type.

Functions

The following non-core XPath functions may be used:

Object c:if ( Boolean condition, Object whenTrue, Object whenFalse);
If condition is true, returns whenTrue, otherwise returns whenFalse.
Number c:min ( Number, ... );
Returns the smallest number passed to the function.
Number c:max ( Number, ... );
Returns the largest number passed to the function.
Rect c:bbox ( SVGLocatable );
Returns the bounding box of the given object.
Number c:currentScale ( );
Returns the currentScale property of the root SVG element.
Rect c:currentTranslate ( );
Returns the currentTranslate property of the root SVG element.
Rect c:viewport ( );
Returns the viewport property of the root SVG element.
Number c:x ( Point );
Number c:x ( Rect );
Returns the x property of the given object.
Number c:y ( Point );
Number c:y ( Rect );
Returns the y property of the given object.
Number c:width ( Rect );
Returns the width property of the given Rect.
Number c:height ( Rect );
Returns the height property of the given Rect.
Number c:time ( );
Returns the number of seconds since the document was loaded.
Element c:instance ( );
Return the element whose shadow tree the context node is a part of.
Object c:property ( Element, String );
Inspects the given element's shadow tree to find the first c:property element with the given name and returns its associated value.

There are also a few constructor functions, used purely for creating objects of SVG specific datatypes, that otherwise would not be supported in XPath expressions.

Length c:Length ( String );
Creates a Length object by parsing the given string.
Point c:Point ( String );
Point c:Point ( Number, Number );
When passed a string, creates a Point object by parsing the given string, which should be two numbers separated by whitespace and/or commas. Alternatively, two numbers may be passed, which will become the x and y values of the new Point.
Matrix c:Matrix ( String );
Matrix c:Matrix ( Number, Number, Number, Number, Number, Number );
When passed a string, creates a Matrix object by parsing the given string, which should be six numbers separated by whitespace and/or commas. Alternatively, six numbers may be passed, which will become the elements of the new Matrix.

Handling SVG types

The three SVG types for which constructor functions exist (that is, Point, Rect and Matrix) can be easily used in XPath expressions. There are however many more types recognised by the constraint system. The full table of attribute types lists the type of each recognised SVG attribute. Many types are simply a choice of a CSS identifier, and it would make no sense to require a constructor function to refer to these values. For this reason, strings will automatically be coerced another datatype when being assigned to an SVG property. Thus an expression defining a fill attribute may be specified as 'blue', for example. All values can be converted to and from strings. Some values can also be converted to values of other, non-string types. The type conversions table lists the conversions which are valid.

XPath operators are overloaded to make sense for opearting on objects of SVG specific datatypes. If an operation isn't defined between two particular types, the default XPath operation will be attempted. For example, multiplying two Point objects does not make sense, so the builtin XPath multiplication operation will attempt to convert both Points to Numbers and then multiply them. Conversion from Point to Number is not defined, however, and an error will result. The behaviour of overloaded operators on all the SVG types is detailed in the operators table.


Last updated: 12th February, 2004