Vector Graphics

Build Flexible, Lightweight XML-Based Images for ASP.NET Using Scalable Vector Graphics

Dennis Forbes

Code download available at:ScalableVectorGraphics.exe(123 KB)

This article assumes you're familiar with HTML, ASP.NET, and C#

Level of Difficulty123


Scalable Vector Graphics (SVG), a W3C graphics standard built around XML, is one of several vector graphics technologies that allows fast, lightweight drawings such as charts and graphs to be rendered on the fly in an appropriate viewer. There are many advantages to such vector graphics, including conservation of bandwidth and storage media, and flexibility. This article explains these benefits and shows you how to easily add powerful, dynamic, interactive visual elements to your Web applications.


Various Vector Graphics Formats
Getting Started with SVG
Dynamic Content
SVG Deployment
Creating SVGs On the Fly
Graphing with SVG
Conservation of Bandwidth
Maintaining Backward Compatibility

Scalable Vector Graphics (SVG) is a standard for authoring and deploying two-dimensional vector graphics using XML documents. It is a World Wide Web Consortium (W3C) recommendation that has been authored and endorsed by a number of industry organizations. Featuring the quality and versatility of vector graphics, the simplicity and extensive tool compatibility of an XML foundation, and the dynamics of a feature-rich Document Object Model (DOM) interface, SVG brings a powerful new ingredient to DHTML projects. Combining this with some creativity—and a little ingenuity—yields a limitless canvas of interactive Web possibilities, enabling you to improve the appearance, usability, and functionality of your Web apps.

Various Vector Graphics Formats

Vector graphics—which include SVG, Vector Markup Language (VML), Windows® metafiles (.wmf and .emf), and TrueType fonts—are stored and conveyed as a list of instructions detailing how to recreate an image using a sequence of shapes, lines, and transformations. This is similar to reproducing a painting by precisely recording and then recreating the sequence of brush strokes originally used to produce it. Vector graphics are usually data efficient, especially when compressed, with minimal storage and bandwidth requirements. They are rendered without pixelation when printed or scaled, even when magnified or enlarged. They are a superb choice for any graphic with limited precision detail, allowing it to be broken down into fundamental shapes and curves. On the downside, because the number of steps required to recreate an image can number in the thousands and beyond, vector graphics can be computationally demanding to render. Vector graphics are generally unsuitable for photo-realistic, highly detailed images, but are usually the best choice for graphs, diagrams, or images composed of basic shapes, such as cartoon-style images.

Raster graphics—such as Portable Network Graphics (PNG), GIF, and JPEG—are by comparison a pixel-by-pixel representation of the final image, prerendered for a specific resolution and zoom. This is similar to waiting for a painting to be completed and then taking a picture at a desired resolution and size for a specific use and output device. Raster images are consistently reproducible across platforms, with minimal computational requirements. They are ideal for highly detailed, photo-realistic images; however, they generally zoom and print poorly. Without resorting to enormous file sizes, they offer minimal dynamic programmability and are generally inefficient to store and transmit.

The highly caffeinated among you might have noticed the reference to VML earlier in this section, and indeed it does deserve special mention: VML offers many of the same advantages of SVG, such as easy text-based authoring and a foundation built on vector graphics. VML, however, has been largely deprecated in favor of other formats, with SVG garnering some of this vector graphic momentum over the past year. There are tools available for converting back and forth between SVG and VML, so it is conceivable that both could be supported simultaneously with minimal effort.

Getting Started with SVG

An SVG is, at its core, an XML document conforming to the schema defined as a part of the SVG ratification process. Defined in the specification is a variety of basic geometric shapes such as rectangles, circles, ellipses, lines, polylines, polygons, and glyph elements such as text. More complex shapes can be created by using the path element, allowing you to define shapes as a sequence of lines, arcs, and complex Bezier curves. Using these basic shapes, along with a bit of ingenuity, even the most complex geometric constructions can be created. Figure 1 illustrates the XML composition of a basic SVG document, and Figure 2 demonstrates how this SVG looks when rendered in an SVG viewer.

Figure 1 SVG Document

<?xml version="1.0" standalone="yes" ?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" ""> <svg xmlns="" width="400px" height="300px"> <g id="oAll"> <rect id="oRect" x="50" y="100" width="100" height="100" fill="red" stroke="black" stroke-width="2" /> <circle id="oCircle" cx="210" cy="150" r="50" fill="green" stroke="black" stroke-width="2" /> <polygon id="oPolygon" points="300,100 250,200 350,200" fill="blue" stroke="black" stroke-width="2" /> </g> <text x="200" y="80" font-family="Verdana" font-size="25" fill="blue" stroke="yellow" stroke-width="0.5" text-anchor="middle">MSDN Magazine</text> </svg>

Figure 2 SVG in a Viewer

Figure 2** SVG in a Viewer **

While some applications (such as CorelDraw, Adobe Illustrator, and Batik SVG toolset) can display or manipulate SVGs directly, in most cases SVGs will be used as an element of an HTML document alongside other HTML text and multimedia content. The classic method for doing this is through the venerable embed tag:

<html> <body> <h1>A Page Containing an SVG</h1> <embed name="ExampleSVG" pluginspage="" src="figure1.svg" width="400" height="300" type="image/svg-xml" /> </body> </html>

The pluginspage attribute directs the user to the applicable content handler if one isn't already installed. In this example, I have referenced the Adobe SVG Viewer.

A noteworthy point is the correlation between the width and height attributes of the embed tag, and the same on the root SVG element in the referenced graphic (a value which incidentally can be expressed in a variety of ways, including pixels, centimeters, and so on). If these figures do not match, the encapsulated graphic will be clipped or modified in some undesirable way. A technique to avoid this problem, and to provide more flexibility in general, is to refrain from tying the SVG to one specific size. Figure 3 shows a slightly modified SVG that uses a viewBox attribute rather than width and height. This sets the bounds of the canvas that I am drawing on and implies the ratio between width and height; however, it does not fix a rendering size. If you change the width and height of the embed element while keeping the relative proportions of width and height consistent, the SVG graphic should smoothly size accordingly.

Figure 3 SVG Using viewBox

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" ""> <svg xmlns="" viewBox="0 0 400 300"> <g id="oAll"> <rect id="oRect" x="50" y="100" width="100" height="100" fill="red" stroke="black" stroke-width="2" /> <circle id="oCircle" cx="210" cy="150" r="50" fill="green" stroke="black" stroke-width="2" /> <polygon id="oPolygon" points="300,100 250,200 350,200" fill="blue" stroke="black" stroke-width="2" /> </g> <text x="200" y="80" font-family="Verdana" font-size="25" fill="blue" stroke="yellow" stroke-width="0.5" text-anchor="middle">MSDN Magazine</text> </svg>

An alternative to the nonstandard HTML embed element, especially if compliance with XHTML is a requirement, is to use an object or iframe element:

<html> <body> <object data="figure1.svg" codetype="image/svg-xml" style="width:400;height:300;"> </object> <iframe src="figure3.svg" width="600" height="450" /> </body> </html>

The object tag presents a particularly powerful feature in that it allows cascades when unsupported types are encountered. For example, the following document tells the browser to first try to render the SVG, but failing that (if SVG functionality isn't available, for instance) to display a raster PNG as an alternative. Such a structure allows you to create a single set of pages supporting both SVG and raster content without needless branching based on the client browser's capabilities, as shown here:

<html> <body> <object data="figure1.svg" codetype="image/svg-xml" style="width:400;height:300;"> <img src="figure1.png" width="400" height="300" /> </object> </body> </html>

A caveat about the object tag is in order. While developing Microsoft® Internet Explorer, Microsoft went to great lengths to make it highly extensible. This is demonstrated by the ability of the browser to instantiate a number of helper components to display multimedia Web pages (either through direct reference in the HTML, for example a specific ActiveX® object via a CODEBASE/CLASSID pair, or through the MIME type and a lookup in HKEY_CLASSES_ROOT\MIME\Database\Content Type). While this offers tremendous versatility, it can lead to trouble when overzealous applications hijack content types and then fail to render them properly when the time comes, or when they fail to reassociate Internet Explorer as the handler during deinstallation. Furthermore, I've encountered some inconsistencies and irregularities when using the object tag with SVG—problems which don't occur when using the embed or iframe tags. The embed tag, however, is being deprecated. As always, your mileage may vary, and it's just something that you should keep in mind when troubleshooting a pesky situation in which a client has installed a wide swath of helper applications.

Dynamic Content

A powerful feature of SVG is the ability to programmatically manipulate the graphical elements of an SVG through internal or external scripts. Figure 4 is an HTML document encapsulating the figure3.svg graphic which is available in the download at the link at the top of this article. This example uses the Adobe SVG Viewer plug-in for Internet Explorer. This example shows the use of ECMAScript (JavaScript) in the containing HTML document to automate the DOM interface exposed by the SVG viewer. By clicking one of the buttons on the Web page shown in Figure 5, the user can shift the colors used on the basic shapes or control the rate of rotation of a group of objects. While this particular example represents nothing more than a rehash of the infamous BLINK tag, it does hint at the breadth of possibilities that SVG offers. New objects can be added to graphics, and the existing content can be completely modified. This allows you to create graphs that are enabled with dynamic interpretation cues, for example, giving the user a better tool to fully comprehend the information.

Figure 4 HTML with Script

<html> <head> <title>Automated SVG</title> </head> <body onload="RotateSVG();"> <script language="javascript"> var SVGDocument = null; var Circle, Polygon, Rect, AllObjects; var CurrentAngle = 0; var AngleDelta = 0.0; function InitializeObjects() { SVGDocument = window.document.getElementById ("SVGEmbed").getSVGDocument(); Circle = SVGDocument.getElementById("oCircle"); Polygon = SVGDocument.getElementById("oPolygon"); Rect = SVGDocument.getElementById("oRect"); AllObjects = SVGDocument.getElementById("oAll"); } function RotateSVG() { window.setTimeout("RotateSVG();",10); if (AngleDelta == 0.0) return; if (!SVGDocument) { InitializeObjects(); } CurrentAngle = CurrentAngle + AngleDelta; if (CurrentAngle > 359) CurrentAngle = CurrentAngle - 360; if (CurrentAngle < 0) CurrentAngle = CurrentAngle + 360; AllObjects.setAttribute("transform","rotate("+CurrentAngle+" 210 150)"); } function ShiftLeft() { if (!SVGDocument) InitializeObjects(); var CircleFill = Circle.getAttribute('fill'); Circle.setAttribute('fill',Polygon.getAttribute('fill')); Polygon.setAttribute('fill',Rect.getAttribute('fill')); Rect.setAttribute('fill',CircleFill); } function ShiftRight() { if (!SVGDocument) InitializeObjects(); var PolygonFill = Polygon.getAttribute('fill'); Polygon.setAttribute('fill',Circle.getAttribute('fill')); Circle.setAttribute('fill',Rect.getAttribute('fill')); Rect.setAttribute('fill',PolygonFill); } </script> <center> <h1>SVG Automation</h1> <object id="SVGEmbed" data="figure3.svg" codebase="" width="600" height="450" type="image/svg+xml"></embed> <br /> <input type="button" value="Spin Left" onclick="AngleDelta=AngleDelta-0.5;" /> <input type="button" value="Color Shift Left" onclick="ShiftLeft();" /> <input type="button" value="Color Shift Right" onclick="ShiftRight();" /> <input type="button" value="Spin Right" onclick="AngleDelta=AngleDelta+0.5;" /> <input type="button" value="Stop" onclick="AngleDelta=0;"/> </center> </body> </html>

Figure 5 Manipulating the Shapes

Figure 5** Manipulating the Shapes **

SVG Deployment

Due to the cutting-edge nature of SVG and its current lack of native support across all major browsers, Web site visitors will often need to download and install a specialized viewer, such as the Adobe SVG Viewer or the upcoming Corel SVG Viewer, to see SVG content within Web pages (at least until the browsers themselves natively support SVG as they do most other graphics formats). The Adobe Viewer itself works on several platforms, including Windows, Macintosh, and Linux, and in virtually all major Web browsers. Given that SVG is a highly documented standard, I feel that it does not represent an esoteric gamble on a proprietary format, but rather an investment in the future that can yield positive results for you today.

Despite the widespread availability of SVG-equipped clients, most sites will still need to continue to support legacy clients which may not be able to install SVG-specific software. Such a solution can easily be accommodated with a graphics infrastructure that generates SVGs, but which can also create static raster graphic renderings for legacy clients. A powerful method of achieving this is detailed later in this article. Indeed, SVG can be used in the back end alongside the powerful XML classes in the Microsoft .NET Framework, even if SVGs are never directly served to Web clients.

Creating SVGs On the Fly

Coupling the graphical prowess of SVG with the wide feature set of the .NET Framework offers a powerful opportunity for Web developers. By leveraging a data back end with automatic SVG document creation, Web sites can offer real-time, aesthetically pleasing, interactive graphics, assisting users in interpreting and understanding information better. This also gives the Web application a more professional, polished look.

In order to make the dynamic creation of the root SVG document easier, and to demonstrate an XmlDocument-derived class with rudimentary SVG intelligence, I've created a trivial class which is shown in Figure 6. This helper class will be used by the following example and should be compiled with the resulting DLL placed in the Web application's bin directory (for example, C:\inetpub\wwwroot\bin):

csc /target:library figure5.cs copy figure5.dll C:\inetpub\wwwroot\bin

Figure 6 Helper Class

using System; using System.Xml; namespace Example.ExampleSvg { /// <summary> /// XmlDocument wrapper for SVG content /// </summary> public class XmlDocumentSvg : System.Xml.XmlDocument { public XmlDocumentSvg(int Width, int Height) : base() { string BaseSvg = "<?xml version=\"1.0\" standalone=\"yes\"?>" + "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\" "+ "\"\">" + "<svg width=\""+Width.ToString()+"\" height=\""+Height.ToString()+"\" xmlns=\"\"> "+ "</svg>"; this.LoadXml(BaseSvg); } } }

Figure 7 shows the foundations of an SVG-generating Web page. It contains a simple server-side ASP.NET script that responds to requests by returning an SVG doc which contains the current time in ISO 8601 format. While this example has limited real-world use, it does show the simplicity of dynamically creating SVG documents on the server. This example concatenates strings to form the XML document. It could also be implemented by programmatically adding each element and attribute using a proper DOM approach. This sample sets the ContentType property of the Response object (it could also use the ContentType attribute of the @Page directive). This allows the client to recognize the content type without depending on tenuous and unreliable file name extensions. Having said that, it is sometimes advantageous to include a false parameter appendage on URLs, such as ?.svg, for clients such as Opera 6 that errantly look for an extension in order to determine content type.

Figure 7 Basic SVG Document

<%@page Buffer="true" %> <%@import Namespace="System.Xml" %> <%@import Namespace="Example.ExampleSvg" %> <script language="C#" runat="server"> private void Page_Load(object sender, System.EventArgs e) { XmlDocumentSvg SvgXml = new XmlDocumentSvg(400,200); XmlNode RootNode = SvgXml["svg"]; RootNode.InnerXml = "<text x=\"100\" y=\"100\" font-family=\"Verdana\" font-size=\"15\" fill=\"blue\">" +System.DateTime.Now.ToString("s")+"</text>"; Response.ContentType = "image/svg+xml"; SvgXml.Save(Context.Response.OutputStream); } </script>

Also notable in this example is the direct streaming of the XML document to the output stream, which eliminates any unnecessary persistence in between and maintains a high level of performance. This is a very important consideration when the page needs to serve the real-time needs of a large number of users.

The resulting SVG generated during a call to the ASP.NET page follows, with the date/time dependent on when the page is called:

<?xml version="1.0" standalone="yes"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" ""[]> <svg width="400" height="200" xmlns=""> <text x="100" y="100" font-family="Verdana" font-size="15" fill="blue">2003-03-28T122002-10-29T12:02:14</text> </svg>

This dynamic SVG document can be included in a basic HTML page, as previously described, using the object tag:

<html> <body> <center> <h1>SVG Test</h1> <object name="ExampleSVG" codebase=" install/" data="figure7.aspx?.svg" width="400" height="200" type="image/svg+xml"/> </center> </body> </html>

Graphing with SVG

Many SVGs will be dynamically generated on demand based on changing data in a database, be it GIS information containing regional land boundaries and partitions or the latest monthly sales figures. As an example of techniques to generate SVG documents from data dynamically, the namespace yafla.yaflaSVG (available by compiling and installing the yafla.yaflaSVG.cs file in from the link at the top of this article) offers a variety of classes to assist in converting data to graphs. These graph types include pie charts (see Figure 8), as well as vertical and horizontal bar charts (see Figure 9). The class hierarchy has been built in an easily expanded object-oriented fashion, and provides numerous settings to control the UI of the generated graphs. Given that the generated SVG graphic is returned as an XML document, you could then use the broad XML capabilities within the .NET Framework to modify the documents before returning them to the caller. While these graphs are no replacement for commercial graphing products, they are an excellent introduction to the world of SVG and may actually offer real-world solutions for some readers. Barchart_example.aspx and piechart_example.aspx, also available in the download, demonstrate the basics of dynamically generating graphs using these classes, in this case creating a bar and pie chart. While these examples manually create and populate a DataTable, it is just as easy to retrieve a DataTable as a result of a query, thus achieving truly dynamic vector graphics.

Figure 8 A Pie Chart

Figure 8** A Pie Chart **

Conservation of Bandwidth

While SVG documents are generally much smaller than their raster-based relatives, when representing basic graphs and charts, the intrinsic verbosity of XML can yield large files for complex graphics. Thankfully, the SVG standard includes native support for GZIP compression (a standard documented in RFC 1952), dramatically reducing the transmission and storage size—often by 80 percent or more. While GZIP does impose some additional computational overhead due to its compression and decompression, this can be alleviated on the server side by appropriate caching. On the client side, the concern is largely not relevant since modern high-power processors outpace the speed of the vast majority of network connections. Considering these factors, it becomes clear that compression is a win-win situation, producing a more responsive, snappier Web site for the client with reduced bandwidth bills for the host.

Figure 9 A Bar Graph

Figure 9** A Bar Graph **

For a static SVG document, compression is as simple as GZIP-ing the file on the server (the resulting compressed file's extension is .svgz; .svg is the extension of an uncompressed SVG document), ensuring that the appropriate MIME type (image/svg+xml) is associated with both .svg and .svgz in Microsoft Internet Information Services (IIS), and ensuring that references point to the compressed copy. By doing this you'll be using a fraction of the bandwidth you would have consumed. Your clients, especially dial-up Internet users, will enjoy a much faster experience.

Things get a little more complex when compressing dynamically generated content. The first step in pursuing dynamic compression is to determine if it's already taking place. Is your Web environment enabled to stream compressed output to equipped clients? Figure 10 is a simple script that takes a URL as a parameter (for example, wscript figure9.js and, using the MSXML ServerXMLHTTP object, reports whether streaming compression is enabled for that URL. As an additional benefit, it also reports the MIME type that the server is returning for the request, allowing you to verify that the server is correctly configured. It is important to test since compression can be transparently taking place through load-balancing front-ends or IIS ISAPI filters compressing output when requested, and double-compression is unnecessarily wasteful in such cases.

Figure 10 Is Streaming Compression On?

var WshShell = WScript.CreateObject("WScript.Shell"); if (WScript.Arguments.Length < 1) { WshShell.Popup("Usage: Pass a URL in the form of protocol://domain_or_server/document"); } else { var XMLRequest = new ActiveXObject("msxml2.serverxmlhttp.3.0");"GET",WScript.Arguments(0),false); XMLRequest.setRequestHeader("Accept-Encoding", "gzip, deflate"); XMLRequest.send(); var CompressionResult; if (XMLRequest.getResponseHeader("Content- Encoding").search("gzip|deflate")!=-1) { CompressionResult = "***enabled***"; } else { CompressionResult = "disabled"; } WshShell.Popup("Compression is "+CompressionResult+" for "+WScript.Arguments(0)+". The content type is "+XMLRequest.getResponseHeader("Content-Type")+"."); }

If you want to enable dynamic SVG compression, there are a variety of .NET-accessible compression components available on the market, from feature-rich, rock-solid commercial packages, to COM Interop wrappers for existing DLLs, to excellent open-source projects. Many of these solutions are implemented as classes derived from the .NET stream objects, allowing for easy use anywhere a stream object is used. For demonstration purposes, I've chosen the open-source SharpZipLib C#-authored library, downloadable at

After placing the binary DLL in the global assembly cache or in the Web application's /bin directory, streaming GZIP compression can be implemented quite easily, as shown in Figure 11 (which is a variation of Figure 7). By using the GZIP stream interface as a transport between the XML document and the HTTP response object, data is compressed while being sent, reducing the bandwidth requirements dramatically. For such a tiny SVG document, the savings achieved by GZIP compression are minimal and are likely outweighed by the computational setup costs. However, for real-world documents the benefits can be dramatic. Compressed_ barchart_example.aspx, available in the download, uses compression with the charting components. In that example, the document is reduced from 7,722 to 1,235 bytes (an 84 percent decrease).

Figure 11 GZIP Compression

<%@page Buffer="true" %> <%@import Namespace="System.Xml" %> <%@import Namespace="Example.ExampleSvg" %> <%@import Namespace="ICSharpCode.SharpZipLib.GZip" %> <script language="C#" runat="server"> private void Page_Load(object sender, System.EventArgs e) { XmlDocumentSvg SvgXml = new XmlDocumentSvg(400,200); XmlNode RootNode = SvgXml["svg"]; RootNode.InnerXml = "<text x=\"100\" y=\"100\" font-family=\"Verdana\" font-size=\"15\" fill=\"blue\" >" +System.DateTime.Now.ToString("s")+"</text>"; Response.ContentType = "image/svg+xml"; GZipOutputStream CompressedOutput = new GZipOutputStream(Response.OutputStream); SvgXml.Save(CompressedOutput); CompressedOutput.Finish(); } </script>

Maintaining Backward Compatibility

It isn't realistic to expect every user to upgrade their system with special components specifically for your Web site. The use of esoteric technologies can lead to endless support calls as users panic about being left out, or, in the case of public Web sites, the inevitable flurry of e-mail messages from advocates of the lowest common denominator furious about anything beyond HTML 1.0. To avoid this situation, one possibility is to render the SVG to a bitmapped graphic on the server, returning the SVG itself for enabled clients while returning the bitmapped graphic for everyone else. This is a credible approach if SVGs themselves are never served to the clients, but are used merely as a dynamic graphic authoring technology which is then rasterized for presentation.

You can just imagine the machinations necessary to capture its GDI output for rasterization. But whether or not the Adobe SVG Viewer can be used for server-side rendering may be addressed in their licensing agreement, which you can check at Another viable option can be had in the form of the Batik project (, a utility that can convert an SVG into a variety of formats, including raster formats like PNG, TIFF, PDF, or JPEG (though the lossy JPEG format is generally only suitable for photo-realistic images or images with smooth color gradients). The GIF format is excluded from its output repertoire, presumably because of the associated licensing issues. Batik unfortunately requires that you persist the SVG to disk, to the temporary folder, for example, where it then processes and generates a raster graphic depending upon the parameters passed. Barchart_example_rasterize.aspx, found in the download, demonstrates how you could take a width input parameter and from that it generates a raster PNG bar chart at the desired size (for example, https://localhost/barchart_example_rasterize.asp?width=750). It would be a simple exercise to add additional logic to take a parameter choosing the destination file type and allowing for the generation of the various types of graphics output at various sizes. A slightly more involved scenario would be the addition of logic to incorporate caching into the system. This would entail persisting the SVG itself to a cache object, even if only for short periods of time, with a dependent raster graphic that is regenerated whenever the source SVG changes. With such changes you can enable a powerful server-side graphics subsystem that can accommodate both your dynamic vector and raster needs, while at the same time using the caching infrastructure of the .NET Framework to achieve vast scalability and excellent response times even with a heavy load.

For performance and integration reasons, it would be preferable to develop a .NET native SVG rasterizer; however, such a project is beyond the scope of this article. Perhaps with the increasing interest in SVG this implementation may become a reality. SVG also currently lacks DHTML behaviors like VML supported. This would be another good community addition to the SVG spec.


By combining the graphics prowess of SVG with the power and versatility of the .NET Framework, you can turn your Web applications into graphical superheroes. With the added dynamic graphical elements, you can add clarity to your data, achieve greater organizational support, and present a more polished, professional image to the world. Hopefully this article has offered enough information to whet your appetite and to encourage further experimentation and research into exciting implementation possibilities of vector graphics in Web development.

For related articles see:
How to use VML on Web Pages
Create Snazzy Web Charts and Graphics On the Fly with the .NET Framework
Manipulate XML Data Easily with Integrated Readers and Writers in the .NET Framework

For background information see:

Dennis Forbes is a new father and professional software developer/consultant from Toronto, Canada. His company specializes in providing solutions for the worlds of .NET and IIS. Visit his Web site at, or contact him at