The Suitability of SVG for Deploying Wireless Applications

John Hayman
Research In Motion Limited
5090 Commerce Blvd, 2nd Floor
Mississauga, ON
Canada L4W 5M4

Phone: (+1) 905 629-4746 x4354
Fax: (+1) 905 629-4869

Email: jhayman@rim.net
Web pages: http://www.rim.net
http://www.plazmic.com

Keywords: Rich Mobile Media using SVG


Abstract

The wireless world is at a crossroads. Until recently, applications for wireless devices were text based and designed to deliver textual information. That is changing. Wireless devices are shipping with colour displays and more advanced graphics rendering libraries. Once applications are deployed that take advantage of these features, consumers will demand a rich media experience from all their wireless applications

Scalable Vector Graphics (SVG) is designed to describe 2D vector graphics (and mixed vector/raster graphics). It allows for interactivity using the event model and animation concepts borrowed from Synchronized Multimedia Integration Language (SMIL). SVG has had some success on the World Wide Web. It is XML-based allowing it to integrate well with existing web technologies. Its endorsement by the W3C (as a recommendation) and Adobe (as a preferred data format) show that it has staying power.

New wireless devices with greater and greater feature sets are being developed and deployed. Writing compelling rich media applications for wireless devices is a daunting task. One must deal with constrained memory, limited processor power and minimal screen real estate. Often the teams that have the skill sets to address these technical problems lack the skill sets to deliver compelling rich media. Even with a content-focussed and technically focussed development team, the constraints of the devices can limit the effectiveness of the presentation.

This paper assesses the suitability of SVG for developing and deploying wireless applications. It highlights the challenges in wireless development and comments on the strengths of SVG in this space. It also addresses the limitations of SVG and the difficulty in delivering SVG to a constrained device.

The paper uses the Plazmic Media Engine(tm) solution as the backdrop for discussion. Plazmic Media Engine is a media player that currently runs on the NTT DoCoMo network in Japan. It is deployed right now and delivers rich mobile media applications to thousands of subscribers. At the heart of the media player is the SVG format. The project brings a unique perspective because SVG was chosen (and later embraced) as the application language after the technology had been developed.

Finally, the paper looks toward the future in the wireless space. It discusses the advances in technology that will make rich media common on wireless devices and SVG's role in that development.

Introduction

This paper discusses the challenges in deploying rich mobile media content to wireless devices such as smart phones. In particular, it looks at SVG as a dataformat for content.

In 1999, Plazmic Inc. was trying to make creation and deployment of rich mobile media easy. We believed that content creation should be in the hands of the creative professionals, like graphic artists. Our strategy was simple: Provide a media player that could take advantage of the multimedia capabilities of the mobile devices and use a content format that empowered graphics professionals. That is, remove the need for a pool of software developers skilled in the art (and it is an art) of deploying applications for constrained mobile devices.

Our proposed solution was the Plazmic Media Engine. It was a full-featured media player written entirely in Java™. Its initial target platform was 503i phones produced for DoCoMo's mobile network in Japan. Although since it was written in Java, it could easily be ported to other devices.

Our solution did not involve SVG at all. We developed a proprietary XML-based data format for content, one that was optimized for the platform to which we were deploying. The content author could create some basic shapes, animate them and use the device's keys to interact with the animations.

State of SVG Standard

Meanwhile, the World Wide Web Consortium (W3C) was drafting a standard for describing 2-D graphics in XML. In August 2000 it was a relatively stable document but had not been accepted as an official recommendation. It would go through revisions in November 2000 and July 2001 before it reached recommendation status in September 2001.

The creation of a largely compliant browser plug-in by Adobe gained the standard credibility. Content developers were assured that if they built it, end users would be able to view it.

Tool vendors were also assured that if they spent the time to produce valid SVG it would be viewable. The available tools fell into two main categories. The first were existing 2-D tool vendors that added SVG export to their products. The second category were general purpose XML-editors or XML-inspired editors that could handle SVG.

In summary, the SVG standard had definite promise. The specification was clear and there existed viewers and editors. However it was in its infancy. It would be some time before it would see widespread adoption. Even today it is unclear whether it will see adoption outside of specific niche markets.

Deficiencies of SVG

Plazmic saw the value of a standards-based solution and SVG closely matched the functionality we were trying to accomplish. While in the process of implementing a SVG viewer on a mobile device we encountered idiosyncracies or omissions in the SVG standard that make it more difficult to deploy on mobile devices. This section lists some of these. Specifically it talks about: the need for dynamic loading; support for common patterns such as animated sprites; better support for devices without mice; audio support; and removal of features that make it difficult to implement a mobile player.

Dynamic Loading

Broadband access to the Internet is becoming more and more common for desktop computers. Unfortunately the same is not true for wireless networks. Transfer rates of less than 9600 bps is more common. The need for content to be downloaded moderately quickly is key. A simple way to solve the problem is to make content smaller. The downside to this is that content is either less compelling or lasts for a very short time.

A better metric to use to measure effective content is the ratio of time spent waiting for content versus the time spent viewing content. Users are willing to wait a little extra time to view something particularly compelling, but there is an upper limit.

Streaming content is a technique to minimize the content waiting versus content viewing ratio. The idea is to stream new content in while watching the existing loaded content. Content players such as RealPlayer, Quicktime and Flash all have the ability to stream content. SVG does not provide such a mechanism.

In fact, because of the nature of SVG documents automatic player-side streaming is difficult. The requirement that all start tags be matched with corresponding end tags means that an SVG file cannot be validated until the last tag is read. The viewer could assume that the content is valid but there would still be problems rendering the content as it loaded. There is no guarantee in SVG (or in XML for that matter) that an identifier is defined before it is used. An SVG document may "use" an element before it is defined. A text on a path may reference a path that hasn't been loaded yet.

Even if strict restrictions were placed on the format that you must define all your identifers before you could reference them, the content may not lend itself well to incremental rendering. Objects are rendered from front to back based on the order that they appear in the document. Content providers often take advantage of this feature by occluding graphics they don't want shown with graphics drawn in front. If a client side renderer naively drew objects as they were loaded, it would expose these content developer techniques.

If there is to be content steaming it should be under the control of the content developer. The Plazmic Media Engine extended SVG to define a very simple element to control content loading. It can be extended to handle streaming in the future. The Media Engine supports a <loadScene> element. It has two attributes: begin and xlink:href.

The semantics of loadScene is as follows. When the criteria specified with the begin attribute is met, the content specified by the xlink:href attribute is loaded. In the initial version of Media Engine the newly loaded content completely replaces the existing content. It is envisioned that in future releases options will exist to load content inline as well.

Common Patterns

A very common content design pattern is that of the animated sprite. An animated sprite is a sequence of related graphics that can be animated independently from the rest of the scene. Typically this is accomplished by making only one of the graphics visible at any given time. By cycling through the graphics in a particular order, it gives the impression of motion.

Creating an animated sprite in SVG is possible by animating the visibility of the component images. Below is some sample code that illustrates this method for creating an animated sprite:

<svg width="80" height="60">
  <g x="5" y="5">
    <animateTransform attributeName="transform" 
        attributeType="translate" dur="12"
        values="5,0;  5,10;  5,20;  5,20;
                5,10; 5,0; 15,0;  30,0;
                25,0; 25,0; 25,0" fill="freeze"/>

  <image id="attack01" xlink:href="images/guy_attack_1.gif"
         width="45" height="45" visibility="hidden">
    <animate attributeName="visibility" 
         dur="10s"
         values="hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;visible;hidden;visible;hidden"
         />
  </image>
  <image id="attack02" xlink:href="images/guy_attack_2.gif"
         width="45" height="45">
    <animate attributeName="visibility" 
         dur="10s"
         values="hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;visible;hidden;visible"
         />
  </image>


  <image id="down01" xlink:href="images/guy_down_1.gif"
         width="32" height="32" visibility="hidden">
    <animate attributeName="visibility" 
         dur="10s"
         values="visible;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden"
         />
  </image>
  <image id="down02" xlink:href="images/guy_down_2.gif"
         width="32" height="32" visibility="hidden">
    <animate attributeName="visibility" 
         dur="10s"
         values="hidden;visible;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden"
         />
  </image>
  <image id="down03" xlink:href="images/guy_down_3.gif"
         width="32" height="32" visibility="hidden">
    <animate attributeName="visibility" 
         dur="10s"
         values="hidden;hidden;visible;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden"
         />
  </image>


  <image id="up01" xlink:href="images/guy_up_1.gif"
         width="32" height="32" visibility="hidden">
    <animate attributeName="visibility" 
         dur="10s"
         values="hidden;hidden;hidden;visible;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden"
         />
  </image>
  <image id="up02" xlink:href="images/guy_up_2.gif"
         width="32" height="32" visibility="hidden">
    <animate attributeName="visibility" 
         dur="10s"
         values="hidden;hidden;hidden;hidden;visible;hidden;hidden;hidden;hidden;hidden;hidden;hidden"
         />
  </image>
  <image id="up03" xlink:href="images/guy_up_3.gif"
         width="32" height="32" visibility="hidden">
    <animate attributeName="visibility" 
         dur="10s"
         values="hidden;hidden;hidden;hidden;hidden;visible;hidden;hidden;hidden;hidden;hidden;hidden"
         />
  </image>

  <image id="right01" xlink:href="images/guy_right_1.gif"
         width="32" height="32" visibility="hidden">
    <animate attributeName="visibility" 
         dur="10s"
         values="hidden;hidden;hidden;hidden;hidden;hidden;visible;hidden;hidden;hidden;hidden;hidden"
         />
  </image>
  <image id="right02" xlink:href="images/guy_right_2.gif"
         width="32" height="32" visibility="hidden">
    <animate attributeName="visibility" 
         dur="10s"
         values="hidden;hidden;hidden;hidden;hidden;hidden;hidden;visible;hidden;hidden;hidden;hidden"
         />
  </image>
  <image id="right03" xlink:href="images/guy_right_3.gif"
         width="32" height="32" visibility="hidden">
    <animate attributeName="visibility" 
         dur="10s"
         values="hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden;hidden"
         />
  </image>
</g>

</svg>

Creating such animations are difficult in SVG. Plazmic Media Engine has introduced a new element, called <switchGroup>. The following shows the same scene implemented using the switchGroup element.

<svg width="80" height="60">
  <switchgroup x="5" y="5">
    <animate attributeName="currentChild" dur="12" 
             values="down01;down02;down03;up01;up02;up03;right01;right02;attack01;attack02;attack01;attack02"/>

    <animateTransform attributeName="transform" 
        attributeType="translate" dur="12"
        values="5,0;  5,10;  5,20;  5,20;
                5,10; 5,0; 15,0;  30,0;
                25,0; 25,0; 25,0" fill="freeze"/>

  <image id="attack01" xlink:href="images/guy_attack_1.gif"
         width="45" height="45" visibility="hidden"/>
  <image id="attack02" xlink:href="images/guy_attack_2.gif"
         width="45" height="45"/>

  <image id="down01" xlink:href="images/guy_down_1.gif"
         width="32" height="32" visibility="hidden"/>
  <image id="down02" xlink:href="images/guy_down_2.gif"
         width="32" height="32" visibility="hidden"/>
  <image id="down03" xlink:href="images/guy_down_3.gif"
         width="32" height="32" visibility="hidden"/>


  <image id="up01" xlink:href="images/guy_up_1.gif"
         width="32" height="32" visibility="hidden"/>
  <image id="up02" xlink:href="images/guy_up_2.gif"
         width="32" height="32" visibility="hidden"/>
  <image id="up03" xlink:href="images/guy_up_3.gif"
         width="32" height="32" visibility="hidden"/>

  <image id="right01" xlink:href="images/guy_right_1.gif"
         width="32" height="32" visibility="hidden"/>
  <image id="right02" xlink:href="images/guy_right_2.gif"
         width="32" height="32" visibility="hidden"/>
  <image id="right03" xlink:href="images/guy_right_3.gif"
         width="32" height="32" visibility="hidden"/>
</switchgroup>

</svg>

The first obvious advantage is that the amount of code is considerably smaller. For each new frame of the animation, a new image element must be added and a new animate element. Existing animate elements must be adjusted as well to take into account the longer duration and timing. Strictly speaking the animate elements shown above could be optimized by removing null transitions (from hidden to hidden or visible to visible), but they are left in for illustration purposes.

Secondly it is easier to add and remove frames from the animation. The new image needs to be added but only one animate elemement needs to be adjusted.

Thirdly the syntax shows exactly what the intent of the code is: to create an animated sprite. This helps a content author or editor understand what is happening if they read the source code directly. It is easier to find potential problems. More importantly it indicates to the SVG user agent what the intent of the code is. The viewer can then make assumptions and optimizations, knowing that the images will not be visible at the same time. For example the bounding boxes of the component images can be pre-calculated. The images can be pre-cached and quickly blitted to the screen. If the images share similar characteristics (size, transparency, filters) then the renderer can reuse calculated results during a sprite animation.

Pointing Device Support

SVG animations can be triggered off timer events or user events. User events include keyboard events and pointer events. On desktop computers pointer events are usually generated by a mouse, trackpad or trackball. In a desktop environment when the user navigates over a visual element a mouse over event is generated. When it navigates off the shape a mouse out event is generated. In addition if there is a activate behaviour associated with the object the application rendering the content will often change the cursor to indicate to the object is selectable. This paradigm does not map well to smart phones, which generally there is no native concept of a pointer device..

A simplistic way to emulate this behaviour on a smart phone would be to draw a physical cursor that can be moved about the phone screen using the phone's built-in arrow keys. Unfortunately moving the cursor using arrow keys is tedious. This is true even though a phone screen is considerably smaller than a computer screen.

In some ways, HTML browsers on mobile devices experience this problem as well. Anchor elements are easy to click on when you have a mouse. Browsers on mobile devices get around this by moving from one hotspot to the next successive hotspot as the user navigates through the document.

This works well in HTML because the layout of the document provides a natural ordering for the hotspots. SVG does not have this luxury. Visual objects can be placed anywhere on screen by specifying the (x,y)-coordinate. In fact the objects can move over time. Providing a predictable ordering for navigation under these circumstances is non-trivial. The situation is complicated when objects move off-screen or are occluded by other objects.

In Plazmic Media Engine we provided a simple solution to this problem. Hotspots are navigatable in the order they are defined in the file. This allows the content creator control over the order in which the hotspots are encountered.

The user experience is necessarily different on a mobile device. On a desktop the user moves the mouse and "discovers" hotspots by changes in the cursor or via explicitly defined mouse over and mouse exit events. The mobile device places the hotspots in the deterministic order (specified by the content developer) and the user must "navigate" through them in order.

A decision has to be made how to indicate to the user that they are on a hotspot and that by selecting the hotspot some action will occur. In desktop environments, changing the cursor from a pointer to a hand is sufficient information. In HTML, browsers hyperlinks are rendered in a different colour with underlining for text. For images a bounding box is usually drawn. This technique certainly can be applied for mobile SVG, but it is recommended that content creators utilize the mouseover functionality provided by SVG.

It may also make sense to extend mobile-SVG to set a global option on how to render hotspots. In HTML the content author can set the colour for links and visited links for any given HTML page. It would be nice to provide similar functionality in SVG. Often the content author will want the same or similar focus-in and focus-out behaviour for all hotspots.

Audio Support

A glaring omission in the SVG specification (from a rich media perspective) is audio support. Interactive animations are dry without a backing soundtrack. The Adobe SVG plug-in includes audio support by supporting an audio element. Macromedia Flash, for example, includes suport for ADPCM and MP3 sound formats.

Of course one could argue that audio support is outside the scope of a vector graphics format. It is the responsibility of other complementary XML initiatives to describe audio. Adding mandatory audio support to SVG viewers could limit the amount of compliant implementations. In addition, for many SVG applications audio just does not make sense.

These arguments hold weight but SVG does need a standard way to include audio. It must integrate with the animation framework so that an audio track can be synchronized with the animation. Ideally this mechanism would extend to other complementary media formats such as MPEG-4, Quicktime, Flash and others.

Media Engine added two elements into its markup. The first was <audioClip>. It has a begin attribute that matches the syntax of the begin attribute of <animate>.

<!ELEMENT audioClip>
<!ATTLIST audioClip
    id ID #IMPLIED
    %testAttrs
    xlink:href %URI;
    begin CDATA #IMPLIED
    dur CDATA #IMPLIED
    repeatCOunt CDATA #IMPLIED
>

Unlike the animate element, the xlink:href attribute references an external sound file. The intent is to reference sound files that are supported by the device. For the 503i Media Engine deployed in Japan, the format is the MLD format. As newer generation smart phones are deployed, file formats such as MIDI, WAV and MP3 are likely to be added.

The second element added was <audioStop>. It also borrows the begin semantics from <animate>. It is designed to stop all playing sounds. This was chosen as a convenience mechanism. A better fit to the SVG-mold would have been to use the "end" attribute in a similar fashion to the animate element.

Barriers to Player Development

There is always a tradeoff when defining a standard. On one side you want to empower the user (in this case an SVG author). The more tools and techniques the author can utilize the more likely he is to use and embrace the standard. There is an upper limit to this because too many features often leads to interfaces that are too complicated to use. On the other side, the more flexible you make the standard the harder it is for user agents to achieve compliance. Flexibility leads to more authors but fewer implementations. If you restrict the functionality you may get more implementations at the expense of attracting authors.

<!-- Code snippet One : using animate -->
  <rect id="r1">
    <animate attributeName="fill" dur="2" calcMode="discrete" 
     values="red;green;blue" keyTimes="0;.5;1"/>
  </rect>

<!-- Code snippet Two : using set -->
  <rect id="r2" fill="green">
    <set attributeName="fill" dur="2" calcMode="discrete"
        values="red;green;blue" keyTimes="0;.5;1"/>
  </rect>

<!-- Code snippet Three : using animateColor -->
  <rect id="r3">
    <animateColor attributeName="fill" dur="2" 
        values="red;green;blue" keyTimes="0;.5;1"/>          
  </rect>

The three code snippets above illustrate that the same results can be achieved many different ways. This gives the content author (or authoring tool) tremendous flexibility. The downside is that it requires extra logic from the viewer to render the content. For a mobile device every byte saved is precious.

There are numerous other examples of "syntactic sugar" in the SVG specification. I'll touch on one other example.

  <!-- Example using from/to -->
  <animate ... from="10" to="20" .../>

  <!-- Example using from/to/by -->
  <animate ... from="10" to="20" by="10" .../>

  <!-- Example using from/by -->
  <animate ... from="10" by="10" .../>

  <!-- Example using to (assuming initial value of 10) -->
  <animate ... to="20" .../>

  <!-- Example using by (assuming initial value of 10) -->
  <animate ... by="10" .../>

  <!-- Example using values -->
  <animate ... values="10;20" .../>

Although the animations shown above are syntactically different, they have the same functional result. The value is animated from a value of 10 to a value of 20. The first three animations can be achieved using only the final syntactic form. The syntax for the first three is simply a short cut for the last one. The The single "to" and "by" do add extra functionality over the final example in instances where the initial value is not known.

So if we want simple implementations, we could eliminate the other possible syntaxes. This would have minimal impact on the content author because they can achieve the same results. However in the specific instances where the other syntaxes can be used, a significant space savings is achieved. See the following as an example.

  <animate ... values="10;20;30;40;50;60;70;80;90;100"/>

  <animate ... from="10" to="100" by="10"/>

A balance needs to be achieved between content author flexibility, ease of viewer implementation, size of viewer implementation and file size of the content.

The Plazmic Solution

The Plazmic Media Engine on DoCoMo 503i phones is not a complete SVG solution. The limitations of the device make that impossible. Applications are limited to 10K of compiled code. It has been estimated that writing a full floating point library would take up 5 K by itself. In developing the application, size was by far the biggest restriction. In fact design meetings were held to determine when loop unrollling techniques lead to smaller or larger compiled code. In some cases using a sequence of if-else statements is smaller than one case statement. All package names were removed at one point to save 10 bytes to bring the file under the mandatory 10 K. Error messages were trimmed to have as few unneeded words as possible.

An attempt was made to take advantage of the feature set of the phones. They provided over-the-air downloading of content. The graphics libraries provided included routines for rendering filled polygons, rectangles and GIF images. There were no libraries for drawing ellipses, for example, and there was not enough space to write custom code to accomplish this (nor was the processor fast enough to do this in real time).

There were some tough decisions to make. Scaling and rotation are not provided. The only interaction with content is with the arrow keys on the phone. Even then, up and down were the same actions as left and right respectively. We did provide support for MLD (the MIDI format supported by the DoCoMo network) audio despite the fact there is no equivalent in SVG. Sacrificing audio was not an option.

Using SVG as Markup

The Plazmic solution was developed independently of SVG. We developed an engine and a data format for that engine that suited our purposes. However, we felt it would be considerably stronger solution if we embraced standards. SVG was an obvious choice.

It was encouraging to find a well defined mapping between our data format and SVG. The set of basic shapes in SVG and in our format were virtually identical and were parameterized in virtually the same manner. Interpolators were also common to both solutions. SVG was a good fit for us and for interactive rich mobile media.

State of Mobile Devices

The set of available mobile devices was small in 1999. The Wireless Access Protocol (WAP) standard was developed to enable fast delivery of information to mobile devices, especially phones and pagers. The standard was established and promised interoperability between different phone manufacturers. It focussed more on delivery of textual information than it did on rich media.

Meanwhile in Japan, NTT DoCoMo was delivering inexpensive mobile phones (or ketai) with impressive multimedia capabilities. These included full colour and support for multi-channel sound. DoCoMo introduced a service called i-mode which allowed users to browse the i-mode information network and the world wide web. Rather than follow the WAP standard, the format for i-mode was Compact HTML (cHTML).

The phones (and the i-mode service) were a success. After only 6 months, NTT DoCoMo had 1 million i-mode subscribers and was getting new subscribers at a rate of 80,000 per week. NTT DoCoMo had created an portal and enlisted over 100 Japanese companies to produce i-mode content. Subscribers could access any web page by typing in the address, and it was estimated that almost 1300 i-mode compatible web sites were produced. Today, NTT DoCoMo claims to have 28 million i-mode subscribers with access to 40,000 Internet sites.

In 2001, NTT DoCoMo introduced a new i-mode service called i-αppli. These headsets added over-the-air (OTA) loading of Java applications. The opportunity for third-party developers here was significant. Rather than static HTML content, developers could write full-fledged applications that users could easily install on their ketai - with the click of a button. To access these services consumers needed the relatively inexpensive 503i series of handsets manufactured by NEC, Panasonic, Mitsubishi Sony and Fujitsu.

Wireless platforms are becoming more impressive. In Japan, DoCoMo competitors J-Phone and KDDI are producing media savvy phones with Java support. They are seeing their market share increase. DoCoMo is extending its platform to include the 504i series of phones. These phones provide more application memory and a richer API. Two years ago there were few devices that supported Java's Mobile Interface Development Platform (MIDP). By the end of this year there will be hundreds by a wide range of platform vendors.

Smart phones are becoming more powerful and are challenging the traditional personal digital assitant platforms. In return the personal digital assistant vendors are delivering greater connectivity with their devices, including adding phone functionality. There is a convergence happening and it is leading to more and more capable devices.

Conclusions

The decision to modularize SVG is a positive step forward. It allows for subsets of SVG to be implemented on a constrained player. In particular the creation of two Mobile Specifications (Tiny and Basic) suggests the future for SVG on mobile devices is strong.

SVG Mobile's success may depend on SVG 1.1's success. If it becomes popular on the desktop, end users will embrace it on their mobile devices. Alternatively, SVG Mobile may be more successful in the wireless space than on desktops. The need for rich mobile media is new and there is no clear market leader. SVG Mobile could be the de facto standard in delivery of interactive media.

To become the de facto standard requires several robust complete implementations on a wide variety of platforms. The strength of the implementations and the push of the players in the space will ultimately determine whether SVG Mobile will succeed or fail.

It is hoped that some of the SVG experiences we have seen will lead to discussions that improve the SVG Mobile standard for deploying rich mobile media. The closer attention we pay to solving the problems in development and deployment of Mobile SVG the more likely it is to be successful.

References

[SVG10] "Scalable Vector Graphics (SVG) 1.0", J. Ferraiolo editor, 4 May 2001. Available at "http://www.w3.org/TR/2001/REC-SVG-20010904/.


Valid XHTML 1.0!