JavaGraphics, using Java and JDOM with SVG, Part 2

Graphics, using Java and JDOM with SVG, Part 2

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Java Programming Notes # 2224


Preface

General

Second of two
parts

This lesson is the second part of two-part tutorial on using Java and JDOM to
create SVG files.

What you have learned

In Part 1, you learned a little about JDOM as an alternative to Sun’s JAXP
DOM API.  You learned how to create an SVG file using raw JDOM commands,
and you learned how to write a Java/JDOM/SVG graphics library to reduce the
effort required to create SVG files using JDOM and Java.

What you will learn

In Part 2, you will expand the JDOM/SVG graphics library to include gradients, Bézier
curves, and elliptical arcs.  You will learn the significance of the word
Scalable in Scalable Vector Graphics (SVG).  You will learn how to use JDOM to write
XHTML output files containing SVG/XML code, and you will learn how to use JDOM to
write Java servlets that deliver XHTML output containing SVG/XML code.

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 Svg17.
  • Figure 2. Graphic output from Svg18 and Svg19.
  • Figure 3. Drawing with a Bezier curve and a
    radial gradient.
  • Figure 4. Bit mapped image enlarged by a factor
    of four.
  • Figure 5. SVG drawing enlarged by a factor of
    four.
  • Figure 6. Svg drawing enlarged by a factor of
    sixteen.
  • Figure 7. Svg drawing enlarged by a factor of
    sixteen.
  • Figure 8. Bit mapped image enlarged by a factor
    of sixteen.

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

See the link to Part 1 of this tutorial in Resources
for background information on JDOM.  See numerous previous lessons in this
series for background information on SVG.

Preview

In this lesson, I will present and explain three programs named Svg17,
Svg18, and Svg19.

Svg17

Svg17 will teach you how to expand the Java/JDOM/SVG graphics library
from the earlier program named Svg16 to include the following new
methods:

  • makeGridString – used with makePath to draw graph paper
  • makePath – used for a variety of purposes including the drawing
    of Bézier curves and elliptical arcs.
  • makeNode – used to create a general node for which a specialized
    method hasn’t been written.
  • makeLinearGradient – used to define a linear gradient.
  • makeRadialGradient – used to define a radial gradient.
  • makeGradientStop – used to control the behavior of linear and
    radial gradients.
  • makeText – used to add text to an SVG drawing.

The graphic output from Svg17

The output SVG/XML code from Svg17 is written into an SVG file, which
can be properly rendered by loading the file into Firefox 2.0.0.4.  The graphic
output from Svg17 is shown in Figure 1.

Figure 1. Graphic output from Svg17.

Svg18

Svg18 will teach you how to use JDOM to write an output XHTML file
containing embedded SVG/XML code.  The file can be rendered in Firefox
2.0.0.4
to produce a scaled down version of the same graphic that is produced by the SVG
file from Svg17 with some additional HTML text inserted into the output.

Svg19

Svg19 will teach you how to convert the JDOM program named Svg18
into a JDOM servlet that will deliver XHTML output with embedded SVG/XML code. 
The servlet can be accessed by Firefox 2.0.0.4 to produce the same graphic that is
produced by Svg18.

The graphic output from Svg18 and Svg19

The output graphic produced by Svg18 and Svg19 is shown in
Figure 2.  Note the overall size of this graphic as compared to the overall
size of the graphic produced by Svg17 in Figure 1.  One of the
advantages of SVG (as compared to bit mapped graphics, for example) is
the ability to scale the size of the graphic up or down with no loss in the
quality of the graphic.

Figure 2. Graphic output from Svg18 and Svg19.

Also note the HTML text above and below the image in Figure 2, which is not
present in the output from Svg17 shown in Figure 1.  One of the advantages of
embedding the SVG code in an XHTML file instead of writing it into an SVG file is
the ability to place the graphic in the
midst of other HTML elements, such as the HTML text shown in Figure 2.

Discussion
and sample code

The program named
Svg17

Proper spelling of Bézier’s name.
Although Bézier is the proper spelling of Pierre
Bézier’s
name, the use of the special character as the
second character in the name makes it very difficult to compose
text using a standard computer keyboard.  Therefore, to make it
easier to compose the remainder of this tutorial, I will take
the liberty of spelling it Bezier.

Description of the program

The program named Svg16 began the development of a Java/JDOM/SVG
graphics library of my own design named JdomSvg.  The use of this
library can eliminate much of the effort involved in writing Java programs to
produce SVG files using JDOM as an alternative to Sun’s JAXP DOM API.  The
purpose of this program is to add the following methods to that graphics library
and to demonstrate their use.

  • makeGridString – used with makePath to draw graph paper
  • makePath – used for a variety of purposes including the drawing
    of Bezier curves and elliptical arcs.
  • makeNode – used to create a general node for which a specialized
    method hasn’t been written.
  • makeLinearGradient – used to define a linear gradient.
  • makeRadialGradient – used to define a radial gradient.
  • makeGradientStop – used to control the behavior of linear and
    radial gradients.
  • makeText – used to add text to an SVG drawing.

The program draws the following graphics elements on a background that looks
like green graph paper (see Figure 1) and writes the output into an SVG
file that can be rendered using an SVG graphics engine such a Firefox 2.0.0.4:

  • A rotated ellipse with a linear gradient that changes from yellow to red
    and back to yellow.
  • A rectangle with a radial gradient centered in the upper left corner of
    the rectangle.
  • A filled (blue) cubic Bezier curve with two Bezier segments
    inside a filled (yellow) polygon.
  • A filled (blue) quadratic Bezier curve with two Bezier segments
    inside a filled (yellow) polyline that is partially transparent.
  • A filled (red) elliptical arc with no rotation.
  • A filled (red) elliptical arc with a rotation of 45 degrees.

Program testing

The output file validates at: http://validator.w3.org/  
It was tested using J2SE 6.0, JDOM 1.0, and Firefox 2.0.0.4 running under WinXP.

Will discuss in fragments

As is my custom, I will present and explain this program in fragments. 
You can view the program in its entirety in Listing 30 near the end of the
lesson.

Some of the code in this lesson is the same as the code that I explained Part
1 of this tutorial (see Resources)
Usually I won’t repeat that explanation in this part of the tutorial.

The program and the main method begin in Listing 1.  The code in the
early portions of the main method is essentially the same as code that I
have explained in previous lessons.  Therefore, that code was deleted from
Listing 1 for brevity.


Controlling physical and virtual canvas dimensions

The expressions for computing the viewBoxWidth and viewBoxHeight
are useful for those cases where you need to make the height to width ratio of
the virtual canvas the same as the height to width ratio of the physical canvas.

Listing 1. Controlling physical and virtual canvas
dimensions.

public class Svg17{
  
  public static void main(String[] args){

    //Code deleted for brevity.

    //Physical dimensions of the graphic on the screen.
    int svgWidth = 460;
    int svgHeight = 552;
    
    //Virtual dimensions of the graphic in user units.
    int viewBoxWidth = 500;
    int viewBoxHeight = 500*svgHeight/svgWidth;

With this formulation, the virtual width will always be 500 regardless of the
physical width, and the virtual height will vary as the physical height varies. 
Note however, that sometimes you may not want to keep these ratios equal. 
For example, it is often desirable to expand the scale on the vertical axis when
plotting curves.


Create path data for the background graph paper

Listing 2 calls the makeGridString method to create the path data for
drawing the background graph paper in Figure 1.

Listing 2. Create path data for the background graph
paper.

    //Code deleted for brevity
  
    //Begin creating graphics elements.

    //Draw light green grid lines every 10 user units on
    // the basis of the virtual dimensions in user units.

    //First create the path data.
    String gridData = JdomSvg.makeGridString(
                           viewBoxWidth,viewBoxHeight,10);

There is nothing about the makeGridString method that is peculiar to
the use of JDOM.  The method is essentially the same as a method having the
same name that I explained in the earlier lesson titled "Drawing grids, Bézier
curves and elliptical arcs using Java and SVG" (see
Resources
)
.


Create the path element and add it to the root

Listing 3 calls the makePath method to create the JDOM element that
represents the background grid with the smallest squares in Figure 1.  (The
grid size of 10 virtual units was established by passing a parameter with a
value of 10 to the makeGridString method in Listing 2.)

When the makePath method returns a reference to the path element, the
code in Listing 3 calls the setAttribute method on the path to set the
stroke color to the very light green that you see in Figure 1.

Listing 3. Create the path element and add it to the root.

//In main.
    Element temp;
    temp = JdomSvg.makePath(svg,ns,gridData);
    //Set the color to a very light green.
    temp.setAttribute("stroke","#ccffcc");

Switching back and forth

From this point forward, I will be switching back and forth between
statements in the main method and methods in the graphics library. 
I will use the notation shown in the upper left corner of Listing 3 to indicate
those code fragments that are part of the main method.


The makePath method

The makePath method is shown in its entirety in Listing 4.

Listing 4. The makePath method.

  static Element makePath(Element parent,
                          String namespace,
                          String d){
    Element path  = new Element("path",namespace);
    parent.addContent(path);
    
    //Set default attribute values.
    path.setAttribute("stroke","black");
    path.setAttribute("stroke-width","1");
    path.setAttribute("fill","none");
    
    //Set user specified default values.
    path.setAttribute("d",d);
    return path;
  }//end makePath

The makePath method constructs and returns a reference to a path
element as a child of the specified parent node in the specified namespace. 
By default, the stroke is set to black one pixel wide, and the fill is set to
none.

(See the method named makeGridString for a utility method that
is designed to create the data string for this method for the special case
of drawing grids that resemble graph paper.  For other cases, simply
create a data string that is compatible with the SVG path element.)

As you will recall from the earlier lesson titled "Drawing grids, Bézier
curves and elliptical arcs using Java and SVG" (see
Resources
)
a variety of different paths including straight lines,
polylines, Bezier curves, elliptical arcs, etc., can be constructed depending on
the contents of the third incoming parameter of type String.

The makePath method is fairly typical of many of the methods in the
SVG graphic library.   It begins by creating the new path element and
attaching it as a child to the specified parent.  Then it calls the
setAttribute
method several times in succession to set the default values
for some of the attributes.  Then it calls the setAttribute method
again to set the user specified attribute values.  In this case, there is
only one user specified attribute, but some of the methods in the library have
several user specified attribute values.


Create the defs element

Listing 5 calls the new makeNode method to create a defs element. 
Recall from an earlier lesson that a defs element serves as a repository
for definitions in XML code that are referenced by other elements.  In this
program, the defs element is used as a repository for gradient
definitions.  Note for the purpose of a later discussion that Listing 5
passes null for the fourth parameter to the makeNode method.

Listing 5. Create the defs element.

//In main.
    //Using the same procedure, draw darker green grid
    // lines every 50 user units and draw them on top of
    // the existing grid lines.

    // Code deleted for brevity.

    Element defs = JdomSvg.makeNode(svg,//parent
                                    ns,//namespace
                                    "defs",
                                    null//no attributes
                                    );


The makeNode method

The graphics library contains a number of specialized methods that are
designed to construct and return specific kinds of elements such as makeLine,
makeCircle, makeEllipse, etc.

In addition, the library contains the makeNode method, the purpose of
which 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 makeNode method is shown in its entirety in Listing 6.

Listing 6. The makeNode method.

  static Element makeNode(Element parent,
                          String namespace,
                          String nodeType,
                          String[] data){

    Element element = new Element(nodeType,namespace);
    parent.addContent(element);
  
    //Deal with elements that have no attributes.
    if(data == null){
      return element;
    }//end if
    
    //Extract the values from the array and construct
    // each of the attributes and its value.
    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

The first parameter is a reference to the parent node to which this node is
to be attached so as to become a child of that node.  The second parameter
is a String that specifies the namespace for the element represented by
the new node.  The third parameter is a String that specifies the
type of the new node.  (Looking back at Listing 5, you can see that for
the case at hand, the type of the new node is "defs.")

The fourth parameter must be a reference to an array object of type
String[]
.  This array must contain an even number of elements. 
Each pair of elements specifies the name and the value of one attribute, in the
order name, value, name, value, etc.  If there are no attributes, the value
of this parameter should be null.  (Once again, looking back at Listing
5, the value of the fourth parameter is null for the case at hand.)

The method begins by constructing the new element of the specified type in
the specified namespace and attaching it as a child to the specified parent.

Then the method tests to determine if the value of the fourth parameter is
null.  If so, it simply returns the element that was just constructed.

If the value of the fourth parameter is not null, the method extracts the
String
values from the referenced array object in pairs, using the two
values as parameters in a call to the setAttribute method to set the
value of the specified attribute to the specified value.

The makeNode method is intended to be used primarily to construct
elements for which there is no corresponding specialized method in the library. 
However, it can be used to construct any of the SVG elements.


Create a linear gradient element

Listing 7 calls the new makeLinearGradient method to create a linear
gradient element that is a child of the defs element and identified for
later reference purposes as gradientA.

Listing 7. Create a linear gradient element.

//In main.
    Element gradientA = JdomSvg.makeLinearGradient(
                                         defs,//parent
                                         ns,//namespace
                                         "gradientA");//id

Although it won’t be determined until later in the program, this gradient
starts with yellow, changes to red, and then changes back to yellow going from
left to right.  This is the gradient that was used to fill the ellipse in
the upper left of Figure 1.


The makeLinearGradient method

The makeLinearGradient method is shown in it entirety in Listing 8.

Listing 8. The makeLinearGradient method.

  static Element makeLinearGradient(Element parent,
                                    String namespace,
                                    String id){
    Element gradient = 
                  new Element("linearGradient",namespace);
    parent.addContent(gradient);
    gradient.setAttribute("id",id);
    return gradient;
  }//End makeLinearGradient

This method creates a linear gradient node to which stop elements must
be appended to specify the behavior of the gradient.  There are no default
attribute values and only one user specified attribute named id
The id attribute is used to differentiate this gradient node from other gradient
nodes in the same program scope.  The id provides a handle by which
other elements can reference this gradient.


Create the first of three stop nodes

The code in Listing 9 calls the makeGradientStop method to create the
first of three stop nodes that identify the colors used to produce gradientA
and specify where the colors begin and end

Listing 9. Create the first of three stop nodes.

//In main. 
    JdomSvg.makeGradientStop(gradientA,//parent
                             ns,
                             "4%",//start here
                             "yellow");//color


The makeGradientStop method

The makeGradientStop method is shown in its entirety in Listing 10.

Listing 10. The makeGradientStop method.

  static Element makeGradientStop(Element parent,
                                  String namespace,
                                  String offset,
                                  String color){
    Element stopElement = new Element("stop",namespace);
    parent.addContent(stopElement);
    
    stopElement.setAttribute("offset",offset);
    stopElement.setAttribute("stop-color",color);
    return stopElement;
  }//End makeGradientStop

The makeGradientStop method creates a gradient stop node in a
specified namespace and attaches it as a child to a specified parent. 
(The node must be attached as a child to a linear gradient node or a radial
gradient node to be useful.)
  There are two user specified parameters
in Listing 10 that specify the color and specify where the color changes in the
shape being filled with the gradient.

Could have called makeEllipse.
Note that I could have called the makeEllipse method, but I wanted to
demonstrate the use of the more general makeNode method for a graphics element
that has several attributes.


Call the makeNode method to draw an ellipse

Listing 11 calls the makeNode method to draw an ellipse.  The
ellipse is filled with linear gradientA and rotated by 30 degrees. 
This is the ellipse that appears in the upper left of Figure 1.  Note the
boldface syntax used to specify a gradient as the value for the fill
attribute.

Listing 11. Call the makeNode method to draw an
ellipse.

// In main.
    //Code to create two more stop nodes deleted for
    // brevity.

    JdomSvg.makeNode(
            svg,//parent
            ns,
            "ellipse",//node type
            new String[]{"cx","150",
                         "cy","80",
                         "rx","100",
                         "ry","40",
                         "fill","url(#gradientA)",
                         "stroke","blue",
                         "stroke-width","3",
                         "opacity","0.5",
                         "transform","translate(150,80) "
                                   + "rotate(-30) "
                                   + "translate(-150,-80)"
                        }//end array definition
    );//end makeNode method

A comment regarding opacity and gradients

Setting the opacity attribute value to 0.6 in Listing 11 is of no
consequence when the output file is rendered using Firefox 1.5 as shown in
Figure 1.  In
that case, the gradient continues to be totally opaque.  However, this is a
weakness in the Firefox 1.5 rendering engine and is not a limitation of SVG. 
When the file is rendered using an Adobe rendering engine, the gradient is
partially transparent as it should be for an opacity attribute value of
0.6. 

(This problem has been fixed in Firefox 2.0.0.4.  If you run this
program and render the resulting output file in Firefox 2.0.0.4, the grid on the
graph paper will show through the gradient fill inside the ellipse.)


Define a radial gradient and save it as a child of defs

Listing 12 calls the makeRadialGradient method to define a gradient
identified as gradientB.  This gradient element provides a radial
gradient that goes through yellow, red, green, and blue. It is used to fill the
rectangle in the upper right of Figure 1.

Listing 12. Define a radial gradient and save it as
a child of defs.

//In main.
    int rectCornerX = 300;
    int rectCornerY = 10;
    int rectWidth = 150;
    int rectHeight = 150;

    Element gradientB = JdomSvg.makeRadialGradient(
                               defs,//parent
                               ns,//namespace
                               "gradientB",//ID
                               "userSpaceOnUse",
                               rectCornerX,//cx
                               rectCornerY,//cy
                               ((int)(rectWidth*1.2)));//r


The makeRadialGradient method

The makeRadialGradient method is shown in its entirety in Listing 13.

Listing 13. The makeRadialGradient method.

  static Element makeRadialGradient(Element parent,
                                    String namespace,
                                    String id,
                                    String gradientUnits,
                                    int cx,//center
                                    int cy,//center
                                    int r  //radius
                                    ){
    Element gradient = 
                  new Element("radialGradient",namespace);
    parent.addContent(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

With the exception of the fourth parameter identified as gradientUnits,
the code in Listing 13 is straightforward and shouldn’t require further
explanation.

I recommend that you consult the SVG documentation (see
Resources)
for more information on the various
attributes of the radial gradient element.


Draw a cubic Bezier curve

Listing 14 calls the makePath method to draw the cubic Bezier curve
shown at the middle left in Figure 1.

Listing 14. Draw a cubic Bezier curve.

//In main.

    //Code for four calls to the makeGradientStop
    // method deleted for brevity.

    //Draw the rectangle and fill it with gradientB.
    //Call to the makeRect method and the setAttribute
    // method deleted for brevity.

    //Draw a cubic Bezier curve consisting of two Bezier
    // segments.  First draw a polygon that shows the
    // start points, the end points, and the control
    // points for the segments. Fill the polygon with
    // yellow to provide a background color for the 
    // Bezier curve.
    //Call to the makePolygon method deleted for brevity.
    
    //Draw the cubic curve. Note the use of absolute
    // coordinate values only and also the use of the S
    // command. Stroke the curve with red and fill it with
    // blue.
    temp = JdomSvg.makePath(svg,ns,"M180,290 C50,250,"
                    + "150,200,150,300 S250,350,120,310");
    temp.setAttribute("stroke","red");
    temp.setAttribute("fill","blue");

Recall from the earlier lesson titled "Drawing grids, Bézier curves and
elliptical arcs using Java and SVG" (see Resources)
that a Bezier curve (either cubic or quadratic) is drawn by drawing a
path
with the correct value for the String parameter named d
(see Listing 4).  The string value that produced the cubic Bezier
curve at the middle left in Figure 1 is shown highlighted in boldface near the
bottom of Listing 14.


Draw an elliptical arc with no rotation

Listing 15 calls the makePath method to draw a red elliptical arc with
no rotation shown at the bottom left in Figure 1.

Listing 15. Draw an elliptical arc with no rotation.

//In main.
    //Draw a quadratic Bezier curve consisting of two
    // Bezier segments.  First draw a polyline that shows
    // the start points, the end points, and the control
    // points.  Fill the polyline with yellow color with
    // an opacity attribute value of 0.5.  This allows
    // the grid to show through the filled polyline.
    //Call to the makePolyline method deleted for brevity.
    //Call to the makePath method to draw the quadratic
    // Bezier curve deleted for brevity.

    //Draw an elliptical arc to illustrate the appearance
    // of such an arc with no rotation. Stroke it with
    // yellow and fill it with red.
    temp = JdomSvg.makePath(svg,ns,
                        "M100,500 a70,30 0 1,0 50,-50 z");
    temp.setAttribute("stroke","yellow");
    temp.setAttribute("stroke-width","2");
    temp.setAttribute("fill","red");

Recall from the earlier lesson titled "Drawing grids, Bézier curves and
elliptical arcs using Java and SVG" (see Resources)
that an elliptical arc is drawn by drawing a path with the correct value
for the String parameter named d (see Listing 4).  The
string value that produced the elliptical arc at the bottom left in Figure 1 is
shown highlighted in boldface near the bottom of Listing 15.


Label the vertical lines of the grid

Listing 16 calls the new makeText method several times in succession
(inside a for loop) to create and position the text labels at the
bottom of the vertical lines in Figure 1.

Listing 16. Label the vertical lines of the grid.

    //Draw an elliptical arc to illustrate the appearance
    // of such an arc with a rotation of 45 degrees.
    // Stroke it with yellow and fill it with red.
    //Call to makePath to draw the elliptical arc at 
    // the bottom right in Figure 1 deleted for brevity.


    for(int cnt = 0;cnt < viewBoxWidth;cnt += 50){
      JdomSvg.makeText(
                     svg,ns,cnt,viewBoxHeight - 4,""+cnt);
    }//end for loop


The makeText method

The makeText method is shown in its entirety in Listing 17.

Listing 17. The makeText method.

  static Element makeText(Element parent,
                          String namespace,
                          int x,
                          int y,
                          String text){
    Element textNode = new Element("text",namespace);
    parent.addContent(textNode);

    textNode.setAttribute("x",""+x);
    textNode.setAttribute("y",""+y);
    
    textNode.addContent(text);

    return textNode;
  }//end makeText

The code in Listing 17 is reasonably straightforward.  The only thing
that may be unusual is the interpretation of the values for x and y relative to
the location of the text.  It appears from experiments that the bottom left
corner of the first character in the text string is located at coordinates (x,y)
when the text is drawn.


Label the graphics elements

Listing 18 calls the makeText method to place the label underneath the
ellipse in the upper left corner of Figure 1.  The code to label the
remaining five graphics elements was deleted from Listing 18 for brevity.

Listing 18. Label the graphics elements.

//In main.
    //Now label the horizontal lines.    
    //Code to label the horizontal lines deleted for
    // brevity.

    
    //Label each of the graphics elements
    JdomSvg.makeText(svg,ns,50,180,
                          "Ellipse with linear gradient");

    //Code to draw labels under each of the remaining five
    // graphics elements deleted for brevity.


Write the output file

Listing 19 calls the writePrettyFile method to write the SVG/XML code
into the output file named Svg17.svg.

Listing 19. Write the output file.

    //Write the output file.
    JdomSvg.writePrettyFile("Svg17.svg",doc);
    //JdomSvg.writeCompactFile("Svg17.svg",doc);
    
  }//end main

The writePrettyFile method was explained in Part 1 of this tutorial
(see Resources)
.

The end of the program

Listing 19 also signals the end of the main method and the end of the
explanation of the program named Svg17.

The program named Svg18

Description of the program

The intended primary purpose of this program was to use JDOM to embed SVG
code into what would otherwise be a valid XHTML file.

A secondary purpose of this program, in comparison with the earlier program
named Svg017, was to demonstrate the significance of the use of the word
Scalable in the name Scalable Vector Graphics (SVG).

The secondary purpose was achieved.  However, for the reasons explained
below, the primary purpose was not completely achieved, because the output XHTML
file is not valid.

Embedding graphics between HTML elements

One of the advantages of embedding SVG code in an XHTML document is that it
then becomes possible to place the graphic in and among other HTML elements. 
That is not possible when creating an SVG file.  That capability is
demonstrated by the program output shown in Figure 2.  Figure 2 shows HTML
text inserted above and below the graphic image that was created by rendering
the SVG code that was embedded in the XHTML code.

Not a valid XHTML file

My attempt to create a valid XHTML file using JDOM was not successful because
the JDOM Element constructor insists on creating an xmlns
attribute for each child of the root html node.  This is true
regardless of the name of the child node, and even when the constructor version
is used that doesn’t take a namespace as a parameter.  Regardless of
whether that version is used, or the version that takes a namespace as a
parameter is used by passing null for the namespace, the resulting XML code
contains the following attribute declaration in all child nodes of the root
html
node:

xmlns=""

The DTD for XHTML doesn’t allow for attributes named xmlns on the
head
and body elements.  (Note that the xmlns attribute
is created even if I change the name of the child node from head to joe.)
 
This may be a bug in the version of the Element
constructor that doesn’t take a namespace parameter.  The behavior of that
version seems to be the same as passing null for the namespace to the version
that does take a namespace parameter.  Therefore, the output file produced
by this program is not a valid XHTML file.  (Apparently there is
something about this that I don’t understand.) 
Nonetheless, the output
file renders very
successfully in Firefox 2.0.0.4.

The Java/JDOM/SVG graphics library

The program named Svg16 began the development of a Java/JDOM/SVG
graphics library of my own design named JdomSvg.  The use of this
library eliminates much of
the effort involved in writing Java programs to produce SVG files using JDOM as
an alternative to the standard Sun JAXP DOM API.

Several new methods were added to the library in the program named Svg017.  Those
methods were all associated with the creation of SVG/XML code.  I added the
following method to the graphics library in this program:

makeXhtmlRoot

This method is used to create an ordinary XML root node that doesn’t necessarily have
anything to do with graphics, but rather is required for the creation of an
XHTML file.

Create some text content

This program begins by placing the following text content in a paragraph
element at the beginning of the body element:

There is a graphic below here.

That text appears above the image in Figure 2.

Create some graphic content

Then the program draws the following graphics elements on a background that
looks like graph paper (going from left to right, top to bottom in Figure 2).

  • A rotated ellipse with a linear gradient.
  • A rectangle with a radial gradient.
  • A filled cubic Bezier curve with two Bezier segments inside a filled
    polygon.
  • A filled quadratic Bezier curve with two Bezier segments inside a filled
    polyline that is partially transparent.
  • A filled elliptical arc with no rotation.
  • A filled elliptical arc with a rotation of 45 degrees.

Some more text content

After drawing the graphic elements, the program places the following text
content in a paragraph element below the graphic elements at the end of the body
element.

There is a graphic above here.

Finally, the program writes the output into an XHTML file named
Svg18.xhtml
that can be rendered using an SVG graphics engine such a Firefox
2.0.0.4.

The significance of Scalable in Scalable
Vector Graphics

You will notice that the output from Svg18 shown in Figure 2 is a
scaled down version of the output from Svg17 shown in Figure 1, with HTML
text inserted before and after the graphic image.  The fact
that the SVG graphic image can be scaled with no loss in quality illustrates the
significance of the word Scalable in the name Scalable Vector Graphics
(SVG)
.

At this point, I am going to take a short side trip and show you some other
examples that illustrate the significance of the word Scalable,
particularly in contrast with bit mapped graphics.

Figure 3 shows a small graphic image containing a blue cubic Bezier curve and
in a yellow background and a radial gradient in a rectangle.  This drawing
was produced using a program similar to Svg17.

Figure 3. Drawing with a Bezier curve and a radial
gradient.

Enlarge the bit mapped image

I captured the image shown in Figure 3 as a bit mapped JPEG image using a standard screen
capture program.  Then I enlarged the bit mapped image by a factor of four,
producing the result shown in Figure 4.

Figure 4. Bit mapped image enlarged by a factor of
four.

As you would expect (if you are familiar with the limitations of bit
mapped images)
the results aren’t very pleasing.  To make a long story
short, bit mapped images aren’t very scalable, at least insofar as enlargement
is concerned.

Enlarge the SVG drawing by a factor of four

Figure 5 shows the result of increasing the size of the SVG drawing by the
same factor of four.  This result shows the significance of the word
Scalable
in the name Scalable Vector Graphics (SVG).

Figure 5. SVG drawing enlarged by a factor of four.

Import degradation.
There is a small amount of degradation showing in Figure 5.  That
degradation was introduced by the process of capturing the image from the screen
as a JPEG image and importing it into this document.  That degradation did
not exist in the original rendered version of the drawing in the Firefox
2.0.0.4
browser window.

The quality of the scaled image in Figure 5 is excellent.  No
degradation in image quality was introduced by enlarging the SVG drawing.

Enlarge the SVG drawing by a factor of sixteen

To drive home the point that scaling the SVG drawing does not degrade the
quality of the drawing, Figure 6 and Figure 7 show captured portions of the
drawing produced by enlarging the original drawing shown in Figure 3 by a factor of sixteen.

Figure 6. Svg drawing enlarged by a factor of
sixteen.

Note that because I am limited as to the width of images that I can publish
on this web site, I was unable to publish the entire enlarged drawing. 
Therefore, it was necessary for me to capture and publish portions of the
drawing that was enlarged by a factor of sixteen.

Figure 7. Svg drawing enlarged by a factor of
sixteen.

Once again, the small amount of degradation that you can see in Figure 6 and
Figure 7 did not exist in the original rendering of the drawing in the Firefox
2.0.0.4 browser window.  That degradation was introduced by the process of
capturing the image from the screen as a JPEG image and importing it into this
document.

Could continue to enlarge indefinitely

I could continue this process of enlarging the SVG drawing indefinitely and
no degradation would be introduced into the drawing.  That is the true
significance of the word Scalable in the name Scalable Vector Graphics
(SVG)
.

What about the bit mapped image?

To drive home the point that bit mapped images are not scalable, Figure 8 shows the result of enlarging the original bit mapped image
from Figure 3 by a factor of sixteen and capturing a portion of the enlarged
image for publication in this lesson.

Figure 8. Bit mapped image enlarged by a factor of
sixteen.

The image shown in Figure 8 is approximately the same portion of the enlarged
image as that shown in Figure 6.  However, because of the poor quality of
the enlarged bit mapped image, it was not possible for me to identify and capture the exact same
portion.

As you can see, bit mapped images are not scalable in the same way that SVG
drawings are scalable.

Program Testing

The program was tested using J2SE 6.0, JDOM 1.0, and Firefox 2.0.0.4 running
under WinXP.

Will discuss in fragments

I will present and explain this program in fragments.  You can view the
program in its entirety in Listing 31 near the end of the lesson.  Much of
the code in this program is the same as, or very similar to code that I have
explained in this or other lessons.  I will skip over that code and won’t
repeat those explanations here.


The namespace URI

The program class and the main method both begin in Listing 20. 
This class begins pretty much like the class for the programs named Svg17
(see the listing for Svg17 in Listing 30 near the end of the lesson).
Both programs begin by creating a String variable that contains a
namespace URI.

Listing 20. The namespace URI.

public class Svg18{
  
  public static void main(String[] args){
    String xns = "http://www.w3.org/1999/xhtml";

There is one major difference, however.  The namespace URI for the
program named Svg17 is as follows:

http://www.w3.org/2000/svg

This is different from the namespace URI shown in Listing 20 above.  The
difference is that the namespace for the program named Svg17 is
appropriate for an SVG file, but is not correct for an XHTML file.  The
namespace URI shown in Listing 20 is the correct namespace for an XHTML file. 
(In fact, the SVG namespace URI
will also show up later in this
program, because this program produces an XHTML file that contains SVG code.)


Specify DTD information

The program named Svg18 continues in Listing 21 by specifying the name of
the element that is constrained by the DTD (the html root element), the Public ID of the DTD, and the System ID of the DTD.

Listing 21. Specify DTD information.

    //For clarity, create strings containing the name of
    // the element that is constrained by the DTD (the
    // html root element), the Public ID of the DTD, and 
    // the System ID of the DTD.
    String dtdConstrainedElement = "html";
    String dtdPublicID = 
                 "-//W3C//DTD XHTML 1.0 Transitional//EN";
    String dtdSystemID = "http://www.w3.org/TR/xhtml1/"
                          + "DTD/xhtml1-transitional.dtd";

Once again, the approach is the same, but the three values are different from
the corresponding values for the SVG file that was created in Listing 30. 
The root node for an SVG file is named svg, whereas the root node for an
XHTML file is named html.  Similarly, the DTD information for an
XHTML file is different from the DTD information for an SVG file.


Create the Document node

Following this, the code in Listing 22:

  • Creates the DocType node based on the correct DTD information for
    an XHTML file.
  • Creates the root node named html, which is the required name for
    the root node in an XHTML document.
  • Creates the Document node on the basis of the root node and the
    DocType node that are correct for an XHTML document.

Listing 22. Create the Document node.

    //Create the DTD declaration node.
    DocType docType = new DocType(
           dtdConstrainedElement,dtdPublicID,dtdSystemID);

    //Create the XHTML root node named html.
    Element html = JdomSvg.makeXhtmlRoot(xns);
    
    //Create the document node.
    Document doc = new Document(html,docType);

Note that Listing 22 calls the new makeXhtmlRoot method to create the
XHTML root node named html.  This method is very similar to the
method named makeSvg, so it shouldn’t require any explanation beyond the
embedded comments.

Create optional and required elements

The root node for an XHTML document must be named html.  The
html
element must have a child element named body.  The html
element may also have a child element named head, which in turn may have
a child element named title(I believe, but am not certain,
that these latter two elements are optional.)

Listing 23 calls the makeNode method that I explained earlier in this
lesson to create these head, title, and body nodes.

Listing 23. Create optional and required elements.

    Element head = JdomSvg.makeNode(html,xns,"head",null);
    Element title = JdomSvg.makeNode(
                                   head,xns,"title",null);
    title.setText("XHTML/SVG Graphic Demo.");
                         
    Element body = JdomSvg.makeNode(html,xns,"body",null);

Listing 23 also calls the setText method to populate the title
element with text content that describes the program.


Create a paragraph node

Listing 24 calls the makeNode method to create a paragraph (p)
node and attach it to the body node.

Listing 24. Create a paragraph node.

    Element temp = JdomSvg.makeNode(body,xns,"p",null);
    temp.setText("There is a graphic below here.");

Then Listing 24 calls the setText method to add text content to the
p
node.  This is the text that appears above the graphic in Figure 2.

Switch your thinking into graphics mode

Now you need to switch your thinking into graphics mode.  At this point,
the program creates a complete SVG graphic sub-tree with a root named svg
and attaches it as a child of the body node.  In the end, the
body
node will have three child nodes.  One of them is the p
node that was created and populated in Listing 24.  A second one is another
p node that will be attached to the body node later, and
represents the HTML text that is visible below the graphic in Figure 2. 
The third child is an svg node, which forms the root of a complete SVG
graphics sub-tree.


Specify the SVG namespace

The process of attaching an SVG sub-tree as a child of the body node
begins in Listing 25.  This code should look familiar.  The single
statement in Listing 25 creates a String variable containing the SVG
namespace URI.  (I told you
earlier
that this was going to happen.)

Listing 25. Specify the SVG namespace.

    String ns = "http://www.w3.org/2000/svg";

The significance of the word Scalable in
Scalable Vector Graphics (SVG)

This program creates and displays exactly the same graphics elements that
were created and displayed by the earlier program named Svg17
However, the physical size of the canvas in this program was reduced to
75-percent of its size in the earlier program for the purpose of demonstrating
the significance of the word Scalable in the name Scalable Vector
Graphics (SVG)

The code that accomplishes the scaling is shown in Listing 26, where a scale
factor of 0.75 was applied to the canvas dimensions used in the earlier program
named Svg17.

With SVG, just about any scale factor can be applied to change the physical
dimensions of the canvas.  As long as the pixel granularity of the display
screen is not a factor, changing the size of the physical dimensions of the
canvas, and hence the physical size of the graphic images being drawn, will not
degrade the graphic quality of those images.

Listing 26. Scaling the drawing.

    //Physical dimensions of the graphic on the screen.
    int svgWidth = (int)(460*0.75);
    int svgHeight = (int)(552*0.75);

    //Virtual dimensions of the graphic in user units.
    int viewBoxWidth = 500;
    int viewBoxHeight = 500*svgHeight/svgWidth;

Listing 26 also establishes the virtual dimensions of the canvas through the
use of the viewBox attribute.  It is important to note that because
of the use of virtual dimensions implemented through the use of the viewBox,
it was not necessary to modify any of the SVG graphics code to accommodate the
reduction in the overall physical size of the graphics elements.


Create the svg root and attach as a child to body

Listing 27 calls the makeSvg method to create the node named svg
This node forms the root of a complete SVG graphics sub-tree.  Then Listing
27 attaches that root node as a child of the body node, causing it to
become the second of three child nodes belonging to the body node.

Listing 27. Create the svg root and attach as a
child to body.

    //Create the SVG root element named svg.
    Element svg = JdomSvg.makeSvg(ns,
                                  svgWidth,
                                  svgHeight,
                                  viewBoxWidth,
                                  viewBoxHeight);

    body.addContent(svg);

Write the SVG graphics code

At this point, the program begins creating actual SVG graphics elements. 
With the exception of the changes in the physical dimensions of the graphic on
the screen as implemented above, the graphics code in this program is the same
as the code in the earlier program named Svg017.  Therefore, I will
skip to the end of the graphics code.


Create the second paragraph node

Having skipped all of the graphics code that draws the graphic image in
Figure 2, Listing 28 creates a second p node and attaches it as a child
of the body node.  If you were to examine the XHTML code produced by
this program, you would see that the first p node occurs at the beginning
of the body node.  This is followed by the svg node, which is
the root for a complete SVG graphics sub-tree.  The svg node is
followed in turn by the p node that is created and attached to the
body
node in Listing 28.

Listing 28. Create the second paragraph node.

    //Create a paragraph element containing text content
    // and place it at the end of the body element.
    temp = JdomSvg.makeNode(body,xns,"p",null);
    temp.setText("There is a graphic above here.");

The result is that the text from the first p node is displayed above
the graphic image in Figure 2, and the second p node is displayed below
the graphic image in Figure 2.


Write the output file

Listing 28 calls the writePrettyFile method to write the raw SVG/XML
output to a file named Svg18.xhtml.  I explained the writePrettyFile
method earlier, so no further explanation should be required.

Listing 29. Write the output file named Svg18.xhtml.

     //Write the output file. Note that this is an XHTML
     // file and not an SVG file.
    JdomSvg.writePrettyFile("Svg18.xhtml",doc);
    //JdomSvg.writeCompactFile("Svg18.svg",doc);
    
  }//end main

}//end class Svg18

The end of the program.

Listing 28 signals the end of the main method, the end of the class
named Svg18, and the end of my explanation of the program named Svg18.

The program named Svg19

Description of the program

The program named Svg18 was a stand alone desktop program that used
JDOM to produce an output XHTML file with embedded SVG/XML code.  That SVG
code can be rendered by loading the file into an SVG graphics engine such as
Firefox 2.0.0.4.

The purpose of this program is to convert Svg18 into a servlet that
will deliver XHTML code with the same embedded SVG/XML code to a client browser. 
The SVG/XML code should be properly rendered when the servlet is accessed using
an SVG capable browser such as Firefox 2.0.0.4.

Steps for making the conversion

In this case, I will not discuss the program in fragments.  Rather, I
will simply present the program in its entirety in Listing 32 and explain the
steps that were required to

  • Prepare the server for execution
    of the JDOM servlet.
  • Convert the desktop program named Svg18 into
    the servlet program named Svg19

The following steps were required:

  1. Copy the jdom.jar file into the proper location on the server. 
    On my jakarta-tomcat server running as a localhost server, the proper
    location was:


    C:jakarta-tomcat-5.0.27webappsROOTWEB-INFlib

    This assumes that the servlet class files
    are copied into:

    C:jakarta-tomcat-5.0.27webappsROOTWEB-INFclasses
     
  2. Declare the following import directives in Svg19:

    import javax.servlet.*;
    import javax.servlet.http.*;
     
  3. Declare that the class named Svg19 extends the HttpServlet
    class.
     
  4. Replace the signature for the main method in Svg18 with the following
    signature for the doGet method in Svg19:

    public void doGet(HttpServletRequest req,
              HttpServletResponse res)
              throws ServletException,IOException{
     
  5. Execute the following statement to set the content type delivered by the
    servlet.

    res.setContentType("image/svg+xml");
     
  6. In order to cause the servlet to send the output to the client browser
    instead of writing it to a file, I defined the following new methods and placed
    them in the JdomSvg graphics library class:

    writePrettyPrintWriter(PrintWriter out,Document doc)
    writeCompactPrintWriter(PrintWriter out,Document doc)
     
  7. Use the following expression to get an output stream to be used by the
    XMLOutputter object to send the output data to the client browser:

    res.getWriter()

    Depending on whether I wanted a pretty-print format or a compact format, I
    modified an output statement from the main method of Svg18 to
    match one of the following statements:

    JdomSvg.writePrettyPrintWriter(res.getWriter(),doc);
    JdomSvg.writeCompactPrintWriter(res.getWriter(),doc);

It was not necessary or me to make any other changes to the program named Svg18
to convert it into the servlet program.  (See the program named Svg18
for additional comments regarding the program.)

Program testing

Having deployed the servlet on my localhost server, I can execute the
servlet by starting the server running and accessing the servlet using
Firefox 2.0.0.4 at:

http://localhost/servlet/Svg19

The program was tested using J2SE 6.0, JDOM 1.0, Firefox 2.0.0.4, and
jakarta-tomcat-5.0.27 running as a localhost server under WinXP.

That concludes the discussion of the program named Svg19.

Run the programs


I encourage you to download and install the JDOM API.  Then copy the
code from Listing 30 through Listing 32 into your text editor.  Compile the code
and execute it.  Deploy Svg19 on a server, being careful to make
certain that the server is compatible with JDOM.  View the output files
in an SVG graphics engine such as Firefox 2.0.0.4.  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 if you approach it with a positive attitude.

Summary

In Part 2 of this two-part tutorial, you learned how to expand the JDOM/SVG
graphics library to include gradients, Bézier curves, and elliptical arcs.  You
learned about the significance of the word Scalable in Scalable Vector
Graphics (SVG).  You learned how to use JDOM to write XHTML output files
containing SVG/XML code.  Finally, you learned how to use JDOM to write Java
servlets that deliver XHTML output containing SVG/XML code.

What’s next?

Future lessons in this series will teach you how to write SVG programs that
deal with the following SVG topics:

  • The text element.
  • Bit-mapped images in SVG.
  • How to create and re-use graphics elements.
  • How to use SVG symbols.
  • Stroke caps in SVG in comparison with similar caps in Java 2D.
  • The switch element in SVG.
  • Other features of SVG, such as animation.

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
2216 Using
Java to produce SVG code in XHTML data
2218 Writing
Java servlets to produce XHTML code that references external SVG files
2220 Drawing grids, Bézier curves and elliptical arcs using Java and SVG
2222 Graphics, using Java and JDOM with SVG, Part 1
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
SVG in HTML
pages

Bézier Curves
What’s a
Bézier Curve?

Wikipedia, Bézier curve
Bézier Curve
Demo

Miscellaneous
W3C Markup Validation
Service

XMLvalidation.com
Reflection in
a Line

An Intuitive
Notion of Line Reflections

JDOM
JDOM Main Page
Simplify
XML programming with JDOM

Interactive Java &
JDOM Online Tutorial

Easy
Java/XML integration with JDOM

Wikipedia, JDOM
Chapter
14
(JDOM) of Elliotte Rusty Harold’s book, Processing XML with Java

Chapter 15
(The JDOM Model) of Elliotte Rusty Harold’s book, Processing
XML with Java
.

Complete program listings


Complete listings of the programs discussed in this lesson are shown in Listing
30 through Listing 32 below.

Listing 30. Program code for Svg17.

/*File Svg17.java,
Copyright 2007, R.G.Baldwin

The program named Svg16 began the development of a
Java/JDOM/SVG graphics library of my own design named
JdomSvg, which eliminates much of the effort involved in 
writing Java programs to produce SVG files using JDOM as 
an alternative to the standard Sun DOM classes and 
methods.

The purpose of this program is add the following methods
to that graphics library and to demonstrate their use.

makeGridString
makePath
makeNode
makeLinearGradient
makeRadialGradient
makeGradientStop
makeText

The program draws the following graphics elements on a 
background that looks like graph paper. and writes the 
output into an SVG file that can be rendered using an SVG 
graphics engine such a Firefox 2.0.0.4:

* A rotated ellipse with a linear gradient.
* A rectangle with a radial gradient.
* A filled cubic Bezier curve with two Bezier segments
  inside a filled polygon.
* A filled quadratic Bezier curve with two Bezier segments
  inside a filled polyline that is partially transparent.
* A filled elliptical arc with no rotation.
* A filled elliptical arc with a rotation of 45 degrees.

The output file validates at: 

http://validator.w3.org/

Tested using J2SE 6.0, JDOM 1.0, and Firefox 2.0.0.4
running under WinXP.
*********************************************************/
import java.io.*;
import org.jdom.*;
import org.jdom.output.XMLOutputter;
import org.jdom.output.Format;
public class Svg17{
  
  public static void main(String[] args){
    //Create a String variable containing the namespace
    // URI to reduce the amount of typing that is required
    // later. Note that the variable name is short and
    // easy to type.
    String ns = "http://www.w3.org/2000/svg";
    
    //For clarity, create strings containing the name of
    // the element that is constrained by the DTD (the
    // root element), the Public ID of the DTD, and the
    // System ID of the DTD.
    String dtdConstrainedElement = "svg";
    String dtdPublicID = "-//W3C//DTD SVG 1.1//EN";
    String dtdSystemID = 
       "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";

    //Physical dimensions of the graphic on the screen.
    int svgWidth = 460;
    int svgHeight = 552;
    
    //Virtual dimensions of the graphic in user units.
    int viewBoxWidth = 500;
    int viewBoxHeight = 500*svgHeight/svgWidth;

    //Create the SVG root element named svg.
    Element svg = JdomSvg.makeSvg(ns,
                                  svgWidth,
                                  svgHeight,
                                  viewBoxWidth,
                                  viewBoxHeight);

    //Create the DTD declaration node.
    DocType docType = new DocType(
           dtdConstrainedElement,dtdPublicID,dtdSystemID);

    //Create the document node.
    Document doc = new Document(svg,docType);
    
    
    //Begin creating graphics elements.

    //Draw light green grid lines every 10 user units on
    // the basis of the virtual dimensions in user units..
    //First create the path data.
    String gridData = JdomSvg.makeGridString(
                           viewBoxWidth,viewBoxHeight,10);

    //Create the path element and add it to the root.
    Element temp;
    temp = JdomSvg.makePath(svg,ns,gridData);
    //Set the color to a very light green.
    temp.setAttribute("stroke","#ccffcc");

    //Using the same procedure, draw darker green grid
    // lines every 50 user units and draw them on top of
    // the existing grid lines.
    gridData = JdomSvg.makeGridString(
                           viewBoxWidth,viewBoxHeight,50);
    temp = JdomSvg.makePath(svg,ns,gridData);
    temp.setAttribute("stroke","#99ff99");
  
    //Draw even darker green grid lines every 100 user
    // units.
    gridData = JdomSvg.makeGridString(
                          viewBoxWidth,viewBoxHeight,100);
    temp = JdomSvg.makePath(svg,ns,gridData);
    temp.setAttribute("stroke","#22ff22");
    
    //Draw an even darker green border on the perimeter of
    // the grid.
    temp = JdomSvg.makeRect(svg,ns,1,1,
                          viewBoxWidth-2,viewBoxHeight-2);
    temp.setAttribute("stroke","#00ff00");
    temp.setAttribute("stroke-width","2");
    
    //Call the makeNode method to create a defs element
    // that will be the container for gradient
    // definitions.
    Element defs = JdomSvg.makeNode(svg,//parent
                                    ns,//namespace
                                    "defs",
                                    null//no attributes
                                    );
    
    //Define gradientA, which provides a linear gradient
    // that starts with yellow, changes to red, and
    // changes back to yellow going from left to right.
    Element gradientA = JdomSvg.makeLinearGradient(
                                         defs,//parent
                                         ns,//namespace
                                         "gradientA");//id
                                       
    //Create three stop nodes that identify the colors
    // used to produce gradientA and specify where the
    // colors begin and end, 
    JdomSvg.makeGradientStop(gradientA,//parent
                             ns,
                             "4%",//start here
                             "yellow");//color
                           
    JdomSvg.makeGradientStop(gradientA,
                             ns,  
                             "50%",
                            "red");
                           
    JdomSvg.makeGradientStop(gradientA,
                             ns,
                            "96%",
                            "yellow");

    //Call the makeNode method to draw an ellipse. Fill it
    // with linear gradientA and rotate it by 30 degrees.
    // Note that I could have called the makeEllipse
    // method, but I wanted to demonstrate the use of the
    // more general makeNode method for a graphics element
    // that has several attributes.
    JdomSvg.makeNode(
            svg,//parent
            ns,
            "ellipse",//node type
            new String[]{"cx","150",
                         "cy","80",
                         "rx","100",
                         "ry","40",
                         "fill","url(#gradientA)",
                         "stroke","blue",
                         "stroke-width","3",
                         "opacity","0.5",
                         "transform","translate(150,80) "
                                   + "rotate(-30) "
                                   + "translate(-150,-80)"
                        }//end array definition
    );//end makeNode method
    
    //Note: Setting the opacity attribute value to 0.6 is
    // of no consequence when the output file is rendered
    // using Firefox 2.0.0.4.  The gradient continues to
    // be opaque.  However, this is a weakness in the
    // Firefox rendering engine and is not a limitation of
    // SVG.  When the file is rendered using an Adobe
    // rendering engine, the gradient is partially
    // transparent as it should be.
    
    //Define gradientB, which provides a radial gradient
    // that goes through yellow, red, green, and blue.
    // Use it to fill a rectangle.
    int rectCornerX = 300;
    int rectCornerY = 10;
    int rectWidth = 150;
    int rectHeight = 150;
    Element gradientB = JdomSvg.makeRadialGradient(
                               defs,//parent
                               ns,//namespace
                               "gradientB",//ID
                               "userSpaceOnUse",
                               rectCornerX,//cx
                               rectCornerY,//cy
                               ((int)(rectWidth*1.2)));//r
                                       
    //Create three stop nodes that identify the colors
    // used to produce gradientB and to specify where
    // the colors begin and end, 
    JdomSvg.makeGradientStop(gradientB,//parent
                             ns,//namespace
                             "10%",//start here
                             "yellow");//color
                           
    JdomSvg.makeGradientStop(gradientB,
                             ns,  
                             "33%",
                            "red");
                           
    JdomSvg.makeGradientStop(gradientB,
                             ns,
                            "66%",
                            "green");
                            
    JdomSvg.makeGradientStop(gradientB,
                             ns,
                            "96%",
                            "blue");
                            
    //Draw the rectangle and fill it with gradientB.
    temp = JdomSvg.makeRect(svg,
                            ns,
                            rectCornerX,
                            rectCornerY,
                            rectWidth,
                            rectHeight);
    temp.setAttribute("fill","url(#gradientB)");

    //Draw a cubic Bezier curve consisting of two Bezier
    // segments.  First draw a polygon that shows the
    // start points, the end points, and the control
    // points for the segments. Fill the polygon with
    // yellow to provide a background color for the 
    // Bezier curve.
    temp = JdomSvg.makePolygon(
                     svg,
                     ns,
                     new int[] {180,290,50,250,150,200,
                                150,400,250,350,120,310});
    temp.setAttribute("stroke","red");
    temp.setAttribute("fill","yellow");
    
    //Draw the cubic curve. Note the use of absolute
    // coordinate values only and also the use of the S
    // command. Stroke the curve with red and fill it with
    // blue.
    temp = JdomSvg.makePath(svg,ns,"M180,290 C50,250,"
                    + "150,200,150,300 S250,350,120,310");
    temp.setAttribute("stroke","red");
    temp.setAttribute("fill","blue");
    
    //Draw a quadratic Bezier curve consisting of two
    // Bezier segments.  First draw a polyline that shows
    // the start points, the end points, and the control
    // points.  Fill the polyline with yellow color with
    // an opacity attribute value of 0.5.  This allows
    // the grid to show through the filled polyline.
    temp = JdomSvg.makePolyline(
                       svg,
                       ns,
                       new int[] {300,300,400,200,500,300,
                                  400,400,300,300});
    temp.setAttribute("stroke","red");
    temp.setAttribute("fill","yellow");
    temp.setAttribute("opacity","0.5");

    //Draw the quadratic curve. Note the use of relative
    // coordinate values. Stroke the curve with a wide
    // red stroke and fill it with blue.
    temp = JdomSvg.makePath(svg,ns,
              "M300,300 q100,-100,200,0,-100,100,-200,0");

    temp.setAttribute("stroke","red");
    temp.setAttribute("stroke-width","3");
    temp.setAttribute("fill","blue");


    //Draw an elliptical arc to illustrate the appearance
    // of such an arc with no rotation. Stroke it with
    // yellow and fill it with red.
    temp = JdomSvg.makePath(svg,ns,
                        "M100,500 a70,30 0 1,0 50,-50 z");
    temp.setAttribute("stroke","yellow");
    temp.setAttribute("stroke-width","2");
    temp.setAttribute("fill","red");

    //Draw an elliptical arc to illustrate the appearance
    // of such an arc with a rotation of 45 degrees.
    // Stroke it with yellow and fill it with red.
    temp = JdomSvg.makePath(svg,ns,
                       "M350,500 a70,30 45 1,0 50,-50 z");
    temp.setAttribute("stroke","yellow");
    temp.setAttribute("stroke-width","2");
    temp.setAttribute("fill","red");

    //Label the grid. First label the vertical lines.
    for(int cnt = 0;cnt < viewBoxWidth;cnt += 50){
      JdomSvg.makeText(
                     svg,ns,cnt,viewBoxHeight - 4,""+cnt);
    }//end for loop

    //Now label the horizontal lines.    
    for(int cnt = 0;cnt < viewBoxHeight;cnt += 50){
      JdomSvg.makeText(svg,ns,3,cnt,""+cnt);
    }//end for loop
  
    //Label each of the graphic elements
    JdomSvg.makeText(svg,ns,50,180,
                          "Ellipse with linear gradient");
    JdomSvg.makeText(svg,ns,270,180,
                        "Rectangle with radial gradient");
    JdomSvg.makeText(svg,ns,100,420,
                                    "Cubic Bezier curve");
    JdomSvg.makeText(svg,ns,280,420,
                         "Quadratic Bezier with opacity");
    JdomSvg.makeText(svg,ns,50,555,
                       "Elliptical arc without rotation");
    JdomSvg.makeText(svg,ns,300,555,
                          "Elliptical arc with rotation");
    
    //End creation of graphics elements.
 
    //Write the output file.
    JdomSvg.writePrettyFile("Svg17.svg",doc);
    //JdomSvg.writeCompactFile("Svg17.svg",doc);
    
  }//end main
  //----------------------------------------------------//

}//end class Svg17
//======================================================//

//This is a graphics library that is designed to eliminate
// much of the effort involved in writing JDOM code to
// create SVG output. The library contains individual
// static methods that are used to construct and return
// many of the standard SVG graphics elements. It also
// contains a method named makeNode that can be used to
// create any graphic element.
//Many of the methods have default attribute values.  If
// you need different attribute values for a particular
// graphic element, you can call the setAttribute method
// on the returned value to change the attribute values 
// after the method returns.
//Many of the methods set the stroke attribute value to
// black and set the stroke-width attribute value to 1
// by default.  If you don't want to be able to see the
// outline of the shape, change the stroke-width attribute
// value to 0 after the method returns.
class JdomSvg{
  //----------------------------------------------------//

  //This method writes the XML code into an output file
  // in pretty-print format. The pretty-print format
  // is less efficient than the compact format, but it
  // is very useful during test and debugging because
  // you can view source in your browser and the XML
  // code will be reasonably well formatted.
  public static void writePrettyFile(
                              String fname, Document doc){
    try{
      FileOutputStream out = new FileOutputStream(fname);

      XMLOutputter xmlOut = 
               new XMLOutputter(Format.getPrettyFormat());
      xmlOut.output(doc,out);

      out.flush();
      out.close();
    }catch (IOException e){
      System.err.println(e);
    }//end catch
  }//end writePrettyFile
  //----------------------------------------------------//
  
  //This method writes the XML code into an output file
  // in whitespace-normalized format. This format is more
  // compact and efficient than the pretty-print format.
  public static void writeCompactFile(
                              String fname, Document doc){
    try{
      FileOutputStream out = new FileOutputStream(fname);

      XMLOutputter xmlOut = 
              new XMLOutputter(Format.getCompactFormat());
      xmlOut.output(doc,out);

      out.flush();
      out.close();
    }catch (IOException e){
      System.err.println(e);
    }//end catch
  }//end writePrettyFile
  //----------------------------------------------------//

  //This method constructs and returns a reference to an
  // SVG root element node named svg with a viewBox
  // attribute to map the virtual dimensions into the
  // physical dimensions of the canvas.
  //By default, the min-x and min-y attribute values of
  // the viewBox are set to 0 0 and the value of the
  // preserveAspectRatio is set to none.
  //The svg element represents the canvas on which
  // various shapes can be drawn. The width and height
  // attribute values of the svg element establish the
  // physical size of the canvas on the screen. The
  // values of the viewBox attribute establish the
  // size of the canvas in "user units."  The
  // dimensions of the viewbox map directly into the
  // dimensions of the canvas.  This makes it possible
  // for the programmer to work in dimensions that are
  // convenient, such as 1000x1000, instead of having
  // to work with less convenient dimensions such as
  // 459x459.
  //Setting the value of the preserveAspectRatio
  // attribute to none prevents the system from
  // attempting to automatically preserve the aspect
  // ratio.  In this case, it is probably a good idea
  // for the programmer to make the ratio of the height
  // and width of the viewbox the same as the ratio of
  // the height and width of the canvas. That way, a
  // circle will look like a circle instead of looking
  // like an ellipse.
  //Note that the root element node does not have a parent
  // like the other nodes. Rather, it is passed as a 
  // parameter to the constructor for the Document node in
  // order to establish its place in the tree.
  public static Element makeSvg(
                      String ns,//namespace URI
                      int svgWidth,//physical screen width
                      int svgHeight,//physical height
                      int vbWidth,//virtual width
                      int vbHeight//virtual height
                      ){
    Element svg = new Element("svg",ns);
    
    //Set default attribute values.
    svg.setAttribute("version","1.1");
    svg.setAttribute("preserveAspectRatio","none");
    String vbMinX = "0 ";
    String vbMinY = "0 ";
    
    //Set user-specified attribute values. Note the 
    // format that is required to construct the attribute
    // value for the viewBox. If you need to call the
    // setAttribute method after this method returns to
    // change the attribute value for the viewBox
    // attribute, you will need to comply with the format
    // requirements for that value.
    svg.setAttribute("width",""+svgWidth);
    svg.setAttribute("height",""+svgHeight);
    svg.setAttribute("viewBox",
             vbMinX + vbMinY + ""+vbWidth + " "+vbHeight);
    
    return svg;
  }//end makeSvg
  //----------------------------------------------------//
  
  //This method constructs and returns a description node
  // for a given namespace and a given parent.
  public static Element makeDescription(
              Element parent,//The parent of this element.
              String nameSpace,//The namespace.
              String text//Text content for this element.
              ){
    Element desc = new Element("desc",nameSpace);
    parent.addContent(desc);
    desc.setText(text);

    return desc;
  }//end makeDescription
  //----------------------------------------------------//
  
  //This method constructs and returns a comment node
  // for a given parent.
  public static Comment makeComment(
              Element parent,//The parent of this element.
              String text//Text content for this element.
              ){
    Comment comment = new Comment(text);
    parent.addContent(comment);

    return comment;
  }//end makeComment
  //----------------------------------------------------//

  //This method constructs and returns a rect node for a
  // given parent in a given namespace.  By default,the
  // stroke is black, the stroke-width is 1, and the fill
  // is none. Setting the fill to none has the effect of
  // causing the shape to be totally transparent.
  public static Element makeRect(
                  Element parent,
                  String namespace,
                  int x,//Coordinate of upper-left corner.
                  int y,//Coordinate of upper-left corner.
                  int width,
                  int height
                  ){
    Element rect = new Element("rect",namespace);
    parent.addContent(rect);
    
    //Set default attribute values.
    rect.setAttribute("fill","none");
    rect.setAttribute("stroke","black");
    rect.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    rect.setAttribute("x",""+x);
    rect.setAttribute("y",""+y);
    rect.setAttribute("width",""+width);
    rect.setAttribute("height",""+height);
    
    return rect;
  }//end makeRect
  //----------------------------------------------------//

  //This method constructs and returns an ellipse node for
  // a given parent in a given namespace.  By default,the
  // stroke is black, the stroke-width is 1, and the fill
  // is none.
  public static Element makeEllipse(
                                Element parent,
                                String namespace,
                                int cx,//Center coordinate
                                int cy,//Center coordinate
                                int rx,//Horizontal radius
                                int ry //Vertical radius
                                ){
    Element ellipse = new Element("ellipse",namespace);
    parent.addContent(ellipse);
    
    //Set default attribute vales
    ellipse.setAttribute("fill","none");
    ellipse.setAttribute("stroke","black");
    ellipse.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    ellipse.setAttribute("cx",""+cx);
    ellipse.setAttribute("cy",""+cy);
    ellipse.setAttribute("rx",""+rx);
    ellipse.setAttribute("ry",""+ry);
    
    return ellipse;
  }//end makeEllipse
  //----------------------------------------------------//

  //This method constructs and returns a polyline node for
  // a given parent in a given namespace..
  //The array of type int[], which contains the
  // coordinates for each point in the polyline, must
  // contain an even number of values for the polyline
  // to be drawn correctly. Otherwise, it simply won't be
  // drawn.
  //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.
  //The main difference between a polyline and a polygon
  // (see below) is that a polygon is automatically closed
  // by connecting the last point to the first point.
  // While you may close a polyline if you cause the
  // coordinates of the last point to match the
  // coordinates of the first point, it is not
  // automatically closed as in the case of a polygon.
  //Be careful if you fill a polyline that is not closed.
  // The results may not be what you expect.
  static Element makePolyline(Element parent,
                              String namespace,
                              int[] points){//See above.
    Element polyline = new Element("polyline",namespace);
    parent.addContent(polyline);

    //Set default attributes
    polyline.setAttribute("stroke","black");
    polyline.setAttribute("stroke-width","1");
    polyline.setAttribute("fill","none");
    
    //Set user specified attributes.
    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + " ";
    }//end for loop
    polyline.setAttribute("points",dataPoints);
    
    return polyline;
  }//end makePolyline
  //----------------------------------------------------//
  
  //This method constructs and returns a polygon node for
  // a given parent in a given namespace..
  //The array of type int[], which contains the
  // coordinates for each point in the polyline, must
  // contain an even number of values for the polyline
  // to be drawn correctly. Otherwise, it simply won't be
  // drawn.
  //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.
  //The main difference between a polygon and a polyline
  // (see above) is that a polygon is automatically closed
  // by connecting the last point to the first point.
  static Element makePolygon(Element parent,
                             String namespace,
                             int[] points){
    Element polygon = new Element("polygon",namespace);
    parent.addContent(polygon);
    
    //Set default attributes.
    polygon.setAttribute("stroke","black");
    polygon.setAttribute("stroke-width","1");
    polygon.setAttribute("fill","none");
    
    //Set user specified attributes.
    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + ",";
    }//end for loop
    polygon.setAttribute("points",dataPoints);

    return polygon;
  }//end makePolygon
  //----------------------------------------------------//
  
  //This method constructs and returns a line node for a
  // given parent in a given namespace.  By default,the
  // stroke is black and the stroke-width is 1.
  public static Element makeLine(
                                Element parent,
                                String namespace,
                                int x1,//Start coordinate
                                int y1,//Start coordinate
                                int x2,//End coordinate
                                int y2 //End coordinate
                                ){
    Element line = new Element("line",namespace);
    parent.addContent(line);
    
    //Set default attribute vales
    line.setAttribute("stroke","black");
    line.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    line.setAttribute("x1",""+x1);
    line.setAttribute("y1",""+y1);
    line.setAttribute("x2",""+x2);
    line.setAttribute("y2",""+y2);
    
    return line;
  }//end makeLine
  //----------------------------------------------------//
  
  //This method constructs and returns a circle node for a
  // given parent in a given namespace.  By default,the
  // stroke is black, the stroke-width is 1, and the fill
  // is none.
  public static Element makeCircle(
                                Element parent,
                                String namespace,
                                int cx,//Center coordinate
                                int cy,//Center coordinate
                                int r  //Radius
                                ){
    Element circle = new Element("circle",namespace);
    parent.addContent(circle);
    
    //Set default attribute vales
    circle.setAttribute("fill","none");
    circle.setAttribute("stroke","black");
    circle.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    circle.setAttribute("cx",""+cx);
    circle.setAttribute("cy",""+cy);
    circle.setAttribute("r",""+r);
    
    return circle;
  }//end makeCircle
  //----------------------------------------------------//
  
  //This is a utility method that is used to construct a
  // string that describes a grid pattern consisting of 
  // horizontal and vertical lines at a specified pixel
  // spacing for a rectangular area of a specified width
  // and height. The string is intended to be used as the
  // data string for a call to the method named makePath.
  //There is nothing about this method that is peculiar to
  // the use of JDOM.
  //Note that this method makes use of the special
  // horizontal and vertical lineto commands in an attempt
  // to reduce the download size and the bandwidth
  // requirements for a drawing containing a large number
  // of horizontal and vertical lines in the grid pattern.
  static String makeGridString(
                        int width,int height,int spacing){
    //Construct the data string for the vertical lines.
    String data = "M0,0 ";
    for(int cnt = 0;cnt < width;cnt += spacing){
      data += "v" + height + " M" + cnt + ",0 ";
    }//end for loop
    //Add the final vertical line.
    data += "v" + height + " n";
    
    //Now add the horizontal lines to the data string.
    data += "M0,0 ";
    for(int cnt = 0;cnt < height;cnt += spacing){
      data += "h" + width + " M0," + cnt + " ";
    }//end for loop
    //Add the final horizontal line.
    data += "h" + width + "n ";
    
    return data;

  }//end makeGridString
  //----------------------------------------------------//

  //This method returns a reference to a path. By
  // default, the stroke is set to black one pixel wide,
  // and the fill is set to none.
  //See the method named makeGridString for a utility
  // method that is designed to create the data string
  // for this method for the special case of drawing grids
  // that resemble graph paper. For other cases, simply
  // create a data string that is compatible with the SVG
  // path element.
  static Element makePath(Element parent,
                          String namespace,
                          String d){
    Element path  = new Element("path",namespace);
    parent.addContent(path);
    
    //Set default attribute values.
    path.setAttribute("stroke","black");
    path.setAttribute("stroke-width","1");
    path.setAttribute("fill","none");
    
    //Set user specified default values.
    path.setAttribute("d",d);
    return path;
  }//end makePath
  //----------------------------------------------------//
  
  /*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 parent node
   to which this node is to be appended so as to become a
   child of that node.
   
  The second parameter is a String that specifies the
   namespace for the element represented by the new 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.
   If there are no attributes, the value of this parameter
   should be null.
  
  An example of the recommended usage of the method
   follows:
  Element abc = JdomSvg.makeNode(
                     parent,
                     namespace,
                     "NodeType",
                     new String[]{"name","value",
                                  "name","value",
                                  "name","value"
                                 });//end call to makeNode
  */
  static Element makeNode(Element parent,
                          String namespace,
                          String nodeType,
                          String[] data){

    Element element = new Element(nodeType,namespace);
    parent.addContent(element);
  
    //Deal with elements that have no attributes.
    if(data == null){
      return element;
    }//end if
    
    //Extract the values from the array and construct
    // each of the attributes and its value.
    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 method creates a linear gradient node to which
  // stop elements must be appended. The id attribute is
  // used to differentiate this gradient node from other
  // gradient nodes in the same program scope.
  static Element makeLinearGradient(Element parent,
                                    String namespace,
                                    String id){
    Element gradient = 
                  new Element("linearGradient",namespace);
    parent.addContent(gradient);
    gradient.setAttribute("id",id);
    return gradient;
  }//End makeLinearGradient
  //----------------------------------------------------//
  
  //This method creates a radial gradient node to which
  // stop elements must be appended. The id attribute is
  // used to differentiate this gradient node from other
  // gradient nodes. See the SVG documentation for more
  // information on the various attributes of the radial
  // gradient element.
  static Element makeRadialGradient(Element parent,
                                    String namespace,
                                    String id,
                                    String gradientUnits,
                                    int cx,//center
                                    int cy,//center
                                    int r  //radius
                                    ){
    Element gradient = 
                  new Element("radialGradient",namespace);
    parent.addContent(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. See the SVG documentation for more
  // information on the various attributes of the gradient
  // stop element.                          
  static Element makeGradientStop(Element parent,
                                  String namespace,
                                  String offset,
                                  String color){
    Element stopElement = new Element("stop",namespace);
    parent.addContent(stopElement);
    
    stopElement.setAttribute("offset",offset);
    stopElement.setAttribute("stop-color",color);
    return stopElement;
  }//End makeGradientStop
  //----------------------------------------------------//
  
  //This method returns a reference to a text element
  // node. The x and y parameters specify the location of
  // the lower left corner of the first text character.
  static Element makeText(Element parent,
                          String namespace,
                          int x,
                          int y,
                          String text){
    Element textNode = new Element("text",namespace);
    parent.addContent(textNode);

    textNode.setAttribute("x",""+x);
    textNode.setAttribute("y",""+y);
    
    textNode.addContent(text);

    return textNode;
  }//end makeText
  //----------------------------------------------------//

}//end class JdomSvg

Listing 31. Program code for Svg18.

/*File Svg18.java,
Copyright 2007, R.G.Baldwin

The intended primary purpose of this program was to use 
JDOM to embed SVG code into what would otherwise be a 
valid XHTML file.  

A secondary purpose of this program, in comparison with 
the earlier program named Svg017, was to demonstrate the
use of the word Scalable in the name Scalable Vector
Graphics (SVG).

The secondary purpose was achieved.  However, for the
reasons explained below, the primary purpose was not
completely achieved, because the output XHTML file is
not valid.

One of the advantages of embedding SVG code in an 
XHTML document is that it then becomes possible to place 
the graphic in and among other HTML elements.  That is not
possible when creating an SVG file.  That capability is 
demonstrated by this program.

However, my attempt to create a valid XHTML file using 
JDOM was not successful because the JDOM Element 
constructor insists on creating an xmlns attribute for 
children of the root html node. This is true even when 
the version of the constructor is used that doesn't take 
a namespace as a parameter.  Whether that version is used,
or the version that takes a namespace as a parameter is 
used by passing null for the namespace, the resulting XML 
code contains . 

The DTD for XHTML doesn't allow for attributes named 
xmlns on the head and body elements. 

Note that the xmlns attribute is created even if I change 
the name of the child element from head to joe. I suspect,
but can't be sure that this is a bug in the version of 
the Element constructor that doesn't take a namespace 
parameter. The behavior of that version seems to be the 
same as passing null for the namespace to the version that
does take a namespace parameter.

Therefore, the output file produced by this program is 
not a valid XHTML file. Nonetheless, it renders very
successfully in Firefox 2.0.0.4.

The program named Svg16 began the development of a
Java/JDOM/SVG graphics library of my own design named
JdomSvg, which eliminates much of the effort involved in 
writing Java programs to produce SVG files using JDOM as 
an alternative to the standard Sun DOM classes and 
methods. Several new methods were added to that library in
the program named Svg017. Those methods were all 
associated with the creation of SVG/XML code.

I added the following method to the graphics library in 
this program:

* makeXhtmlRoot

This method has to do with ordinary XML elements 
that don't necessarily have anything to do with graphics,
but rather are involved in the creation of an XHTML file.

This program begins by placing the following text content 
in a paragraph element at the beginning of the body 
element.

"There is a graphic below here."

Then the program draws the following graphics elements on 
a background that looks like graph paper.

* A rotated ellipse with a linear gradient.
* A rectangle with a radial gradient.
* A filled cubic Bezier curve with two Bezier segments
  inside a filled polygon.
* A filled quadratic Bezier curve with two Bezier segments
  inside a filled polyline that is partially transparent.
* A filled elliptical arc with no rotation.
* A filled elliptical arc with a rotation of 45 degrees.

After drawing the graphic elements, the program places the
following text content in a paragraph element below the 
graphic elements at the end of the body element.

"There is a graphic above here."

Finally, the program writes the output into an XHTML file
named Svg18.xhtml that can be rendered using an SVG 
graphics engine such a Firefox 2.0.0.4.

Tested using J2SE 6.0, JDOM 1.0, and Firefox 2.0.0.4
running under WinXP.
*********************************************************/
import java.io.*;
import org.jdom.*;
import org.jdom.output.XMLOutputter;
import org.jdom.output.Format;
public class Svg18{
  
  public static void main(String[] args){

    //Create a String variable containing the XHTML
    // namespace URI to reduce the amount of typing that 
    // is required later. Note that the variable name is 
    // short and easy to type.
    String xns = "http://www.w3.org/1999/xhtml";
  
    //For clarity, create strings containing the name of
    // the element that is constrained by the DTD (the
    // html root element), the Public ID of the DTD, and 
    // the System ID of the DTD.
    String dtdConstrainedElement = "html";
    String dtdPublicID = 
                 "-//W3C//DTD XHTML 1.0 Transitional//EN";
    String dtdSystemID = "http://www.w3.org/TR/xhtml1/"
                          + "DTD/xhtml1-transitional.dtd";

    //Create the DTD declaration node.
    DocType docType = new DocType(
           dtdConstrainedElement,dtdPublicID,dtdSystemID);

    //Create the XHTML root node named html.
    Element html = JdomSvg.makeXhtmlRoot(xns);
    
    //Create the document node.
    Document doc = new Document(html,docType);

    //Add optional and required elements to the document
    // node, placing text content in the title node, 
    Element head = JdomSvg.makeNode(html,xns,"head",null);
    Element title = JdomSvg.makeNode(
                                   head,xns,"title",null);
    title.setText("XHTML/SVG Graphic Demo.");
                         
    Element body = JdomSvg.makeNode(html,xns,"body",null);

    //Put a paragraph element in the body element.
    Element temp = JdomSvg.makeNode(body,xns,"p",null);
    temp.setText("There is a graphic below here.");

    //Now switch your thinking into graphics programming
    // mode to place a large and fairly complex SVG
    // graphic in the body element.

    //Create a String variable containing the SVG
    // namespace URI to reduce the amount of typing that
    // is required later. Note that the variable name is
    // short and easy to type.
    String ns = "http://www.w3.org/2000/svg";

    //This program creates and displays exactly the same
    // graphics elements that were created and displayed
    // by the earlier program named Svg17.  However, the
    // physical size of the canvas in this program was
    // reduced to 75-percent of its size in the earlier
    // program for the purpose of demonstrating the use of
    // the word "Scalable" in the name Scalable Vector
    // Graphics (SVG).  Note also that because of the use
    // of virtual dimensions implemented through the use
    // of the viewBox, it was not necessary to modify any
    // of the SVG graphics code to accommodate the
    // reduction in the overall physical size of the
    // graphics elements.

    //Physical dimensions of the graphic on the screen.
    int svgWidth = (int)(460*0.75);
    int svgHeight = (int)(552*0.75);
    
    //Virtual dimensions of the graphic in user units.
    int viewBoxWidth = 500;
    int viewBoxHeight = 500*svgHeight/svgWidth;

    //Create the SVG root element named svg.
    Element svg = JdomSvg.makeSvg(ns,
                                  svgWidth,
                                  svgHeight,
                                  viewBoxWidth,
                                  viewBoxHeight);

    //The makeSvg method does not automatically add the
    // new node to a parent for reasons that are explained
    // in the method.  Therefore, it is necessary to add
    // the node to the parent outside of the makeSvg
    // method.
    body.addContent(svg);

    //Begin creating actual graphics elements.  With the
    // exception of the changes in the physical dimensions
    // of the graphic on the screen as implemented above,
    // the graphics code in this program is the same as
    // the code in the earlier program named Svg017.

    //Draw light green grid lines every 10 user units on
    // the basis of the virtual dimensions in user units.
    //First create the path data.
    String gridData = JdomSvg.makeGridString(
                           viewBoxWidth,viewBoxHeight,10);

    //Create the path element and add it to the root.
    temp = JdomSvg.makePath(svg,ns,gridData);
    //Set the color to a very light green.
    temp.setAttribute("stroke","#ccffcc");

    //Using the same procedure, draw darker green grid
    // lines every 50 user units and draw them on top of
    // the existing grid lines.
    gridData = JdomSvg.makeGridString(
                           viewBoxWidth,viewBoxHeight,50);
    temp = JdomSvg.makePath(svg,ns,gridData);
    temp.setAttribute("stroke","#99ff99");
  
    //Draw even darker green grid lines every 100 user
    // units.
    gridData = JdomSvg.makeGridString(
                          viewBoxWidth,viewBoxHeight,100);
    temp = JdomSvg.makePath(svg,ns,gridData);
    temp.setAttribute("stroke","#22ff22");
    
    //Draw an even darker green border on the perimeter of
    // the grid.
    temp = JdomSvg.makeRect(svg,ns,1,1,
                          viewBoxWidth-2,viewBoxHeight-2);
    temp.setAttribute("stroke","#00ff00");
    temp.setAttribute("stroke-width","2");
    
    //Call the makeNode method to create a defs element
    // that will be the container for gradient
    // definitions.
    Element defs = JdomSvg.makeNode(svg,//parent
                                    ns,//namespace
                                    "defs",
                                    null//no attributes
                                    );
    
    //Define gradientA, which provides a linear gradient
    // that starts with yellow, changes to red, and
    // changes back to yellow going from left to right.
    Element gradientA = JdomSvg.makeLinearGradient(
                                         defs,//parent
                                         ns,//namespace
                                         "gradientA");//id
                                       
    //Create three stop nodes that identify the colors
    // used to produce gradientA and specify where the
    // colors begin and end, 
    JdomSvg.makeGradientStop(gradientA,//parent
                             ns,
                             "4%",//start here
                             "yellow");//color
                           
    JdomSvg.makeGradientStop(gradientA,
                             ns,  
                             "50%",
                            "red");
                           
    JdomSvg.makeGradientStop(gradientA,
                             ns,
                            "96%",
                            "yellow");

    //Call the makeNode method to draw an ellipse. Fill it
    // with linear gradientA and rotate it by 30 degrees.
    // Note that I could have called the makeEllipse
    // method, but I wanted to demonstrate the use of the
    // more general makeNode method for a graphics element
    // that has several attributes.
    JdomSvg.makeNode(
            svg,//parent
            ns,
            "ellipse",//node type
            new String[]{"cx","150",
                         "cy","80",
                         "rx","100",
                         "ry","40",
                         "fill","url(#gradientA)",
                         "stroke","blue",
                         "stroke-width","3",
                         "opacity","0.5",
                         "transform","translate(150,80) "
                                   + "rotate(-30) "
                                   + "translate(-150,-80)"
                        }//end array definition
    );//end makeNode method
    
    //Note: Setting the opacity attribute value to 0.6 is
    // of no consequence when the output file is rendered
    // using Firefox 2.0.0.4.  The gradient continues to
    // be opaque.  However, this is a weakness in the
    // Firefox rendering engine and is not a limitation of
    // SVG.  When the file is rendered using an Adobe
    // rendering engine, the gradient is partially
    // transparent as it should be.
    
    //Define gradientB, which provides a radial gradient
    // that goes through yellow, red, green, and blue.
    // Use it to fill a rectangle.
    int rectCornerX = 300;
    int rectCornerY = 10;
    int rectWidth = 150;
    int rectHeight = 150;
    Element gradientB = JdomSvg.makeRadialGradient(
                               defs,//parent
                               ns,//namespace
                               "gradientB",//ID
                               "userSpaceOnUse",
                               rectCornerX,//cx
                               rectCornerY,//cy
                               ((int)(rectWidth*1.2)));//r
                                       
    //Create three stop nodes that identify the colors
    // used to produce gradientB and to specify where
    // the colors begin and end, 
    JdomSvg.makeGradientStop(gradientB,//parent
                             ns,//namespace
                             "10%",//start here
                             "yellow");//color
                           
    JdomSvg.makeGradientStop(gradientB,
                             ns,  
                             "33%",
                            "red");
                           
    JdomSvg.makeGradientStop(gradientB,
                             ns,
                            "66%",
                            "green");
                            
    JdomSvg.makeGradientStop(gradientB,
                             ns,
                            "96%",
                            "blue");
                            
    //Draw the rectangle and fill it with gradientB.
    temp = JdomSvg.makeRect(svg,
                            ns,
                            rectCornerX,
                            rectCornerY,
                            rectWidth,
                            rectHeight);
    temp.setAttribute("fill","url(#gradientB)");

    //Draw a cubic Bezier curve consisting of two Bezier
    // segments.  First draw a polygon that shows the
    // start points, the end points, and the control
    // points for the segments. Fill the polygon with
    // yellow to provide a background color for the 
    // Bezier curve.
    temp = JdomSvg.makePolygon(
                     svg,
                     ns,
                     new int[] {180,290,50,250,150,200,
                                150,400,250,350,120,310});
    temp.setAttribute("stroke","red");
    temp.setAttribute("fill","yellow");
    
    //Draw the cubic curve. Note the use of absolute
    // coordinate values only and also the use of the S
    // command. Stroke the curve with red and fill it with
    // blue.
    temp = JdomSvg.makePath(svg,ns,"M180,290 C50,250,"
                    + "150,200,150,300 S250,350,120,310");
    temp.setAttribute("stroke","red");
    temp.setAttribute("fill","blue");
    
    //Draw a quadratic Bezier curve consisting of two
    // Bezier segments.  First draw a polyline that shows
    // the start points, the end points, and the control
    // points.  Fill the polyline with yellow color with
    // an opacity attribute value of 0.5.  This allows
    // the grid to show through the filled polyline.
    temp = JdomSvg.makePolyline(
                       svg,
                       ns,
                       new int[] {300,300,400,200,500,300,
                                  400,400,300,300});
    temp.setAttribute("stroke","red");
    temp.setAttribute("fill","yellow");
    temp.setAttribute("opacity","0.5");

    //Draw the quadratic curve. Note the use of relative
    // coordinate values. Stroke the curve with a wide
    // red stroke and fill it with blue.
    temp = JdomSvg.makePath(svg,ns,
              "M300,300 q100,-100,200,0,-100,100,-200,0");

    temp.setAttribute("stroke","red");
    temp.setAttribute("stroke-width","3");
    temp.setAttribute("fill","blue");


    //Draw an elliptical arc to illustrate the appearance
    // of such an arc with no rotation. Stroke it with
    // yellow and fill it with red.
    temp = JdomSvg.makePath(svg,ns,
                        "M100,500 a70,30 0 1,0 50,-50 z");
    temp.setAttribute("stroke","yellow");
    temp.setAttribute("stroke-width","2");
    temp.setAttribute("fill","red");

    //Draw an elliptical arc to illustrate the appearance
    // of such an arc with a rotation of 45 degrees.
    // Stroke it with yellow and fill it with red.
    temp = JdomSvg.makePath(svg,ns,
                       "M350,500 a70,30 45 1,0 50,-50 z");
    temp.setAttribute("stroke","yellow");
    temp.setAttribute("stroke-width","2");
    temp.setAttribute("fill","red");

    //Label the grid. First label the vertical lines.
    for(int cnt = 0;cnt < viewBoxWidth;cnt += 50){
      JdomSvg.makeText(
                     svg,ns,cnt,viewBoxHeight - 4,""+cnt);
    }//end for loop

    //Now label the horizontal lines.    
    for(int cnt = 0;cnt < viewBoxHeight;cnt += 50){
      JdomSvg.makeText(svg,ns,3,cnt,""+cnt);
    }//end for loop
  
    //Label each of the graphic elements
    JdomSvg.makeText(svg,ns,50,180,
                          "Ellipse with linear gradient");
    JdomSvg.makeText(svg,ns,270,180,
                        "Rectangle with radial gradient");
    JdomSvg.makeText(svg,ns,100,420,
                                    "Cubic Bezier curve");
    JdomSvg.makeText(svg,ns,280,420,
                         "Quadratic Bezier with opacity");
    JdomSvg.makeText(svg,ns,50,555,
                       "Elliptical arc without rotation");
    JdomSvg.makeText(svg,ns,300,555,
                          "Elliptical arc with rotation");
    
    //End creation of graphics elements.
    
 
    //Create a paragraph element containing text content
    // and place it at the end of the body element.
    temp = JdomSvg.makeNode(body,xns,"p",null);
    temp.setText("There is a graphic above here.");
 
     //Write the output file. Note that this is an XHTML
     // file and not an SVG file.
    JdomSvg.writePrettyFile("Svg18.xhtml",doc);
    //JdomSvg.writeCompactFile("Svg18.svg",doc);
    
  }//end main
  //----------------------------------------------------//

}//end class Svg18
//======================================================//

//This is a graphics library that is designed to eliminate
// much of the effort involved in writing JDOM code to
// create SVG output. The library contains individual
// static methods that are used to construct and return
// many of the standard SVG graphics elements. It also
// contains a method named makeNode that can be used to
// create any graphic element.
//Many of the methods have default attribute values.  If
// you need different attribute values for a particular
// graphic element, you can call the setAttribute method
// on the returned value to change the attribute values 
// after the method returns.
//Many of the methods set the stroke attribute value to
// black and set the stroke-width attribute value to 1
// by default.  If you don't want to be able to see the
// outline of the shape, change the stroke-width attribute
// value to 0 after the method returns.
class JdomSvg{
  //----------------------------------------------------//

  //This method writes the XML code into an output file
  // in pretty-print format. The pretty-print format
  // is less efficient than the compact format, but it
  // is very useful during test and debugging because
  // you can view source in your browser and the XML
  // code will be reasonably well formatted.
  public static void writePrettyFile(
                              String fname, Document doc){
    try{
      FileOutputStream out = new FileOutputStream(fname);

      XMLOutputter xmlOut = 
               new XMLOutputter(Format.getPrettyFormat());
      xmlOut.output(doc,out);

      out.flush();
      out.close();
    }catch (IOException e){
      System.err.println(e);
    }//end catch
  }//end writePrettyFile
  //----------------------------------------------------//
  
  //This method writes the XML code into an output file
  // in whitespace-normalized format. This format is more
  // compact and efficient than the pretty-print format.
  public static void writeCompactFile(
                              String fname, Document doc){
    try{
      FileOutputStream out = new FileOutputStream(fname);

      XMLOutputter xmlOut = 
              new XMLOutputter(Format.getCompactFormat());
      xmlOut.output(doc,out);

      out.flush();
      out.close();
    }catch (IOException e){
      System.err.println(e);
    }//end catch
  }//end writePrettyFile
  //----------------------------------------------------//

  //This method constructs and returns a reference to an
  // SVG root element node named svg with a viewBox
  // attribute to map the virtual dimensions into the
  // physical dimensions of the canvas.
  //By default, the min-x and min-y attribute values of
  // the viewBox are set to 0 0 and the value of the
  // preserveAspectRatio is set to none.
  //The svg element represents the canvas on which
  // various shapes can be drawn. The width and height
  // attribute values of the svg element establish the
  // physical size of the canvas on the screen. The
  // values of the viewBox attribute establish the
  // size of the canvas in "user units."  The
  // dimensions of the viewbox map directly into the
  // dimensions of the canvas.  This makes it possible
  // for the programmer to work in dimensions that are
  // convenient, such as 1000x1000, instead of having
  // to work with less convenient dimensions such as
  // 459x459.
  //Setting the value of the preserveAspectRatio
  // attribute to none prevents the system from
  // attempting to automatically preserve the aspect
  // ratio.  In this case, it is probably a good idea
  // for the programmer to make the ratio of the height
  // and width of the viewbox the same as the ratio of
  // the height and width of the canvas. That way, a
  // circle will look like a circle instead of looking
  // like an ellipse.
  //Note that the root element node does not have a parent
  // like the other nodes. Rather, it is passed as a 
  // parameter to the constructor for the Document node in
  // order to establish its place in the tree.
  public static Element makeSvg(
                      String ns,//namespace URI
                      int svgWidth,//physical screen width
                      int svgHeight,//physical height
                      int vbWidth,//virtual width
                      int vbHeight//virtual height
                      ){
    Element svg = new Element("svg",ns);
    
    //Set default attribute values.
    svg.setAttribute("version","1.1");
    svg.setAttribute("preserveAspectRatio","none");
    String vbMinX = "0 ";
    String vbMinY = "0 ";
    
    //Set user-specified attribute values. Note the 
    // format that is required to construct the attribute
    // value for the viewBox. If you need to call the
    // setAttribute method after this method returns to
    // change the attribute value for the viewBox
    // attribute, you will need to comply with the format
    // requirements for that value.
    svg.setAttribute("width",""+svgWidth);
    svg.setAttribute("height",""+svgHeight);
    svg.setAttribute("viewBox",
             vbMinX + vbMinY + ""+vbWidth + " "+vbHeight);
    
    return svg;
  }//end makeSvg
  //----------------------------------------------------//
  
  //This method constructs and returns a description node
  // for a given namespace and a given parent. Note that
  // this is SVG code.
  public static Element makeDescription(
              Element parent,//The parent of this element.
              String nameSpace,//The namespace.
              String text//Text content for this element.
              ){
    Element desc = new Element("desc",nameSpace);
    parent.addContent(desc);
    desc.setText(text);

    return desc;
  }//end makeDescription
  //----------------------------------------------------//
  
  //This method constructs and returns a comment node
  // for a given parent.
  public static Comment makeComment(
              Element parent,//The parent of this element.
              String text//Text content for this element.
              ){
    Comment comment = new Comment(text);
    parent.addContent(comment);

    return comment;
  }//end makeComment
  //----------------------------------------------------//

  //This method constructs and returns a rect node for a
  // given parent in a given namespace.  By default,the
  // stroke is black, the stroke-width is 1, and the fill
  // is none. Setting the fill to none has the effect of
  // causing the shape to be totally transparent.
  public static Element makeRect(
                  Element parent,
                  String namespace,
                  int x,//Coordinate of upper-left corner.
                  int y,//Coordinate of upper-left corner.
                  int width,
                  int height
                  ){
    Element rect = new Element("rect",namespace);
    parent.addContent(rect);
    
    //Set default attribute values.
    rect.setAttribute("fill","none");
    rect.setAttribute("stroke","black");
    rect.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    rect.setAttribute("x",""+x);
    rect.setAttribute("y",""+y);
    rect.setAttribute("width",""+width);
    rect.setAttribute("height",""+height);
    
    return rect;
  }//end makeRect
  //----------------------------------------------------//

  //This method constructs and returns an ellipse node for
  // a given parent in a given namespace.  By default,the
  // stroke is black, the stroke-width is 1, and the fill
  // is none.
  public static Element makeEllipse(
                                Element parent,
                                String namespace,
                                int cx,//Center coordinate
                                int cy,//Center coordinate
                                int rx,//Horizontal radius
                                int ry //Vertical radius
                                ){
    Element ellipse = new Element("ellipse",namespace);
    parent.addContent(ellipse);
    
    //Set default attribute vales
    ellipse.setAttribute("fill","none");
    ellipse.setAttribute("stroke","black");
    ellipse.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    ellipse.setAttribute("cx",""+cx);
    ellipse.setAttribute("cy",""+cy);
    ellipse.setAttribute("rx",""+rx);
    ellipse.setAttribute("ry",""+ry);
    
    return ellipse;
  }//end makeEllipse
  //----------------------------------------------------//

  //This method constructs and returns a polyline node for
  // a given parent in a given namespace..
  //The array of type int[], which contains the
  // coordinates for each point in the polyline, must
  // contain an even number of values for the polyline
  // to be drawn correctly. Otherwise, it simply won't be
  // drawn.
  //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.
  //The main difference between a polyline and a polygon
  // (see below) is that a polygon is automatically closed
  // by connecting the last point to the first point.
  // While you may close a polyline if you cause the
  // coordinates of the last point to match the
  // coordinates of the first point, it is not
  // automatically closed as in the case of a polygon.
  //Be careful if you fill a polyline that is not closed.
  // The results may not be what you expect.
  static Element makePolyline(Element parent,
                              String namespace,
                              int[] points){//See above.
    Element polyline = new Element("polyline",namespace);
    parent.addContent(polyline);

    //Set default attributes
    polyline.setAttribute("stroke","black");
    polyline.setAttribute("stroke-width","1");
    polyline.setAttribute("fill","none");
    
    //Set user specified attributes.
    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + " ";
    }//end for loop
    polyline.setAttribute("points",dataPoints);
    
    return polyline;
  }//end makePolyline
  //----------------------------------------------------//
  
  //This method constructs and returns a polygon node for
  // a given parent in a given namespace..
  //The array of type int[], which contains the
  // coordinates for each point in the polyline, must
  // contain an even number of values for the polyline
  // to be drawn correctly. Otherwise, it simply won't be
  // drawn.
  //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.
  //The main difference between a polygon and a polyline
  // (see above) is that a polygon is automatically closed
  // by connecting the last point to the first point.
  static Element makePolygon(Element parent,
                             String namespace,
                             int[] points){
    Element polygon = new Element("polygon",namespace);
    parent.addContent(polygon);
    
    //Set default attributes.
    polygon.setAttribute("stroke","black");
    polygon.setAttribute("stroke-width","1");
    polygon.setAttribute("fill","none");
    
    //Set user specified attributes.
    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + ",";
    }//end for loop
    polygon.setAttribute("points",dataPoints);

    return polygon;
  }//end makePolygon
  //----------------------------------------------------//
  
  //This method constructs and returns a line node for a
  // given parent in a given namespace.  By default,the
  // stroke is black and the stroke-width is 1.
  public static Element makeLine(
                                Element parent,
                                String namespace,
                                int x1,//Start coordinate
                                int y1,//Start coordinate
                                int x2,//End coordinate
                                int y2 //End coordinate
                                ){
    Element line = new Element("line",namespace);
    parent.addContent(line);
    
    //Set default attribute vales
    line.setAttribute("stroke","black");
    line.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    line.setAttribute("x1",""+x1);
    line.setAttribute("y1",""+y1);
    line.setAttribute("x2",""+x2);
    line.setAttribute("y2",""+y2);
    
    return line;
  }//end makeLine
  //----------------------------------------------------//
  
  //This method constructs and returns a circle node for a
  // given parent in a given namespace.  By default,the
  // stroke is black, the stroke-width is 1, and the fill
  // is none.
  public static Element makeCircle(
                                Element parent,
                                String namespace,
                                int cx,//Center coordinate
                                int cy,//Center coordinate
                                int r  //Radius
                                ){
    Element circle = new Element("circle",namespace);
    parent.addContent(circle);
    
    //Set default attribute vales
    circle.setAttribute("fill","none");
    circle.setAttribute("stroke","black");
    circle.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    circle.setAttribute("cx",""+cx);
    circle.setAttribute("cy",""+cy);
    circle.setAttribute("r",""+r);
    
    return circle;
  }//end makeCircle
  //----------------------------------------------------//
  
  //This is a utility method that is used to construct a
  // string that describes a grid pattern consisting of 
  // horizontal and vertical lines at a specified pixel
  // spacing for a rectangular area of a specified width
  // and height. The string is intended to be used as the
  // data string for a call to the method named makePath.
  //There is nothing about this method that is peculiar to
  // the use of JDOM.
  //Note that this method makes use of the special
  // horizontal and vertical lineto commands in an attempt
  // to reduce the download size and the bandwidth
  // requirements for a drawing containing a large number
  // of horizontal and vertical lines in the grid pattern.
  static String makeGridString(
                        int width,int height,int spacing){
    //Construct the data string for the vertical lines.
    String data = "M0,0 ";
    for(int cnt = 0;cnt < width;cnt += spacing){
      data += "v" + height + " M" + cnt + ",0 ";
    }//end for loop
    //Add the final vertical line.
    data += "v" + height + " n";
    
    //Now add the horizontal lines to the data string.
    data += "M0,0 ";
    for(int cnt = 0;cnt < height;cnt += spacing){
      data += "h" + width + " M0," + cnt + " ";
    }//end for loop
    //Add the final horizontal line.
    data += "h" + width + "n ";
    
    return data;

  }//end makeGridString
  //----------------------------------------------------//

  //This method returns a reference to a path. By
  // default, the stroke is set to black one pixel wide,
  // and the fill is set to none.
  //See the method named makeGridString for a utility
  // method that is designed to create the data string
  // for this method for the special case of drawing grids
  // that resemble graph paper. For other cases, simply
  // create a data string that is compatible with the SVG
  // path element.
  static Element makePath(Element parent,
                          String namespace,
                          String d){
    Element path  = new Element("path",namespace);
    parent.addContent(path);
    
    //Set default attribute values.
    path.setAttribute("stroke","black");
    path.setAttribute("stroke-width","1");
    path.setAttribute("fill","none");
    
    //Set user specified default values.
    path.setAttribute("d",d);
    return path;
  }//end makePath
  //----------------------------------------------------//
  
  /*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 parent node
   to which this node is to be appended so as to become a
   child of that node.
   
  The second parameter is a String that specifies the
   namespace for the element represented by the new 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.
   If there are no attributes, the value of this parameter
   should be null.
  
  An example of the recommended usage of the method
   follows:
  Element abc = JdomSvg.makeNode(
                     parent,
                     namespace,
                     "NodeType",
                     new String[]{"name","value",
                                  "name","value",
                                  "name","value"
                                 });//end call to makeNode
  */
  static Element makeNode(Element parent,
                          String namespace,
                          String nodeType,
                          String[] data){

    Element element = new Element(nodeType,namespace);
    parent.addContent(element);
  
    //Deal with elements that have no attributes.
    if(data == null){
      return element;
    }//end if
    
    //Extract the values from the array and construct
    // each of the attributes and its value.
    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 method creates a linear gradient node to which
  // stop elements must be appended. The id attribute is
  // used to differentiate this gradient node from other
  // gradient nodes in the same program scope.
  static Element makeLinearGradient(Element parent,
                                    String namespace,
                                    String id){
    Element gradient = 
                  new Element("linearGradient",namespace);
    parent.addContent(gradient);
    gradient.setAttribute("id",id);
    return gradient;
  }//End makeLinearGradient
  //----------------------------------------------------//
  
  //This method creates a radial gradient node to which
  // stop elements must be appended. The id attribute is
  // used to differentiate this gradient node from other
  // gradient nodes. See the SVG documentation for more
  // information on the various attributes of the radial
  // gradient element.
  static Element makeRadialGradient(Element parent,
                                    String namespace,
                                    String id,
                                    String gradientUnits,
                                    int cx,//center
                                    int cy,//center
                                    int r  //radius
                                    ){
    Element gradient = 
                  new Element("radialGradient",namespace);
    parent.addContent(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. See the SVG documentation for more
  // information on the various attributes of the gradient
  // stop element.                          
  static Element makeGradientStop(Element parent,
                                  String namespace,
                                  String offset,
                                  String color){
    Element stopElement = new Element("stop",namespace);
    parent.addContent(stopElement);
    
    stopElement.setAttribute("offset",offset);
    stopElement.setAttribute("stop-color",color);
    return stopElement;
  }//End makeGradientStop
  //----------------------------------------------------//
  
  //This method returns a reference to a text element
  // node. The x and y parameters specify the location of
  // the lower left corner of the first text character.
  // Note that this is graphic text and not XML text.
  static Element makeText(Element parent,
                          String namespace,
                          int x,
                          int y,
                          String text){
    Element textNode = new Element("text",namespace);
    parent.addContent(textNode);

    textNode.setAttribute("x",""+x);
    textNode.setAttribute("y",""+y);
    
    textNode.addContent(text);

    return textNode;
  }//end makeText
  //----------------------------------------------------//
  
  //This method constructs and returns a reference to a
  // standard XHTML root node named html using the
  // incoming namespace and the attributes that are hard
  // coded into the method.
  public static Element makeXhtmlRoot(String xns){

    //The reason for expanding the namespace to include
    // the pair of attributes is because JDOM won't allow
    // me to set an attribute with a name that contains a
    // colon. This is a workaround for a problem that I
    // don't know to solve otherwise.
    String expandedNs = 
                     xns + "" xml_lang="en" lang="en";

    return new Element("html",expandedNs);
  }//end makeXhtmlRoot
  //----------------------------------------------------//

}//end class JdomSvg

Listing 32. Program code for Svg19.

/*File Svg19.java,
Copyright 2007, R.G.Baldwin

The program named Svg18 was a stand alone desktop program 
that used JDOM to produce an output XHTML file with
embedded SVG/XML code.  That SVG code can be rendered by 
loading the file into an SVG graphics engine such as 
Firefox 2.0.0.4.

The purpose of this program is to convert the code from 
Svg18 into a servlet that will deliver XHTML code with 
the same embedded SVG/XML code to a client browser. That 
SVG/XML code should be properly rendered when the servlet
is accessed using an SVG capable browser such as
Firefox 2.0.0.4.

The following steps were required to make the conversion:

1.  Copy the jdom.jar file into the proper location on the
    server. On my jakarta-tomcat server running as a 
    localhost server, the proper location is:

    C:jakarta-tomcat-5.0.27webappsROOTWEB-INFlib

    This assumes that the servlet class files will be 
    copied into:

    C:jakarta-tomcat-5.0.27webappsROOTWEB-INFclasses

2.  Declare the following import directives in Svg19:

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

3.  Declare that the class named Svg19 extends the 
    HttpServlet class.

4.  Replace the signature for the main method in Svg18 
    with the following signature for the doGet method in 
    Svg19:

    public void doGet(HttpServletRequest req,
                      HttpServletResponse res)
                             throws ServletException,
                              IOException{

5.  Execute the following statement to set the content 
    type delivered by the servlet. (See further discussion
    of this topic below.)

    res.setContentType("image/svg+");

6.  Use the following expression to get an output stream 
    to be used by the XMLOutputter object to send the 
    output data to the client browser:

    res.getWriter()

7.  Start the server running and access the servlet using
    Firefox 2.0.0.4 at http://localhost/servlet/Svg19


In order to cause the servlet to send the output to the 
client browser instead of writing it to a file, I defined
the following new methods and placed them in the JdomSvg 
graphics library class:

    writePrettyPrintWriter(PrintWriter out, Document doc)
    writeCompactPrintWriter(PrintWriter out, Document doc)

Depending on whether I wanted a prettyPrint format or a 
compact format, I modified an output statement from the 
main method of Svg18 to read as follows:

    JdomSvg.writePrettyPrintWriter(res.getWriter(),doc);
    JdomSvg.writeCompactPrintWriter(res.getWriter(),doc);

Otherwise, it was not necessary or me to make any 
additional changes to the program named Svg18 to convert 
it into the servlet. See the program named Svg18 for 
additional comments regarding the program.

Tested using J2SE 6.0, JDOM 1.0, Firefox 2.0.0.4, and
jakarta-tomcat-5.0.27 running as a localhost server
under WinXP.
*********************************************************/
import java.io.*;
import org.jdom.*;
import org.jdom.output.XMLOutputter;
import org.jdom.output.Format;

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

public class Svg19 extends HttpServlet{
  
  public void doGet(HttpServletRequest req,
                    HttpServletResponse res)
                           throws ServletException,
                            IOException{
                              
    //The following statement was disabled for the 
    // reason explained in the comments above.
    res.setContentType("image/svg+xml");

    //Create a String variable containing the XHTML
    // namespace URI to reduce the amount of typing that 
    // is required later. Note that the variable name is 
    // short and easy to type.
    String xns = "http://www.w3.org/1999/xhtml";
  
    //For clarity, create strings containing the name of
    // the element that is constrained by the DTD (the
    // html root element), the Public ID of the DTD, and 
    // the System ID of the DTD.
    String dtdConstrainedElement = "html";
    String dtdPublicID = 
                 "-//W3C//DTD XHTML 1.0 Transitional//EN";
    String dtdSystemID = "http://www.w3.org/TR/xhtml1/"
                          + "DTD/xhtml1-transitional.dtd";

    //Create the DTD declaration node.
    DocType docType = new DocType(
           dtdConstrainedElement,dtdPublicID,dtdSystemID);

    //Create the XHTML root node named html.
    Element html = JdomSvg.makeXhtmlRoot(xns);
    
    //Create the document node.
    Document doc = new Document(html,docType);

    //Add optional and required elements to the document
    // node, placing text content in the title node, 
    Element head = JdomSvg.makeNode(html,xns,"head",null);
    Element title = JdomSvg.makeNode(
                                   head,xns,"title",null);
    title.setText("XHTML/SVG Servlet Graphic Demo.");
                         
    Element body = JdomSvg.makeNode(html,xns,"body",null);

    //Put a paragraph element in the body element.
    Element temp = JdomSvg.makeNode(body,xns,"p",null);
    temp.setText("There is a graphic below here.");

    //Now switch your thinking into graphics programming
    // mode to place a large and fairly complex SVG
    // graphic in the body element.

    //Create a String variable containing the SVG
    // namespace URI to reduce the amount of typing that
    // is required later. Note that the variable name is
    // short and easy to type.
    String ns = "http://www.w3.org/2000/svg";

    //This program creates and displays exactly the same
    // graphics elements that were created and displayed
    // by the earlier program named Svg17.  However, the
    // physical size of the canvas in this program was
    // reduced to 75-percent of its size in the earlier
    // program for the purpose of demonstrating the use of
    // the word "Scalable" in the name Scalable Vector
    // Graphics (SVG).  Note also that because of the use
    // of virtual dimensions implemented through the use
    // of the viewBox, it was not necessary to modify any
    // of the SVG graphics code to accommodate the
    // reduction in the overall physical size of the
    // graphics elements.

    //Physical dimensions of the graphic on the screen.
    int svgWidth = (int)(460*0.75);
    int svgHeight = (int)(552*0.75);
    
    //Virtual dimensions of the graphic in user units.
    int viewBoxWidth = 500;
    int viewBoxHeight = 500*svgHeight/svgWidth;

    //Create the SVG root element named svg.
    Element svg = JdomSvg.makeSvg(ns,
                                  svgWidth,
                                  svgHeight,
                                  viewBoxWidth,
                                  viewBoxHeight);

    //The makeSvg method does not automatically add the
    // new node to a parent for reasons that are explained
    // in the method.  Therefore, it is necessary to add
    // the node to the parent outside of the makeSvg
    // method.
    body.addContent(svg);

    //Begin creating actual graphics elements.  With the
    // exception of the changes in the physical dimensions
    // of the graphic on the screen as implemented above,
    // the graphics code in this program is the same as
    // the code in the earlier program named Svg017.

    //Draw light green grid lines every 10 user units on
    // the basis of the virtual dimensions in user units.
    //First create the path data.
    String gridData = JdomSvg.makeGridString(
                           viewBoxWidth,viewBoxHeight,10);

    //Create the path element and add it to the root.
    temp = JdomSvg.makePath(svg,ns,gridData);
    //Set the color to a very light green.
    temp.setAttribute("stroke","#ccffcc");

    //Using the same procedure, draw darker green grid
    // lines every 50 user units and draw them on top of
    // the existing grid lines.
    gridData = JdomSvg.makeGridString(
                           viewBoxWidth,viewBoxHeight,50);
    temp = JdomSvg.makePath(svg,ns,gridData);
    temp.setAttribute("stroke","#99ff99");
  
    //Draw even darker green grid lines every 100 user
    // units.
    gridData = JdomSvg.makeGridString(
                          viewBoxWidth,viewBoxHeight,100);
    temp = JdomSvg.makePath(svg,ns,gridData);
    temp.setAttribute("stroke","#22ff22");
    
    //Draw an even darker green border on the perimeter of
    // the grid.
    temp = JdomSvg.makeRect(svg,ns,1,1,
                          viewBoxWidth-2,viewBoxHeight-2);
    temp.setAttribute("stroke","#00ff00");
    temp.setAttribute("stroke-width","2");
    
    //Call the makeNode method to create a defs element
    // that will be the container for gradient
    // definitions.
    Element defs = JdomSvg.makeNode(svg,//parent
                                    ns,//namespace
                                    "defs",
                                    null//no attributes
                                    );
    
    //Define gradientA, which provides a linear gradient
    // that starts with yellow, changes to red, and
    // changes back to yellow going from left to right.
    Element gradientA = JdomSvg.makeLinearGradient(
                                         defs,//parent
                                         ns,//namespace
                                         "gradientA");//id
                                       
    //Create three stop nodes that identify the colors
    // used to produce gradientA and specify where the
    // colors begin and end, 
    JdomSvg.makeGradientStop(gradientA,//parent
                             ns,
                             "4%",//start here
                             "yellow");//color
                           
    JdomSvg.makeGradientStop(gradientA,
                             ns,  
                             "50%",
                            "red");
                           
    JdomSvg.makeGradientStop(gradientA,
                             ns,
                            "96%",
                            "yellow");

    //Call the makeNode method to draw an ellipse. Fill it
    // with linear gradientA and rotate it by 30 degrees.
    // Note that I could have called the makeEllipse
    // method, but I wanted to demonstrate the use of the
    // more general makeNode method for a graphics element
    // that has several attributes.
    JdomSvg.makeNode(
            svg,//parent
            ns,
            "ellipse",//node type
            new String[]{"cx","150",
                         "cy","80",
                         "rx","100",
                         "ry","40",
                         "fill","url(#gradientA)",
                         "stroke","blue",
                         "stroke-width","3",
                         "opacity","0.5",
                         "transform","translate(150,80) "
                                   + "rotate(-30) "
                                   + "translate(-150,-80)"
                        }//end array definition
    );//end makeNode method
    
    //Note: Setting the opacity attribute value to 0.6 is
    // of no consequence when the output file is rendered
    // using Firefox 2.0.0.4.  The gradient continues to
    // be opaque.  However, this is a weakness in the
    // Firefox rendering engine and is not a limitation of
    // SVG.  When the file is rendered using an Adobe
    // rendering engine, the gradient is partially
    // transparent as it should be.
    
    //Define gradientB, which provides a radial gradient
    // that goes through yellow, red, green, and blue.
    // Use it to fill a rectangle.
    int rectCornerX = 300;
    int rectCornerY = 10;
    int rectWidth = 150;
    int rectHeight = 150;
    Element gradientB = JdomSvg.makeRadialGradient(
                               defs,//parent
                               ns,//namespace
                               "gradientB",//ID
                               "userSpaceOnUse",
                               rectCornerX,//cx
                               rectCornerY,//cy
                               ((int)(rectWidth*1.2)));//r
                                       
    //Create three stop nodes that identify the colors
    // used to produce gradientB and to specify where
    // the colors begin and end, 
    JdomSvg.makeGradientStop(gradientB,//parent
                             ns,//namespace
                             "10%",//start here
                             "yellow");//color
                           
    JdomSvg.makeGradientStop(gradientB,
                             ns,  
                             "33%",
                            "red");
                           
    JdomSvg.makeGradientStop(gradientB,
                             ns,
                            "66%",
                            "green");
                            
    JdomSvg.makeGradientStop(gradientB,
                             ns,
                            "96%",
                            "blue");
                            
    //Draw the rectangle and fill it with gradientB.
    temp = JdomSvg.makeRect(svg,
                            ns,
                            rectCornerX,
                            rectCornerY,
                            rectWidth,
                            rectHeight);
    temp.setAttribute("fill","url(#gradientB)");

    //Draw a cubic Bezier curve consisting of two Bezier
    // segments.  First draw a polygon that shows the
    // start points, the end points, and the control
    // points for the segments. Fill the polygon with
    // yellow to provide a background color for the 
    // Bezier curve.
    temp = JdomSvg.makePolygon(
                     svg,
                     ns,
                     new int[] {180,290,50,250,150,200,
                                150,400,250,350,120,310});
    temp.setAttribute("stroke","red");
    temp.setAttribute("fill","yellow");
    
    //Draw the cubic curve. Note the use of absolute
    // coordinate values only and also the use of the S
    // command. Stroke the curve with red and fill it with
    // blue.
    temp = JdomSvg.makePath(svg,ns,"M180,290 C50,250,"
                    + "150,200,150,300 S250,350,120,310");
    temp.setAttribute("stroke","red");
    temp.setAttribute("fill","blue");
    
    //Draw a quadratic Bezier curve consisting of two
    // Bezier segments.  First draw a polyline that shows
    // the start points, the end points, and the control
    // points.  Fill the polyline with yellow color with
    // an opacity attribute value of 0.5.  This allows
    // the grid to show through the filled polyline.
    temp = JdomSvg.makePolyline(
                       svg,
                       ns,
                       new int[] {300,300,400,200,500,300,
                                  400,400,300,300});
    temp.setAttribute("stroke","red");
    temp.setAttribute("fill","yellow");
    temp.setAttribute("opacity","0.5");

    //Draw the quadratic curve. Note the use of relative
    // coordinate values. Stroke the curve with a wide
    // red stroke and fill it with blue.
    temp = JdomSvg.makePath(svg,ns,
              "M300,300 q100,-100,200,0,-100,100,-200,0");

    temp.setAttribute("stroke","red");
    temp.setAttribute("stroke-width","3");
    temp.setAttribute("fill","blue");


    //Draw an elliptical arc to illustrate the appearance
    // of such an arc with no rotation. Stroke it with
    // yellow and fill it with red.
    temp = JdomSvg.makePath(svg,ns,
                        "M100,500 a70,30 0 1,0 50,-50 z");
    temp.setAttribute("stroke","yellow");
    temp.setAttribute("stroke-width","2");
    temp.setAttribute("fill","red");

    //Draw an elliptical arc to illustrate the appearance
    // of such an arc with a rotation of 45 degrees.
    // Stroke it with yellow and fill it with red.
    temp = JdomSvg.makePath(svg,ns,
                       "M350,500 a70,30 45 1,0 50,-50 z");
    temp.setAttribute("stroke","yellow");
    temp.setAttribute("stroke-width","2");
    temp.setAttribute("fill","red");

    //Label the grid. First label the vertical lines.
    for(int cnt = 0;cnt < viewBoxWidth;cnt += 50){
      JdomSvg.makeText(
                     svg,ns,cnt,viewBoxHeight - 4,""+cnt);
    }//end for loop

    //Now label the horizontal lines.    
    for(int cnt = 0;cnt < viewBoxHeight;cnt += 50){
      JdomSvg.makeText(svg,ns,3,cnt,""+cnt);
    }//end for loop
  
    //Label each of the graphic elements
    JdomSvg.makeText(svg,ns,50,180,
                          "Ellipse with linear gradient");
    JdomSvg.makeText(svg,ns,270,180,
                        "Rectangle with radial gradient");
    JdomSvg.makeText(svg,ns,100,420,
                                    "Cubic Bezier curve");
    JdomSvg.makeText(svg,ns,280,420,
                         "Quadratic Bezier with opacity");
    JdomSvg.makeText(svg,ns,50,555,
                       "Elliptical arc without rotation");
    JdomSvg.makeText(svg,ns,300,555,
                          "Elliptical arc with rotation");
    
    //End creation of graphics elements.
    
 
    //Create a paragraph element containing text content
    // and place it at the end of the body element.
    temp = JdomSvg.makeNode(body,xns,"p",null);
    temp.setText("There is a graphic above here.");
 
     //Write the output to the client browser.
    //JdomSvg.writePrettyPrintWriter(res.getWriter(),doc);
    JdomSvg.writeCompactPrintWriter(res.getWriter(),doc);
    
  }//end doGet
  //----------------------------------------------------//

}//end class Svg19
//======================================================//

//This is a graphics library that is designed to eliminate
// much of the effort involved in writing JDOM code to
// create SVG output. The library contains individual
// static methods that are used to construct and return
// many of the standard SVG graphics elements. It also
// contains a method named makeNode that can be used to
// create any graphic element.
//Many of the methods have default attribute values.  If
// you need different attribute values for a particular
// graphic element, you can call the setAttribute method
// on the returned value to change the attribute values 
// after the method returns.
//Many of the methods set the stroke attribute value to
// black and set the stroke-width attribute value to 1
// by default.  If you don't want to be able to see the
// outline of the shape, change the stroke-width attribute
// value to 0 after the method returns.
class JdomSvg{
  //----------------------------------------------------//

  //This method writes the XML code into an output file
  // in pretty-print format. The pretty-print format
  // is less efficient than the compact format, but it
  // is very useful during test and debugging because
  // you can view source in your browser and the XML
  // code will be reasonably well formatted.
  public static void writePrettyFile(
                              String fname, Document doc){
    try{
      FileOutputStream out = new FileOutputStream(fname);

      XMLOutputter xmlOut = 
               new XMLOutputter(Format.getPrettyFormat());
      xmlOut.output(doc,out);

      out.flush();
      out.close();
    }catch (IOException e){
      System.err.println(e);
    }//end catch
  }//end writePrettyFile
  //----------------------------------------------------//
  
  //This method writes the XML code into an output file
  // in whitespace-normalized format. This format is more
  // compact and efficient than the pretty-print format.
  public static void writeCompactFile(
                              String fname, Document doc){
    try{
      FileOutputStream out = new FileOutputStream(fname);

      XMLOutputter xmlOut = 
              new XMLOutputter(Format.getCompactFormat());
      xmlOut.output(doc,out);

      out.flush();
      out.close();
    }catch (IOException e){
      System.err.println(e);
    }//end catch
  }//end writeCompactFile
  //----------------------------------------------------//

  //This method writes the XML code into a PrintWriter
  // output stream in pretty-print format.
  public static void writePrettyPrintWriter(
                           PrintWriter out, Document doc){
    try{
      XMLOutputter xmlOut = 
               new XMLOutputter(Format.getPrettyFormat());
      xmlOut.output(doc,out);

      out.flush();
      out.close();
    }catch (IOException e){
      System.err.println(e);
    }//end catch
  }//end writePrettyFile
  //----------------------------------------------------//
  
  //This method writes the XML code into PrintWriter
  // output stream in whitespace-normalized format.
  public static void writeCompactPrintWriter(
                           PrintWriter out, Document doc){
    try{
      XMLOutputter xmlOut = 
              new XMLOutputter(Format.getCompactFormat());
      xmlOut.output(doc,out);

      out.flush();
      out.close();
    }catch (IOException e){
      System.err.println(e);
    }//end catch
  }//end writeCompactPrintWriter
  //----------------------------------------------------//
  //This method constructs and returns a reference to an
  // SVG root element node named svg with a viewBox
  // attribute to map the virtual dimensions into the
  // physical dimensions of the canvas.
  //By default, the min-x and min-y attribute values of
  // the viewBox are set to 0 0 and the value of the
  // preserveAspectRatio is set to none.
  //The svg element represents the canvas on which
  // various shapes can be drawn. The width and height
  // attribute values of the svg element establish the
  // physical size of the canvas on the screen. The
  // values of the viewBox attribute establish the
  // size of the canvas in "user units."  The
  // dimensions of the viewbox map directly into the
  // dimensions of the canvas.  This makes it possible
  // for the programmer to work in dimensions that are
  // convenient, such as 1000x1000, instead of having
  // to work with less convenient dimensions such as
  // 459x459.
  //Setting the value of the preserveAspectRatio
  // attribute to none prevents the system from
  // attempting to automatically preserve the aspect
  // ratio.  In this case, it is probably a good idea
  // for the programmer to make the ratio of the height
  // and width of the viewbox the same as the ratio of
  // the height and width of the canvas. That way, a
  // circle will look like a circle instead of looking
  // like an ellipse.
  //Note that the root element node does not have a parent
  // like the other nodes. Rather, it is passed as a 
  // parameter to the constructor for the Document node in
  // order to establish its place in the tree.
  public static Element makeSvg(
                      String ns,//namespace URI
                      int svgWidth,//physical screen width
                      int svgHeight,//physical height
                      int vbWidth,//virtual width
                      int vbHeight//virtual height
                      ){
    Element svg = new Element("svg",ns);
    
    //Set default attribute values.
    svg.setAttribute("version","1.1");
    svg.setAttribute("preserveAspectRatio","none");
    String vbMinX = "0 ";
    String vbMinY = "0 ";
    
    //Set user-specified attribute values. Note the 
    // format that is required to construct the attribute
    // value for the viewBox. If you need to call the
    // setAttribute method after this method returns to
    // change the attribute value for the viewBox
    // attribute, you will need to comply with the format
    // requirements for that value.
    svg.setAttribute("width",""+svgWidth);
    svg.setAttribute("height",""+svgHeight);
    svg.setAttribute("viewBox",
             vbMinX + vbMinY + ""+vbWidth + " "+vbHeight);
    
    return svg;
  }//end makeSvg
  //----------------------------------------------------//
  
  //This method constructs and returns a description node
  // for a given namespace and a given parent. Note that
  // this is SVG code.
  public static Element makeDescription(
              Element parent,//The parent of this element.
              String nameSpace,//The namespace.
              String text//Text content for this element.
              ){
    Element desc = new Element("desc",nameSpace);
    parent.addContent(desc);
    desc.setText(text);

    return desc;
  }//end makeDescription
  //----------------------------------------------------//
  
  //This method constructs and returns a comment node
  // for a given given parent.
  public static Comment makeComment(
              Element parent,//The parent of this element.
              String text//Text content for this element.
              ){
    Comment comment = new Comment(text);
    parent.addContent(comment);

    return comment;
  }//end makeComment
  //----------------------------------------------------//

  //This method constructs and returns a rect node for a
  // given parent in a given namespace.  By default,the
  // stroke is black, the stroke-width is 1, and the fill
  // is none. Setting the fill to none has the effect of
  // causing the shape to be totally transparent.
  public static Element makeRect(
                  Element parent,
                  String namespace,
                  int x,//Coordinate of upper-left corner.
                  int y,//Coordinate of upper-left corner.
                  int width,
                  int height
                  ){
    Element rect = new Element("rect",namespace);
    parent.addContent(rect);
    
    //Set default attribute values.
    rect.setAttribute("fill","none");
    rect.setAttribute("stroke","black");
    rect.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    rect.setAttribute("x",""+x);
    rect.setAttribute("y",""+y);
    rect.setAttribute("width",""+width);
    rect.setAttribute("height",""+height);
    
    return rect;
  }//end makeRect
  //----------------------------------------------------//

  //This method constructs and returns an ellipse node for
  // a given parent in a given namespace.  By default,the
  // stroke is black, the stroke-width is 1, and the fill
  // is none.
  public static Element makeEllipse(
                                Element parent,
                                String namespace,
                                int cx,//Center coordinate
                                int cy,//Center coordinate
                                int rx,//Horizontal radius
                                int ry //Vertical radius
                                ){
    Element ellipse = new Element("ellipse",namespace);
    parent.addContent(ellipse);
    
    //Set default attribute vales
    ellipse.setAttribute("fill","none");
    ellipse.setAttribute("stroke","black");
    ellipse.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    ellipse.setAttribute("cx",""+cx);
    ellipse.setAttribute("cy",""+cy);
    ellipse.setAttribute("rx",""+rx);
    ellipse.setAttribute("ry",""+ry);
    
    return ellipse;
  }//end makeEllipse
  //----------------------------------------------------//

  //This method constructs and returns a polyline node for
  // a given parent in a given namespace..
  //The array of type int[], which contains the
  // coordinates for each point in the polyline, must
  // contain an even number of values for the polyline
  // to be drawn correctly. Otherwise, it simply won't be
  // drawn.
  //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.
  //The main difference between a polyline and a polygon
  // (see below) is that a polygon is automatically closed
  // by connecting the last point to the first point.
  // While you may close a polyline if you cause the
  // coordinates of the last point to match the
  // coordinates of the first point, it is not
  // automatically closed as in the case of a polygon.
  //Be careful if you fill a polyline that is not closed.
  // The results may not be what you expect.
  static Element makePolyline(Element parent,
                              String namespace,
                              int[] points){//See above.
    Element polyline = new Element("polyline",namespace);
    parent.addContent(polyline);

    //Set default attributes
    polyline.setAttribute("stroke","black");
    polyline.setAttribute("stroke-width","1");
    polyline.setAttribute("fill","none");
    
    //Set user specified attributes.
    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + " ";
    }//end for loop
    polyline.setAttribute("points",dataPoints);
    
    return polyline;
  }//end makePolyline
  //----------------------------------------------------//
  
  //This method constructs and returns a polygon node for
  // a given parent in a given namespace..
  //The array of type int[], which contains the
  // coordinates for each point in the polyline, must
  // contain an even number of values for the polyline
  // to be drawn correctly. Otherwise, it simply won't be
  // drawn.
  //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.
  //The main difference between a polygon and a polyline
  // (see above) is that a polygon is automatically closed
  // by connecting the last point to the first point.
  static Element makePolygon(Element parent,
                             String namespace,
                             int[] points){
    Element polygon = new Element("polygon",namespace);
    parent.addContent(polygon);
    
    //Set default attributes.
    polygon.setAttribute("stroke","black");
    polygon.setAttribute("stroke-width","1");
    polygon.setAttribute("fill","none");
    
    //Set user specified attributes.
    String dataPoints = "";
    for(int cnt=0;cnt<points.length;cnt++){
      dataPoints += "" + points[cnt] + ",";
    }//end for loop
    polygon.setAttribute("points",dataPoints);

    return polygon;
  }//end makePolygon
  //----------------------------------------------------//
  
  //This method constructs and returns a line node for a
  // given parent in a given namespace.  By default,the
  // stroke is black and the stroke-width is 1.
  public static Element makeLine(
                                Element parent,
                                String namespace,
                                int x1,//Start coordinate
                                int y1,//Start coordinate
                                int x2,//End coordinate
                                int y2 //End coordinate
                                ){
    Element line = new Element("line",namespace);
    parent.addContent(line);
    
    //Set default attribute vales
    line.setAttribute("stroke","black");
    line.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    line.setAttribute("x1",""+x1);
    line.setAttribute("y1",""+y1);
    line.setAttribute("x2",""+x2);
    line.setAttribute("y2",""+y2);
    
    return line;
  }//end makeLine
  //----------------------------------------------------//
  
  //This method constructs and returns a circle node for a
  // given parent in a given namespace.  By default,the
  // stroke is black, the stroke-width is 1, and the fill
  // is none.
  public static Element makeCircle(
                                Element parent,
                                String namespace,
                                int cx,//Center coordinate
                                int cy,//Center coordinate
                                int r  //Radius
                                ){
    Element circle = new Element("circle",namespace);
    parent.addContent(circle);
    
    //Set default attribute vales
    circle.setAttribute("fill","none");
    circle.setAttribute("stroke","black");
    circle.setAttribute("stroke-width","1");
    
    //Set user specified attribute values.
    circle.setAttribute("cx",""+cx);
    circle.setAttribute("cy",""+cy);
    circle.setAttribute("r",""+r);
    
    return circle;
  }//end makeCircle
  //----------------------------------------------------//
  
  //This is a utility method that is used to construct a
  // string that describes a grid pattern consisting of 
  // horizontal and vertical lines at a specified pixel
  // spacing for a rectangular area of a specified width
  // and height. The string is intended to be used as the
  // data string for a call to the method named makePath.
  //There is nothing about this method that is peculiar to
  // the use of JDOM.
  //Note that this method makes use of the special
  // horizontal and vertical lineto commands in an attempt
  // to reduce the download size and the bandwidth
  // requirements for a drawing containing a large number
  // of horizontal and vertical lines in the grid pattern.
  static String makeGridString(
                        int width,int height,int spacing){
    //Construct the data string for the vertical lines.
    String data = "M0,0 ";
    for(int cnt = 0;cnt < width;cnt += spacing){
      data += "v" + height + " M" + cnt + ",0 ";
    }//end for loop
    //Add the final vertical line.
    data += "v" + height + " n";
    
    //Now add the horizontal lines to the data string.
    data += "M0,0 ";
    for(int cnt = 0;cnt < height;cnt += spacing){
      data += "h" + width + " M0," + cnt + " ";
    }//end for loop
    //Add the final horizontal line.
    data += "h" + width + "n ";
    
    return data;

  }//end makeGridString
  //----------------------------------------------------//

  //This method returns a reference to a path. By
  // default, the stroke is set to black one pixel wide,
  // and the fill is set to none.
  //See the method named makeGridString for a utility
  // method that is designed to create the data string
  // for this method for the special case of drawing grids
  // that resemble graph paper. For other cases, simply
  // create a data string that is compatible with the SVG
  // path element.
  static Element makePath(Element parent,
                          String namespace,
                          String d){
    Element path  = new Element("path",namespace);
    parent.addContent(path);
    
    //Set default attribute values.
    path.setAttribute("stroke","black");
    path.setAttribute("stroke-width","1");
    path.setAttribute("fill","none");
    
    //Set user specified default values.
    path.setAttribute("d",d);
    return path;
  }//end makePath
  //----------------------------------------------------//
  
  /*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 parent node
   to which this node is to be appended so as to become a
   child of that node.
   
  The second parameter is a String that specifies the
   namespace for the element represented by the new 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.
   If there are no attributes, the value of this parameter
   should be null.
  
  An example of the recommended usage of the method
   follows:
  Element abc = JdomSvg.makeNode(
                     parent,
                     namespace,
                     "NodeType",
                     new String[]{"name","value",
                                  "name","value",
                                  "name","value"
                                 });//end call to makeNode
  */
  static Element makeNode(Element parent,
                          String namespace,
                          String nodeType,
                          String[] data){

    Element element = new Element(nodeType,namespace);
    parent.addContent(element);
  
    //Deal with elements that have no attributes.
    if(data == null){
      return element;
    }//end if
    
    //Extract the values from the array and construct
    // each of the attributes and its value.
    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 method creates a linear gradient node to which
  // stop elements must be appended. The id attribute is
  // used to differentiate this gradient node from other
  // gradient nodes in the same program scope.
  static Element makeLinearGradient(Element parent,
                                    String namespace,
                                    String id){
    Element gradient = 
                  new Element("linearGradient",namespace);
    parent.addContent(gradient);
    gradient.setAttribute("id",id);
    return gradient;
  }//End makeLinearGradient
  //----------------------------------------------------//
  
  //This method creates a radial gradient node to which
  // stop elements must be appended. The id attribute is
  // used to differentiate this gradient node from other
  // gradient nodes. See the SVG documentation for more
  // information on the various attributes of the radial
  // gradient element.
  static Element makeRadialGradient(Element parent,
                                    String namespace,
                                    String id,
                                    String gradientUnits,
                                    int cx,//center
                                    int cy,//center
                                    int r  //radius
                                    ){
    Element gradient = 
                  new Element("radialGradient",namespace);
    parent.addContent(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. See the SVG documentation for more
  // information on the various attributes of the gradient
  // stop element.                          
  static Element makeGradientStop(Element parent,
                                  String namespace,
                                  String offset,
                                  String color){
    Element stopElement = new Element("stop",namespace);
    parent.addContent(stopElement);
    
    stopElement.setAttribute("offset",offset);
    stopElement.setAttribute("stop-color",color);
    return stopElement;
  }//End makeGradientStop
  //----------------------------------------------------//
  
  //This method returns a reference to a text element
  // node. The x and y parameters specify the location of
  // the lower left corner of the first text character.
  // Note that this is graphic text and not XML text.
  static Element makeText(Element parent,
                          String namespace,
                          int x,
                          int y,
                          String text){
    Element textNode = new Element("text",namespace);
    parent.addContent(textNode);

    textNode.setAttribute("x",""+x);
    textNode.setAttribute("y",""+y);
    
    textNode.addContent(text);

    return textNode;
  }//end makeText
  //----------------------------------------------------//
  
  //This method constructs and returns a reference to a
  // standard XHTML root node named html using the
  // incoming namespace and the attributes that are hard
  // coded into the method.
  public static Element makeXhtmlRoot(String xns){

    //The reason for expanding the namespace to include
    // the pair of attributes is because JDOM won't allow
    // me to set an attribute with a name that contains a
    // colon. This is a workaround for a problem that I
    // don't know to solve otherwise.
    String expandedNs = 
                     xns + "" xml_lang="en" lang="en";

    return new Element("html",expandedNs);
  }//end makeXhtmlRoot
  //----------------------------------------------------//

}//end class JdomSvg


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.

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

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories