I'm a graphics junkie: I admit it. Since I was a kid, I've always favored computers that had great graphics. That's why I liked the Apple II computer better than the TRS-80 computer. But who doesn't like good graphics? Who wasn't blown away by the Pixar films? There is a lot of truth in the statement, "a picture's worth a thousand words," because a picture imparts a lot of data quickly and easily.
Nowhere is graphing more important than with business data. And to get the most out of your graphics code with standards like SVG is important, because by all accounts, the Web has thus far been a graphics loser. Sure, you can put images into pages, but all too often these images don't function well. The graphics don't zoom or pan; they don't interact; they don't print or scale well. But with Web 2.0, I believe that things are changing. No longer is the emphasis on the technology. The goal is now to give the user an experience, which includes graphics. Take the Google Finance page shown in Figure 1. As you view a stock, you can use an interactive graph control to scroll through the data and find ranges that interest you. Does it matter that it's done in Macromedia Flash? No. What matters is the end result -- the experience for the customer.
Figure 1. The Google Finance page
In this article, I demonstrate how you can use the SVG format with the PHP programming language to create pretty and interactive graphics. To begin, here's some background on SVG and how it relates to graphic technologies for the Web.
The SVG standard is an XML-based format for presenting vector graphics. The primitives are elements such as lines, rectangles, shapes, images, and text. All these are specified within "view coordinates," which are not pixels but any arbitrary range of values that you can define in whatever way makes sense for your application. In this way, you can render the graphic model that the XML specifies into any graphic space -- no matter how big or small -- and scale appropriately. A vector image prints at the highest resolution of the printer without the jagged pixels so often seen when you zoom in on bitmaps for printing.
The format also allows for you to apply effects to any object or group of objects. These effects include drop shadows, bevels, embossing, outer glow, inner glow, and so on. If you're familiar with Adobe PhotoShop or Elements, you'll recognize these effects. You can also use rotate, skew, sheer, clip, and all the rest.
But why stop there? SVG also has tags that you can use to animate these properties, so you can move a graphics object along a path or fade it in or out. Beyond that, SVG allows you to add JavaScript code within the model to accompany the graphic elements, effects, and animations to add behavior.
When I first read about SVG, I was overwhelmed and dug in immediately. From my point of view, Adobe was giving away the PhotoShop engine as a control that you could embed in a Web page. In fact, that's still true today. The downside came when I realized that SVG wasn't installed on every client and that installing it required downloading certain software. That was too much for me to ask of my customers. So, I put SVG on the back burner for a while, until I recently discovered that a subset of SVG is installed in Mozilla Firefox V1.5. Now, I think things will pick up for SVG.
But let me step back a little farther and contextualize SVG within Web graphics.
A few years ago, if you wanted to create graphics for the Web, the choices were limited. You could create PHP image graphics and build .jpeg, .gif, or .png files on the fly. But those graphics tended to be a bit ugly, because PHP's image library is primitive and doesn't support effects. Also, the graphics didn't scale well.
Technology has advanced a bit, and now more options are available. Certainly, there's SVG. But there's also Flash. Flash can draw arbitrary graphics using a canvas object and JavaScript code. It can also read the data to graph from the server directly as XML or JavaScript Serialized Object Notation (JSON).
On top of Flash are two relative newcomers. The first is Adobe Flex, an XML markup-based language that renders into Flash and includes a graphing library. A competitor of Flex is Laszlo. Laszlo also works on tags but has the advantage of being open source.
You can also use the <canvas> tag in the browser. This new tag is a graphics canvas
on which you can draw lines and rectangles, place images, perform rotations, and more. It sounds great, but
Microsoft® Internet Explorer doesn't support it -- yet. Fortunately, Google has an open source project called
InternetCanvas that gives you the same capability within Internet Explorer.
Yet another option on the horizon is Microsoft Windows Presentation Foundation (WPF), which provides an XML markup for creating Windows User Interfaces that you can embed within a browser. Just for Microsoft Windows® you say? Sure. But, WPF/E is also coming out, and that will provide a subset of WPF functionality on all the mainstream browsers for both Mac and Windows.
You certainly have a lot of options. But for this article, I choose SVG and use it in combination with PHP to do a bit of stock graphing.
To make sure that your Firefox V1.5 browser is working properly for SVG or you have a browser with the SVG plug-in installed, I put together a simple "Hello World"-style example that you can use. First, I create a file called start.html, shown in Listing 1. This file is the Web page that references the .svg file.
Listing 1. Start.html
<html> <body> <embed src="start.svg" height="300" width="300" type="image/svg+xml" pluginspage="http://www.adobe.com/svg/viewer/install/" style="border: 1px solid black; padding:5px;"/> </body> </html> |
Next, I need an file called start.svg, shown in Listing 2. You can see that the
<embed> tag in the .html file references the .svg file.
Listing 2. Start.svg
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/SVG/DTD/svg10.dtd">
<svg style="shape-rendering:geometricPrecision;"
viewBox="0 0 100 100" xml:space="preserve"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid meet">
<path fill-rule="nonzero"
style="fill:#000000;stroke:#FF0000;"
d="M0 0 L100 100 Z"/>
<path fill-rule="nonzero"
style="fill:#000000;stroke:#00FF00;"
d="M50 0 L50 100 Z"/>
<path fill-rule="nonzero"
style="fill:#000000;stroke:#FF0000;"
d="M0 100 L100 0 Z"/>
<path fill-rule="nonzero"
style="fill:#000000;stroke:#00FF00;"
d="M0 50 L100 50 Z"/>
</svg>
|
When I open this file in the browser, I see something that looks like Figure 2.
Figure 2. The test .svg file
Looking back at the file, you can see that I defined four paths: two going from corner to corner and two more in green going from top to bottom along the centers. If you see this graphic, your browser is properly rendering at least a small subset of SVG.
Graphing starts with the data. In this case, I take stock data from a 31-day period for two companies. A portion of the XML that contains the stock values is shown in Listing 3.
Listing 3. Excerpt from data.xml
<stocks> <stock> <day high="35.13" low="32" close="33.75" /> <day high="32.25" low="28.75" close="31.75" /> <day high="29" low="28.5" close="28.87" /> <day high="29.25" low="28.75" close="28.75" /> <day high="29.5" low="28.5" close="29.25" /> <day high="30.25" low="29" close="29.25" /> ... </stock> </stocks> |
The format is straightforward. A root tag called <stocks> contains a set of
<stock> tags. Each <stock> tag has a set of
day tags, where each has high, low, and
close attributes.
The next step is to write the code that reads this file into memory for use when generating the SVG. For that, I use a set of three PHP classes, as shown in Listing 4.
Listing 4. Data.php
<?php
class Day
{
var $low;
var $high;
var $close;
public function __construct( $low, $high, $close )
{
$this->low = $low;
$this->high = $high;
$this->close = $close;
}
}
class Trace
{
var $days = array();
var $high = 0.0;
var $low = 100000.0;
public function addDay( $low, $high, $close )
{
$this->days []= new Day( $low, $high, $close );
if ( $low < $this->low ) $this->low = $low;
if ( $high > $this->high ) $this->high = $high;
}
public function hiloPath( $trans )
{
$p = new Path();
$d = 0;
foreach( $this->days as $day )
{
$x = $trans->xscale( $d );
$y = $trans->yscale( $day->low );
$p->add( $x, $y );
$d += 1;
}
for( $d = (count( $this->days ) - 1); $d >= 0; $d -= 1 )
{
$x = $trans->xscale( $d );
$y = $trans->yscale( $this->days[$d]->high );
$p->add( $x, $y );
}
return $p;
}
public function closePath( $trans )
{
$p = new Path();
$d = 0;
foreach( $this->days as $day )
{
$x = $trans->xscale( $d );
$y = $trans->yscale( $day->close );
$p->add( $x, $y );
$d += 1;
}
return $p;
}
}
class Data
{
var $traces = array();
var $high = 0;
var $low = 100000;
function parseXML( $file )
{
$data_dom = new DomDocument();
$data_dom->load( $file );
$elStocks = $data_dom->getElementsByTagName( 'stock' );
foreach( $elStocks as $stock )
{
$trace = new Trace();
$days = $stock->getElementsByTagName( 'day' );
foreach( $days as $day )
{
$trace->addDay( (float)$day->getAttribute('low'),
(float)$day->getAttribute('high'),
(float)$day->getAttribute('close') );
}
$this->traces []= $trace;
if ( $trace->high > $this->high ) $this->high = $trace->high;
if ( $trace->low < $this->low ) $this->low = $trace->low;
}
}
}
?>
|
The Data class contains the set of all the data in the file. Each stock is stored as
a Trace object. Within each Trace object is a set of
Day objects, which define the low, high, and close for that day. Additionally,
Trace objects know how to create a Path object for
either the high-low portion of the graph or the close portion of the graph.
That Path object is a PHP class that I wrote specifically to make building SVG paths
easier on myself. That helper class is defined in the svg.php file shown in Listing 5.
Listing 5. Svg.php
<?php
interface ITransform
{
function xscale( $x );
function yscale( $x );
}
class Transform implements ITransform
{
protected $ox;
protected $oy;
protected $xscale;
protected $yscale;
protected $xoffset = 0;
protected $yoffset = 0;
public function xscale( $x ) {
return $this->ox + ( ( $x - $this->xoffset ) * $this->xscale );
}
public function yscale( $y ) {
return $this->oy - ( ( $y - $this->yoffset ) * $this->yscale );
}
}
class Point
{
var $x;
var $y;
public function __construct( $x, $y )
{
$this->x = $x;
$this->y = $y;
}
}
class Path
{
private $points = array();
public function add( $x, $y )
{
$this->points []= new Point( $x, $y );
}
public function toSVG()
{
$svg = "";
$svg .= "M ".$this->points[0]->x." ".$this->points[0]->y." ";
foreach( $this->points as $pt ) {
$svg .= "L ".$pt->x." ".$pt->y." ";
}
return $svg;
}
}
?>
|
Here, I have defined two classes that are fairly easy to understand: Point and
Path. The Point class represents an X-Y pair; the
Path class is a set of Points. The
ITransform interface and the Transform base class are
more interesting. To draw the stock graph, I must change the scale of the dollar and day values into SVG view
coordinates. The ITransform interface allows me to do just that. But, more on
that in a bit.
I'll work up to building the complete graph in stages.
Version 1: Draw the close path
In the first version, I draw just the close path so that you can see how the two stocks in my data.xml file performed over the 31 days. Listing 6 shows this first version of the code.
Listing 6. The first version of graph.php
<?php
require_once( 'svg.php' );
require_once( 'data.php' );
class GraphTransform extends Transform
{
public function __construct( $ox, $oy, $width,
$height, $low, $high, $days )
{
$this->ox = $ox;
$this->oy = $oy;
$this->xscale = $width / ( $days - 1 );
$this->yscale = $height / ( $high - $low );
$this->yoffset = $low;
}
}
$d = new Data();
$d->parseXML( 'data.xml' );
$ystart = (int)$d->low - 2;
$yend = (int)$d->high + 2;
$gt = new GraphTransform( 20, 90, 80, 80,
$ystart, $yend, count( $d->traces[0]->days ) );
header( "Content-type: text/xml" );
echo( "<?xml version=\"1.0\" standalone=\"no\"?>\n" );
?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/SVG/DTD/svg10.dtd">
<svg style="shape-rendering:geometricPrecision;"
viewBox="0 0 100 100" xml:space="preserve"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid meet">
<style type="text/css">
.close { fill-opacity: 0; stroke-opacity:0.8;
stroke-width: 0.4; }
</style>
<?php
foreach( $d->traces as $trace )
{
$closePath = $trace->closePath( $gt );
$closeSVG = $closePath->toSVG();
$color = "#ff0000";
?>
<path fill-rule="nonzero"
d="<?php echo( $closeSVG ); ?>" class="close"
fill="<?php echo( $color ); ?>"
stroke="<?php echo( $color ); ?>"
/>
<?php
}
?>
</svg>
|
At the top of the file, I create a version of the ITransform interface that
transforms dollar values along the Y-axis and transforms day values along the X-axis into SVG points.
Then, at the bottom of the file, I use a foreach iterator to walk through
the list of traces in the data. From each trace, I get the SVG Path ($closeSVG)
for the close value, then convert that value to SVG drawing commands using the toSVG()
method. From there, I create an SVG <path> tag and use the PHP
echo commands to write in the drawing commands and the fill and color values.
When I browse to the .html file that references the graph, I get the result shown in Figure 3.
Figure 3. The first version of the graph
It's not much to look at, but it's correct, and that's a start.
Version 2: Bracket the close line
The next step is to bracket the close line in a slightly dimmer shape that shows the path of the highs and lows for the day. That will give the customer some idea of the variability in the stock.
To do that, I make the alterations to the graphing PHP file as shown in Listing 7.
Listing 7. The second version of graph.php
<style type="text/css">
.close { fill-opacity: 0; stroke-opacity:0.8; stroke-width: 0.4; }
.hilo { fill-opacity: 0.2; stroke-opacity:0.8; stroke-width: 0.1; }
</style>
<?php
$colors = array( '#ff0000','#0000ff','#00ff00' );
$c = 0;
foreach( $d->traces as $trace )
{
$closePath = $trace->closePath( $gt );
$closeSVG = $closePath->toSVG();
$hiloPath = $trace->hiloPath( $gt );
$hiloSVG = $hiloPath->toSVG();
$color = $colors[$c];
$c += 1;
?>
<path fill-rule="nonzero"
d="<?php echo( $hiloSVG ); ?>" class="hilo"
fill="<?php echo( $color ); ?>"
stroke="<?php echo( $color ); ?>"
/>
<path fill-rule="nonzero"
d="<?php echo( $closeSVG ); ?>" class="close"
fill="<?php echo( $color ); ?>"
stroke="<?php echo( $color ); ?>"
/>
<?php
}
?>
|
I do much the same as I did with the original close line, except that this time, I get the
hiloPath. From there, I create another path with the close
class. Notice how SVG uses the idea of Cascading Style Sheet (CSS) classes applied to various graphic
objects. You can set the stroke, opacity, fill, or any of the graphic styles either at the class level
or at the local level; you can even change them on the fly with a script as needed.
Figure 4 shows this updated version of the graph.
Figure 4. The updated graph with the high/low shape
Now you can see the variability in the stock. And if you look really closely at the beginning of the graph where the two overlap the opacity on the high/low shape means that you can still see the blue trace through the red trace.
Version 3: Add borders and text
In the next version of the graph, I add borders and some text to indicate the dollar values. That change is shown in Listing 8.
Listing 8. The third version of graph.php
<style type="text/css">
.close { fill-opacity: 0; stroke-opacity:0.8; stroke-width: 0.4; }
.hilo { fill-opacity: 0.2; stroke-opacity:0.8; stroke-width: 0.1; }
.range { text-anchor: end; font-size: 5pt; }
</style>
...
<line x1="18" y1="10" x2="20" y2="10"
stroke="black" stroke-width="0.2" />
<line x1="20" y1="10" x2="20" y2="92"
stroke="black" stroke-width="0.2" />
<line x1="18" y1="90" x2="100" y2="90"
stroke="black" stroke-width="0.2" />
<line x1="100" y1="90" x2="100" y2="92"
stroke="black" stroke-width="0.2" />
<text x="18" y="12" class="range"><?php echo($yend); ?>
</text>
<text x="18" y="92" class="range"><?php echo($ystart); ?>
</text>
</svg>
|
The PHP code to render the graph remains the same. I'm simply drawing a few lines and some text along the Y-axis. This updated version is shown in Figure 5.
Figure 5. The graph with borders and Y-axis values
As a final touch, I add a little background gradient fill behind the data to accentuate it. Listing 9 shows how easy it is to add such a fill.
Listing 9. Adding a gradient fill
<svg style="shape-rendering:geometricPrecision;"
viewBox="0 0 100 100" xml:space="preserve"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid meet">
<defs>
<linearGradient id="BackGradient"
gradientUnits="objectBoundingBox"
gradientTransform="rotate(90)">
<stop offset="0%" stop-color="#ccc"/>
<stop offset="50%" stop-color="white"/>
<stop offset="100%" stop-color="#ccc"/>
</linearGradient>
</defs>
<style type="text/css">
.close { fill-opacity: 0; stroke-opacity:0.8; stroke-width: 0.4; }
.hilo { fill-opacity: 0.2; stroke-opacity:0.8; stroke-width: 0.1; }
.range { text-anchor: end; font-size: 5pt; }
.background { stroke-width: 0; fill:url(#BackGradient); }
</style>
<rect x="20" y="10" width="80" height="80" class="background" />
...
|
This code shows that the new background class uses the BackGradient
resource. That linearGradient fill has three stops between a subtle gray and a
white, then back to the gray again. You can see the result in Figure 6.
Figure 6. The finished graph
Now, to demonstrate how the scalability in SVG works, I rewrote the page a bit to increase the width and
the height of the <embed> tag. You can see the result in
Figure 7.
Figure 7. The zoomed-in version of the graph
Everything has scaled perfectly with no jagged pixels. How cool is that? Well . . . I'll tell you. It's very cool. I especially like how SVG works in units that are convenient for the programmer rather than working directly in pixels.
Obviously, PHP isn't the only way to create .svg files. Any programming language -- C#,
C, the Java™ language, Perl -- can build SVG text files. Perl has an SVG module
in its Comprehensive Perl Archive Network (CPAN) library. PHP has a PEAR module for SVG, and folks writing Java
code should check out the Batik SVG Toolkit from the Apache Foundation.
Another language alternative is Extensible Stylesheet Language Transformation (XSLT), which is excellent for converting XML data files into .svg files. ChartSVG is an open source project for graphing and uses SVG that is written in XSLT.
As for front-end tools, Adobe Illustrator and GoLive can create .svg files. The GNU Image Manipulation Program (GIMP) also supports exporting paths to SVG. My favorite drawing program, OmniGraffle V4.0, supports export to SVG, as well. For scientific applications, Mathematica has an extension that exports SVG.
The addition of SVG into Firefox V1.5 -- even in its limited form -- opens a world of possibilities for Web designers. Sure, Firefox doesn't have the penetration that Internet Explorer has, but it has a mind share particular to the technical community that grows daily. That share will provide a powerful incentive for other manufacturers to include better SVG support in their browsers and open the doors to create richer graphics experiences for your customers.
| Description | Name | Size | Download method |
|---|---|---|---|
| Contains start.html and start.svg | x-graphxmlsvg/start.zip | 1KB | HTTP |
| The data.xml file | x-graphxmlsvg/data.zip | 1KB | HTTP |
| The graph.php file | x-graphxmlsvg/graph.zip | 2KB | HTTP |
Information about download methods
Learn
- PHP home page: The PHP home page contains a lot of useful material on the scripting language.
- W3C XML page: The World Wide Web Consortium (W3C) XML page is a good place to learn more about XML and its related standards.
- SVG standard: The W3C also hosts the SVG standard.
- Adobe's SVG site: Adobe's SVG site is an excellent resource for more information on SVG.
- Mozilla SVG Project: The Mozilla SVG Project explains what Firefox does and does not support.
- SVG.org: The SVG home page has news and information on SVG.
- SVG Essentials: SVG Essentials by David Eisenberg (O'Reilly, 2002) is a solid introduction to SVG.
- Add interactivity to your SVG (developerWorks, August 2003): Add interactive elements in your SVG documents that respond to user input.
- XML Matters: Program with SVG (developerWorks, April 2005): Read about scripting and animation with SVG, and how to manipulate SVG through DOM and other general XML tools and libraries.
- Interactive, dynamic Scalable Vector Graphics (developerWorks, June 2003): Create SVG images dynamically from XML data, for example from an XML-enabled relational database management system, using XSLT and other technologies. In this tutorial, the example uses JavaScript that enables users to dynamically control the content and appearance of a floor plan rendered with SVG.
- Introduction to Scalable Vector Graphics (developerWorks, March 2004): Learn to easily generate graphics (such as graphs and charts) from database information and include animation and interactivity. This tutorial demonstrates the concepts you need to build SVG documents, including basic shapes, paths, text, and painting models, plus animation and scripting.
- IBM XML 1.1 certification: Find out how you can become an IBM Certified Developer in XML 1.1 and related technologies.
- XML: See developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
Get products and technologies
- WPF and XAML: WPF and Extensible Application Markup Language (XAML) are Microsoft's answer to SVG.
- SVG CPAN module: This manual is a good resource for Perl programmers.
- Batik: The Apache XML Project's Batik is an SVG toolkit for the Java language.
- ChartSVG: Hardcoded Software's ChartSVG is an XSLT translation system for building SVG graphs.
- OmniGraffle: Omni Group's OmniGraffle is a Macintosh application for business and technical design.
- Mathematica: Wolfram Research's Mathematica is a scientific toolkit that includes export options for SVG.
Jack D. Herrington is a senior software engineer with more than 20 years of experience. He's the author of three books: Code Generation in Action, Podcasting Hacks, and PHP Hacks. He has also written more than 30 articles. You can reach Jack at jherr@pobox.com.





