September 23, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Using Java to Produce SVG Code in XHTML Data

  • May 8, 2007
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 2216


Preface

General

This is the third lesson in a series designed to teach you how to write servlets to produce SVG code that will be rendered in graphic form by an SVG-compatible browser.  In this lesson you will learn how to write Java code to produce XHTML files containing in-line SVG/XML code.  You will also learn how to write servlets that produce XHTML output containing in-line SVG/XML code.

An SVG graphics library

In the previous two lessons titled "Java JAXP, Creating graphics using Java and SVG" and "An improved approach for creating SVG/XML code and SVG/XML DOM nodes using Java" (see Resources), I taught you how write your own SVG graphics library to eliminate, or at least alleviate the requirement to write raw XML code or raw JAXP DOM code.  The use of the SVG graphics library makes it possible for you to produce SVG output simply by making typical Java method calls.

Viewing tip

I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.

Figures

  • Figure 1. Graphic output from Svg03 and Svg04 programs.

Listings

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials.  You will find a consolidated index at www.DickBaldwin.com.

General background information

SVG is a language for describing two-dimensional graphics in XML. SVG allows for three types of graphic objects:

  • vector graphic shapes
  • images
  • text

The Scalable Vector Graphics (SVG) 1.1 Specification is extensive, but it is not easy reading, particularly if your knowledge of XML is weak.

While I don't claim to be an expert in XML, I do know enough about the topic that I can usually make sense of the material provided in the SVG specification.  One of my objectives for this series is to help Java programmers who lack a strong background in XML to take advantage of the capabilities of SVG.

Great graphics in the browser

 
No experience with IE/SVG plug-in
The SVG rendering engines that are available include Firefox 1.5.  I have also read that IE can be made SVG compatible by installing an SVG plug-in.   Please note however that I have not installed and tested the SVG plug-in for IE.  The sample programs in this series were tested using Firefox 1.5.
You can write Java programs that will produce XML output, which, when loaded into an SVG rendering engine, will result in the display of rich graphic material.  When combined with the use of servlets, this makes it possible to display graphic material in the client area of a web browser that competes favorably with the graphic material that can be displayed using the Java 2D API (see Resources) in desktop applications.

Preview

Not valid XHTML data
Note that the output data produced by these two programs will not validate as XHTML at http://validator.w3.org/file-upload.html because the SVG elements are not valid XHTML elements.  There is probably a way around this, but as of this writing, I don't know what it is.
Two different programs

In this lesson, I will present and explain two different programs.  The first program named Svg03 produces an output file containing SVG data embedded in XHTML data (see the sidebar).  The file can be rendered by loading it directly into a Firefox 1.5 browser.

The second program named Svg04 is a servlet program.  When the servlet is deployed and then accessed by a Firefox 1.5 browser, the servlet returns SVG data embedded in XHTML data (see the sidebar), which is rendered in graphic form by a Firefox 1.5 browser.

The graphic output

Both programs produce the same graphic output, which is shown in Figure 1.

Figure 1. Graphic output from Svg03 and Svg04 programs.

The programs illustrate a large number of important SVG capabilities

The graphic output shown in Figure 1 illustrates a large number of important capabilities of SVG including the following:

  • An svg element that contains other SVG elements and serves as a canvas upon which SVG graphics elements are drawn.  The available graphics elements are listed below.  The subset of graphics elements shown in boldface in the following list are known as the
  • Basic Shapes.  Each of the basic shapes is illustrated in Figure 1.  The other graphics elements will be illustrated in future lessons.
    • path
    • text
    • rect
    • circle
    • ellipse
    • line
    • polyline
    • polyline
    • image
    • use
  • A rect element that produces a black border (stroke) on the canvas with a thickness of one pixel.
  • A defs element, which is a container for elements that are referenced by other elements in the SVG code.  In this case, the defs element contains two linearGradient elements, one radialGradient element, and several stop elements associated with the gradient elements.
  • A linearGradient element that produces a left-to-right color gradient that begins with yellow, changes to red, and finishes with blue.  This gradient is referenced to fill the ellipse.
  • A second linearGradient element that produces a color gradient that begins with green and finishes with blue.  This gradient is referenced to fill the circle.
  • A radialGradient element, which produces a color gradient where the color changes going outward from a center point.  The rounded rectangle in Figure 1 is filled with this radial gradient where the color starts as yellow at the center, changes to red, and finishes with blue at the outer extremity of the gradient.
  • Several stop elements, which determine where and how the changes in color gradients occur.
  • A g element, which is a container element for grouping together related graphics elements.  This g element is the container for all the graphics elements in Figure 1 other than the rectangle that forms the black border on the canvas.
  • An ellipse, with a blue border (stroke) two pixels thick rotated by fifteen degrees clockwise around its center and filled with a three-color linear gradient.
  • A circle rotated by fifteen degrees clockwise around its center and filled with a two-color linear gradient.
  • A rectangle with rounded corners filled with a three-color radial gradient.
  • A polyline described by four points and rotated by ten degrees clockwise around the first point on the left.
  • A polygon described by four points with a red border (stroke) three pixels thick and filled with green.
  • A green line with a width of twelve pixels and an opacity of 0.6.  This line is drawn on top of the other graphics elements from the upper left to the bottom right of the canvas.  Note that the other graphics elements can be seen showing through the green line because its opacity is less than 1.0.

As I discuss the code for the two programs in the next section, remember that both of the programs produce the graphic output shown in Figure 1.

Discussion and sample code

The program named Svg03

Description of the program

The main purpose of this program is to illustrate the inclusion of SVG graphics elements in an XHTML file that can be rendered in Firefox 1.5.

This program uses an SVG graphic library of my own design that has been significantly upgraded relative to the version used in the earlier lessons of this series (see Resources).  The upgraded version of the library supports the following basic shapes plus linear and radial gradients and dozens of attributes and transforms on each shape.

  • rect
  • circle
  • ellipse
  • line
  • polyline
  • polygon

The shapes in the above list are all of the basic shapes provided by SVG.

Convenience methods

The library also contains some convenience methods for creating elements, nodes, and blocks of code.  These convenience methods are designed to reduce the labor required to write programs such as this one.

Create and transform a DOM tree

This program creates a DOM tree describing the SVG code for the image shown in Figure 1.  Then it transforms the DOM tree into SVG code that is embedded in a file that would otherwise be a valid XHTML file.  As mentioned earlier, inclusion of the SVG code prevents the code from being valid XHTML code because the SVG element names are not recognized by XHTML validator programs

Converting Svg03 into a servlet.
As you will see later, only a couple of changes will be required to turn the program named Svg03 into the servlet program named Svg04.
A precursor to writing a servlet program

The capability demonstrated by this program is a precursor to being able to create inline SVG code in a servlet and to cause that SVG code to be rendered in a Firefox browser that accesses the servlet.

The output file produced by this program can be rendered by loading it directly into Firefox 1.5.

The program was tested using J2SE 5.0, Firefox v1.5.0.9, and WinXP.

Will discuss in fragments

I will discuss and explain this program in fragments.  You can view the entire program in Listing 30 near the end of the lesson.  Listing 1 shows the beginning of the program class for the program named Svg03.

Listing 1. Beginning of Svg03 program class.

public class Svg03{

  public static void main(String argv[]){
    
    //The following data values will be used to create
    // SVG graphics.
    int ellipseCenterX = 110;
    int ellipseCenterY = 100;
    int ellipseWidth = 100;
    int ellipseHeight = 40;
    int ellipseRotate = 15;//degrees
    
    int circleCenterX = 110;
    int circleCenterY = 100;
    int circleRadius = 30;
    int circleRotate = 15;

    int rectCenterX = 110;
    int rectCenterY = 200;
    int rectWidth = 110;
    int rectHeight = 80;
    int rectRoundX = 26;
    int rectRoundY = 25;
    
    int rotatePolyline = 10;
    
    try{
      //Create a DOM tree that describes a particular
      // graphic image.
      //Begin by creating a Document object and a root
      // node named svg.  All of the graphic content will
      // be contained in the svg element. This code was
      // explained in the earlier program named
      // Xslt01.java that was explained in lesson 2202
      // named "Getting Started with Java JAXP and XSL
      // Transformations (XSLT)".
      
      //At this point, the program starts using the 
      // SVG graphics library encapsulated in the class
      // named SvgGraphics.
      Document document = SvgGraphics.getDocument();

Listing 1 begins by declaring and initializing some variables that will be used later by the program

Then the program calls the getDocument method of the SvgGraphics class to get a reference to the Document object that represents the entire XML document.  I explained this method in an earlier lesson and won't repeat that explanation here.

Create the svg node

Listing 2 calls the makeNode method to create the root node named svg and append it to the document node.  Listing 2 also sets some attributes on the svg node that are required for proper rendering.

Listing 2. Create the svg node.

      Element svg = SvgGraphics.makeNode(
        document,
        null,//parent
        "svg",//node type
        new String[]{"xmlns","http://www.w3.org/2000/svg",
                     "version","1.1",
                     "width","220px",
                     "height","440px",
                     "position","static",
                     "top","0",
                     "left","0"
                    });//end call to makeNode

As I mentioned earlier, the svg node is used to create the canvas upon which the Basic Shape graphics elements shown in Figure 1 are drawn.

The makeNode method

I explained the usage of the method named makeNode in the earlier lesson titled "An improved approach for creating SVG/XML code and SVG/XML DOM nodes using Java" (see Resources) and won't repeat that explanation here.

The xmlns and position attributes.
I will leave it as an exercise for the reader to go to the SVG specifications (see Resources) to investigate the meaning of the attributes named xmlns and position.  Be forewarned, however, that unless you already understand quite a lot about XML, you may have some difficulty understanding the purpose of these attributes, particular the one named xmlns.
Attributes of the svg node

With the possible exception of the attributes named xmlns and position, the purpose of the attributes that are set on the svg node in Listing 2 should be fairly obvious on the basis of their names.

Show the outline of the canvas

Listing 3 calls the makeNode method to create a node of type rect to draw the black rectangular outline on the canvas in Figure 1.

Listing 3. Show the outline of the canvas.

      Element outline = SvgGraphics.makeNode(
                     document,
                     svg,//parent could be null
                     "rect",//node type
                     new String[]{"x","0",
                                  "y","0",
                                  "width","220",
                                  "height","440",
                                  "fill","none",
                                  "stroke","black",
                                  "stroke-width","1"
                                 });//end call to makeNode

The stroke and stroke-width attributes

The stroke attribute value in Listing 3 specifies that the rectangle should be drawn (or stroked) with a black line.  The stroke-width attribute in Listing 3 specifies that the black line should be one pixel in thickness.

The x, y, width, and height attributes

If you compare the x, y, width, and height attribute values in Listing 3 with the left, top, width, and height attribute values in Listing 2, you will see that the outline of the black rectangle matches the outline of the canvas that was created by Listing 2.  You can't see the canvas in Figure 1.  However, you can see its outline that is highlighted by the black rectangle.

Create the defs node

Listing 4 calls the makeNode method to create a node of type defs.

Listing 4. Create the defs node.

      Element defs = SvgGraphics.makeNode(document,
                                          svg,//parent
                                          "defs",
                                          null);

The defs node will be the parent (container) for three gradient nodes.  As explained earlier, the defs element will contain two linear gradient elements and one radial gradient element.

In this case, a value of null is passed as the fourth parameter (which is normally a reference to an array object containing attribute name/value pairs) to indicate that the defs node has no attributes.

Create the first linearGradient node

Listing 5 is the beginning of a section of code that creates nodes that define three different gradient coloring schemes.  These nodes will be child nodes of the defs node.

Listing 5. Create the first linearGradient node.

      Element gradientA = SvgGraphics.makeLinearGradient(
                              document,    //this document
                              defs,        //parent
                              "gradientA");//id

A container for referenced elements

Identification of gradientA.
The node that is created in Listing 5 is identified as gradientA by the value of the third parameter.
Recall that a defs element in the final SVG code is a container for elements that are referenced by other elements in the SVG code.  The three gradient nodes will be identified as gradientA, gradientB, and gradientC.  The other elements in the SVG code will use these identifiers to reference the gradient nodes.

The gradient elements will be referenced later to specify the fill colors for an ellipse, a circle, and a rounded rectangle.

Call the makeLinearGradient method

The code in Listing 5 calls the makeLinearGradient method to create the node of type linearGradient for gradientA.  Although it isn't obvious yet, the gradient element that results from transforming this node into raw XML code will provide a linear gradient that begins with yellow, changes to red, and ends with blue going from left to right.

Did not call the makeNode method

Creating the linearGradient node
It would also have been acceptable to call the makeNode method in place of the makeLinearGradient method, but that would have required a little more typing.

It is important to note that Listing 5 did not call the general makeNode method to create this node.  Rather, it called the more specialized method named makeLinearGradient.

Specialized methods in my SVG graphics library

My SvgGraphics library contains a number of specialized methods that are used to create nodes with most of the attributes having default values.  Only the attribute values that are most likely to change from one call to the next are passed as parameters to these methods.  They are provided as a convenience because they can be called with a little less effort and a little less thought than the makeNode method for those cases where they are applicable.

The makeLinearGradient method

It will probably be useful to take a look at the code in the makeLinearGradient method as an example of the more specialized methods in the library.  The method is shown in its entirety in Listing 6.

Listing 6. The makeLinearGradient method.

  static Element makeLinearGradient(Document document,
                                    Element parent,
                                    String id){
    Element gradient = 
        (Element)document.createElement("linearGradient");
    parent.appendChild(gradient);
    gradient.setAttribute("id",id);
    return gradient;
  }//End makeLinearGradient

This method creates and returns a reference to a node of type linearGradient with an id attribute whose value is set to the value of the third parameter.  Before returning, the new node is appended as a child to the node whose reference is received as the second parameter.

This method is straightforward and shouldn't require any further explanation.

What is a gradient?
According to the W3C "Gradients consist of continuously smooth color transitions along a vector from one color to another, possibly followed by additional transitions along the same vector to other colors. SVG provides for two types of gradients, linear gradients and radial gradients."
Create three stop nodes

Now, returning to the main thread of the discussion, a linearGradient element alone isn't very useful.  To be useful, the linearGradient element must be supplemented by stop elements that control how and where the colors change.  One stop element is required for each color change.  The stop elements must be children of the linearGradient element.

According to the W3C, "The ramp of colors to use on a gradient is defined by the stop elements that are child elements to either the linearGradient element or the radialGradient element."

Call the makeGradientStop method

Listing 7 calls the makeGradientStop method three times in succession to establish the "ramp of colors" to be used on the linearGradient node that was created in Listing 6.

Listing 7. Create three stop nodes.

      SvgGraphics.makeGradientStop(document,
                                   gradientA,//parent
                                   "2%",     //start here
                                   "yellow");//color
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientA,
                                   "50%",
                                   "red");
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientA,
                                   "98%",
                                   "blue");

You can view the makeLinearGradient method in its entirety in Listing 30, but you don't need to know much about the code in the method to be able to use it.  The important thing is to understand the meaning of the four parameters to the method.

The four parameters to the makeGradientStop method

The first parameter is a reference to the document to which the element belongs, and the second parameter is a reference to the node that will be the parent of the stop node.

The gradient vector

The third parameter is perhaps the most complicated of the four.  This parameter specifies the location, (proceeding from the beginning to the end along a linearGradient vector or from the center point outward for a radialGradient vector) at which the color starts changing.

The length and direction of the gradient vector
For a linearGradient as used in this lesson, the length of the vector is the width of the component to which the gradient is being applied and the direction is from left to right.  However, it is possible to provide much more fine grained control over the length and direction of the vector when the linearGradient element is created.  For a radialGradient, the length of the vector is specified as a radius value when the radialGradient element is defined.
This value can be specified as a percentage (0% to 100%) or a number (0.0 to 1.0) representing a fraction of the total length of the vector to which the gradient will be applied, (such as the width of ellipse in Figure 1).

For this example, the color starts as pure yellow at the left end of the ellipse and starts changing to red two-percent of the way to the right end of the ellipse.

The specified colors

The fourth parameter simply specifies the color at the point specified by the third parameter.

Thus the combination of the first two stop nodes in Listing 7 specify that the color will be yellow at and to the left of the two-percent point and will be red at the fifty-percent point.  The color will change in a linear gradient fashion from yellow to red along the portion of the gradient vector that connects those two points.

Similarly, the combination of the last two stop nodes in Listing 7 specify that the color will be red at the fifty-percent point and will be blue at the ninety-eight-percent point and beyond.  The color will change in a linear gradient fashion from red to blue along the portion of the gradient vector that connects those two points.

Create the other linearGradient node

Listing 8 creates the linearGradient node and two child nodes of type stop that result in the green-to-blue gradient in the circle in Figure 1.

Listing 8. Create the other linearGradient node.

      Element gradientB =SvgGraphics.makeLinearGradient(
                             document,    //this document
                             defs,        //parent
                             "gradientB");//id
                   
      SvgGraphics.makeGradientStop(document,
                                   gradientB,//parent
                                   "0%",     //start here
                                   "green"); //color
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientB,
                                   "100%",
                                   "blue");

Create the radialGradient node

Listing 9 calls the makeRadialGradient method once and then calls the makeGradientStop method three times in succession to create the radial gradient node that results in the color gradient shown in the rounded rectangle in Figure 1.  This gradient starts with yellow at the center, changes to red at a distance of fifty-percent of the distance to the outer edge and ends up with blue at the outer edge of the rounded rectangle.

Listing 9. Create the radialGradient node.

      Element gradientC = 
                 SvgGraphics.makeRadialGradient(
                   document,        //this document
                   defs,            //parent
                   "gradientC",     //ID
                   "userSpaceOnUse",
                   rectCenterX,     //cx
                   rectCenterY,     //cy
                   rectWidth/2);    //r
                   
      SvgGraphics.makeGradientStop(document,
                                   gradientC,
                                   "0%",
                                   "yellow");
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientC,
                                   "50%",
                                   "red");
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientC,
                                   "100%",
                                   "blue");

The parameters to the makeRadialGradient method are similar to, but not identical to the parameters to the makeLinearGradient method in Listing 5 and Listing 8.  For the radial gradient, the first three parameters have the same meaning as in Listing 5.

The attribute value of "userSpaceOnUse"

I'm not even going to try to explain the meaning of the fourth parameter with the value "userSpaceOnUse."  For this parameter, you can either continue to use the same value, or you can go to the SVG specifications (see Resources) to learn more about this parameter.

The center point and radius of the radialGradient

The fifth and sixth parameters, cx and cy, specify the point from which the gradient will radiate.

The seventh parameter, r specifies the distance from that center point that represents 100% insofar as the stop element is concerned.  Stated differently, this distance represents the length of the gradient vector discussed earlier.

The fill color results

Thus, when this radial gradient is used to fill the rounded rectangle shown in Figure 1, the color at the center of the rectangle will be yellow.  The color half way between the center and the outer edge on the horizontal axis will be red.  The color at the outer edge on the horizontal axis will be blue.  The colors will change in a linear gradient fashion along any vector that extends from the center to a point that is at a distance from the center equal to one-half the width of the rectangle.  (As you can see in Figure 1, because the height of the rectangle is less than the width, the color never quite reaches the blue state at the top and the bottom of the rectangle.)

The center point can be anywhere

By the way, there is no requirement for the center point of the radial gradient to be at the center of the rectangle.  In fact, there is no requirement for the center point of the radial gradient to even be inside the rectangle.  When the radial gradient is used to fill the rectangle, the effect is as if an invisible version of the radial gradient is created at a specified location with a specified radius on the canvas but only that portion that overlaps the rectangle actually becomes visible.

Create the node of type g

Listing 10 calls the makeNode method to create a group node named g.  As described earlier, a g element is a container element for grouping together related graphics elements.  This g node will result in an element that will be the container for an ellipse, a circle, a rectangle, a polyline, a polygon, and a line.

Listing 10. Create the node of type g.

      Element g = SvgGraphics.makeNode(document,
                                       svg,//parent
                                       "g",
                                       null);

Because this node doesn't have any attributes, a value of null is passed as the fourth parameter to the method.

Create the basic ellipse

Listing 11 shows the beginning of a section of code that creates the ellipse shown at the top of Figure 1.   Eventually, this ellipse will be:

  • Given a blue border that is two pixels thick.
  • Filled with gradientA, which is a linear gradient that begins with yellow, changes to red, and finishes with blue.
  • Rotated by fifteen degrees clockwise around its center.

The width and the height
Note that the width and height dimensions for the ellipse apply to the shape of the ellipse before it is rotated.
However, the code in Listing 11 simply calls the makeEllipse method to create the basic ellipse on the basis of the specified coordinates for the center of the ellipse and the specified values for the width and the height.

Listing 11. Create the basic ellipse.

      Element theEllipse = SvgGraphics.makeEllipse(
                             document,
                             g,//Owner
                             ellipseCenterX,
                             ellipseCenterY,
                             ellipseWidth,
                             ellipseHeight);

Note that the ellipse node that is created in Listing 11 is appended as a child of the g node.

Set or update the appearance of the ellipse

After the basic ellipse node has been created by the code in Listing 11, Listing 12 calls the setAttribute method on the reference to the ellipse node three times in succession to:

  • Fill the ellipse with the linear gradient element that was created and identified as gradientA earlier.  (This is what I meant when I discussed the possibility of SVG elements referencing elements that are created within the defs element.)
  • Set the border color to blue.
  • Set the border thickness to two pixels.

Listing 12. Set the appearance of the ellipse.

      theEllipse.setAttribute("fill","url(#gradientA)");
      theEllipse.setAttribute("stroke","blue");
      theEllipse.setAttribute("stroke-width","2");

The different kinds of transforms.
You can learn about the different kinds of transforms that are supported by the SVG in the SVG specifications (see Resources).
The transform attribute

The transform attribute can be set repeatedly on a graphics element to perform a series of sequential transforms on the element.  Available transforms include:

  • translate
  • scale
  • rotate
  • skewX
  • skewY

Rotate the ellipse (the hard way)

In this case, I was interested only in rotating the ellipse about its center.  It should be possible to rotate a shape around any location specified by x and y coordinate values.  However, I was unable to identify the proper syntax for doing that, so I used a work around that consisted of three sequential steps to accomplish the desired rotation.

The three steps

The three steps were:

  1. Translate the origin so that the origin was at the center of the ellipse.
  2. Rotate the ellipse by fifteen degrees clockwise around the origin.
  3. Translate the origin so that the center of the now-rotated ellipse was back in its original location relative to the origin.

The code to accomplish the three steps is shown in Listing 13.

Listing 13. Rotate the ellipse.

      theEllipse.setAttribute("transform",
                  "translate(" + ellipseCenterX + "," 
                  + ellipseCenterY + ") "
                  + "rotate(" + ellipseRotate + ") "
                  + "translate(" + (-ellipseCenterX) + ","
                  + (-ellipseCenterY) + ") ");

If Listing 13 seems confusing, rewrite the statement in Listing 13, substituting the numeric values for the variables in place of the variable names.  It may make more sense if you look at it that way.

Create the circle

Listing 14 calls the makeCircle method to position a basic circle so that it appears to be inside the ellipse in Figure 1.  However, it isn't really inside the ellipse in a containment sense.  Rather, the circle is simply positioned on top of the ellipse.  The default opacity value for the circle is 1.0.  Therefore, the ellipse doesn't show through the circle.

As you can see, the parameter list for the makeCircle method in Listing 14 is a little simpler than the parameter list for the makeEllipse method in Listing 11.  This is because it is necessary to specify both a width and a height for an ellipse, but it is only necessary to specify a radius for a circle.

Listing 14. Create the circle.

      Element theCircle = SvgGraphics.makeCircle(
                            document,
                            g,//Owner
                            circleCenterX,
                            circleCenterY,
                            circleRadius);

Once the code in Listing 14 has finished executing, the basic circle has been created and positioned, but it hasn't been "dressed up" in the manner shown in Figure 1.

Set the appearance and rotate the circle

Listing 15 calls the setAttribute method twice in succession to:

  • Fill the circle with the green-to-blue linear gradient, created and identified earlier as gradientB, as shown in the circle in Figure 1.
  • Rotate the circle by fifteen degrees clockwise around its center.

Listing 15. Set the appearance and rotate the circle.

      theCircle.setAttribute("fill","url(#gradientB)");
      theCircle.setAttribute("transform",
                   "translate(" + circleCenterX + "," 
                   + circleCenterY + ") "
                   + "rotate(" + circleRotate + ") "
                   + "translate(" + (-circleCenterX) + ","
                   + (-circleCenterY) + ") ");

Why rotate the circle?

You might be wondering what is to be gained by rotating a circle by fifteen degrees around its center.  If the circle had been filled with a solid color, rotation would not change the appearance of the circle.  However, because the circle was filled with a linear gradient such that it is green on the left and blue on the right, rotating the circle about its center does change its appearance.

Create and fill the rectangle

Listing 16 calls the makeRect method to create a basic rectangle.  The coordinates of the upper-left corner of the rectangle are specified by the values of the third and fourth parameters.  The width and height of the rectangle are specified by the values of the fifth and sixth parameters.

Listing 16. Create and fill the rectangle.

      Element theRect = SvgGraphics.makeRect(
                            document,
                            g,//Owner
                            rectCenterX - rectWidth/2,//x
                            rectCenterY - rectHeight/2,//y
                            rectWidth,
                            rectHeight);

      theRect.setAttribute("fill","url(#gradientC)");

Then Listing 16 calls the setAttribute method to fill the rectangle with the radial gradient created and identified earlier as gradientC.

Note that at this point, the rectangle still has square corners.  It is not yet a "rounded rectangle."

Round the corners of the rectangle

Listing 17 calls the setAttribute method twice in succession to set the values for the attributes named rx and ry.  The purpose of these two attributes is to control the amount of rounding that is applied to the corners.

Listing 17. Round the corners of the rectangle.

      //Round the corners.
      theRect.setAttribute("rx",""+ rectRoundX);
      theRect.setAttribute("ry",""+ rectRoundY);

Although I can't explain exactly how these attribute values are used to round the corners from a geometry viewpoint (I would like to see some explanatory pictures that describe this process), I can tell you generally how they behave.  If you set either attribute value to zero, all four corners will be square.

If you set rx to half the width of the rectangle and set ry to half the height of the rectangle, the corners will be rounded so much that the rectangle will take on the appearance of an ellipse.

Values in between those two extremes result in rounded corners as shown in Figure 1.

What is a polyline?

A polyline is a shape that results from "connecting the dots" between each consecutive pair of points in a set of points.  Each point is connected to only the point that follows it in the specification of the locations of the points. 

There is no requirement for the shape to be closed, although it can be closed if the coordinates for the last point are the same as the coordinates for the first point.  (The polyline in Figure 1 is not closed.)

There is also nothing to prohibit a line drawn between two points from crossing a previously drawn line between two other points as shown in Figure 1.

There are no gaps or open spaces along the line.  You can always start at the beginning specified by the first point and follow the line to the end that is specified by the last point.

Create a polyline and rotate it

Figure 1 shows a black polyline that was drawn using coordinate values for the points as shown in Listing 18.

Listing 18 begins by creating and populating an array object containing eight values of type int.  Each consecutive pair of int values placed in the array object represents the coordinates of a single point.  Thus, the array object contains the coordinate values for four points.

Call the makePolyline method

Then Listing 18 calls the makePolyline method to construct and return a reference to a node that describes a polyline element representing the four points whose coordinate values were used to populate the array object.

Listing 18. Create a polyline and rotate it.

      //Draw a polyline with four points.
      int[] polylinePoints = 
                         {10,235,210,235,110,275,110,225};
      Element polyline = SvgGraphics.makePolyline(
                                          document,
                                          g,//owner
                                          polylinePoints);
      
      //Rotate the polyline by 10 degrees around the first
      // point.
      polyline.setAttribute("transform",
                    "translate(" + polylinePoints[0] + ","
                    + polylinePoints[1] + ")" 
                    + "rotate(" + rotatePolyline + ")" 
                    + "translate(" + (-polylinePoints[0]) 
                    + "," + (-polylinePoints[1]) + ")");

Rotate the polyline around the first point

When the makePolyline method returns the reference to the polyline node, the code in Listing 18 calls the setAttribute method to rotate the entire polyline shape by ten degrees clockwise around the first point.  Thus, the first line drawn for the polyline in Figure 1 has a general downward slope going from left to right.

What is a polygon?

The big difference between a polyline and a polygon is that a polygon must always be a closed shape.  As a result, once the polygon points have been connected with straight lines in the same fashion as a polyline, the SVG rendering engine draws one additional line for a polygon that connects the first and last points.  This additional line always closes the shape.

Create a polygon and dress it up

Listing 19 calls the makePolygon method to create a polygon node using a set of points similar to those used earlier to create the polyline node.  However, the polygon is not subsequently rotated as was the case with the polyline.  Rather, the setAttribute method is called on the reference to the polygon node three times in succession to give the polygon a red border that is three pixels thick, and to fill the polygon with the solid color green.

Listing 19. Create a polygon and dress it up.

      //Draw a polygon with four points. Give it a red
      // border and fill it with green.
      int[] polygonPoints = 
                         {10,335,210,335,110,375,110,325};
      Element polygon = SvgGraphics.makePolygon(
                                           document,
                                           g,//parent
                                           polygonPoints);
      polygon.setAttribute("fill","green");
      polygon.setAttribute("stroke","red");
      polygon.setAttribute("stroke-width","3");

The green and red polygon is shown at the bottom of Figure 1.

Create a wide, green, partially transparent line

Listing 20 calls the makeLine method to create a line node that extends from the upper-left corner of the canvas to the lower-right corner of the canvas.  By default, this would be a black line one pixel wide.

Listing 20. Create a wide, green, partially transparent line.

      Element line = SvgGraphics.makeLine(document,
                                          g,   //owner
                                          0,   //x1
                                          0,   //y1
                                          220, //x2
                                          440);//y2
      line.setAttribute("stroke","green");
      line.setAttribute("stroke-width","12");
      line.setAttribute("stroke-opacity","0.6");

Then Listing 20 calls the setAttribute method three times in succession to:

  • Set the color of the line to green.
  • Set the width of the line to twelve pixels.
  • Set the opacity of the line to 0.6 or 60-percent.  (You could also say that the line is 40-percent transparent.)

Because the line is the last shape to be drawn on the canvas in Figure 1, it is drawn on top of the other shapes.  However, because the opacity of the line is 0.6 (as opposed to 1.0), it is partially transparent and the other shapes that are behind the line show through the line.

Wrap the SVG code in XHTML data

At this point, a DOM tree that represents the SVG code for the graphic image shown in Figure 1 has been constructed.  The remaining code in the class is used to transform the DOM tree into SVG code and to wrap the SVG code in a document that would otherwise be a valid XHTML document.  This process begins in Listing 21 and becomes recursive later on.

Listing 21 begins by instantiating an object of the class.  Then it gets an output stream that will be used to write the XHTML data and the SVG data into an output file named junk.xhtml.

Listing 21. Get an output stream to write the XHTML file.

      //Instantiate an object of this class
      Svg03 thisObj = new Svg03();

      //Get an output stream for the output produced by
      // the program code.
      PrintWriter out = new PrintWriter(
                      new FileOutputStream("junk.xhtml"));

Process the DOM tree

The last executable statement in the main method is shown in Listing 22.  This statement calls the method named processDocumentNode passing a reference to the output stream and a reference to the Document node of the DOM tree as parameters.

Listing 22. Process the DOM tree.

      thisObj.processDocumentNode(out,document);

    }catch(Exception e){
      //Note that no effort was made to provide meaningful
      // information in the event of an exception or
      // error.
      e.printStackTrace(System.err);
    }//end catch

  }// end main()

An XHTML output file is required

If you have studied the two previous lessons in this series (see Resources), you will recall that the sample programs in those lessons produced SVG files as the main output from the program.  Those programs didn't attempt to create XHTML files.  You will also recall that the programming task was relatively easy at this point in the program because JAXP methods already exist to do transform the DOM tree into raw XML code.  Unfortunately, things aren't quite so easy when the program is required to produce an XHTML file as its output.  There is a lot of programming that you are responsible for providing in this case.

XHTML text is required

There is quite a lot of XML text that is required to cause a file to qualify as a valid XHTML file.  I won't go into the reasons for that text because that is beyond the scope of this tutorial series.  However, I will show you how to write the code to create the text.

The processDocumentNode method

The processDocumentNode method is used to produce the XML text that is required in the output at the document level, (such as the XML declaration).  The method also produces the top level element tags.

The processDocumentNode method is shown in its entirety in Listing 23.

Listing 23. The processDocumentNode method.

 
  void processDocumentNode(PrintWriter out,Node node){
    //Create the beginning of the XHTML document.
    out.println("<?xml version=\"1.0\" "
                       + "encoding=\"UTF-8\"?>");
    out.println(
          "<!DOCTYPE html PUBLIC \"-//W3C//DTD "
             + "XHTML 1.0 Transitional//EN\" "
             + "\"http://www.w3.org/TR/xhtml1/"
             + "DTD/xhtml1-transitional.dtd\">");

    out.println(SvgGraphics.makeElement(false,"html",
      new String[]{"xmlns","http://www.w3.org/1999/xhtml",
                   "xml:lang","en",
                   "name","lang"
                  })//end call to makeElement
    );//end println

    out.println("<head>");
    
    out.println(SvgGraphics.makeElement(
               true,
               "meta",
               new String[]{"http-equiv","content-type",
                            "content",
                            "image/svg+xml; charset=UTF-8"
                           })//end call to makeElement
    );//end println
  
    out.println("<title>Generated XHTML file</title>");

    out.println("</head>");
    
    out.println(SvgGraphics.makeElement(
                 false,
                 "body",
                 new String[]{"id","body",
                              "style","position:absolute;"
                              + "z-index:0;"
                              + "border:1px solid black;"
                              + "left:0;"
                              + "top:0;"
                              + "width:220px;"
                              + "height:440px;"
                             })//end call to makeElement
    );//end println

    //Go process the root (document) node. This method
    // call triggers a recursive process that will 
    //process the entire DOM tree.
    processNode(out,node);

    //The entire DOM tree has been processed when control
    // returns to this point.
    //Now finish the output document and flush the output
    // buffer.

    out.println("</body></html>");
    out.flush();
  }//end processDocumentNode

Behavior of the processDocumentNode method

The makeElement method
Recall from the earlier lesson that the purpose of the makeElement method is simply to make it easier to construct strings representing XML elements that contain a lot of attribute values surrounded by quotation marks.

The behavior of the processDocumentNode method is pretty straightforward and shouldn't require a lot of explanation beyond the embedded comments.

Down to the point where the method makes a recursive call to the method named processNode, the code in the method consists simply of print statements and calls to the makeElement method.

The call to the processNode method

The call to the processNode method near the end of the processDocumentNode method is where the recursive processing of the DOM tree begins in earnest.  (The processNode method is passed a reference to the output stream along with a reference to the root node of the DOM tree at this point.)

When control returns to the processDocumentNode method from the call to the processNode method in Listing 23, the entire DOM tree will have been processed recursively.  At that point, one more print statement is executed to put the final string of XML text into the output file.  Then the output buffer is flushed, the processDocumentNode method terminates, and the program terminates leaving an XHTML file named junk.xhtml in the current directory.

The processNode method

A general DOM tree could contain any of the following seven types of nodes:

  • root or document
  • element
  • attribute
  • text
  • comment
  • processing instruction
  • namespace

A general purpose DOM tree processor would have to be capable of handling any combination of the seven types of nodes in the above list.  However, because the nodes in this DOM tree are produced by the program, it is known in advance which types of nodes will be encountered.  Therefore, the processNode method only handles element nodes, attribute nodes, and document nodes.

Beginning of the recursive processNode method

The recursive processNode method begins in Listing 24.  The code in Listing 24 is straightforward.

Listing 24. Beginning of the recursive processNode method.

  void processNode(PrintWriter out,Node node){
    try{
      if (node == null){
        System.out.println("Nothing to do, node is null");
        return;
      }//end if

Process nodes according to their type

Each time the processNode method is called, it receives an incoming parameter that specifies the node from the DOM tree that is to be processed recursively.  Each node is processed according to its type.  A switch statement is used to select among the different types.

Process nodes of type ELEMENT_NODE

Listing 25 shows the code that is executed to process nodes of type ELEMENT_NODE.

Listing 25. Process nodes of type ELEMENT_NODE.

      int type = node.getNodeType();
      switch(type){
        case Node.ELEMENT_NODE:{
          
          //Start creating the start tag.
          String nodeName = node.getNodeName();
          out.print("<" + nodeName + " ");
          
          //Now add each attribute to the start tag.
          NamedNodeMap theList = node.getAttributes();
          int length = theList.getLength();
          for(int cnt = 0;cnt < length;cnt++){
            Node item = theList.item(cnt);
            out.print(item.getNodeName() + "=\"" 
                           + item.getNodeValue() + "\" ");
          }//end for loop
          out.println(">");//close the start tag.
          
          //Process all XML child nodes recursively.
          processChildNodes(out,node);
          
          //The element and all of its children have been
          // processed when control returns to this point.
          //Create the end tag for the element.
          out.println("</" + nodeName + ">");
            
          break;
        }//end case ELEMENT_NODE

Create the beginning of the element start tag

The code in Listing 25 begins by creating the XML text that constitutes the beginning of the start tag for an XML element consisting of "<nodeName ".

Process each attribute

Then Listing 25 uses a for loop to:

  • Get each attribute belonging to the node
  • Construct an XML text string in the proper syntax for the name and value of the attribute
  • Concatenate the newly-constructed text string onto the start tag

Finish the start tag

When all attributes belonging to the node have been processed, the for loop terminates and an angle bracket is appended to the element start tag.  At this point, a start tag looking something like the following has been sent to the output stream:

<elementName attrName="attrValue" attrName="attrValue">

Process the child nodes

Before creating the element's end tag, it is necessary to process any child nodes belonging to the node that is being processed.  This is accomplished by making a call to the recursive method named processChildNodes.

Construct the end tag

When control returns from the recursive processChildNodes method, the entire DOM sub-tree below the current node will have been processed.  The code in Listing 25:

  • Constructs the end tag for the element
  • Sends it to the output stream
  • Breaks out of the switch statement to return control to the method from which the processNode method was called

Put switch statement on the back burner

At this point, I am going to put the switch statement in Listing 25 on the back burner, and continue explaining the processing of nodes of type ELEMENT_NODE.

As mentioned above, before creating the text for the element's end tag and sending it to the output, the code in Listing 25 calls the recursive method named processChildNodes, passing a reference to the node currently being processed, along with a reference to the output stream as parameters.  The purpose is to recursively process all of the child nodes belonging to the node that is currently being processed.

The recursive processChildNodes method

The processChildNodes method is shown in its entirety in Listing 26.

Listing 26. The recursive processChildNodes method.

  void processChildNodes(PrintWriter out,Node node){
    NodeList children = node.getChildNodes();
    if (children != null){
      int len = children.getLength();
      //Iterate on NodeList of child nodes.
      for(int i = 0; i < len; i++){
        //This is a recursive call.
        processNode(out,children.item(i));
      }//end for loop
    }//end if children != null

  }//end processChildNodes

Recursive processing
If you are unfamiliar with recursive processing algorithms, this may seem like black magic to you.  In that case, just trust me that it works, and then embark on an effort to gain an understanding of recursive algorithms.
Get a list of child nodes

The code in Listing 26 begins by getting a list of all the child nodes belonging to the node specified by the second incoming parameter. 

Process each child node

Then it executes a for loop, calling the processNode method during each iteration of the loop and passing a child node's reference to the processNode method.  (This causes the specified child node and all of its children to be processed recursively.)

When the for loop in Listing 26 terminates, all of the child nodes belonging to the node that was specified by the second incoming parameter will have been recursively processed, and the processChildNodes method returns control to the method from which it was called.

The DOCUMENT_NODE case

That completes the explanation of the processing of nodes of type ELEMENT_NODE that began with the switch statement in Listing 25.  There is another case in the switch statement, however, that hasn't been explained yet.  That case is shown in Listing 27.

Listing 27. The DOCUMENT_NODE case.

        case Node.DOCUMENT_NODE:{
          //No action is required other than to process
          // the child nodes recursively.
          processChildNodes(out,node);
          break;
        }//end case DOCUMENT_NODE

      }//end switch

    }catch(Exception e){
      e.printStackTrace(System.err);
    }//end catch
  }//end processNode(Node)

Listing 27 shows the code that processes the incoming node to the processNode method for the case where the type of the node is DOCMENT_NODE.

Will occur only once

This case will occur only once at the beginning of the processing of the DOM tree because the DOM tree has only one document node.  As indicated in the comments in Listing 27, no action is required for this case other than to assure that all of the child nodes of the document node are processed recursively.  Therefore, the code in Listing 27 simply makes a call to the processChildNodes method passing a reference to the document node as a parameter.

The entire DOM tree will have been processed

When control returns from the call to processChildNodes in the DOCUMENT_NODE case in Listing 27, the entire DOM tree will have been processed.  At that point:

  • Control breaks out of the switch statement
  • The processNode method returns to the method from which it was called
  • Control progresses up the call stack to the bottom of Listing 23 causing the final XML text to be written into the output stream and causing the output buffer to be flushed

The program terminates

Control then returns to the main method in Listing 22.  Since there is nothing more for the program to do in the main method, the program terminates at that point, leaving a new file named junk.xhtml in the current directory.

End of explanation for Svg03

And that ends the explanation of the program named Svg03.  As indicated earlier, you will find a complete listing of all of the code discussed above, plus the code for my SVG graphics library class named SvgGraphics in Listing 30 near the end of the lesson.

The program named Svg04

Description of the program

This program demonstrates the rendering of SVG graphic content in a Firefox 1.5 client browser that is produced by a servlet that is accessed by the browser.

Very similar to the previous program

Except for the fact that this program is a servlet that generates XHTML code as its output, and the previous program named Svg03 is a desktop application that produces an output file containing XHTML code, the two programs are identical.

This program defines a servlet that first creates a DOM tree describing the graphic image in Figure 1, and then transforms that tree into corresponding SVG XML code for transmission to the client.

Not valid XHTML

The program wraps the SVG graphic code in code that would otherwise be valid XHTML code.  However the inclusion of the SVG code prevents the code produced by the servlet from being valid XHTML because the SVG element names are not recognized by XHTML validator programs, such as the program at the following URL:

 http://validator.w3.org/file-upload.html

Program testing

For testing, the servlet must be deployed on a servlet-compatible server and then accessed using an SVG-compatible browser, such as Firefox 1.5.  On my system, the servlet can be tested by first copying it into the following directory on my Tomcat server:

C:\jakarta-tomcat-5.0.27\webapps\ROOT\WEB-INF\classes

Having copied the servlet onto the server and having started the server running, the servlet can be accessed and the SVG code can be rendered by accessing the servlet in Firefox using the following URL:

http://localhost/servlet/Svg04

What about Internet Explorer

As I mentioned earlier, I have not installed the SVG plug-in for IE6.  Accessing the servlet using IE6 (without an SVG plug-in installed) simply causes the browser to request permission to save the output produced by the server in a local file.  The file name that is recommended by IE6 is simply Svg04 with no extension.

Therefore, the program was tested using J2SE 5.0, Firefox 1.5.09, and jakarta-tomcat-5.0.27 running as a localhost server under WinXP.

Will discuss in fragments

As usual, I will discuss this program in servlets.  However, because the code in this program is almost identical to the code in the earlier program named Svg03, I will only discuss the code that is different between the two.  You can view the program named Svg04 in its entirety in Listing 31 near the end of the lesson.

As you will see, the difference between the two programs is confined to two small sections of code.

First section of different code

The first section of code that differs between the programs named Svg03 and Svg04 is shown in Listing 28.

Listing 28. First section of different code.

import javax.servlet.*;
import javax.servlet.http.*;

public class Svg04 extends HttpServlet{
  
  public void doGet(HttpServletRequest req,
                    HttpServletResponse res)
                    throws ServletException,
                                     IOException{
                                      
    //Note:  This statement is critical to causing
    // Firefox to recognize and render the SVG
    // code produced by the servlet.
    res.setContentType("image/svg+xml");

Additional import directives are required

First, because Svg04 is a servlet program, it must declare the two import directives shown in Listing 28 (or fully qualify the classes contained in those two packages when they are referenced in the program code.).

Extend HttpServlet class

Second, because Svg04 is a servlet program, the class must extend the HttpServlet class.

Define the doGet method

Third, because Svg04 is a servlet program and not a desktop application, the main method in the desktop application named Svg03 must be replaced by the method named doGet shown in Listing 28.

Set the content type

Fourth, the program must set the content type as shown in the last statement in Listing 28.

Last section of different code

The last section of code that differs between the two programs is the single statement shown in Listing 29.

Listing 29. Last section of different code.

      //Get an output stream for the output produced by
      // the program code.
      PrintWriter out = res.getWriter();

At this point in the program, the desktop application named Svg03 instantiates a PrintWriter output stream object based on a FileOutputStream object.  That causes the output produced by the program to be written into a file named junk.svg.

As you can see in Listing 29, the code required to get an appropriate output stream for a servlet is somewhat simpler.  Output that is placed in the output stream by the servlet ends up at the client browser for rendering and display.

Those are the only differences between the two programs

Unless I missed something during my comparison of the two programs, the only differences between the two programs are shown in Listing 28 and Listing 29.

It is fortunate that the differences between the two programs are so few in number and so insignificant in content.  Testing and debugging servlets can be a difficult task.  However, in this case at least, the program can be developed, tested, and debugged as a stand alone Java application, and then converted to a servlet by making the small number of changes shown in Listing 28 and Listing 29.

That ends the discussion and explanation of the program named Svg04.  Once again, you can view the program in its entirety in Listing 31 near the end of the lesson.

Run the programs

I encourage you to copy the code from Listing 30 and Listing 31 into your text editor, compile it, and execute it.  Experiment with it, making changes, and observing the results of your changes.  Then view the results in Firefox 1.5, or some other suitable SVG rendering engine.  Experiment with the code, making changes, and observing the results of your changes.

Above all, enjoy the process. Programming, particularly graphics programming can be fun.

Summary

In this lesson, I taught you how to write Java code to produce XHTML files containing in-line SVG/XML code.  I also taught you how to write servlets that produce XHTML output containing in-line SVG/XML code.

An SVG program was developed, tested, and debugged as a stand-alone desktop application that produced an output XHTML file to produce the image shown in Figure 1.

Then that desktop application was converted to a servlet program by making the following five changes to the code in the desktop application:

  1. Declare the following import directives:
      import javax.servlet.*;
      import javax.servlet.http.*;
  2. Define the main class to extend the HttpServlet class.
  3. Replace the signature for the main method in the desktop application with the signature for the doGet method in the servlet program:
      public void doGet(HttpServletRequest req,
                        HttpServletResponse res)
                               throws ServletException,
    	                          IOException{
  4. Execute the following statement to set the content type:
      res.setContentType("image/svg+");
  5. Execute the following statement to get an output stream:
      PrintWriter out = res.getWriter();

What's next?

The next lesson in this series will teach you how to write servlets that produce XHTML output containing references to external SVG files.  Those SVG files may be created on-the-fly during the execution of the servlet.

Future lessons will teach you how to write servlets that:

  • Deal with the following graphics elements:
    • path
    • text
    • image (Deal with bit-mapped images in SVG.)
    • use (Create and re-use graphics elements.)
  • Use SVG symbols.
  • Deal with stroke caps in SVG in comparison with similar caps in Java 2D.
  • Use the switch element in SVG.
  • Deal with other features of SVG, such as animation.

Complete program listings

Complete listings of the programs discussed in this lesson are shown in Listing 30 and Listing 31 below.

Listing 30. Program code for Svg03

/*File Svg03.java
Copyright 2006 R.G.Baldwin

The main purpose of this program is to illustrate the 
inclusion of SVG graphics in an XHTML file that can be 
rendered in Firefox 1.5.

This program uses an SVG graphic library that has been
significantly improved over the one used in the earlier 
program named Svg02.  The library now supports the 
following basic shapes plus linear and radial gradients
and dozens of attributes and transforms on each shape.

rect
circle
ellipse
line
polyline
polygon

These are all of the basic shapes provided by SVG.

The library also contains some convenience methods for 
creating elements, nodes, and blocks of code.  These
convenience methods are designed to reduce the labor 
required to write programs such as this one.

This program creates a DOM tree describing the SVG code 
for a particular graphic image.  Then it transforms the 
DOM tree into SVG code that is wrapped in a file that 
would otherwise be a valid XHTML file.  Inclusion of the 
SVG code, however, prevents the code from being valid 
XHTML because the SVG element names are not recognized 
by XHTML validator programs, such as the program at the 
following URL:

http://validator.w3.org/file-upload.html

The capability demonstrated in this program is a 
precursor to being able to create inline SVG code in a 
servlet and to cause that SVG code to be rendered in a 
Firefox browser that accesses the servlet.

(As you will see, creating an XHTML file is a good bit 
more difficult than creating an SVG file as was done
in the earlier program named Svg02.java.)

The output file produced by this program can be rendered 
by loading it directly into Firefox 1.5.

Tested using J2SE 5.0, Firefox v1.5.0.8, and WinXP.
*********************************************************/

import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;

public class Svg03{

  public static void main(String argv[]){
    
    //The following data values will be used to create
    // SVG graphics.
    int ellipseCenterX = 110;
    int ellipseCenterY = 100;
    int ellipseWidth = 100;
    int ellipseHeight = 40;
    int ellipseRotate = 15;//degrees
    
    int circleCenterX = 110;
    int circleCenterY = 100;
    int circleRadius = 30;
    int circleRotate = 15;

    int rectCenterX = 110;
    int rectCenterY = 200;
    int rectWidth = 110;
    int rectHeight = 80;
    int rectRoundX = 26;
    int rectRoundY = 25;
    
    int rotatePolyline = 10;
    
    
    try{
      //Create a DOM tree that describes a particular
      // graphic image.
      //Begin by creating a Document object and a root
      // node named svg.  All of the graphic content will
      // be contained in the svg element. This code was
      // explained in the earlier program named
      // Xslt01.java that was explained in lesson 2202
      // named "Getting Started with Java JAXP and XSL
      // Transformations (XSLT)".
      
      //At this point, the program starts using the 
      // SVG graphics library encapsulated in the class
      // named SvgGraphics.
      Document document = SvgGraphics.getDocument();      

      //Create the root node named svg and append it to
      // the document.
      //Set some attributes on the root node that are
      // required for proper rendering.
      Element svg = SvgGraphics.makeNode(
        document,
        null,//parent could be null
        "svg",//node type
        new String[]{"xmlns","http://www.w3.org/2000/svg",
                     "version","1.1",
                     "width","220px",
                     "height","440px",
                     "position","static",
                     "top","0",
                     "left","0"
                    });//end call to makeNode

      //Show outline of canvas using 'rect' element
      Element outline = SvgGraphics.makeNode(
                     document,
                     svg,//parent could be null
                     "rect",//node type
                     new String[]{"x","0",
                                  "y","0",
                                  "width","220",
                                  "height","440",
                                  "fill","none",
                                  "stroke","black",
                                  "stroke-width","1"
                                 });//end call to makeNode

      //Create a node named defs, which will be the parent
      // for three gradient definitions.  There will be 
      // two linear gradients and one radial gradient.
      // Pass null for the reference to the object for the
      // special case where the node has no attributes.
      Element defs = SvgGraphics.makeNode(document,
                                          svg,//parent
                                          "defs",
                                          null);

      //Create nodes that define three different gradient
      // coloring schemes.
      //The definitions are identified as gradientA,
      // gradientB, and gradientC.  They will be referred
      // to later to specify the fill colors for an 
      // ellipse, a circle, and a rounded rectangle.

      //Define gradientA, which provides a linear
      // gradient from yellow to red to blue going from
      // left to right.
      Element gradientA = SvgGraphics.makeLinearGradient(
                              document,    //this document
                              defs,        //parent
                              "gradientA");//id
      //Create three stop nodes that identify the colors
      // used to produce the gradient and specify where
      // the colors begin and end, 
      SvgGraphics.makeGradientStop(document,
                                   gradientA,//parent
                                   "2%",     //start here
                                   "yellow");//color
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientA,
                                   "50%",
                                   "red");
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientA,
                                   "98%",
                                   "blue");
      
      //Define gradientB, which provides a linear
      // gradient having two stops from green to blue
      // going from left to right.
      Element gradientB =SvgGraphics.makeLinearGradient(
                             document,    //this document
                             defs,        //parent
                             "gradientB");//id
                   
      SvgGraphics.makeGradientStop(document,
                                   gradientB,//parent
                                   "0%",     //start here
                                   "green"); //color
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientB,
                                   "100%",
                                   "blue");
                                     
      //Define gradientC, which provides a radial
      // gradient from yellow to red to blue going from
      // the center to the outer edge.
      Element gradientC = 
                 SvgGraphics.makeRadialGradient(
                   document,        //this document
                   defs,            //parent
                   "gradientC",     //ID
                   "userSpaceOnUse",
                   rectCenterX,     //cx
                   rectCenterY,     //cy
                   rectWidth/2);    //r
                   
      SvgGraphics.makeGradientStop(document,
                                   gradientC,
                                   "0%",
                                   "yellow");
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientC,
                                   "50%",
                                   "red");
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientC,
                                   "100%",
                                   "blue");
                                   
      //Create a node named g, which will be the parent
      // for an ellipse,a circle, a rectangle, a line,
      // a polyline, and a polygon. Pass null for the
      // reference to the object for the special case
      // where the node has no attributes.
      Element g = SvgGraphics.makeNode(document,
                                       svg,//parent
                                       "g",
                                       null);

      //Create an ellipse with a blue border that is two
      // pixels wide. Fill it with the yellow-red-blue
      // gradient defined by gradientA.
      Element theEllipse = SvgGraphics.makeEllipse(
                             document,
                             g,//Owner
                             ellipseCenterX,
                             ellipseCenterY,
                             ellipseWidth,
                             ellipseHeight);
      //Set the appearance of the ellipse.
      theEllipse.setAttribute("fill","url(#gradientA)");
      theEllipse.setAttribute("stroke","blue");
      theEllipse.setAttribute("stroke-width","2");
      //Rotate the ellipse by 15-degrees clockwise about
      // its center.
      theEllipse.setAttribute("transform",
                  "translate(" + ellipseCenterX + "," 
                  + ellipseCenterY + ") "
                  + "rotate(" + ellipseRotate + ") "
                  + "translate(" + (-ellipseCenterX) + ","
                  + (-ellipseCenterY) + ") ");

      //Position a circle so that it appears to be inside
      // the ellipse.  Fill it with the green-blue
      // gradient defined by gradientB.
      Element theCircle = SvgGraphics.makeCircle(
                            document,
                            g,//Owner
                            circleCenterX,
                            circleCenterY,
                            circleRadius);
      //Set the appearance of the circle and rotate it by
      // 15-degrees clockwise about its center.
      theCircle.setAttribute("fill","url(#gradientB)");
      theCircle.setAttribute("transform",
                   "translate(" + circleCenterX + "," 
                   + circleCenterY + ") "
                   + "rotate(" + circleRotate + ") "
                   + "translate(" + (-circleCenterX) + ","
                   + (-circleCenterY) + ") ");

      //Make a rounded rectangle and fill it with
      // gradientC
      Element theRect = SvgGraphics.makeRect(
                            document,
                            g,//Owner
                            rectCenterX - rectWidth/2,//x
                            rectCenterY - rectHeight/2,//y
                            rectWidth,
                            rectHeight);

      theRect.setAttribute("fill","url(#gradientC)");

      //Round the corners.
      theRect.setAttribute("rx",""+ rectRoundX);
      theRect.setAttribute("ry",""+ rectRoundY);

      //Draw a polyline with four points.
      int[] polylinePoints = 
                         {10,235,210,235,110,275,110,225};
      Element polyline = SvgGraphics.makePolyline(
                                          document,
                                          g,//owner
                                          polylinePoints);
      
      //Rotate the polyline by 10 degrees around the first
      // point.
      polyline.setAttribute("transform",
                    "translate(" + polylinePoints[0] + ","
                    + polylinePoints[1] + ")" 
                    + "rotate(" + rotatePolyline + ")" 
                    + "translate(" + (-polylinePoints[0]) 
                    + "," + (-polylinePoints[1]) + ")");
      
      //Draw a polygon with four points. Give it a red
      // border and fill it with green.
      int[] polygonPoints = 
                         {10,335,210,335,110,375,110,325};
      Element polygon = SvgGraphics.makePolygon(
                                           document,
                                           g,//parent
                                           polygonPoints);
      polygon.setAttribute("fill","green");
      polygon.setAttribute("stroke","red");
      polygon.setAttribute("stroke-width","3");
      
      //Draw a green line 12 pixels wide.  Make the line
      // 60% opaque, or 40% transparent, whichever you
      // prefer.
      Element line = SvgGraphics.makeLine(document,
                                          g,   //owner
                                          0,   //x1
                                          0,   //y1
                                          220, //x2
                                          440);//y2
      line.setAttribute("stroke","green");
      line.setAttribute("stroke-width","12");
      line.setAttribute("stroke-opacity","0.6");
      


      //The remaining code in this class is used to
      // transform the DOM tree into SVG code and to wrap
      // that code in a document that would otherwise be
      // a valid XHTML document.

      //Instantiate an object of this class
      Svg03 thisObj = new Svg03();

      //Get an output stream for the output produced by
      // the program code.
      PrintWriter out = new PrintWriter(
                      new FileOutputStream("junk.xhtml"));

      //Process the DOM tree, beginning with the Document
      // node to produce the output.
      //The invocation of processDocumentNode starts a
      // recursive process that will process the entire
      // DOM tree.
      thisObj.processDocumentNode(out,document);

    }catch(Exception e){
      //Note that no effort was made to provide meaningful
      // information in the event of an exception or
      // error.
      e.printStackTrace(System.err);
    }//end catch

  }// end main()
  //----------------------------------------------------//

  //This method is used to produce the text required in
  // the output at the document level, such as the
  // XML declaration.  It also produces the top level
  // element tags. 
  void processDocumentNode(PrintWriter out,Node node){
    //Create the beginning of the XHTML document.
    out.println("<?xml version=\"1.0\" "
                       + "encoding=\"UTF-8\"?>");
    out.println(
          "<!DOCTYPE html PUBLIC \"-//W3C//DTD "
             + "XHTML 1.0 Transitional//EN\" "
             + "\"http://www.w3.org/TR/xhtml1/"
             + "DTD/xhtml1-transitional.dtd\">");

    out.println(SvgGraphics.makeElement(false,"html",
      new String[]{"xmlns","http://www.w3.org/1999/xhtml",
                   "xml:lang","en",
                   "name","lang"
                  })//end call to makeElement
    );//end println

    out.println("<head>");
    
    out.println(SvgGraphics.makeElement(
               true,
               "meta",
               new String[]{"http-equiv","content-type",
                            "content",
                            "image/svg+xml; charset=UTF-8"
                           })//end call to makeElement
    );//end println
  
    out.println("<title>Generated XHTML file</title>");

    out.println("</head>");
    
    out.println(SvgGraphics.makeElement(
                 false,
                 "body",
                 new String[]{"id","body",
                              "style","position:absolute;"
                              + "z-index:0;"
                              + "border:1px solid black;"
                              + "left:0;"
                              + "top:0;"
                              + "width:220px;"
                              + "height:440px;"
                             })//end call to makeElement
    );//end println

    //Go process the root (document) node. This method
    // call triggers a recursive process that will 
    //process the entire DOM tree.
    processNode(out,node);

    //The entire DOM tree has been processed when control
    // returns to this point.
    //Now finish the output document and flush the output
    // buffer.

    out.println("</body></html>");
    out.flush();
  }//end processDocumentNode
  //----------------------------------------------------//

  //There are seven kinds of nodes and most of them were
  // handled in the earlier program from which this
  // program was derived:
  
  // root or document
  // element
  // attribute
  // text
  // comment
  // processing instruction
  // namespace

  //However, because the nodes in this program are
  // produced by the program and it is known in advance
  // the types of nodes that will be encountered, this
  // program is less general than the earlier program.
  // Therefore, this method only handles element nodes
  // and document nodes.
  void processNode(PrintWriter out,Node node){
    try{
      if (node == null){
        System.out.println("Nothing to do, node is null");
        return;
      }//end if

      //Process the incoming node based on its type.
      int type = node.getNodeType();
      switch(type){
        case Node.ELEMENT_NODE:{
          
          //Start creating the start tag.
          String nodeName = node.getNodeName();
          out.print("<" + nodeName + " ");
          
          //Now add each attribute to the start tag.
          NamedNodeMap theList = node.getAttributes();
          int length = theList.getLength();
          for(int cnt = 0;cnt < length;cnt++){
            Node item = theList.item(cnt);
            out.print(item.getNodeName() + "=\"" 
                           + item.getNodeValue() + "\" ");
          }//end for loop
          out.println(">");//close the start tag.
          
          //Process all XML child nodes recursively.
          processChildNodes(out,node);
          
          //The element and all of its children have been
          // processed when control returns to this point.
          //Create the end tag for the element.
          out.println("</" + nodeName + ">");
            
          break;
        }//end case ELEMENT_NODE

        case Node.DOCUMENT_NODE:{
          //No action is required other than to process
          // the child nodes recursively.
          processChildNodes(out,node);
          break;
        }//end case DOCUMENT_NODE

      }//end switch

    }catch(Exception e){
      e.printStackTrace(System.err);
    }//end catch
  }//end processNode(Node)
  //----------------------------------------------------//

  //This method is called to recursively process
  // the child nodes belonging to a specified
  // node.
  void processChildNodes(PrintWriter out,Node node){
    NodeList children = node.getChildNodes();
    if (children != null){
      int len = children.getLength();
      //Iterate on NodeList of child nodes.
      for(int i = 0; i < len; i++){
        //This is a recursive call.
        processNode(out,children.item(i));
      }//end for loop
    }//end if children != null

  }//end processChildNodes
  //----------------------------------------------------//

}//End class Svg03
//======================================================//


//This is a proof-of-concept graphics class that
// provides method calls for the creation of the following
// DOM tree nodes:
//  A general node of any type
//  A linear gradient element.
//  A radial gradient element.
//  An ellipse.
//  A circle.
//  A rectangle.
//  A line.
//  A polyline.
//  A polygon.

//Each method receives a reference to the overall document
// along with a reference to the parent for the new node.
//When the method returns, the new node has been appended
// to the parent node.
class SvgGraphics{
  //----------------------------------------------------//

  //This method creates a linear gradient node to which
  // stop elements must be appended.
  static Element makeLinearGradient(Document document,
                                    Element parent,
                                    String id){
    Element gradient = 
        (Element)document.createElement("linearGradient");
    parent.appendChild(gradient);
    gradient.setAttribute("id",id);
    return gradient;
  }//End makeLinearGradient
  //----------------------------------------------------//
  
  //This method creates a radial gradient node to which
  // stop elements must be appended. Note that numeric
  // attributes are set as type String.
  static Element makeRadialGradient(Document document,
                                    Element parent,
                                    String id,
                                    String gradientUnits,
                                    int cx,
                                    int cy,
                                    int r){
    Element gradient = 
        (Element)document.createElement("radialGradient");
    parent.appendChild(gradient);
    gradient.setAttribute("id",id);
    gradient.setAttribute("gradientUnits",gradientUnits);
    gradient.setAttribute("cx",""+cx);
    gradient.setAttribute("cy",""+cy);
    gradient.setAttribute("r",""+r);
    return gradient;
  }//End makeRadialGradient
  //----------------------------------------------------//

  //This method creates a gradient stop node to be
  // appended to a linear gradient node or a radial
  // gradient node.                                      
  static Element makeGradientStop(Document document,
                                  Element parent,
                                  String location,
                                  String color){
    Element stopElement = 
                  (Element)document.createElement("stop");
    parent.appendChild(stopElement);
    stopElement.setAttribute("offset",location);
    stopElement.setAttribute("stop-color",color);
    return stopElement;
  }//End makeGradientStop
  //----------------------------------------------------//
  
  //This method returns a reference to an ellipse. The
  // xCoor and yCoor parameters specify the center of the
  // ellipse.  The xRadius and yRadius parameters specify
  // the width and height of the  ellipse respectively
  // while it is in the horizontal plane before being
  // rotated.  Numeric attributes are set at type String.
  static Element makeEllipse(Document document,
                             Element parent,
                             int xCoor,
                             int yCoor,
                             int xRadius,
                             int yRadius){
    Element ellipse  = 
               (Element)document.createElement("ellipse");
    parent.appendChild(ellipse);
    ellipse.setAttribute("cx",""+xCoor);
    ellipse.setAttribute("cy",""+yCoor);
    ellipse.setAttribute("rx",""+xRadius);
    ellipse.setAttribute("ry",""+yRadius);
    return ellipse;
  }//end makeEllipse
  //----------------------------------------------------//

  //This method returns a reference to a circle. The
  // xCoor and yCoor parameters specify the center of the
  // circle.  The radius parameter specifies the radus of
  // the circle.  Numeric attributes are set as type
  // String.
  static Element makeCircle(Document document,
                            Element parent,
                            int xCoor,
                            int yCoor,
                            int radius){
    Element circle  = 
                (Element)document.createElement("circle");
    parent.appendChild(circle);
    circle.setAttribute("cx",""+xCoor);
    circle.setAttribute("cy",""+yCoor);
    circle.setAttribute("r",""+radius);
    return circle;
  }//end makeCircle
  //----------------------------------------------------//
  
  //This method returns a reference to a rectangle. The
  // xCoor and yCoor parameters specify the location of
  // the upper left corner.  The width and height
  // parameters specify the width and the height while
  // the rectangle is in the horizontal plane before
  // being rotated.  Numeric attributes are set as type
  // String.
  static Element makeRect(Document document,
                          Element parent,
                          int xCoor,
                          int yCoor,
                          int width,
                          int height){
    Element rect  = 
                  (Element)document.createElement("rect");
    parent.appendChild(rect);
    rect.setAttribute("x",""+xCoor);
    rect.setAttribute("y",""+yCoor);
    rect.setAttribute("width",""+width);
    rect.setAttribute("height",""+height);
    return rect;
  }//end makeRect
  
  //----------------------------------------------------//
  
  //This method returns a reference to a line. x1 and y1
  // specify the starting point of the line before it is
  // rotated. x2 and y2 specify the end point.  By
  // default, the stroke is set to black one pixel wide.
  // This can be overridden to speciy other colors and
  // other widths if you need to do so.
  static Element makeLine(Document document,
                          Element parent,
                          int x1,
                          int y1,
                          int x2,
                          int y2){
    Element line  = 
                  (Element)document.createElement("line");
    parent.appendChild(line);
    line.setAttribute("x1",""+x1);
    line.setAttribute("y1",""+y1);
    line.setAttribute("x2",""+x2);
    line.setAttribute("y2",""+y2);
    line.setAttribute("stroke","black");
    line.setAttribute("stroke-width","1");
    return line;
  }//end makeLine
  //----------------------------------------------------//

  //This method returns a reference to a polyline. The
  // array of type int[] must contain an even number of
  // values for things to work correctly.
  //The values are extracted from the array and treated
  // as coordinate values x1,y1, x2,y2, x3,y3 ... etc.
  // By default, the stroke is set to black one pixel
  // wide with no fill.  This can be overridden to other
  // colors and other widths if you need to do so.
  static Element makePolyline(Document document,
                              Element parent,
                              int[] points){
    Element polyline  = 
              (Element)document.createElement("polyline");
    parent.appendChild(polyline);

    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + ",";
    }//end for loop
    
    polyline.setAttribute("points",dataPoints);
    polyline.setAttribute("stroke","black");
    polyline.setAttribute("stroke-width","1");
    polyline.setAttribute("fill","none");
    return polyline;
  }//end makePolyline
  //----------------------------------------------------//
  
  //This method returns a reference to a polygon. The
  // array of type int[] must contain an even number of
  // values for things to work correctly.
  //The values are extracted from the array and treated
  // as coordinate values x1,y1, x2,y2, x3,y3 ... etc.
  // By default, the stroke is set to black, one pixel
  // wide with no fill.  This can be overridden to other
  // colors and other widths if you need to do so.
  //The major difference between a polygon and a polyline
  // is that a polyline leaves the last point dangling.
  // However, a polygon automatically draws a line from
  // the last point back to the first point to close
  // the polygon.
  static Element makePolygon(Document document,
                           Element parent,
                           int[] points){
    Element polygon  = 
               (Element)document.createElement("polygon");
    parent.appendChild(polygon);

    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + ",";
    }//end for loop
    
    polygon.setAttribute("points",dataPoints);
    polygon.setAttribute("stroke","black");
    polygon.setAttribute("stroke-width","1");
    polygon.setAttribute("fill","none");
    return polygon;
  }//end makePolygon
  
  //----------------------------------------------------//
  
  /*
  One of the most frustrating things about using Java
   to create elements in XML, XHTML, or HTML is having
   to deal with the escape characters for the many
   required quotation marks. This method constructs an
   element, which may or may not have attributes. Also,
   the element may or may not be empty.
  The user of this method does not have to deal with the
   required quotation marks surrounding attribute values
   and the corresponding escape characters     
  The first incoming parameter must be true if the
   element is empty and false if the element is not
   empty.
  If the first parameter is true, the element is sealed
   off in the required manner for an empty element. If
   the first parameter is false, the method returns the
   complete start tag for the element but does not
   return a complete element. It is the responsibility
   of the calling method to provide the content and the
   end tag for the element.
  The second parameter to the method must be a String
   that specifies the name of the element.
  The third parameter to the method must be a reference
   to an array object of type String.  This array must 
   contain an even number of elements.  Each pair of 
   elements constitutes the name and the value of an 
   attribute, in the order name, value, name, value, etc.

  If the reference to the array object is null and the
   first parameter is false, the method returns the start
   tag for an element that has no attributes and is not 
   empty.
  If the reference is null and the first parameter is
   true, the method returns a complete empty element with 
   no attributes (which probably doesn't make any sense).
   
  An example of the recommended usage of the method
   follows:
   
  String newElement = SvgGraphics.makeElement(
                  true/false,
                  name,
                  new String[]{"name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value"
                              });//end call to makeElement
   
  */
  
  static String makeElement(
          boolean empty,String elementName,String[] data){

    //Begin constructing the start tag.
    String element = "<" + elementName + " ";
    
    //Deal with elements that have no attributes.
    if((empty==false)  & & (data == null)){
      //Return a complete start tag.
      return element + ">";
    }else if((empty==true)  & & (data == null)){
      //Return a complete empty element.
      return element + "/>";
    }//end if

    for(int cnt=0;cnt<data.length;cnt+=2){

      String name = data[cnt];
      String value = data[cnt+1];
      element += name + "=" + "\"" + value + "\" ";
    }//end for loop
    
    if(empty){
      //Terminate the element appropriately for an
      // empty element. A complete empty element will
      // be returned.
      element += "/>";
    }else{
      //End the start tag for an element that is not
      // empty. In this case, only the start tag will
      // be returned.  The calling program must provide
      // the content for the element as well as the end
      // tag for the element.
      element += ">";
    }//end else
      
  return element;
  }//end makeElement
  //----------------------------------------------------//
  
  /*
  The purpose of this method is to create a general node
   having any name, and any number of attributes with any 
   attribute names and any String values for the 
   attributes, or no attributes at all.
   
  The first parameter is a reference to the document to
   which the new node belongs.
  
  The second parameter is a reference to the parent node
   to which this node is to be appended so as to become a
   child of that node. If this parameter is null, the new
   node is appended to the document.  Otherwise, it is
   appended to the specified parent node.
   
  The third parameter is a String that specifies the type
   of node.
  
  The fourth parameter to the method must be a reference
   to an array object of type String.  This array must 
   contain an even number of elements.  Each pair of 
   elements constitutes the name and the value of an 
   attribute, in the order name, value, name, value, etc.
  
  An example of the recommended usage of the method
   follows:
  Element abc = SvgGraphics.makeNode(
                     document,
                     def,//parent could be null
                     "ghi",//node type
                     new String[]{"name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value"
                                 });//end call to makeNode
  */
  static Element makeNode(Document document,
                                Element parent,
                                String nodeType,
                                String[] data){
  
    Element element = 
                (Element)document.createElement(nodeType);
    
    if(parent == null){
      //For the special case of parent equal to null,
      // append the new node to the document.
      document.appendChild(element);
    }else{
      //Otherwise, append the new node to the specified
      // parent.
      parent.appendChild(element);
    }//end else
  
    //Deal with elements that have no attributes.
    if(data == null){
      return element;
    }//end if
    
    for(int cnt=0;cnt<data.length;cnt+=2){
      String name = data[cnt];
      String value = data[cnt+1];
      element.setAttribute(name,value);
    }//end for loop
    
    return element;
  }//end makeNode
  //----------------------------------------------------//
  
  //This is a utility method that is used to execute code
  // that is the same regardless of the graphic image
  // being produced.
  static Document getDocument(){
    Document document = null;
    try{
      DocumentBuilderFactory factory = 
                     DocumentBuilderFactory.newInstance();

      DocumentBuilder builder = 
                             factory.newDocumentBuilder();
      document = builder.newDocument();
      document.setXmlStandalone(false);
    }catch(Exception e){
      e.printStackTrace(System.err);
      System.exit(0);
    }//end catch
    return document;
  }//end getDocument
  //----------------------------------------------------//
}//end class SvgGraphics

 

Listing 31. Program code for Svg04.

/*File Svg04.java,
Copyright 2006, R.G.Baldwin

This program demonstrates the rendering of graphic content
on a Firefox 1.5 client by way of the generation of SVG 
code in a servlet on the server.

Except for the fact that this program is a servlet that
generates XHTML code in its output, and the earlier
program named Svg03 is a desktop application that produces
an output file containing XHTML code, the two programs
are identical.

This program uses an SVG graphic library that has been
significantly improved over the one used in the earlier 
program named Svg02.  The library now supports the 
following basic shapes plus linear and radial gradients
and dozens of attributes on each shape.

rect
circle
ellipse
line
polyline
polygon

These are all of the basic shapes provided by SVG.

The program defines a servlet that first creates a DOM
tree describing a specific graphic image, and then
transforms that tree into corresponding SVG XML code for
transmission to the client. The program wraps the SVG
graphic code in code that would otherwise be valid XHTML
code.  Inclusion of the SVG code, however, prevents the
code from being valid XHTML because the SVG element names
are not recognized by XHTML validator programs, such as
the program at the following URL:

http://validator.w3.org/file-upload.html

This program can only be tested by deploying the servlet
on a servlet-compatible server and then accessing it 
using an SVG-compatible browser, such as Firefox 1.5.

On my system, the servlet can be executed by copying it
into the following directory on the Tomcat server and
accessing it from Firefox using the URL shown below:

C:\jakarta-tomcat-5.0.27\webapps\ROOT\WEB-INF\classes

http://localhost/servlet/Svg04

Accessing the servlet using IE 6 (without an SVG plug-in
installed) simply causes the browser to request permission
to save the output produced by the server in a local file.
The file name that is recommended by the browser is simply
Svg04 with no extension.

Tested using J2SE 5.0, Firefox 1.5.08, and 
jakarta-tomcat-5.0.27 running as a localhost server under
WinXP.
*********************************************************/

import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class Svg04 extends HttpServlet{
  
  public void doGet(HttpServletRequest req,
                    HttpServletResponse res)
                    throws ServletException,
                                     IOException{
                                      
    //Note:  This statement is critical to causing
    // Firefox to recognize and render the SVG
    // code produced by the servlet.
    res.setContentType("image/svg+xml");

    //The following data values will be used to create
    // SVG graphics.
    int ellipseCenterX = 110;
    int ellipseCenterY = 100;
    int ellipseWidth = 100;
    int ellipseHeight = 40;
    int ellipseRotate = 15;//degrees
    
    int circleCenterX = 110;
    int circleCenterY = 100;
    int circleRadius = 30;
    int circleRotate = 15;

    int rectCenterX = 110;
    int rectCenterY = 200;
    int rectWidth = 110;
    int rectHeight = 80;
    int rectRoundX = 26;
    int rectRoundY = 25;
    
    int rotatePolyline = 10;
    
    try{
      //Create a DOM tree that describes a particular
      // graphic image.
      //Begin by creating a Document object and a root
      // node named svg.  All of the graphic content will
      // be contained in the svg element. This code was
      // explained in the earlier program named
      // Xslt01.java that was explained inlesson 2202
      // named "Getting Started with Java JAXP and XSL
      // Transformations (XSLT)".
      
      //At this point, the program starts using the 
      // SVG graphics library encapsulated in the class
      // named SvgGraphics.
      Document document = SvgGraphics.getDocument();      

      //Create the root node named svg and append it to
      // the document.
      //Set some attributes on the root node that are
      // required for proper rendering.
      Element svg = SvgGraphics.makeNode(
        document,
        null,//parent could be null
        "svg",//node type
        new String[]{"xmlns","http://www.w3.org/2000/svg",
                     "version","1.1",
                     "width","220px",
                     "height","440px",
                     "position","static",
                     "top","0",
                     "left","0"
                    });//end call to makeNode
      
      //Show outline of canvas using 'rect' element
      Element outline = SvgGraphics.makeNode(
                     document,
                     svg,//parent could be null
                     "rect",//node type
                     new String[]{"x","0",
                                  "y","0",
                                  "width","220",
                                  "height","440",
                                  "fill","none",
                                  "stroke","black",
                                  "stroke-width","1"
                                 });//end call to makeNode

      //Create a node named defs, which will be the parent
      // for three gradient definitions.  There will be 
      // two linear gradients and one radial gradient.
      // Pass null for the reference to the object for the
      // special case where the node has no attribtes.
      Element defs = SvgGraphics.makeNode(document,
                                          svg,//parent
                                          "defs",
                                          null);

      //Create nodes that define three different gradient
      // coloring schemes.
      //The definitions are identified as gradientA,
      // gradientB, and gradientC.  They will be referred
      // to later to specify the fill colors for an 
      // ellipse, a circle, and a rounded rectangle.

      //Define gradientA, which provides a linear
      // gradient from yellow to red to blue going from
      // left to right.
      Element gradientA = SvgGraphics.makeLinearGradient(
                              document,    //this document
                              defs,        //parent
                              "gradientA");//id
      //Create three stop nodes that identify the colors
      // used to produce the gradient and specify where
      // the colors begin and end, 
      SvgGraphics.makeGradientStop(document,
                                   gradientA,//parent
                                   "2%",     //start here
                                   "yellow");//color
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientA,
                                   "50%",
                                   "red");
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientA,

                                   "98%",
                                   "blue");
      
      //Define gradientB, which provides a linear
      // gradient having two stops from green to blue
      // going from left to right.
      Element gradientB =SvgGraphics.makeLinearGradient(
                             document,    //this document
                             defs,        //parent
                             "gradientB");//id
                   
      SvgGraphics.makeGradientStop(document,
                                   gradientB,//parent
                                   "0%",     //start here
                                   "green"); //color
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientB,
                                   "100%",
                                   "blue");
                                     
      //Define gradientC, which provides a radial
      // gradient from yellow to red to blue going from
      // the center to the outer edge.
      Element gradientC = 
                 SvgGraphics.makeRadialGradient(
                   document,        //this document
                   defs,            //parent
                   "gradientC",     //ID
                   "userSpaceOnUse",
                   rectCenterX,     //cx
                   rectCenterY,     //cy
                   rectWidth/2);    //r
                   
      SvgGraphics.makeGradientStop(document,
                                   gradientC,
                                   "0%",
                                   "yellow");
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientC,
                                   "50%",
                                   "red");
                             
      SvgGraphics.makeGradientStop(document,
                                   gradientC,
                                   "100%",
                                   "blue");
                                   
      //Create a node named g, which will be the parent
      // for an ellipse,a circle, a rectangle, a line,
      // a polyline, and a polygon. Pass null for the
      // reference to the object for the special case
      // where the node has no attribtes.
      Element g = SvgGraphics.makeNode(document,
                                       svg,//parent
                                       "g",
                                       null);

      //Create an ellipse with a blue border that is two
      // pixels wide. Fill it with the yellow-red-blue
      // gradient defined by gradientA.
      Element theEllipse = SvgGraphics.makeEllipse(
                             document,
                             g,//Owner
                             ellipseCenterX,
                             ellipseCenterY,
                             ellipseWidth,
                             ellipseHeight);
      //Set the appearance of the ellipse.
      theEllipse.setAttribute("fill","url(#gradientA)");
      theEllipse.setAttribute("stroke","blue");
      theEllipse.setAttribute("stroke-width","2");
      //Rotate the ellipse by 15-degrees clockwise about
      // its center.
      theEllipse.setAttribute("transform",
                  "translate(" + ellipseCenterX + "," 
                  + ellipseCenterY + ") "
                  + "rotate(" + ellipseRotate + ") "
                  + "translate(" + (-ellipseCenterX) + ","
                  + (-ellipseCenterY) + ") ");

      //Position a circle so that it appears to be inside
      // the ellipse.  Fill it with the green-blue
      // gradient defined by gradientB.
      Element theCircle = SvgGraphics.makeCircle(
                            document,
                            g,//Owner
                            circleCenterX,
                            circleCenterY,
                            circleRadius);
      //Set the appearance of the circle and rotate it by
      // 15-degrees clockwise about its center.
      theCircle.setAttribute("fill","url(#gradientB)");
      theCircle.setAttribute("transform",
                   "translate(" + circleCenterX + "," 
                   + circleCenterY + ") "
                   + "rotate(" + circleRotate + ") "
                   + "translate(" + (-circleCenterX) + ","
                   + (-circleCenterY) + ") ");

      //Make a rounded rectangle and fill it with
      // gradientC
      Element theRect = SvgGraphics.makeRect(
                            document,
                            g,//Owner
                            rectCenterX - rectWidth/2,//x
                            rectCenterY - rectHeight/2,//y
                            rectWidth,
                            rectHeight);

      theRect.setAttribute("fill","url(#gradientC)");

      //Round the corners.
      theRect.setAttribute("rx",""+ rectRoundX);
      theRect.setAttribute("ry",""+ rectRoundY);

      //Draw a polyline with four points.
      int[] polylinePoints = 
                         {10,235,210,235,110,275,110,225};
      Element polyline = SvgGraphics.makePolyline(
                                          document,
                                          g,//owner
                                          polylinePoints);
      
      //Rotate the polyline by 10 degrees around the first
      // point.
      polyline.setAttribute("transform",
                    "translate(" + polylinePoints[0] + ","
                    + polylinePoints[1] + ")" 
                    + "rotate(" + rotatePolyline + ")" 
                    + "translate(" + (-polylinePoints[0]) 
                    + "," + (-polylinePoints[1]) + ")");
      
      //Draw a polygon with four points. Give it a red
      // border and fill it with green.
      int[] polygonPoints = 
                         {10,335,210,335,110,375,110,325};
      Element polygon = SvgGraphics.makePolygon(
                                           document,
                                           g,//parent
                                           polygonPoints);
      polygon.setAttribute("fill","green");
      polygon.setAttribute("stroke","red");
      polygon.setAttribute("stroke-width","3");
      
      //Draw a green line 12 pixels wide.  Make the line
      // 60% opaque, or 40% transparent, whichever you
      // prefer.
      Element line = SvgGraphics.makeLine(document,
                                          g,   //owner
                                          0,   //x1
                                          0,   //y1
                                          220, //x2
                                          440);//y2
      line.setAttribute("stroke","green");
      line.setAttribute("stroke-width","12");
      line.setAttribute("stroke-opacity","0.6");
      

      //The remaining code in this class is used to
      // transform the DOM tree into SVG code and to wrap
      // that code in a document that would otherwise be
      // a valid XHTML document.

      //Instantiate an object of this class
      Svg04 thisObj = new Svg04();

      //Get an output stream for the output produced by
      // the program code.
      PrintWriter out = res.getWriter();

      //Process the DOM tree, beginning with the Document
      // node to produce the output.
      //The invocation of processDocumentNode starts a
      // recursive process that will process the entire
      // DOM tree.
      thisObj.processDocumentNode(out,document);

    }catch(Exception e){
      //Note that no effort was made to provide meaningful
      // information in the event of an exception or
      // error.
      e.printStackTrace(System.err);
    }//end catch

  }//end doGet()
  //----------------------------------------------------//

  //This method is used to produce the text required in
  // the output at the document level, such as the
  // XML declaration.  It also produces the top level
  // element tags. 
  void processDocumentNode(PrintWriter out,Node node){
    //Create the beginning of the XHTML document.
    out.println("<?xml version=\"1.0\" "
                       + "encoding=\"UTF-8\"?>");
    out.println(
          "<!DOCTYPE html PUBLIC \"-//W3C//DTD "
             + "XHTML 1.0 Transitional//EN\" "
             + "\"http://www.w3.org/TR/xhtml1/"
             + "DTD/xhtml1-transitional.dtd\">");

    out.println(SvgGraphics.makeElement(false,"html",
      new String[]{"xmlns","http://www.w3.org/1999/xhtml",
                   "xml:lang","en",
                   "name","lang"
                  })//end call to makeElement
    );//end println

    out.println("<head>");

    out.println(SvgGraphics.makeElement(
               true,
               "meta",
               new String[]{"http-equiv","content-type",
                            "content",
                            "image/svg+xml; charset=UTF-8"
                           })//end call to makeElement
    );//end println

    out.println("<title>Generated XHTML file</title>");

    out.println("</head>");

    out.println(SvgGraphics.makeElement(
                 false,
                 "body",
                 new String[]{"id","body",
                              "style","position:absolute;"
                              + "z-index:0;"
                              + "border:1px solid black;"
                              + "left:0;"
                              + "top:0;"
                              + "width:220px;"
                              + "height:440px;"
                             })//end call to makeElement
    );//end println

    //Go process the root (document) node. This method
    // call triggers a recursive process that will 
    //process the entire DOM tree.
    processNode(out,node);

    //The entire DOM tree has been processed when control
    // returns to this point.
    //Now finish the output document and flush the output
    // buffer.

    out.println("</body></html>");
    out.flush();
  }//end processDocumentNode
  //----------------------------------------------------//

  //There are seven kinds of nodes and most of them were
  // handled in the earlier program from which this
  // program was derived:
  
  // root or document
  // element
  // attribute
  // text
  // comment
  // processing instruction
  // namespace

  //However, because the nodes in this program are
  // produced by the program and it is known in advance
  // the types of nodes that will be encountered, this
  // program is less general than the earlier program.
  // Therefore, this method only handles element nodes
  // and document nodes..
  void processNode(PrintWriter out,Node node){
    try{
      if (node == null){
        System.out.println("Nothing to do, node is null");
        return;
      }//end if

      //Process the incoming node based on its type.
      int type = node.getNodeType();
      switch(type){
        case Node.ELEMENT_NODE:{
          
          //Start creating the start tag.
          String nodeName = node.getNodeName();
          out.print("<" + nodeName + " ");
          
          //Now add each attribute to the start tag.
          NamedNodeMap theList = node.getAttributes();
          int length = theList.getLength();
          for(int cnt = 0;cnt < length;cnt++){
            Node item = theList.item(cnt);
            out.print(item.getNodeName() + "=\"" 
                           + item.getNodeValue() + "\" ");
          }//end for loop
          out.println(">");//close the start tag.
          
          //Process all XML child nodes recursively.
          processChildNodes(out,node);
          
          //The element and all of its children have been
          // processed when control returns to this point.
          //Create the end tag for the element.
          out.println("</" + nodeName + ">");
            
          break;
        }//end case ELEMENT_NODE

        case Node.DOCUMENT_NODE:{
          //No action is required other than to process
          // the child nodes recursively.
          processChildNodes(out,node);
          break;
        }//end case DOCUMENT_NODE

      }//end switch

    }catch(Exception e){
      e.printStackTrace(System.err);
    }//end catch
  }//end processNode(Node)
  //----------------------------------------------------//

  //This method is called to recursively process
  // the child nodes belonging to a specified
  // node.
  void processChildNodes(PrintWriter out,Node node){
    NodeList children = node.getChildNodes();
    if (children != null){
      int len = children.getLength();
      //Iterate on NodeList of child nodes.
      for(int i = 0; i < len; i++){
        //This is a recursive call.
        processNode(out,children.item(i));
      }//end for loop
    }//end if children != null

  }//end processChildNodes
  //----------------------------------------------------//
}//end class Svg04
//======================================================//

//This is a proof-of-concept graphics class that
// provides method calls for the creation of the following
// DOM tree nodes:
//  A general node of any type
//  A linear gradient element.
//  A radial gradient element.
//  An ellipse.
//  A circle.
//  A rectangle.
//  A line.
//  A polyline.
//  A polygon.

//Each method receives a reference to the overall document
// along with a reference to the parent for the new node.
//When the method returns, the new node has been appended
// to the parent node.
class SvgGraphics{
  //----------------------------------------------------//

  //This method creates a linear gradient node to which
  // stop elements must be appended.
  static Element makeLinearGradient(Document document,
                                    Element parent,
                                    String id){
    Element gradient = 
        (Element)document.createElement("linearGradient");
    parent.appendChild(gradient);
    gradient.setAttribute("id",id);
    return gradient;
  }//End makeLinearGradient
  //----------------------------------------------------//
  
  //This method creates a radial gradient node to which
  // stop elements must be appended. Note that numeric
  // attributes are set as type String.
  static Element makeRadialGradient(Document document,
                                    Element parent,
                                    String id,
                                    String gradientUnits,
                                    int cx,
                                    int cy,
                                    int r){
    Element gradient = 
        (Element)document.createElement("radialGradient");
    parent.appendChild(gradient);
    gradient.setAttribute("id",id);
    gradient.setAttribute("gradientUnits",gradientUnits);
    gradient.setAttribute("cx",""+cx);
    gradient.setAttribute("cy",""+cy);
    gradient.setAttribute("r",""+r);
    return gradient;
  }//End makeRadialGradient
  //----------------------------------------------------//

  //This method creates a gradient stop node to be
  // appended to a linear gradient node or a radial
  // gradient node.                                      
  static Element makeGradientStop(Document document,
                                  Element parent,
                                  String location,
                                  String color){
    Element stopElement = 
                  (Element)document.createElement("stop");
    parent.appendChild(stopElement);
    stopElement.setAttribute("offset",location);
    stopElement.setAttribute("stop-color",color);
    return stopElement;
  }//End makeGradientStop
  //----------------------------------------------------//
  
  //This method returns a reference to an ellipse. The
  // xCoor and yCoor parameters specify the center of the
  // ellipse.  The xRadius and yRadius parameters specify
  // the width and height of the  ellipse respectively
  // while it is in the horizontal plane before being
  // rotated.  Numeric attributes are set at type String.
  static Element makeEllipse(Document document,
                             Element parent,
                             int xCoor,
                             int yCoor,
                             int xRadius,
                             int yRadius){
    Element ellipse  = 
               (Element)document.createElement("ellipse");
    parent.appendChild(ellipse);
    ellipse.setAttribute("cx",""+xCoor);
    ellipse.setAttribute("cy",""+yCoor);
    ellipse.setAttribute("rx",""+xRadius);
    ellipse.setAttribute("ry",""+yRadius);
    return ellipse;
  }//end makeEllipse
  //----------------------------------------------------//

  //This method returns a reference to a circle. The
  // xCoor and yCoor parameters specify the center of the
  // circle.  The radius parameter specifies the radus of
  // the circle.  Numeric attributes are set as type
  // String.
  static Element makeCircle(Document document,
                            Element parent,
                            int xCoor,
                            int yCoor,
                            int radius){
    Element circle  = 
                (Element)document.createElement("circle");
    parent.appendChild(circle);
    circle.setAttribute("cx",""+xCoor);
    circle.setAttribute("cy",""+yCoor);
    circle.setAttribute("r",""+radius);
    return circle;
  }//end makeCircle
  //----------------------------------------------------//
  
  //This method returns a reference to a rectangle. The
  // xCoor and yCoor parameters specify the location of
  // the upper left corner.  The width and height
  // parameters specify the width and the height while
  // the rectangle is in the horizontal plane before
  // being rotated.  Numeric attributes are set as type
  // String.
  static Element makeRect(Document document,
                          Element parent,
                          int xCoor,
                          int yCoor,
                          int width,
                          int height){
    Element rect  = 
                  (Element)document.createElement("rect");
    parent.appendChild(rect);
    rect.setAttribute("x",""+xCoor);
    rect.setAttribute("y",""+yCoor);
    rect.setAttribute("width",""+width);
    rect.setAttribute("height",""+height);
    return rect;
  }//end makeRect
  
  //----------------------------------------------------//
  
  //This method returns a reference to a line. x1 and y1
  // specify the starting point of the line before it is
  // rotated. x2 and y2 specify the end point.  By
  // default, the stroke is set to black one pixel wide.
  // This can be overridden to speciy other colors and
  // other widths if you need to do so.
  static Element makeLine(Document document,
                          Element parent,
                          int x1,
                          int y1,
                          int x2,
                          int y2){
    Element line  = 
                  (Element)document.createElement("line");
    parent.appendChild(line);
    line.setAttribute("x1",""+x1);
    line.setAttribute("y1",""+y1);
    line.setAttribute("x2",""+x2);
    line.setAttribute("y2",""+y2);
    line.setAttribute("stroke","black");
    line.setAttribute("stroke-width","1");
    return line;
  }//end makeLine
  //----------------------------------------------------//

  //This method returns a reference to a polyline. The
  // array of type int[] must contain an even number of
  // values for things to work correctly.
  //The values are extracted from the array and treated
  // as coordinate values x1,y1, x2,y2, x3,y3 ... etc.
  // By default, the stroke is set to black one pixel
  // wide with no fill.  This can be overridden to other
  // colors and other widths if you need to do so.
  static Element makePolyline(Document document,
                              Element parent,
                              int[] points){
    Element polyline  = 
              (Element)document.createElement("polyline");
    parent.appendChild(polyline);

    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + ",";
    }//end for loop
    
    polyline.setAttribute("points",dataPoints);
    polyline.setAttribute("stroke","black");
    polyline.setAttribute("stroke-width","1");
    polyline.setAttribute("fill","none");
    return polyline;
  }//end makePolyline
  //----------------------------------------------------//
  
  //This method returns a reference to a polygon. The
  // array of type int[] must contain an even number of
  // values for things to work correctly.
  //The values are extracted from the array and treated
  // as coordinate values x1,y1, x2,y2, x3,y3 ... etc.
  // By default, the stroke is set to black, one pixel
  // wide with no fill.  This can be overridden to other
  // colors and other widths if you need to do so.
  //The major difference between a polygon and a polyline
  // is that a polyline leaves the last point dangling.
  // However, a polygon automatically draws a line from
  // the last point back to the first point to close
  // the polygon.
  static Element makePolygon(Document document,
                           Element parent,
                           int[] points){
    Element polygon  = 
               (Element)document.createElement("polygon");
    parent.appendChild(polygon);

    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + ",";
    }//end for loop
    
    polygon.setAttribute("points",dataPoints);
    polygon.setAttribute("stroke","black");
    polygon.setAttribute("stroke-width","1");
    polygon.setAttribute("fill","none");
    return polygon;
  }//end makePolygon
  
  //----------------------------------------------------//
  
  /*
  One of the most frustrating things about using Java
   to create elements in XML, XHTML, or HTML is having
   to deal with the escape characters for the many
   required quotation marks. This method constructs an
   element, which may or may not have attributes. Also,
   the element may or may not be empty.
  The user of this method does not have to deal with the
   required quotation marks surrounding attribute values
   and the corresponding escape characters     
  The first incoming parameter must be true if the
   element is empty and false if the element is not
   empty.
  If the first parameter is true, the element is sealed
   off in the required manner for an empty element. If
   the first parameter is false, the method returns the
   complete start tag for the element but does not
   return a complete element. It is the responsibility
   of the calling method to provide the content and the
   end tag for the element.
  The second parameter to the method must be a String
   that specifies the name of the element.
  The third parameter to the method must be a reference
   to an array object of type String.  This array must 
   contain an even number of elements.  Each pair of 
   elements constitutes the name and the value of an 
   attribute, in the order name, value, name, value, etc.

  If the reference to the array object is null and the
   first parameter is false, the method returns the start
   tag for an element that has no attributes and is not 
   empty.
  If the reference is null and the first parameter is
   true, the method returns a complete empty element with 
   no attributes (which probably doesn't make any sense).
   
  An example of the recommended usage of the method
   follows:
   
  String newElement = SvgGraphics.makeElement(
                  true/false,
                  name,
                  new String[]{"name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value",
                               "name","value"
                              });//end call to makeElement
   
  */
  
  static String makeElement(
          boolean empty,String elementName,String[] data){

    //Begin constructing the start tag.
    String element = "<" + elementName + " ";
    
    //Deal with elements that have no attributes.
    if((empty==false) && (data == null)){
      //Return a complete start tag.
      return element + ">";
    }else if((empty==true) && (data == null)){
      //Return a complete empty element.
      return element + "/>";
    }//end if

    for(int cnt=0;cnt<data.length;cnt+=2){

      String name = data[cnt];
      String value = data[cnt+1];
      element += name + "=" + "\"" + value + "\" ";
    }//end for loop
    
    if(empty){
      //Terminate the element appropriately for an
      // empty element. A complete empty element will
      // be returned.
      element += "/>";
    }else{
      //End the start tag for an element that is not
      // empty. In this case, only the start tag will
      // be returned.  The calling program must provide
      // the content for the element as well as the end
      // tag for the element.
      element += ">";
    }//end else
      
  return element;
  }//end makeElement
  //----------------------------------------------------//
  
  /*
  The purpose of this method is to create a general node
   having any name, and any number of attributes with any 
   attribute names and any String values for the 
   attributes, or no attributes at all.
   
  The first parameter is a reference to the document to
   which the new node belongs.
  
  The second parameter is a reference to the parent node
   to which this node is to be appended so as to become a
   child of that node. If this parameter is null, the new
   node is appended to the document.  Otherwise, it is
   appended to the specified parent node.
   
  The third parameter is a String that specifies the type
   of node.
  
  The fourth parameter to the method must be a reference
   to an array object of type String.  This array must 
   contain an even number of elements.  Each pair of 
   elements constitutes the name and the value of an 
   attribute, in the order name, value, name, value, etc.
  
  An example of the recommended usage of the method
   follows:
  Element abc = SvgGraphics.makeNode(
                     document,
                     def,//parent could be null
                     "ghi",//node type
                     new String[]{"name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value",
                                  "name","value"
                                 });//end call to makeNode
  */
  static Element makeNode(Document document,
                                Element parent,
                                String nodeType,
                                String[] data){
  
    Element element = 
                (Element)document.createElement(nodeType);
    
    if(parent == null){
      //For the special case of parent equal to null,
      // append the new node to the document.
      document.appendChild(element);
    }else{
      //Otherwise, append the new node to the specified
      // parent.
      parent.appendChild(element);
    }//end else
  
    //Deal with elements that have no attributes.
    if(data == null){
      return element;
    }//end if
    
    for(int cnt=0;cnt<data.length;cnt+=2){
      String name = data[cnt];
      String value = data[cnt+1];
      element.setAttribute(name,value);
    }//end for loop
    
    return element;
  }//end makeNode
  //----------------------------------------------------//
  
  //This is a utility method that is used to execute code
  // that is the same regardless of the graphic image
  // being produced.
  static Document getDocument(){
    Document document = null;
    try{
      DocumentBuilderFactory factory = 
                     DocumentBuilderFactory.newInstance();

      DocumentBuilder builder = 
                             factory.newDocumentBuilder();
      document = builder.newDocument();
    }catch(Exception e){
      e.printStackTrace(System.err);
      System.exit(0);
    }//end catch
    return document;
  }//end getDocument
  //----------------------------------------------------//
}//end class SvgGraphics&n/pre>
      

 


Copyright

Copyright 2007, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

Resources

Java 2D Graphics
300 Java 2D Graphics, Nested Top-Level Classes and Interfaces 
302 Java 2D Graphics, The Point2D Class 
304 Java 2D Graphics, The Graphics2D Class 
306 Java 2D Graphics, Simple Affine Transforms 
308 Java 2D Graphics, The Shape Interface, Part 1 
310 Java 2D Graphics, The Shape Interface, Part 2 
312 Java 2D Graphics, Solid Color Fill 
314 Java 2D Graphics, Gradient Color Fill 
316 Java 2D Graphics, Texture Fill 
318 Java 2D Graphics, The Stroke Interface 
320 Java 2D Graphics, The Composite Interface and Transparency 
322 Java 2D Graphics, The Composite Interface, GradientPaint, and Transparency 
324 Java 2D Graphics, The Color Constructors and Transparency
Java 2D API Specification
Java 2D API

Java API for XML Processing (JAXP)
2200 Java API for XML Processing (JAXP), Getting Started
2202 Getting Started with Java JAXP and XSL Transformations (XSLT)
2204 Java JAXP, Exposing a DOM Tree
2206 Java JAXP, Implementing Default XSLT Behavior in Java
2208 Java JAXP, Writing Java Code to Emulate an XSLT Transformation
2210 Java JAXP, Transforming XML to XHTML
Links to numerous XML tutorials by Richard G. Baldwin

Scalable Vector Graphics (SVG)
2212 Java JAXP, Creating graphics using Java and SVG
2214 An improved approach for creating SVG/XML code and SVG/XML DOM nodes using Java
Scalable Vector Graphics (SVG) 1.1 Specification
Adobe SVG Viewer plug-in
Create vector graphics in the browser with SVG by Uche Ogbuji
SVG Tutorial
SVG Basics

Miscellaneous
W3C Markup Validation Service.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP).  His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments.  (TI is still a world leader in DSP.)  In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel