Adaptive layout using one-way constraints in SVG

Keywords: layout, constraints, expression evaluation

Cameron McCormack
PhD student
Monash University
Clayton, 3800
Victoria
Australia
clm@csse.monash.edu.au
http://www.csse.monash.edu.au/~clm/

Biography

Cameron is a PhD student at Monash University in Melbourne.

Kim Marriott
Researcher
Monash University
Clayton, 3800
Victoria
Australia
marriott@csse.monash.edu.au
http://www.csse.monash.edu.au/~marriott/

Biography

Kim is a co-director of the Optimisation and Constraint Solving Research Group at Monash University, Melbourne.

Bernd Meyer
Senior Lecturer
Monash University
Clayton, 3800
Victoria
Australia
bernd.meyer@acm.org
http://welcome.to/bernd.meyer/

Biography

Bernd is senior lecturer at Monash University, Melbourne.


Abstract


SVG [SVG11] does not support adaptation of the documents layout to the viewing context, such as adapting the layout to take into account the users desire for larger fonts or the size of the browser window. Adding declarative layout mechanisms to SVG would allow such adaptive behaviour. One suggestion is that SVG allow attribute values to be specified with expressions and that these expressions be evaluated at display time to determine the attribute values [MMT02] . Here we describe a minimal extension to SVG 1.1, CSVG, that provides this capability. We demonstrate that this extension allows the author to create documents with sophisticated layout behaviours that otherwise would not be possible without the use of script. We have fully implemented the suggested SVG extensions in Batik. Our implementation demonstrates that this can be achieved with remarkably little code overhead compared to the standard version. Furthermore, we show that our extension combines well with XSLT-definition of custom elements, allowing efficient automatic update of the shadow tree if custom element attributes are changed as a result of user interaction or animation.


Table of Contents


1. Introduction
2. Extension to SVG
     2.1 Specifying constraints
     2.2 Expression evaluation
3. CSVG example
4. Templates
     4.1 Templates and constraints
     4.2 Template example
5. Implementation
6. Comparison with scripting
7. Future work
8. Conclusion
Footnotes
Bibliography

1. Introduction

The desire for declarative layout mechanisms in SVG [SVG11] which allow the layout to adapt to the viewing context has been expressed before [BTM01] and has been explicitly included in previous SVG requirements documents [SVGReq] . One suggestion is that SVG allow attribute values to be specified with expressions and that these expressions be evaluated at display time to determine the attribute values [MMT02] . Here we describe a minimal extension to SVG 1.1, CSVG, that provides this capability. We demonstrate that this extension allows the author to create documents with sophisticated layout behaviours that otherwise would not be possible without the use of script. We have fully implemented the suggested SVG extensions in Batik[1] Our implementation demonstrates that this can be achieved with remarkably little code overhead compared to the standard version.

As a motivating example consider the user interface widget shown in Figure 1 . This has been written in CSVG and demonstrates three kinds of layout adaptation. First, the layout adapts to the size of text elements. Text is unique in SVG in being the only graphical object for which the bounding box cannot be determined before rendering. Unfortunately, as our motivating example illustrates, SVG authors often wish to lay out other graphical objects relative to the dimensions of text. In standard SVG the author must either assume the dimensions of the text are unchanged at display time or use script to get the bounding box at display time. Of course, it is unsafe to assume the text size is fixed as the user agent may use a font different from the one specified in the SVG document. An important instance of this scenario is when documents are viewed by users with poor sight who require large font sizes. Most current SVG documents do not adapt well to such client-side font changes.

uict-rendering2.png

Figure 1: Widget layout (rendering)

Adaptation to text element size is also useful in documents that must adapt to different languages. If a switch element is used to select different text based on the user agent's selected language, the dimensions of the text element will likely be different and the layout must be adapted to the new size. Figure 2 shows how the layout adapts to a larger font size and to a different language.

uict-rendering1.png

Figure 2: Widget layout with larger font size and different language (rendering)

The second kind of adaptation provided by our example widget is to adjust the layout to the dimensions of the browsing window. Figure 3 demonstrates how the example widget layout adjusts to a narrower browsing window such as that on a PDA. Such adaptation is important if SVG is to allow the same document to be viewable on a wide range of viewing devices rather than requiring the author to generate different documents with essentially the same content for different platforms. In some cases the simple scaling currently afforded by SVG 1.1 is sufficient but this may result in small, illegible text and poor use of the display area.

uict-rendering3.png

Figure 3: Widget layout adapting to window size (rendering)

Another example illustrating the need for adaptation to the browsing window is the linear flow diagram shown in Figure 15 . When displayed on a computer monitor the diagram would be better shown laid out horizontally, as computer monitors typically have a greater width than height. When viewed on a PDA, however, the diagram should be displayed vertically, since a PDA usually has a portrait orientation. If it is being viewed in a resizable window, we want the diagram to adapt to these changes in canvas dimensions as the resizing occurs.

The third kind of layout adaptation provided by our example is layout modification as a result of user interaction. This is shown in Figure 4 , where unchecking the "Use HTTP proxy" checkbox causes a part of the form to be hidden and the remaining widgets to be re-layed out. Adaptation to user interaction is clearly an important requirement for interactive applications and user interfaces. For instance, menus or expanding/collapsing directories in a file system tree or nodes in an organisation chart.

uict-rendering4.png

Figure 4: Widget layout adapting to user interaction (rendering)

Layout adaptation as a result of animation is closely related to this. Rather than having to explicitly animate every component in the document whose layout changes as a result of the animation one need only animate the principal objects and allow the layout of the secondary objects to be updated automatically. An example of this is given in Figure 20 and Figure 21 . Here the edges of the graph automatically follow the animated nodes without being explicitly animated themselves.

The need to support efficient, incremental re-layout of document elements is, we believe, a powerful reason for providing declarative mechanisms in SVG for specifying layout and style properties. Of course script can be used to modify SVG element attributes when other document elements are modified as a result of user interaction but this requires event handlers to be explicitly added to objects to be notified of some property change and consequent propagation of these changes, in a cascading effect. With a declarative syntax for specifying object properties with expressions, the SVG viewer can determine which expressions depend on a particular property and force the re-evaluation of these dependant expressions. This re-evaluation must be performed regardless of the method of implementation, be it script or constraints, but declarative specification allows the user agent to manage the updates for the author.

The other main extension provided by CSVG is custom elements whose shadow tree is specified declaratively using XSLT (similar to the dropped RCC proposal). One might think that providing such custom elements would remove the need for display-time evaluation of base SVG attribute values since one could use XSLT to compute the values of the attributes of the base SVG elements when generating the shadow tree. In fact even with custom elements it proves very useful to allow base SVG elements to have dynamically evaluated attribute values.

The first reason is that XSLT performs static generation of the shadow tree and so it does not support efficient regeneration of the shadow tree when custom element attributes are modified as a result of user interaction or animation. Since CSVG allows the generated shadow tree elements' attributes to be specified in terms of the custom element attributes, regeneration of the shadow tree is not required in many common cases. Propagation of changes from the custom element to the shadow tree is performed automatically.

A second reason is that XSLT is intended to implement a purely syntactic transformation: it has no way of determining semantic properties, such as the bounding box of text elements. Thus layout adaptation to text size requires either script or the shadow tree elements to have expressions specifying attribute values.

We describe CSVG and the language design decisions and how we have extended Batik to support this extension. Our implementation utilises one-way constraint solving algorithms to propagate changes which occur due to interaction. One-way constraints are efficient to compute, and are used in a variety of applications including GUIs, spreadsheets and customisable graphic editors such as Visio [VHM01] . We demonstrate the usefulness of CSVG by showing how it provides the different types of adaptation discussed above and compare the resulting code with that obtained using script and SVG 1.1. Furthermore, we show that such an extension is straightforward to implement and requires relatively little additional code (our extension to Batik required only 2.5% more code assuming that an XPath implementation is available). Importantly, CSVG's syntax and semantics is designed to be backwards compatible with standard SVG documents and allows default values to be supplied for browsers which do not understand the extension.

2. Extension to SVG

The extension we have developed to allow expression-based attributes is reasonably straightforward.

2.1 Specifying constraints

To specify an expression to be used to compute the value of an attribute or CSS property a c:constraint element is placed as the child of the element whose attribute or CSS property is being constrained. The use of the c:constraint element parallels the use and behaviour of the various SMIL elements used to animate attributes and CSS properties in SVG. An attributeName attribute indicates which attribute or CSS property is being constrained and a value attribute specifies the actual expression to be evaluated. The expression is written in XPath 1.0.

  1    <svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300">
  2      <circle id="c" cx="100" cy="100" r="75"/>
  3      <rect width="150" height="150">
  4        <c:constraint attributeName="x" value="id('c')/@cx"/>
  5        <c:constraint attributeName="y" value="id('c')/@cy"/>
  6      </rect>
  7    </svg>

Figure 5: Simple CSVG example (code)

simple-rendering.png

Figure 6: Simple CSVG example (rendering)

Figure 5 and Figure 6 show the code and resulting image for a simple example where the x and y attributes of a rectangle are specified to be equal to the centre point of a circle. In both expressions, the core XPath function id selects the circle element. In the first expression, the cx attribute of the circle element is then selected, in the second expression the cy attribute is chosen. In this example it did not matter that an XPath implementation would evaluate both of the expressions by getting the string value of the respective attributes. Complex expressions combining different values may need to be able to distinguish between different types of expressions (here, for example, the fact that cx and cy are lengths).

  1    <svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300">
  2      <rect id="r" x="50" y="50" width="100" height="200" fill="blue"/>
  3      <rect y="50" width="100" height="100" fill="red">
  4        <c:constraint attributeName="x" value="id('r')/@x + id('r')/@width"/>
  5      </rect>
  6    </svg>

Figure 7: Simple CSVG example adding lengths (code)

simple2-rendering.png

Figure 8: Simple CSVG example adding lengths (rendering)

To allow XPath expressions to operate on SVG types the XPath implementation in the CSVG extension is extended to overload operators. For example, take the code in Figure 7 , where a red rectangle is placed to the right of a blue rectangle. In a standard XPath implementation the expression id('r')/@x + id('r')/@width would result in the string values of the x and width attributes of the first rectangle being converted to numbers and these numbers then added together. Since the strings "2in" and "2cm" are not valid numbers, the result is NaN. In our implementation the XPath engine knows the types of each attribute that can occur on an SVG element and also allows overloading the numeric operators so that sensible operations can occur on SVG types. Here, the two Length values are converted to user units and added to result in a new Length value.

The SVG types that the XPath processor supports are Length, Point, Rect and Matrix. The standard XPath operators are overloaded for certain combinations of these values to perform useful operations. For example, a Rect can be multiplied by a standard XPath number to result in a Rect whose x, y, width and height values have been multiplied by that number. The availability of operators on matrices allows us to work with transforms directly, for example, a transformation can be applied by multiplying a Point with the current transformation matrix. Any combination of operands and operator that does not have a special behaviour in CSVG will be evaluated by falling back to the standard XPath operator. A table of all of the available operator overloadings can be seen on the project website. A CSVG implementation will know the type of each standard attribute of the SVG elements, which is why in the previous example referring to a rect element's x attribute gets a Length value.

The special operators are required for the remaining types in SVG. However, they are still used in checking expressions for type errors. For example, assume that the font-size attribute of an element is constrained to be equal to the fill attribute of another element. A CSVG implementation will flag this as an error. If for some reason an attribute manipulation is required that cannot be implemented in terms of the given operators, string functions can still be used to represent the representation of these attributes.

The special operators are required for the remaining types in SVG. However, they are still used in checking expressions for type errors. For example, assume that the font-size attribute of an element is constrained to be equal to the fill attribute of another element. A CSVG implementation will flag this as an error. If for some reason an attribute manipulation is required that cannot be implemented in terms of the given operators, string functions can still be used to represent the representation of these attributes.

There are also a number of XPath extension functions which are defined in CSVG. Table 1 lists these functions. In addition to extension functions which perform some calculation that is possible with the DOM — such as finding the bounding box of an element, or its transformation matrix — there are those which create SVG values and manipulate them. For example, to add 2cm to some element's x attribute you could use the expression id('some')/@x + c:Length('2cm'). This uses the c:Length constructor function to create a Length value from the string "2cm". Since some lengths depend on a context (such as the current viewport for perctentage values, and the current font size for em and ex values), the length constructor functions take a node as an optional second argument to be used as the context.

Because of the operator overloading and handling of types, the XPath engine is extended in some backwards incompatible ways. Obviously, this would be a concern for implementors, since leverage of existing code is important. In cases where an existing XPath implementation is not readily extended to support the expressions possible in CSVG, these expressions could be thought of as just a shorthand for the traditional XPath syntax. Both the typed attribute selection and operator overloading could instead be performed only with extension functions. XPath 1.0 does allow extension functions and for such functions to return values of non-standard types. CSVG expressions written in this form would be much less readable and less intuitive to write, but they would be equivalent. A CSVG user agent could preprocess CSVG expressions in to a form that is understood by a standard XPath engine.

Extension functions available in the CSVG XPath engine
Function name Description
c:if Returns one value if a given condition is true, another value if it is false.
c:min Returns the smallest number passed to the function.
c:max Returns the largest number passed to the function.
c:bbox Returns the bounding box of an object.
c:screenCTM Returns the screen current transformation matrix of an object.
c:CTM Returns the current transformation matrix of an object.
c:height Returns the height property of a Rect.
c:width Returns the width property of a Rect.
c:x Returns the x property of a Point or Rect.
c:y Returns the y property of a Point or Rect.
c:instance When used inside a shadow tree, returns the custom element which instantiated the shadow tree.
c:inverse Inverts a Matrix.
c:property Gets a property from a custom element.
c:time Returns the number of seconds since the document was loaded.
c:viewport Returns the viewport property of the root SVG element.
c:Length Constructor function to create a Length.
c:LengthH Constructor function to create a horizontal Length.
c:LengthV Constructor function to create a vertical Length.
c:Matrix Constructor function to create a Matrix.
c:Point Constructor function to create a Point.

Table 1

Figure 9 shows an example that uses the c:bbox extension function to get the bounding box of a text element. The c:bbox function returns a Rect, whose four properties are extracted using the c:x, c:y, c:width and c:height functions. These values are used to position a rectangle to fit around the text. In Figure 10 , the user agent's default font family and size are used. Figure 11 shows the same example when a user stylesheet has been applied.

  1    <svg xmlns="http://www.w3.org/2000/svg"
  2         xmlns:c="http://mcc.id.au/2004/csvg"
  3         width="600" height="100">
  4      <text id="t" x="300" y="50" text-anchor="middle">
  5        The rect should contain this text
  6      </text>
  7      <rect width="0" height="0" stroke="red" stroke-width="2" fill="none">
  8        <c:constraint attributeName="x" value="c:x(c:bbox(id('t'))) - 4"/>
  9        <c:constraint attributeName="y" value="c:y(c:bbox(id('t'))) - 4"/>
 10        <c:constraint attributeName="width" value="c:width(c:bbox(id('t'))) + 8"/>
 11        <c:constraint attributeName="height" value="c:height(c:bbox(id('t'))) + 8"/>
 12      </rect>
 13    </svg>

Figure 9: Simple CSVG example adapting to text size (code)

simple3-rendering1.png

Figure 10: Simple CSVG example adapting to text size (rendering)

simple3-rendering2.png

Figure 11: Simple CSVG example adapting to text size with user stylesheet (rendering)

SMIL defines its animations in terms of a function f(t) which maps times to values. CSVG has a function c:time which returns the number of seconds since the document was loaded. This can be used to perform any sort of animation by writing an expression in terms of this time function.

Any XPath expression can be used in a c:constraint element so long as it does not cause a cyclic dependency. That is, an expression defining the value for a particular attribute cannot directly or indirectly, via another constraint, refer to itself. Figure 12 shows an example which is invalid due to a cyclic dependency.

  1    <svg xmlns="http://www.w3.org/2000/svg" width="300" height="300">
  2      <text id="t1">
  3        <c:constraint attributeName="y" value="id('t2')/@y + 10"/>
  4        Some text
  5      </text>
  6      <text id="t2">
  7        <c:constraint attributeName="y" value="id('t1')/@y"/>
  8        More text
  9      </text>
 10    </svg>

Figure 12: Cyclic dependency (code)

Variables can be defined with the c:variable element. Such elements have a name attribute, giving the name of the variable, and a value attribute gives its expression. This allows the author to break up complex expressions for reuse. Variables can be referenced in XPath expressions using the dollar character followed by the variable name, as in XSLT. The scoping rules for variables in CSVG are also similar to those in XSLT. When a variable is referenced, the corresponding declaration will be searched for in all enclosing contexts of the expression in the inverse order of depth and within a single level in reverse document order. The first c:variable element with a matching name that is found is the variable that the variable reference is resolved to. Thus variables can be redefined by redeclaring them in a position such that they shadow an existing variable with the same name. Variable references from that point in the document onwards will see this new variable with the same name.

In some cases it may be necessary to refer to a variable which occurs later in the document. For this, a location path can be used to select the relevant c:variable element and get its value attribute.

2.2 Expression evaluation

As mentioned above, one of the primary benefits of using a declarative model for the specification of expressions for attribute value computation is that the user agent can take away from the author the burden of determining when expression re-evaluation should occur. The constraint system that is being used in CSVG is a system of one-way constraints. These constraints, also known as dataflow constraints, are similar to the systems of expressions one can use in spreadsheets, where a cell can be assigned a value from an arbitrary expression, so long as there is no cyclic dependency.

To ensure that expressions are not re-evaluated needlessly, a CSVG user agent must determine the dependencies for the document. The dependency analysis will associate with each expression a set of forward and reverse dependencies. A forward dependency is something that the current expression depends upon. Each reverse dependency is an expression which depends upon the current expression. Currently, there are seven types of dependencies:

Attribute dependency
This form of dependency exists when one expression refers to another attribute or CSS property in the document.
Variable dependency
If an expression refers to a variable then the closest scoped c:variable element with the same name must be watched for changes in its value. [2]
Bounding box dependency
An expression has a bounding box dependency on a particular element if it contains a c:bbox function call on that element. Bounding boxes must be handled differently, as many properties (but not all) will cause an object's dimensions to change if they are modified.
Viewport dependency
A viewport dependency will exist if an expression calls the c:viewport function to get the current dimensions of the viewport. When an SVGResize event occurs, expressions with viewport dependencies will be updated.
Zoom and pan dependency
If an expression uses the c:screenCTM function to get the screen current transformation matrix for an element then a zoom and pan dependency will exist for this expression, since zooming and panning in the user agent (SVGScroll and SVGZoom events) will cause the screen CTM to change.
Time dependency
An expression will have a time dependency if it calls the c:time function to get the current animation time. Such expressions will be updated each time the animation engine causes a clock tick.
Node dependency
When an expression uses a location path to traverse the document, the nodes selected by each step of the path must be monitored for mutations as they could potentially cause the expression to evaluate differently. A node dependency exists for each step of the path.

It is this last kind of dependency that is the most difficult to analyse. Since a location path can pass over a number nodes in the document as it is evaluated, modifications to any of these nodes may require the expression to be re-evaluated. We have devised an algorithm for determining the node dependencies for a given XPath expression. It involves partially evaluating the XPath expression by following the steps in any path which is not inside a predicate. At each step as the path is being partially evaluated node dependencies are added for any node which matches the node test of the step.

  1    <svg xmlns="http://www.w3.org/2000/svg"
  2         xmlns:svg="http://www.w3.org/2000/svg"
  3         xmlns:c="http://mcc.id.au/2004/csvg"
  4         width="400" height="400">
  5    
  6      <g id="g" fill="red">
  7        <svg height="350">
  8          <circle cx="100" cy="100" r="10"/>
  9          <circle cx="200" cy="200" r="20"/>
 10          <circle cx="300" cy="300" r="30"/>
 11        </svg>
 12      </g>
 13    
 14      <text x="0" y="370" text-anchor="middle" font-size="20">
 15        This is
 16        <c:constraint attributeName="x" value="id('g')/*/svg:circle[@r > 25]/@cx"/>
 17      </text>
 18      <text x="0" y="395" text-anchor="middle" font-size="20">
 19        a big circle
 20        <c:constraint attributeName="x" value="preceding-sibling::svg:text/@x"/>
 21      </text>
 22    </svg>

Figure 13: Dependency analysis example (code)

To illustrate dependency analysis, consider the example in Figure 13 . The constraint on line 16 will get the cx attribute of the third circle element in the document, but there are more dependencies that will be set up than just the one on that attribute. Beginning at the first step of the location path, the element with ID "g" is selected. Once that node is selected, all child elements are selected. In this case there is only one element, an svg, but if another element is inserted as the child of the g element (or one is removed) the expression will need to be re-evaluated. So, we add a node dependency to the expression on the g element for any node that matches the XPath node test *. From the svg element we select any circle element whose r attribute is greater than 25 user units. For the purpose of dependency analysis, predicates on attribute values in the XPath expression must be inspected to ensure modifications which would cause the predicate to evaluate differently cause the whole expression to be evaluated. However, when following to the next step of the location path, we ignore the predicate and simply select all of the circle elements that are children of the svg element. Thus in this case a node dependency is added to the expression on the svg element for all children which match the node test svg:circle. Then, attribute dependencies are added for each of the circle elements' r attributes, as a change to an r may cause a different circle to be selected. Finally, an attribute dependency is added on the circle elements for the cx attribute. Note that even though initially only one circle element is selected according to the predicate, watching all cx attributes of the circle elements is safe and will ensure that constraints are propagated correctly.

When the values of any expression change while the document is being displayed, the change can quickly be propagated by transitively following the dependencies that have been set up. As there are no cycles in the dependency graph, the propagation is guaranteed to terminate after time linear in the number of expressions.

When the expression that determines an attribute or CSS property's value is updated the effect is just as if a SMIL animation had caused the update. That is, for attributes the animated value of the appropriate attribute of the DOM object is changed. For CSS properties, the override stylesheet is modified to include the new value for the property being updated.

3. CSVG example

To demonstrate our extension to SVG we describe a simple example and how a CSVG user agent would handle it. The example is that of a flow diagram with three nodes. Each node is a rectangle with text centered within and arrows are placed between adjacent nodes. We wish this flow diagram to be viewable both on a computer monitor, which has a landscape orientation (its width is greater than its height) and also on a PDA, which has a portrait orientation (height greater than width). To this end, we want the flow diagram to be laid out different depending on the available space. If there is sufficient horizontal space to lay the flow diagram out horizontally then it should be done so. Otherwise, it should be displayed vertically. We also want each node in the diagram to be the same size, just large enough to hold the largest node label.

  1    <svg width="100" height="600" overflow="visible"
  2      xmlns="http://www.w3.org/2000/svg"
  3      xmlns:c="http://mcc.id.au/2004/csvg">
  4    
  5      <defs>
  6        <marker id="Triangle" viewBox="0 0 10 10" refX="10" refY="5"
  7          markerUnits="strokeWidth" markerWidth="16" markerHeight="12"
  8          orient="auto">
  9          <path d="M0,0 L10,5 L0,10 z"/>
 10        </marker>
 11      </defs>
 12    
 13      <!-- horizontal: whether the layout is horizontal or vertical -->
 14      <c:variable name="horizontal"
 15        value="c:width(c:viewport()) >= $boxWidth * 3 + $desiredGap * 2
 16                 + $margin * 2"/>
 17    
 18      <!-- minGap: the gap below which the layout will change -->
 19      <c:variable name="desiredGap" value="30"/>
 20    
 21      <!-- margin: the gap between the canvas border and the boxes -->
 22      <c:variable name="margin" value="10"/>
 23    
 24      <!-- boxPadding: the padding between text and a box border -->
 25      <c:variable name="boxPadding" value="10"/>
 26    
 27      <!-- boxWidth: the width of the boxes -->
 28      <c:variable name="boxWidth" value="c:max($t1w, $t2w, $t3w) + $boxPadding * 2"/>
 29    
 30      <!-- boxHeight: the height of the boxes -->
 31      <c:variable name="boxHeight" value="c:max($t1h, $t2h, $t3h) + $boxPadding * 2"/>
 32    
 33      <!-- box coordinates -->
 34      <c:variable name="b1x" value="c:if($horizontal, $margin,
 35        c:width(c:viewport()) div 2 - $boxWidth div 2)"/>
 36      <c:variable name="b1y" value="c:if($horizontal,
 37        c:height(c:viewport()) div 2 - $boxHeight div 2, $margin)"/>
 38    
 39      <c:variable name="b2x" value="c:width(c:viewport()) div 2 - $boxWidth div 2"/>
 40      <c:variable name="b2y" value="c:height(c:viewport()) div 2 - $boxHeight div 2"/>
 41    
 42      <c:variable name="b3x" value="c:if($horizontal,
 43        c:width(c:viewport()) - $margin - $boxWidth,
 44        c:width(c:viewport()) div 2 - $boxWidth div 2)"/>
 45      <c:variable name="b3y" value="c:if($horizontal,
 46        c:height(c:viewport()) div 2 - $boxHeight div 2,
 47        c:height(c:viewport()) - $margin - $boxHeight)"/>
 48    
 49      <!-- text coordinates -->
 50      <c:variable name="t1x" value="$b1x + $boxWidth div 2"/>
 51      <c:variable name="t1y" value="$b1y + $boxHeight - $boxPadding"/>
 52    
 53      <c:variable name="t2x" value="$b2x + $boxWidth div 2"/>
 54      <c:variable name="t2y" value="$b2y + $boxHeight - $boxPadding"/>
 55    
 56      <c:variable name="t3x" value="$b3x + $boxWidth div 2"/>
 57      <c:variable name="t3y" value="$b3y + $boxHeight - $boxPadding"/>
 58    
 59      <!-- text dimensions -->
 60      <c:variable name="t1w" value="c:width(c:bbox(id('t1')))"/>
 61      <c:variable name="t1h" value="c:height(c:bbox(id('t1')))"/>
 62      <c:variable name="t2w" value="c:width(c:bbox(id('t2')))"/>
 63      <c:variable name="t2h" value="c:height(c:bbox(id('t2')))"/>
 64      <c:variable name="t3w" value="c:width(c:bbox(id('t3')))"/>
 65      <c:variable name="t3h" value="c:height(c:bbox(id('t3')))"/>
 66    
 67      <!-- boxes -->
 68      <g fill="yellow" stroke="black" stroke-width="3">
 69        <rect id="b1" x="10" y="10" width="80" height="40">
 70          <c:constraint attributeName="x" value="$b1x"/>
 71          <c:constraint attributeName="y" value="$b1y"/>
 72          <c:constraint attributeName="width" value="$boxWidth"/>
 73          <c:constraint attributeName="height" value="$boxHeight"/>
 74        </rect>
 75        <rect id="b2" x="10" y="280" width="80" height="40">
 76          <c:constraint attributeName="x" value="$b2x"/>
 77          <c:constraint attributeName="y" value="$b2y"/>
 78          <c:constraint attributeName="width" value="$boxWidth"/>
 79          <c:constraint attributeName="height" value="$boxHeight"/>
 80        </rect>
 81        <rect id="b3" x="10" y="550" width="80" height="40">
 82          <c:constraint attributeName="x" value="$b3x"/>
 83          <c:constraint attributeName="y" value="$b3y"/>
 84          <c:constraint attributeName="width" value="$boxWidth"/>
 85          <c:constraint attributeName="height" value="$boxHeight"/>
 86        </rect>
 87      </g>
 88    
 89      <!-- texts -->
 90      <g font-size="20" text-anchor="middle">
 91        <text id="t1" x="50" y="40">
 92          <c:constraint attributeName="x" value="$t1x"/>
 93          <c:constraint attributeName="y" value="$t1y"/>
 94          XML file
 95        </text>
 96        <text id="t2" x="50" y="310">
 97          <c:constraint attributeName="x" value="$t2x"/>
 98          <c:constraint attributeName="y" value="$t2y"/>
 99          XSLT
100        </text>
101        <text id="t3" x="50" y="580">
102          <c:constraint attributeName="x" value="$t3x"/>
103          <c:constraint attributeName="y" value="$t3y"/>
104          SVG file
105        </text>
106      </g>
107    
108      <!-- arrows -->
109      <g stroke="black" stroke-width="1" marker-end="url(#Triangle)">
110        <line x1="50" y1="50" x2="50" y2="280">
111          <c:constraint attributeName="x1" value="$b1x + $boxWidth div c:if($horizontal, 1, 2)"/>
112          <c:constraint attributeName="y1" value="$b1y + $boxHeight div c:if($horizontal, 2, 1)"/>
113          <c:constraint attributeName="x2" value="$b2x + c:if($horizontal, 0, $boxWidth div 2)"/>
114          <c:constraint attributeName="y2" value="$b2y + c:if($horizontal, $boxHeight div 2, 0)"/>
115        </line>
116        <line x1="50" y1="320" x2="50" y2="550">
117          <c:constraint attributeName="x1" value="$b2x + $boxWidth div c:if($horizontal, 1, 2)"/>
118          <c:constraint attributeName="y1" value="$b2y + $boxHeight div c:if($horizontal, 2, 1)"/>
119          <c:constraint attributeName="x2" value="$b3x + c:if($horizontal, 0, $boxWidth div 2)"/>
120          <c:constraint attributeName="y2" value="$b3y + c:if($horizontal, $boxHeight div 2, 0)"/>
121        </line>
122      </g>
123    </svg>

Figure 14: Flow diagram (code)

box-arrow-rendering1.png

Figure 15: Flow diagram when $horizontal == true (rendering)

box-arrow-rendering3.png

Figure 16: Flow diagram when $horizontal == false (rendering)

box-arrow-rendering2.png

Figure 17: Flow diagram when $horizontal == false (rendering)

Figure 14 shows the code for this flow diagram. Most of the layout logic has been separated out into c:variable elements. The first variable, $horizontal, determines whether the layout of the diagram will be horizontal or not. It will be true if the width of the viewport is greater than or equal to the space to fit three boxes, some nominal gap between these boxes and also some margin space between the edge of the viewport and the boxes. Since this variable uses the c:viewport function it has a viewport dependency. If the browser window is resized then this expression will be re-evaluated.

The $boxWidth and $boxHeight variables calculate the dimensions of the rect elements that form the nodes in the flow diagram. Since we want all the rects to the be same size, and all large enough to fit their text, the c:max function is used to determine the maximum of the text elements' widths and heights (plus some small amount of padding). These widths are computed in the variables $t1w, $t2w and $t3w, while the heights are in $t1h, $t2h and $t3h. Those text dimension variables use the c:bbox function to get the bounding box of each of the text elements.

The box coordinate variables are used to determine the actual positions of the rect elements in the diagram. In this layout the position of the middle rectangle will be the same whether the horizontal or vertical layout is used, since it will be in the center of the viewport. Thus, $b2x is simply the average of the width of the viewport and the box width, and $b2y is the average of the viewport height and box height. The coordinates for the first and thirds rects must be positioned differently depending on the value of $horizontal, though. In our example, if we are using the horizontal layout then the first box should be position at the middle-left of the viewport and in a vertical layout, it should be at the top-middle. So we use the c:if function to choose the correct coordinates based on $horizontal. The same applies to $b3x and $b3y for the third box, except that it is placed at the middle-right for horizontal layout and bottom-middle for vertical. The text positioning variables ($t1x, $t1y, $t2x, $t2y, $t3x and $t3y) simply compute the center of each box.

Following the c:variable elements are the actual SVG elements for the flow diagram. Each of rect elements has four c:constraint child elements to set the expression to be used for the x, y, width and height attributes. The value attributes of these c:constraint elements just refer to the variables that have the computed position and dimension values. The text elements have similar c:constraint elements as children for the x and y attributes.

Because the user agent will have analysed the expressions in the document for dependencies it will know which expressions to re-evaluate if the user resizes the browser window. Initially, the window resize will cause all expressions with viewport dependencies to be updated. In this example, the $horizontal variable and the box coordinate variables all contain a call to the c:viewport function and so will be marked dirty and re-evaluated. Any constraint which depends upon these variables will also need to be updated, so the user agent will follow the reverse dependency list for each of these variables once they have been updated. The expressions that depend on the $horizontal variable include four of the box coordinate variables (which have updated already), and the constraints on the coordinate attributes of the two line elements at the end of the file. Those that depend on the box coordinate variables are the text coordinate variables and the constraints assigning the box coordinate variables to the attributes of the rect elements themselves. This process continues until the user agent has re-evaluated all of the expressions found as if by computing the transitive closure of the reverse dependencies. In this example it will result all of the constraints on the SVG elements and the following variables being updated: $horizontal, $b1x, $b1y, $b2x, $b2y, $b3x, $b3y, $t1x, $t2y, $t3x and $t3y. Other expressions, such as those computing the bounding boxes of the text elements, will not be re-evaluated since their values will not have changed.

4. Templates

In CSVG one can use templates to define the content that is to be rendered in place of the custom element. The RCC-XSLT syntax proposed in a SVG 1.2 working draft is used. [SVG12] [3]

There are a couple of reasons why XSL templates should be used as the prototyping mechanism for CSVG. Firstly, it is a widely used language for specifying transformations from one XML language to another. Rendering custom content is exactly this problem. Many simple transformations are not as easily done when using DOM calls in script.

The issue of shadow tree regeneration is an important one. With script based RCC, the author must explicitly control when and how the shadow tree is to be updated. This will mostly be by attaching DOM mutation event handlers to the custom element. Because the initial shadow tree generation is done using script it is difficult for the user agent to automatically determine the minimum changes necessary. When XSLT and constraints are used, the constraints can be set up to depend on the custom element's attributes, so that changes to the custom element will automatically result in an update in the shadow tree. Notice that efficient incremental update of shadow tree elements properties does not require incremental XSLT processing [VL02] , rather it is achieved by generating elements whose attrbutes are constrained to be functions of the custom element attributes.

Supporting shadow tree elements having constraints on custom elements also allows CSVG to provide animation on custom elements, something which cannot be done with current RCC, since SVG has no support for capturing events when changes caused by animation occur. In CSVG elements in the shadow tree could have attributes which depend on animated attributes of the custom elements, resulting in much more flexibility in animation than is possible in the RCC proposal.

A major hindrance to including XSLT as a mechanism for RCC is that it has no knowledge of the document beyond that of the plain XML. Anything that requires the use of SVG types, such as adding two lengths together or determining a bounding box, cannot be done in plain XSLT. By allowing XSLT to generate constraints we bypass this problem by having these expressions in the shadow tree perform the SVG-dependent computations.

4.1 Templates and constraints

The syntax for using XSLT to generate shadow trees is currently just as a previous SVG 1.2 working draft had suggested. To allow constraints to work between shadow trees and the main document tree, two extra XPath extension functions and one CSVG element are used.

The c:instance function returns the custom element that the shadow tree containing the expression is associated with. This makes it possible to jump back to the original document tree to, for example, select attributes from the custom element. Constraints will then be set up between the custom element's attributes and variables or attributes in the shadow tree so that if the attributes on the custom element are changed — by DOM mutation, constraint propagation or by animation — the changes will be propagated to the shadow tree.

Also available for use are the c:property element and the c:property function. The c:property element, when used inside a shadow tree, defines a property for the custom element. This c:property element is used just like a c:variable element; it has a name and a value attribute. Properties of custom elements though are accessible from outside the shadow tree by the c:property XPath extension function. The function takes as its two arguments a nodeset containing a custom element and the name of a property defined in that custom element's shadow tree. This allows information to be made visible from the implementation of a custom element in a controlled fasion and used in other constraints in the document. It is effectively an ouptut argument for the custom element. Only one c:property element with a given name is allowed in a shadow tree.

To allow text strings in a shadow tree to be automatically updated via constraints a c:tval element is available. This element works similarly to the standard tref element in that it inserts some text into the containing text element. Instead of having an xlink:href attribute to refer to a previously defined text string, the c:tval element has a value attribute which contains an XPath expression which will be evaluated to determine the text string to be used. Just like other constraint elements, if any dependency of this expression is updated, the expression will be re-evaluated to obtain the new string value.

4.2 Template example

Figure 18 shows an example of a simple graph, consisting of three nodes with arcs between them and labels on both the nodes and the arcs. We want the graph to be defined using templates and that the positions of the nodes be animatable.

graph-rendering.png

Figure 18: Graph defined with templates (rendering)

The code for this example is shown in Figure 19 . At the end of the listing is the ex:graph element which defines the whole graph. Each node in the graph has a name, cx, cy and label attribute. One of the nodes also has a final attribute which causes the node to be rendered with two outer circles. The ex:arc elements, defining the lines between the nodes, have a from and to attribute, to select between which nodes the arcs are to be drawn, and a label attribute to define the text label for the arc.

  1    <svg xmlns="http://www.w3.org/2000/svg"
  2      xmlns:xlink="http://www.w3.org/1999/xlink"
  3      xmlns:ex="http://mcc.id.au/2004/example"
  4      xmlns:c="http://mcc.id.au/2004/csvg"
  5      width="400" height="400" overflow="visible">
  6    
  7      <extensionDefs namespace="http://mcc.id.au/2004/example">
  8        <xsl:stylesheet id="exampleXSL" version="1.1"
  9          xmlns="http://www.w3.org/2000/svg"
 10          xmlns:svg="http://www.w3.org/2000/svg"
 11          xmlns:ex="http://mcc.id.au/2004/example"
 12          xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 13    
 14          <xsl:template match="ex:graph">
 15            <xsl:copy-of select="ex:arc"/>
 16            <xsl:copy-of select="ex:node"/>
 17          </xsl:template>
 18    
 19          <xsl:template match="ex:node">
 20            <c:variable name="cx" value="c:Length(c:instance()/@cx)"/>
 21            <c:variable name="cy" value="c:Length(c:instance()/@cy)"/>
 22            <c:variable name="final" value="string(c:instance()/@final) = 'true'"/>
 23            <c:variable name="b" value="c:bbox(following-sibling::svg:text)"/>
 24    
 25            <circle r="0" fill="white" stroke-width="2" stroke="black">
 26              <c:constraint attributeName="cx" value="$cx"/>
 27              <c:constraint attributeName="cy" value="$cy"/>
 28              <c:constraint attributeName="r" value="c:max(c:width($b) div 2 + 10, 30) + 5"/>
 29              <c:constraint attributeName="display" value="c:if($final, 'inline', 'none')"/>
 30            </circle>
 31            <circle r="0" fill="#eee" stroke-width="2" stroke="black">
 32              <c:constraint attributeName="cx" value="$cx"/>
 33              <c:constraint attributeName="cy" value="$cy"/>
 34              <c:constraint attributeName="r" value="c:max(c:width($b) div 2 + 10, 30)"/>
 35            </circle>
 36            <text text-anchor="middle">
 37              <c:constraint attributeName="x" value="$cx"/>
 38              <c:constraint attributeName="y" value="$cy + c:height($b) div 2"/>
 39              <xsl:value-of select="@label"/>
 40            </text>
 41          </xsl:template>
 42    
 43          <xsl:template match="ex:arc">
 44            <c:variable name="from" value="c:instance()/@from"/>
 45            <c:variable name="to" value="c:instance()/@to"/>
 46            <c:variable name="x1" value="c:Length(c:instance()/../ex:node[@name=$from]/@cx)"/>
 47            <c:variable name="y1" value="c:Length(c:instance()/../ex:node[@name=$from]/@cy)"/>
 48            <c:variable name="x2" value="c:Length(c:instance()/../ex:node[@name=$to]/@cx)"/>
 49            <c:variable name="y2" value="c:Length(c:instance()/../ex:node[@name=$to]/@cy)"/>
 50            <line x1="0" y1="0" x2="0" y2="0" stroke="black" stroke-width="1">
 51              <c:constraint attributeName="x1" value="$x1"/>
 52              <c:constraint attributeName="y1" value="$y1"/>
 53              <c:constraint attributeName="x2" value="$x2"/>
 54              <c:constraint attributeName="y2" value="$y2"/>
 55            </line>
 56            <c:variable name="midx" value="($x1 + $x2) div 2"/>
 57            <c:variable name="midy" value="($y1 + $y2) div 2"/>
 58            <c:variable name="b" value="c:bbox(following-sibling::svg:text)"/>
 59            <rect width="0" height="0" fill="white">
 60              <c:constraint attributeName="x" value="c:x($b) - 1"/>
 61              <c:constraint attributeName="y" value="c:y($b) - 1"/>
 62              <c:constraint attributeName="width" value="c:width($b) + 2"/>
 63              <c:constraint attributeName="height" value="c:height($b) + 2"/>
 64            </rect>
 65            <text text-anchor="middle">
 66              <xsl:value-of select="@label"/>
 67              <c:constraint attributeName="x" value="$midx"/>
 68              <c:constraint attributeName="y" value="$midy"/>
 69            </text>
 70          </xsl:template>
 71        </xsl:stylesheet>
 72    
 73        <elementDef name="graph">
 74          <transformer xlink:href="#exampleXSL" type="text/xsl"/>
 75        </elementDef>
 76    
 77        <elementDef name="node">
 78          <transformer xlink:href="#exampleXSL" type="text/xsl"/>
 79        </elementDef>
 80    
 81        <elementDef name="arc">
 82          <transformer xlink:href="#exampleXSL" type="text/xsl"/>
 83        </elementDef>
 84      </extensionDefs>
 85      
 86      <g font-size="24" font-family="serif">
 87        <ex:graph>
 88          <ex:node name="n1" cx="70" cy="200" label="Start"/>
 89          <ex:node name="n2" cx="200" cy="330" label="1"/>
 90          <ex:node name="n3" cx="300" cy="100" label="Final" final="true"/>
 91          <ex:arc from="n1" to="n2" label="a*b*"/>
 92          <ex:arc from="n2" to="n3" label="Λ + c"/>
 93        </ex:graph>
 94      </g>
 95    </svg>

Figure 19: Graph defined with templates (code)

The template definition for the ex:graph element from line 87 is just a container for the ex:node and ex:arc elements. It makes sure that the arcs are rendered before the nodes, so that the lines do not appear over the top of the circles.

The XSLT used to generate the shadow tree for the ex:node elements is reasonably simple. First, it defines a few CSVG variables to hold information given in the ex:node's attributes. These variables ($cx, $cy and $final) all use the c:instance function to select the ex:node element which instantiated the current shadow tree. Defined also is $b which holds the bounding box of the node's label. If the label would extend past the edge of the default 30 unit radius circle, the size of the circle is increased. This is done using the c:max function in the r attribute of the two circles in the template.

The first circle is an outer ring for those nodes which are marked as "final". It has a constraint on the display attribute which will prevent it from beind rendered if the node is not a final node. Finally there is the text element into which is copied the label specified on the ex:node element. The label is positioned at the center of the circle, using the width of the bounding box of that text element (c:height($b)) to center it vertically and a simple text-anchor="middle" attribute for the horizontal centering.

The template for the ex:arc element is similar. Note that the $x1, $y1, $x2 and $y2 variables (defining the start and end coordinates of the arc) use the c:instance function to access the custom element which instantiated this shadow tree. XPath is then used to select the sibling ex:node element according to the from and to attributes so the start and end coordinates can be found.

After the CSVG variable definitions the line element which is the arc is copied to the shadow tree. Finally, a text element is used to display the arc label. A white rect is drawn just before this to make sure the label is legible over the line.

Now, if any script modifies the cx and cy attributes of the ex:node elements the positions of the circle, text, line and rect elements which make up the nodes and arcs will reposition themselves accordingly, since these shadow tree elements have dependencies on the custom elements in the main document tree.

We can easily animate a node in the graph by defining an animation on one of the custom elements. Figure 20 shows the graph example with the second node animated to move up the canvas. The arcs follow since in the shadow tree the line endpoints are constrained to be equal to the centers of the two nodes it links. The code shows a commented out SMIL animation and a c:constraint element, both of which would animate the node. Since our implementation is based on Batik, which currently does not support SMIL animation, the SMIL animation syntax is not yet supported (although we plan to do this soon). If it was supported, the constraints would propagate correctly to animate the entire node and the arcs would follow. The c:constraint element achieves the same effect as the SMIL animation by using the c:time function.

  1        <ex:graph>
  2          <ex:node name="n1" cx="70" cy="200" label="Start"/>
  3          <ex:node name="n2" cx="200" cy="330" label="1">
  4            <!-- animate attributeName="cy" from="330" to="0" begin="0s" end="10s"/ -->
  5            <c:constraint attributeName="cy" value="330 * (1 - c:time() div 10)"/>
  6          </ex:node>
  7          <ex:node name="n3" cx="300" cy="100" label="Final" final="true"/>
  8          <ex:arc from="n1" to="n2" label="a*b*"/>
  9          <ex:arc from="n2" to="n3" label="Λ + c"/>
 10        </ex:graph>

Figure 20: Graph with node animated (code)

graph-rendering2.png

Figure 21: Graph with node animated after a few seconds (rendering)

5. Implementation

We have implemented the constraint extensions discussed here as an extension to Batik, a Java SVG browser. The complete implementation can be found at the project's web site.

http://www.csse.monash.edu.au/~clm/csvg/

Batik, aside from animation, provided a quite complete implementation of SVG to build upon. For the XPath expression evaluation support, the XPath package from the Xalan project has been used.

The current implementation is not yet as fast as rendering complex CSVG documents without a noticeable delay would require. Profiling data suggests that a large amount of the computation time is spent in the XPath evaluation, which potentially slows down the evaluation additionally by leaving larger amounts of garbage-collectable objects behind. This suggests optimising the XPath implementation within CSVG could make the browser significantly faster. We are planning to investigate this by (partially) compiling XPath expressions.

Batik is a medium sized project. The Batik CVS sources have 295,795 lines of code in total (counting the files in the xml-batik/sources/org directory) at the time the extension was developed. The addition of constraint handling needed just an additional 7,362 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 46,000 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. An XPath engine will be needed regardless, since the SVG 1.2 DOM will require DOM Level 3 XPath support.

6. Comparison with scripting

To evaluate the constraint extension and compare to scripting we have taken the user interface example and implemented it in both CSVG with RCC/XSLT and standard SVG 1.2 with RCC/ECMAScript. There are 6 widgets used in the document: a window frame, a labelled grid layout, a text field, a checkbox miniform, a regular checkbox and a button bar. Each of these widgets is defined by a custom element. Figure 22 shows the instantiation of these custom elements to create the "Account details" window. This code is common to both the CSVG RCC/XSLT and the RCC/ECMAScript implementations.

  1      <ex:frame id="f" x="100" y="24" width="352" height="350" title="Account details">
  2        <ex:labelledGrid>
  3          <ex:row label="Username:"><ex:textField/></ex:row>
  4          <ex:row label="Password:"><ex:textField/></ex:row>
  5        </ex:labelledGrid>
  6        <ex:checkBoxMiniForm label="Use HTTP proxy">
  7          <ex:labelledGrid>
  8            <ex:row label="Hostname:"><ex:textField/></ex:row>
  9            <ex:row label="Port:"><ex:textField/></ex:row>
 10          </ex:labelledGrid>
 11        </ex:checkBoxMiniForm>
 12        <ex:checkBox label="Enable account"/>
 13        <ex:buttonBar>
 14          <ex:button label="Save changes"/>
 15          <ex:button id="b" label="Discard changes"/>
 16        </ex:buttonBar>
 17      </ex:frame>

Figure 22: User interface custom elements (code snippet)

The ex:frame widget will lay out its child widgets vertically. The ex:labelledGrid widget will lay out widgets with a text label to the left of each widget. Since the text labels and widgets are layed out in two columns, the x coordinate of each widget will be equal, and positioned such that none of the text labels are overlapped. ex:textField is implemented just as a rectangle that expands to fill all of the available width of its viewport. The ex:checkBox element is simple and just positions a text label to the right of a box which can be toggled on and off. ex:checkBoxMiniForm defines a checkbox as the ex:checkBox element does and also defines indented viewport to place child widgets. This viewport, or miniform, is hidden if the checkbox is not currently toggled on. Finally, the ex:buttonBar widget will lay out buttons horizontally at the bottom of its viewport and will place a line above these buttons. In all of the cases here, widgets will take up all of the vertical space available to them in their viewports. The height of the viewport for any widget will be the maximum space available but typically the widget will not take up all of that space. The amount of vertical space used up by a widget will determine the y coordinate of the following widget's viewport, as the widgets are layed out down the screen.

In the interest of brevity, we will look at only the definition of one of the widgets in the example — the c:checkBoxMiniPage element. Figure 23 and Figure 24 show the ECMAScript version and the CSVG version of the c:checkBoxMiniPage widget, respectively.

  1        <elementDef name="checkBoxMiniForm">
  2          <prototype>
  3            <rect id="r" width="1em" height="1em" rx="3" ry="3" fill="#88c" stroke-width="1" stroke="#66a"/>
  4            <text id="t" x="2em" y="1em"/>
  5            <svg id="s" x="32" overflow="visible"/>
  6          </prototype>
  7          <script ev:event="SVGBindEnd"><![CDATA[
  8            var s = evt.target.shadowTree;
  9            var t = s.getElementById("t");
 10            var r = s.getElementById("r");
 11            t.appendChild(document.createTextNode(evt.target.getAttributeNS(null, "label")));
 12            var b = getTextBBox(t); // t.getBBox();
 13            t.setAttributeNS(null, "y", r.height.baseVal.value / 2 + b.height / 2);
 14            var child = evt.target.firstChild;
 15            while (child.nodeType != 1) {
 16              child = child.nextSibling;
 17            }
 18            var svg = s.getElementById("s");
 19            svg.setAttributeNS(null, "y", b.height + 16);
 20            svg.setAttributeNS(null, "width", svg.width.baseVal.value - 32);
 21            svg.setAttributeNS(null, "height", svg.height.baseVal.value - b.height - 16);
 22            svg.appendChild(child.cloneNode(true));
 23    
 24            var domAttrModifiedListener = {
 25              handleEvent: function(evt) {
 26                if (evt.attrName == "label") {
 27                  var t = evt.target.shadowTree.getElementById("t");
 28                  t.firstChild.nodeValue = evt.newValue;
 29                }
 30              }
 31            };
 32            evt.target.addEventListener("DOMAttrModified", domAttrModifiedListener, false);
 33            var clickListener = {
 34              customElement: evt.target,
 35              handleEvent: function(evt) {
 36                var svg = evt.target;
 37                while (svg.nodeType != 1 || svg.namespaceURI != svgns || svg.localName != "svg") {
 38                  svg = svg.nextSibling;
 39                }
 40                var checked = svg.getAttributeNS(null, "display") == "none";
 41                evt.target.setAttributeNS(null, "fill", checked ? "#88c" : "white");
 42                svg.setAttributeNS(null, "display", checked ? "inline" : "none");
 43                resize(document.getElementById("f")); // hack
 44              }
 45            };
 46            r.addEventListener("click", clickListener, false);
 47          ]]></script>
 48        </elementDef>

Figure 23: c:checkBoxMiniForm widget implemented in RCC/ECMAScript (code snippet)

  1      <script><![CDATA[
  2        // ...
  3        function checkBoxClick(evt) {
  4          var p = evt.target.parentNode;
  5          while (p.getAttributeNS(null, "name") != "checked") {
  6            p = p.previousSibling;
  7          }
  8          p.setAttributeNS(null, "value", p.getAttributeNS(null, "value") == "true()" ? "false()" : "true()");
  9        }
 10      ]]></script>
 11    
 12      <!-- ... -->
 13    
 14          <xsl:template match="ex:checkBoxMiniForm">
 15            <c:variable name="checked" value="true()"/>
 16            <svg y="8" overflow="visible">
 17              <rect width="1em" height="1em" rx="3" ry="3" stroke-width="1" stroke="#66a" onclick="checkBoxClick(evt)">
 18                <c:constraint attributeName="fill" value="c:if($checked, '#88c', 'white')"/>
 19                <c:constraint attributeName="y" value="following-sibling::svg:text[1]/@y div 2 - c:LengthV('1em') div 2"/>
 20              </rect>
 21              <text x="0" y="0">
 22                <c:constraint attributeName="x" value="c:Length('1em') + 8"/>
 23                <c:constraint attributeName="y" value="c:height(c:bbox(.))"/>
 24                <xsl:value-of select="@label"/>
 25              </text>
 26              <svg x="32" y="0" overflow="visible">
 27                <c:constraint attributeName="y" value="preceding-sibling::svg:text[1]/@y + 16"/>
 28                <c:constraint attributeName="width" value="c:LengthH('100%') - 32"/>
 29                <c:constraint attributeName="height" value="c:LengthV('100%') - @y"/>
 30                <c:constraint attributeName="display" value="c:if($checked, 'inline', 'none')"/>
 31                <xsl:copy-of select="*"/>
 32              </svg>
 33            </svg>
 34          </xsl:template>

Figure 24: c:checkBoxMiniForm widget implemented in CSVG RCC/XSLT (code snippet)

We can see from these two examples that the RCC/ECMAScript version has a number of shortcomings. Firstly, the script from line 8 to line 22 that is used to set the computed attribute values of the shadow tree elements is much less clear than the simple, one element per attribute syntax seen in the CSVG version. Secondly we have the explicit DOM attribute modification event listener that is set up to handle changes to the label attribute of the c:checkBoxMiniForm element, from line 24 to 32. This is simply not needed in the CSVG version, since the user agent will track the dependencies and update the shadow tree automatically. We also find in the ECMAScript version that the event handler used to capture mouse clicks on the checkbox must explicitly relayout the widgets in the window due to its changing size. Again, in CSVG this relayout is handled automatically due to the constraints set up by the c:frame element when it originally layed out the widgets. The short checkBoxClick script function in the CSVG code only modifies the $checked variable's value. Since the display attribute of the svg element containing the miniform depends on this variable, it is automatically hidden or shown whenever the variable is modified.

Taking the entire code for each of these two implementations it is obvious that there is much script used in the RCC/ECMAScript version to manually set up event handlers and effect updates to the shadow trees when attributes on custom elements are modified. The final file size of the CSVG implementation is approximately half the size of the RCC/ECMAScript version (12KB versus 24KB). [4]

7. Future work

At the time of writing, a pure ECMAScript version of the constraint extension was currently being written. This will allow authors using browsers other than Batik to utilise the extensions. Some aspects of CSVG cannot be exactly implemented in script, however, since access to the animation engine of the user agent is too limited. There is no way in SVG 1.1 to have an event fired when an animation tick occurs and so constraint propagation due to animations cannot be initiated immediately. Instead, some sort of polling process must be used to check if any animated values have changed. Also, there is no way to modify the animated values of XML attributes in the DOM without creating SMIL elements to effect the change. Thus, to cause updates to animated attributes the script will have to create and continually modify a SMIL set element. The mere existence of this element in the document could cause problems for XPath expressions written by the document author unless they are careful to know where they will be inserted and to avoid them.

Some extra features are being considered for inclusion in CSVG. One is the use of SMIL timing attributes on constraint elements. This would allow constraints to be applied at certain times or in response to events fired. Also a new element, tenantively named c:setExpression, could be used similarly to the standard SMIL set element to perform a one off property assignment. It would use an expression to determine the value to assign to the property. This element, in conjunction with the SMIL timing attributes, would help avoid the small amount of script needed for the interactive examples discussed earlier where CSVG variables are modified on mouse click.

These extra features could even be used to declaratively specify a complex interaction such as dragging and dropping a graphical element in the document. Figure 25 shows some hypothetical CSVG code to do this.

  1    <svg>
  2      <!-- Variables to store the offset of the mouse pointer when dragging begins -->
  3      <c:variable name="dx" value="0">
  4        <c:constraint begin="r1.mousedown" attributeName="value"
  5                      value="c:clientX(c:event()) - id('r1')/@x"/>
  6      </c:variable>
  7      <c:variable name="dy" value="0">
  8        <c:constraint begin="r1.mousedown" attributeName="value"
  9                      value="c:clientY(c:event()) - id('r1')/@y"/>
 10      </c:variable>
 11    
 12      <!-- Background rectangle to capture mouse move events while dragging -->
 13      <rect id="bg" x="0" y="0" width="100%" height="100%" fill="white" pointer-events="none">
 14        <set begin="r1.mousedown" end="mouseup" attributeName="pointer-events" to="all"/>
 15      </rect>
 16    
 17      <!-- Rectangle to be dragged -->
 18      <rect id="r1" x="100" y="100" width="300" height="200">
 19        <set begin="mousedown" end="bg.mouseup" attributeName="pointer-events" to="none"/>
 20        <c:constraint begin="bg.mousemove" attributeName="x"
 21                      value="c:clientX(c:event()) - $dx"/>
 22        <c:constraint begin="bg.mousemove" attributeName="y"
 23                      value="c:clientY(c:event()) - $dy"/>
 24      </rect>
 25    </svg>

Figure 25: Hypothetical drag and drop example using timing attributes on constraints (code)

8. Conclusion

We have described a simple extension to SVG 1.1 which allows attribute values to be specified with expressions whose value is evaluated at display time to determine the final layout and element appearance. The idea of using expression-based evaluation of attributes (under the name of one-way constraints) for specifying layout is not new. They have been used for widget layout, e.g. [Ell2002] , for page layout [WeW94] , [HMP03] , [JLSBS03] , layout of VRML [DK00] as well as for expression animation, e.g. [Dui87] .

The main contribution of the current paper is to demonstrate that adding expression-based evaluation of attributes to SVG adds little implementation effort but allows the document to adapt to different sized browsing windows, to adapt to changes in the size of text elements such as those arising from the use of a larger font or a different language, and to adapt to changes resulting from user interaction and animation. Remarkably all three kinds of adaptation are supported by the same simple mechanism. Such capabilities are increasingly important for user interaction, in particular for application user interfaces, and for universal access, where users may have unforseen viewing requirements.

Expression-based evaluation of attributes has two main advantages over script-based adaptation. The first is that it removes the need for the document author or authoring tool to worry about how to perform efficient incremental updates when document elements are modified. It is now the responsibility of the SVG agent to perform this automatically. Expression-based attributes would also allow style sheets to interact more tightly with the user agent. For example, a style could match a line thickness in a diagram to a font size without having to know the concrete font size in advance. The other main advantage of expression-based evaluation of attributes is that it removes most of the need for SVG authoring tools and document authors to understand script, thus facilitating editing and interchange of SVG documents.

The only reason that we can see for not providing expression-based evaluation of attributes in SVG is that future standards for generation of SVG elements from custom elements will provide the necessary adaptive layout. However as we have discussed in the Introduction our experience with CSVG suggests that even with custom elements it proves very useful to allow base SVG elements to have dynamically evaluated attribute values since this supports automatic, efficient update of the shadow tree when custom element attributes are modified as a result of user interaction or animation.

Footnotes

  1. Batik project website: http://xml.apache.org/batik/

  2. To ensure that the document remains consistent, any other c:variable element in the scope of the expression between that variable and the expression must be watched for modifications of its name attribute. This is since the variable that the expression refers to may change if a closer variable has its name changed. Such a use of variables would be strange at best, and it is not clear if it is worth the implementation effort required to track these sorts of changes.

  3. Since the implementation was written, RCC has been dropped in favour of sXBL, a different language for specifying shadow tree generation. At the time of writing sXBL did not seem to afford shadow tree generation using XSLT.

  4. The full code for the two implementations of the user interface widget layout example are located at http://www.csse.monash.edu.au/~clm/2004/07/widgets.html.

Bibliography

[BTM01]
A constraint extension to scalable vector graphics, Greg Badros, Jojada Tirtowidjojo, Kim Marriott, Bernd Meyer, Will Portnoy and Alan Borning, Proc. 10th World Wide Web Conference, Hong Kong, May 2001.
[JLSBS03]
Adaptive Grid-Based Document Layout, Charles Jacobs, Wilmot Li, Evan Schreier, David Bargeron and David Salesin, ACM SIGGRAPH Conference, 2003.
[Dui87]
Animation using temporal constraints: An over of the Animus system, Robert Duisberg, Human-Computer Interaction, 3(3), 275—308, 1987.
[VL02]
An Incremental XSLT Transformation Processor for XML Document Manipulation, Lionel Villard and Nabil Layaïda, Proc. 11th International World Wide Web Conference, May 2002.
[WeW94]
Automatic presentation of multimedia documents using relational grammars, L. Weitzman and K. Wittenburg, in ACM Multimedia Conference, pages 443—451, 1994.
[HMP03]
Cobweb: A Constraint-Based Web Browser, Nathan Hurst, Kim Marriott and Peter Moulder, Proc. 26th Australasian Computer Science Conference, Adelaide, February 2003.
[MMT02]
Fast and Efficient Client-side Adaptivity for SVG, Kim Marriott, Bernd Meyer and Laurent Tardif, Proc. 11th International World Wide Web Conference, May 2002.
[VHM01]
Lessons learned about one-way, dataflow constraints in the Garnet and Amulet graphical toolkits, Bradley T. Vander Zanden, Richard L. Halterman, Brad A. Myers, Rich McDaniel, Rob Miller, Pedro Szekely, Dario Giuse and David Kosbie, ACM Transactions on Programming Languages and Systems, volume 26 number 6, pp.776–796, 2001.
[Ell2002]
RelativeLayout, a Constraint-Based Layout Manager, James Elliott, September 2002, http://www.onjava.com/pub/a/onjava/2002/09/18/relativelayout.html.
[SVG11]
Scalable Vector Graphics (SVG) 1.1 Specification, Jon Ferraiolo, Fujisawa Jun and Dean Jackson, January 2003, http://www.w3.org/TR/2003/REC-SVG11-20030114/.
[SVG12]
Scalable Vector Graphics (SVG) 1.2, W3C Working Draft 10 May 2004, Dean Jackson, May 2004, http://www.w3.org/TR/2004/WD-SVG12-20040510/.
[SVGReq]
SVG 1.1/1.2/2.0 Requirements, W3C Working Draft 22 April 2002, Dean Jackson, April 2002, http://www.w3.org/TR/2002/WD-SVG2Reqs-20020422/.
[DK00]
VRML with Constraints, Stephan Diehl and Jorg Keller, Proc. Web3D VRML 2000 fifth symposium on virtual reality modeling language, Monterey, California, February 2000.

XHTML rendition created by gcapaper Web Publisher v2.1, © 2001-3 Schema Software Inc.

HTTP/1.1 200 OK Date: Fri, 19 Apr 2024 06:29:31 GMT Server: Apache/2.4.6 (Red Hat Enterprise Linux) Content-Length: 0 Connection: close Content-Type: application/x-perl