Java Programming Notes # 2214
- Preface
- General
background information - Preview
- Discussion and
sample code - Run the program
- Summary
- What’s next?
- Complete program
listings - Copyright
- Resources
- About the author
Preface
General
This is the second lesson in a series designed to teach you how to write
servlets to produce SVG code that will be rendered in graphic form by an
SVG-compatible browser.
An SVG graphics library
In the previous lesson titled "Java JAXP, Creating graphics using Java and
SVG" (see Resources) I taught you how write your
own SVG graphics library to eliminate, or at least alleviate the requirement to
write raw XML code or raw JAXP DOM code, making it possible for you to produce
SVG output simply by making typical Java method calls.
At the end of the previous lesson, I promised that this lesson would teach
you how to take what you learned in the previous lesson and apply it to the
generation of XHTML files containing in-line SVG/XML code. I also promised that
this lesson would teach you how to apply that knowledge to the writing of
servlets that produce XHTML output containing in-line SVG/XML code. Upon
further consideration, however, I decided to delay that material until the
lesson following this one.
Upgrading the SVG graphics library
In this lesson, I will provide two more methods for the SVG graphics library
that make it even easier to write servlets to produce SVG output. The
first method eliminates the frustration of dealing with the escape sequences
required for the quotation marks that surround attribute values in XML.
The second method makes it possible to create a general node in the DOM tree
being used to represent an SVG graphic.
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. You may also find it useful
to open a third browser window at the Resources section near the end of the
document. That will make it easy for you to open those resources when they
are mentioned in the text.
Figures
- Figure 1. Svg07 screen output.
- Figure 2. Recommended usage of the makeElement
method. - Figure 3. Graphic output from Svg08.
- Figure 4. Recommended usage of the makeNode
method.
Listings
- Listing 1. Beginning of the class named Svg07.
- Listing 2. The beginning of the makeElement
method. - Listing 3. Dealing with elements that have no
attributes. - Listing 4. Processing the attributes.
- Listing 5. Decision: empty element or non-empty
element. - Listing 6. Remaining code in the main method.
- Listing 7. Begin creating the DOM tree.
- Listing 8. Create the root node named svg.
- Listing 9. Beginning of the makeNode method.
- Listing 10. Append new node to its parent.
- Listing 11. Nodes that have no attributes.
- Listing 12. Processing the attribute names and
values. - Listing 13. Show outline of the canvas.
- Listing 14. Create a group container named g.
- Listing 15. Draw a blue line.
- Listing 16. The makeLine method.
- Listing 17. Draw the black ellipse.
- Listing 18. The makeEllipse method.
- Listing 19. Draw the red ellipse.
- Listing 20. Draw the green rotated ellipse.
- Listing 21. Transform the DOM and write the
output file. - Listing 22. The program named Svg07.
- Listing 23. The program named Svg08.
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
According to the W3C
"SVG is a language for describing two-dimensional graphics in XML. SVG allows
for three types of graphic objects: vector graphic shapes (e.g., paths
consisting of straight lines and curves), images and text. Graphical objects can
be grouped, styled, transformed and composited into previously rendered objects.
The feature set includes nested transformations, clipping paths, alpha masks,
filter effects and template objects."
Not easy reading
The Scalable Vector Graphics (SVG) 1.1
Specification is extensive. Unfortunately, a person needs a fairly
strong background in XML to be able to understand much of the information in the
specification, thereby reducing the accessibility of that information to Java
programmers who lack such a background in XML.
Will present in a more understandable form
While I don’t claim to have expertise in XML, I do know enough about the
topic that I can usually make sense of the material provided in the SVG
specification. One of my objectives for this series is to extract much of the important
information from the specification and to present it in such a way as to make it
accessible to Java programmers who lack a strong background in XML and who don’t
have the time available to gain that background knowledge.
The bottom line – great graphics in the browser
|
The bottom line is that Java programs can be written to produce XML
output, which, when presented to an SVG rendering engine, (such as Firefox 1.5 or
IE 6 with the SVG plug-in installed), will cause the display of graphic material
in the following three categories:
- Vector graphic shapes
- Images
- Text
As a practical matter, when combined with the use of servlets, this makes it
possible to display graphic material in the client area of a web browser that
competes favorably with the graphic material that can be displayed using the
Java 2D API in desktop applications.
Preview
|
The program named Svg07
Despite your best intentions, when you are writing Java programs that produce
XML output, you must occasionally write an output statement that produces raw
XML code, often in the form of a very complex XML element having multiple
attributes. One of the most frustrating things about writing Java code to
create elements in XML (also XHTML and HTML) is having to deal with the
escape sequences for the many required quotation marks that surround attribute
values.
The program named Svg07 demonstrates a method that I will add to my
SVG graphics library, which can be used to easily create XML elements without
having to deal with those escape sequences. The elements produced by the
method may or may not be empty, and may or may not have attributes.
The program named Svg08
The program named Svg08 uses a similar technique as Svg07 to deal with another issue that frequently
surfaces when using Java to create SVG graphics. This issue has to do with
the accommodation of a very large number of possible attributes that can be
applied to each node in the DOM tree.
My original SVG graphics library
The previous lesson (see Resources) presented
a small proof-of-concept SVG graphics library that provided method calls
for the creation of the following DOM tree nodes:
- A simple element with no attributes.
- A linear gradient element.
- An ellipse.
- A circle.
Once the DOM tree representing a particular graphic image has been created,
it can easily be transformed into an output SVG file containing the XML code
required for rendering the graphic using an SVG rendering engine such as Firefox 1.5.
An expanded SVG graphics library
The next lesson in this series will expand my SVG graphics library to provide
method calls for all of the Basic
Shapes in the SVG specification (see Resources), plus some other elements as well.
This will include the following basic shapes:
- rect
- circle
- ellipse
- line
- polyline
- polygon
Many attributes for each shape
When programming with raw SVG/XML code, there are a dozen or more attributes that can
be applied to each shape when it is rendered.
A programmer calling a higher level method
to create a shape, (such as an ellipse for example), would usually prefer not to have to routinely deal
with more than a dozen attributes for each ellipse. That programmer would
probably prefer to routinely deal with only those attributes that are most
likely to be different from one ellipse to the next.
Therefore,
the methods in my SVG graphics library attempt to default most of the attributes
and require the programmer to deal only with the attributes that are most likely
to be different from one instance of the shape to the next.
Must provide a fallback position
Having taken that approach, however, it is necessary to provide the
programmer with a method that can be used to create a shape dealing with
all or any combination of the available attributes. The program named
Svg08 demonstrates an SVG graphics method that can be used to create a
general DOM tree node having any name and any number of attributes with any
attribute names and any String value for each attribute. I will
also add this method to my SVG graphics library to be used in the next and all
subsequent lessons in this series.
This program produces an SVG file as its output. The file can be
rendered using Firefox 1.5. Note, however, that the method is equally appropriate for
use in producing XHTML output code, which I plan to explain in the next lesson.
Discussion
and sample code
The Svg07 program
Description of the program
As I mentioned earlier, one of the most frustrating things about using Java
to create raw code elements in XML, XHTML, or HTML is having to deal with the
escape sequences for the many required quotation marks that surround attribute
values. This program demonstrates a method that can be used to easily
create raw XML code elements without having to deal with those escape sequences.
The elements produced by the method may or may not be empty, and may or may not
have attributes.
|
Svg07 screen output
The screen output of the program named Svg07 is shown in Figure 1. I will refer back to this screen output as I discuss the program code.
Figure 1. Svg07 screen output.
Demo empty element with attributes. <ElementName color="Color.RED" width="10" height="20" /> Demo empty element with no attributes. Probably doesn't make any sense. <ElementName /> Demo non-empty element with attributes. <ElementName color="Color.RED" width="10" height="20" > Content of element. </ElementName> Demo non-empty element with no attributes. <ElementName > Content of element. </ElementName> |
This program was tested using J2SE 5.0 and WinXP.
Will discuss in fragments
As is my custom, I will discuss and explain this program in fragments.
(You can view the entire program in Listing 22 near the end of the lesson.) The first fragment in Listing 1 shows the beginning of the class and the
beginning of the main method.
Listing 1. Beginning of the class named Svg07.
public class Svg07{ public static void main(String argv[]){ String name = "ElementName"; System.out.println( "Demo empty element with attributes."); String emptyElement = makeElement(true,name, new String[]{"Color","Color.RED", "width","10", "height","20"}); System.out.println(emptyElement); |
No requirement to program using the escape sequence
The code in Listing 1 produces the first block of output text shown in Figure
1. Note the correspondence between the values in the elements of the array
object of type String[] in Listing 1 and the attribute names and values
in the first block of output text in Figure 1. Note also that there was no
requirement in Listing 1 to create string data using the escape sequence for
quotation marks as shown below:
"color="Color.RED" width="10" height="20""
Instead, each attribute name and each attribute value was specified as a
simple String in Listing 1, and the method named makeElement
produced the required output format shown in the first block of text in Figure
1. While this method is very simple, it is also extremely useful.
(Sometimes the most useful things are the simplest.)
The method named makeElement
At this point, I am going to put the main method on the back burner
and explain the method named makeElement.
As mentioned earlier, one of the most frustrating things about using Java to
create raw elements in XML, XHTML, or HTML is having to deal with the escape
sequences for the many required quotation marks that surround attribute values.
The method named makeElement constructs an element, which may or may not
have attributes. Also, the element may or may not be empty.
The user of this method does not have to deal with the required quotation
marks surrounding attribute values and the corresponding escape sequences.
Recommended usage of the makeElement method
The recommended usage of the makeElement method is shown in Figure 2.
Figure 2. Recommended usage of the makeElement method.
String newElement = makeElement( true/false, name, new String[]{"name","value", "name","value", "name","value", ... "name","value", }); |
(Note that the ellipses (…) in Figure 2 indicate that any number of
name/value pairs is allowed.)
The first parameter to the method
The first parameter must be true if the element is empty and false
if the element is not empty.
If the first parameter is true, the element is
sealed off in the required manner for an empty element (see the end of the
black boldface text in the first block of text in Figure 1).
If the first parameter
is false, the method returns the complete start tag for the element but does not
return a complete element. (See the black boldface text in the third
and fourth blocks of text in Figure 1.) In this case, it is the responsibility of the calling method to
provide the content and the end tag for the element. (See
the red text in the third and fourth blocks of text in Figure 1.)
The second parameter to the method
The second parameter to the
makeElement method must be a String that specifies the name of the element.
The third parameter to the method
The third
parameter to the makeElement method must be a reference to an array object of type String[]
or null.
This array must contain an even number of elements. Each pair of array elements
constitutes the name and the value of an attribute, in the order name, value,
name, value, etc.
|
If the reference to the array object is null and the first
parameter is false, the method returns the start tag for an element that has no
attributes and is not empty. (See the black boldface text in the fourth block
of text in Figure 1.)
|
If the reference to the array object is null and the first parameter is
true, the method returns a complete empty element with no attributes (see the
black boldface text in the second block of text in Figure 1).
The most useful combinations
Because the purpose of the method is to make it easier to deal with the
quotation marks that must surround attribute values in XML elements, the most
useful combinations are represented by the first and third blocks of text in
Figure 1, which are respectively:
- An empty element with attributes.
- An element with attributes that is not empty.
The beginning of the makeElement method
The method named makeElement begins in Listing 2.
Listing 2. The beginning of the makeElement method.
static String makeElement(boolean empty, String elementName, String[] data){ //Begin constructing the start tag. String element = "<" + elementName + " "; |
|
The code in Listing 2 instantiates a String object, which consists of
a left angle bracket concatenated with the name of the element. This
object will be used to create a succession of new String objects
containing attribute names and values as the method executes.
Dealing with elements that have no attributes
Listing 3 deals with elements that have no attributes on the basis of the
values of the first and third parameters.
Listing 3. Dealing with elements that have no
attributes.
if((empty==false) && (data == null)){ //Return a complete start tag. return element + ">"; }else if((empty==true) && (data == null)){ //Return a complete empty element. return element + "/>"; }//end if |
The code in Listing 3 is straightforward. Because there are no
attributes, no further processing within the method is required. Depending
on the combination of parameter values, this code will return the String
object shown by the black boldface text in either the second block of text or
the fourth block of text in Figure 1.
Processing the attributes
The code in Listing 4 is executed when the third parameter is not null,
meaning that there are attribute names and values in the array object referenced by the value
of the third parameter.
Listing 4. Processing the attributes.
for(int cnt=0;cnt<data.length;cnt+=2){ String name = data[cnt]; String value = data[cnt+1]; element += name + "=" + """ + value + "" "; }//end for loop |
The code in Listing 4 extracts the values from the array in pairs and uses
those pairs of values to successively create extended String objects that
are formatted properly for the specification of XML element attribute names and
values. (See the black boldface text in the first and third blocks of
text in Figure 1.) Note that the code in Listing 4 uses the escape
sequence to properly surround the attribute values with quotation marks, thus
relieving the using programmer of the requirement to perform this task.
Decision time: empty element or non-empty element
When the loop in Listing 4 terminates, the element has been constructed out
to the point where it is necessary to decide whether to seal it off as an empty
element (/>) or to seal it off as the end of the start tag (>) for an element
that is not empty. That decision is made by the code in Listing 5 on the
basis of the value of the first parameter.
Listing 5. Decision: empty element or non-empty
element.
if(empty){ //Terminate the element appropriately for an // empty element. A complete empty element will // be returned. element += "/>"; }else{ //End the start tag for an element that is not // empty. In this case, only the start tag will // be returned. The calling program must provide // the content for the element as well as the end // tag for the element. element += ">"; }//end else return element; }//end makeElement |
The code in Listing 5 is straightforward and shouldn’t require further
explanation.
Return the element and terminate the method
Listing 5 also returns a reference to the String object containing the
newly-constructed element and terminates the method named makeElement.
Remaining code in the main method
Now that you understand how the method named makeElement works, you
shouldn’t have any difficulty understanding the remaining code in the main
method, which is shown in Listing 6.
Listing 6. Remaining code in the main method.
System.out.println(); System.out.println( "Demo empty element with no attributes."); System.out.println( "Probably doesn't make any sense."); emptyElement = makeElement(true,name,null); System.out.println(emptyElement); System.out.println(); System.out.println( "Demo non-empty element with attributes."); String elementStartTag = makeElement(false,name, new String[]{"Color","Color.RED", "width","10", "height","20"}); System.out.println(elementStartTag); System.out.println("Content of element."); //Create end tag for element System.out.println("</" + name + ">"); System.out.println(); System.out.println( "Demo non-empty element with no attributes."); elementStartTag = makeElement(false,name,null); System.out.println(elementStartTag); System.out.println("Content of element."); //Create end tag for element System.out.println("</" + name + ">"); }// end main() |
The code in Listing 6 calls the makeElement method three times to
produce the screen output shown in Figure 1. You should have no difficulty
correlating the code in Listing 6 with the screen output shown in Figure 1.
Displayed on the screen for illustration
For illustration purposes, this program has simply used a println
statement to display the XML elements produced by the makeElement method
on the screen. In future lessons, those elements will either be written
into SVG files as part of the SVG code required to render a graphic image, or
sent from a servlet to a browser as part of the SVG code produced by the
servlet.
The Svg08 program
Graphic output from Svg08
Before getting into the details of the program code for the program named
Svg08, I want to show you the
graphic output from this program and explain what it mean. That output is
shown in Figure 3.
Figure 3. Graphic output from Svg08.
|
The first way to draw an ellipse
Basically, this program illustrates three different ways to use the methods
in my SVG graphics library to draw an ellipse. The first way is to call
the method named makeEllipse, providing attribute values for the
coordinates that define the center of the ellipse, the width of the ellipse, and
the height of the ellipse, and accept the default attribute
values for all of the other attributes. This approach resulted in the black ellipse at
the top of Figure 3. As you can see, this ellipse is completely opaque,
and the blue line beneath it does not show through.
The second way to draw an ellipse
The second way to draw an ellipse is to do the same thing as above, but to
follow that up by making two successive calls to the setAttribute method
to set the style attribute to fill:red, and to set the opacity
attribute to 0.6, (or 60-percent opaque). This resulted in
the pink ellipse in the center of Figure 3, with the white background and the
wide blue line beneath the ellipse showing through.
The third way to draw an ellipse
The third way to draw an ellipse is to call the makeNode method to
draw an ellipse with the following attributes names and values:
- cx: 110 (coordinate of center along the x-axis)
- cy 300 (coordinate of center along the y-axis)
- rx: 100 (radius along the x-axis)
- ry: 40 (radius along the y-axis)
- style: fill:green (fill color for the ellipse)
- opacity: 0.6 (opacity attribute value)
- transform: translate(110,300) rotate(15) translate(-110,-300)
This resulted in the rotated green ellipse at the bottom of Figure 3.
The first four attributes in the above list are the same attributes for which
the values were set for the black ellipse (but with different attribute
values).
The first six attributes in the above list are the same attributes for which
the values were set for the red ellipse (again with different attribute
values).
The last attribute in the above list was an extra attribute that was set on
the third (green) ellipse to cause it to be rotated by 15 degrees clockwise
about its center. This attribute was not set for either the black ellipse
or the red ellipse.
|
Other graphic elements
In addition to the three ellipses that were drawn by this program, a
wide blue line was drawn underneath the three ellipses in order to demonstrate the transparency that
results from setting the opacity attribute value to less than 1.0.
Also, a black border, one pixel wide, was drawn to outline the canvas on which
the ellipses and the line were drawn.
Description of the program
The purpose of this program is to demonstrate the use of a graphics
method named makeNode that can be used to create a general DOM tree node having
any name and any number of attributes with any attribute names and any String
values for the attributes.
The program creates a DOM tree describing the graphic image shown in Figure 3
in SVG format. Then the program transforms the DOM tree into raw XML code
and writes the XML code out into an XML file named junk.svg.
The program illustrates three different ways to use the methods of the SVG
graphics library to draw ellipses having different attribute values. The output
file produced by this program can be rendered by loading it into Firefox 1.5.
The program was tested using J2SE 5.0, Firefox v1.5.0.8, and WinXP.
Will discuss in fragments
As before, I will present and explain the program in fragments. (You
can view the entire program in Listing 23 near the end of the lesson.)
|
The first fragment is shown in Listing 7. This fragment shows the
beginning of the class and the main method, along with the beginning of
the code required to create the DOM tree.
Listing 7. Begin creating the DOM tree.
public class Svg08 { public static void main(String[] args){ //Begin by creating a DOM tree that represents the XML // code that will be rendered to produce the image of // interest. //Get the Document object. Document document = SvgGraphics.getDocument(); |
Create the root node named svg
Listing 8 calls the makeNode method to create the root node named
svg and to append it to the document.
Listing 8. Create the root node named svg.
Element svg = SvgGraphics.makeNode( document, null,//parent "svg",//node type new String[]{"xmlns","http://www.w3.org/2000/svg", "version","1.1", "width","220", "height","440", "position","absolute", "top","0", "left","0", });//end makeNode method |
For this special case where the new node needs to be a child of the document, null is passed as the second parameter
(that specifies the
parent). Otherwise, a reference to the actual parent node should be passed as
the
second parameter.
With the exception of the attributes named xmlns and position,
you can probably pretty well surmise what the purpose of each attribute is.
I will leave it as an exercise for the reader to go to the SVG specification
(see Resources) to learn about those attributes.
The makeNode method
At this point, I am going to put the discussion of the main method on
the back burner while I present and explain the makeNode method.
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.
Recommended usage of the makeNode method
Figure 4 shows an example of the recommended usage for the makeNode
method.
Figure 4. Recommended usage of the makeNode method.
Element abc = SvgGraphics.makeNode( document, def,//parent could be null "ghi",//node type new String[]{"name","value", "name","value", ... "name","value", });//end makeNode method |
The first parameter is a reference to the document to which the new node
belongs
The second parameter is a reference to the parent node to which this node is
to be appended so as to become a child of that node. If this parameter is
null, the new node is appended to the document. Otherwise, it is appended
to the specified parent node.
The third parameter is a String that specifies the type of the new node.
Reference to a String array object
The fourth parameter must be a reference to an array object of type String
or it must be null. This array must contain an even number of elements.
Each pair of elements constitutes the name and the value of an attribute, in the
following order: name, value, name, value, etc. (Any number of
name/value pairs is allowed.)
Referring back to Listing 8, you see a call to the makeNode method to
create a new node of type svg and append it to (make it a child node
of) the document node.
A reference to an array object of type String[] was passed as the
fourth parameter. The array object in Listing 8 contained seven different pairs of
strings specifying the attribute names and corresponding attribute values for
seven different attributes.
Note the values of the width, height, top,
and left attributes in Listing 8. Basically, these values specify the location and
dimensions of the canvas upon which the ellipses will be drawn. These
values will
become important when I return to the discussion of the code in the main
method.
Beginning of the makeNode method
Listing 9 shows the beginning of the makeNode method, including the
creation of the new node of type Element. The node is bare at
this point, but will be dressed up to contain attribute nodes, etc., as the execution of the method progresses.
Listing 9. Beginning of the makeNode method.
static Element makeNode(Document document, Element parent, String nodeType, String[] data){ Element element = (Element)document.createElement(nodeType); |
Append new node to its parent
Listing 10 calls the appendChild method, to cause the new node to
become a child node of the specified parent.
Listing 10. Append new node to its parent.
if(parent == null){ //For the special case of parent equal to null, // append the new node to the document. document.appendChild(element); }else{ //Otherwise, append the new node to the specified // parent. parent.appendChild(element); }//end else |
For the special case where the parent is specified as null, the new node is
appended to the document. Otherwise, it is appended to the specified
parent node.
Nodes that have no attributes
Listing 11 deals with the case where the reference to the array object is
null, indicating that this new node has no attributes.
Listing 11. Nodes that have no attributes.
//Deal with elements that have no attributes. if(data == null){ return element; }//end if |
For this case, the node that was created in Listing 10 and appended to its
parent in Listing 11 is all that is required. Therefore, the code in
Listing 11 simply returns a reference to that node and the method terminates.
Processing the attribute names and values
The for loop in Listing 12 extracts each attribute-name/value pair
from the array object and uses that information to set an attribute on the new
node with a matching name and a matching value.
Listing 12. Processing the attribute names and
values.
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 |
At this point, you could set an attribute on the node having just about any
name and just about any String value on the new node. However, in
order for an attribute to be effective in the ultimate rendering of the graphic
image, the name and value must be included in the names and values that are allowed
by the SVG specification for an element of that type (see
Resources).
Return the new node
Listing 12 also returns a reference to the new node and terminates the
method.
Show outline of the canvas
Now, returning to the discussion of the main method and continuing
from where we left off in Listing 8, Listing 13 calls the makeNode method to
draw the rectangular outline at the edge of the drawing canvas shown in Figure
3.
Listing 13. Show outline of the canvas.
Element outline = SvgGraphics.makeNode(document, svg,//parent "rect",//type new String[]{"x","0", "y","0", "width","220", "height","440", "fill","none", "stroke","black", "stroke-width","1", });//end makeNode method |
The attribute values
The earlier code in Listing 8 created the canvas at the location and with the
dimensions that I pointed out earlier. Note that the
location and dimensions of the rectangular outline drawn by the code in Listing
13 matches the location and dimensions of the canvas created in Listing 8.
The fill attribute specifies what is to be used to fill the interior
of the rectangle, and in this case, "none" is specified.
The stroke attribute specifies information about the stroke
that forms the outline of the rectangle. In this case it was simply
specified as "black."
The stroke-width attribute specifies the thickness of the stroke that
forms the outline of the rectangle. In this case the width was specified
to be the width of a single pixel.
Create a group container named g
Listing 14 calls the makeNode method to create a non-visual node named
g. This node will be the parent for a line and three ellipses.
Although I won’t demonstrate it in this program, having assigned the three
visual components to the same group, it would be possible for me to manipulate
them as a group in various ways.
Listing 14. Create a group container named g.
Element g = SvgGraphics.makeNode(document, svg,//parent "g", null); |
Note that Listing 14 passes null in place of a reference to an array object
containing attribute name/value pairs. As a result, the node named g
is constructed with no attributes.
Draw a blue line
Listing 15 draws the blue line shown in Figure 3 beginning at the upper left
corner of the canvas and extending to the lower right corner of the canvas.
The line is 12 pixels wide.
Listing 15. Draw a blue line.
Element line = SvgGraphics.makeLine(document, g, //owner 0, //x1 0, //y1 220, //x2 440);//y2 line.setAttribute("stroke","blue"); line.setAttribute("stroke-width","12"); |
|
The drawing of the line is accomplished in three steps. The first step is
to draw the line as a default black line one pixel wide by calling the makeLine
method, passing the coordinates for the beginning and end points of the line as
parameters to the method.
The second step is to call the setAttribute method to change
the color from black to blue.
The third step is to call the setAttribute method again to change the line
width from one pixel to twelve pixels.
This is a case of originally drawing the image by calling a very specialized
method, and then supplementing that call by two successive calls to a general
method that can be used to set attribute values, one attribute at a time.
The makeLine method
Once again, let’s put the discussion of the main method on the back
burner while we take a look at the code in the makeLine method, which is
shown in its entirety in Listing 16.
Listing 16. The makeLine method.
static Element makeLine(Document document, Element parent, int x1, int y1, int x2, int y2){ Element line = (Element)document.createElement("line"); parent.appendChild(line); line.setAttribute("x1",""+x1); line.setAttribute("y1",""+y1); line.setAttribute("x2",""+x2); line.setAttribute("y2",""+y2); line.setAttribute("stroke","black"); line.setAttribute("stroke-width","1"); return line; }//end makeLine |
The parameter values
This method returns a reference to a line. The parameters x1 and y1
specify the starting point of the line. The parameters x2 and y2
specify the end point of the line. (The line could be rotated and
translated later to make it appear differently but that wasn’t done in this
case.) By default, the stroke is set to black one pixel wide.
There is nothing complicated in Listing 16, so it shouldn’t require further
explanation.
Draw the black ellipse
Returning now to the main method, Listing 17 calls the makeEllipse
method to draw the black ellipse at the top in Figure 3.
Listing 17. Draw the black ellipse.
SvgGraphics.makeEllipse(document,//This document g,//Owner 110,//Center x-coordinate 100,//Center y-coordinate 100,//Width 40); //Height |
The call to the makeEllipse method in Listing 17 passes parameter
values for four of the attributes. These four attributes, which are set by
the makeEllipse method, specify the location and size of the ellipse. The
ellipse is black because the attribute that controls the color was not set.
The makeEllipse method
The makeEllipse method is shown in its entirety in Listing 18.
Listing 18. The makeEllipse method.
static Element makeEllipse(Document document, Element parent, int xCoor, int yCoor, int xRadius, int yRadius){ Element ellipse = (Element)document.createElement("ellipse"); parent.appendChild(ellipse); ellipse.setAttribute("cx",""+xCoor); ellipse.setAttribute("cy",""+yCoor); ellipse.setAttribute("rx",""+xRadius); ellipse.setAttribute("ry",""+yRadius); return ellipse; }//end makeEllipse |
This makeEllipse method returns a reference to an ellipse. The
xCoor and yCoor parameters specify the center of the ellipse. The
xRadius and yRadius parameters specify the width and height of the
ellipse respectively. The code in
Listing 18 is straightforward and shouldn’t require further explanation.
Draw the red ellipse
Returning once more to the main method, Listing 19 draws the red
(pink) ellipse in the middle of Figure 3.
Listing 19. Draw the red ellipse.
Element ellipse2 = SvgGraphics.makeEllipse( document, g,//Owner 110,//Center x-coordinate 200,//Center y-coordinate 100,//Width 40); //Height ellipse2.setAttribute("style","fill:red"); ellipse2.setAttribute("opacity","0.6"); |
As with the line that was drawn earlier, the red ellipse in Figure 3 was
drawn in three steps. The first step was to call the makeEllipse
method to draw a black ellipse in the correct position with the correct shape.
The second step was to call the setAttribute method to change the
fill color from black to red.
The third step was to call the setAttribute method again to change the
opacity from the default value of 100-percent opaque to a new value of
60-percent opaque (0.6).
Draw the green rotated ellipse
The green rotated ellipse shown at the bottom of Figure 3 could have been
drawn using the same approach as for the red ellipse and adding one more call to
the setAttribute method to accomplish the rotation. However,
instead of drawing it that way, as shown in Listing 20, the green ellipse was
drawn by calling the makeNode method, and passing all of the relevant
attribute names and attribute values as the contents of an array object.
Listing 20. Draw the green rotated ellipse.
Element ellipse3 = SvgGraphics.makeNode( document, g,//parent "ellipse",//node type new String[]{"cx","110", "cy","300", "rx","100", "ry","40", "style","fill:green", "opacity","0.6", "transform","translate(110,300) " + "rotate(15) " + "translate(-110,-300)" });//end makeNode method |
|
The makeNode method
If you examine the code in Listing 20 carefully, and compare it with the code
for the makeNode method in Listing 9 through Listing 12, you will see
that it draws a green ellipse with 60% opacity rotated by 15-degrees clockwise
relative to its center.
As I explained earlier, the makeNode method lets you set all, none, or
any combination of attribute values in order to take advantage of any combination
of available attributes with no assumptions being made about any of
the attributes. This is the most general of the three approaches presented and
discussed in this lesson.
Transform the DOM and write the output file
Listing 21 transforms the DOM tree into raw XML code and writes that XML code
into an output file named junk.svg.
Listing 21. Transform the DOM and write the output file.
SvgGraphics.transformTheDom(document,"junk.svg"); }// end main() |
The method named transformTheDom was explained in an earlier lesson.
That explanation won’t be repeated here.
The end of the program
Listing 21 also signals the end of the main method and the end of the
program.
Run the program
I encourage you to copy the code from Listing 22 and Listing 23 into your text
editor, compile it, and execute it. Then view the resulting SVG files in
Firefox 1.5, or some other suitable SVG rendering engine. Experiment with the
code, making changes, and observing the results of your changes.
Above all, enjoy the process. Programming can be fun.
Summary
Continuing with the SVG graphics library from the previous lesson, I taught
you how to write a method that deals with the escape sequences required for the
quotation marks that surround attribute values in XML thereby making it much
easier to write Java code that generates XML code.
I also taught you how to write a method that makes it possible to create a
general node in the DOM tree being constructed to represent an SVG graphic,
making no prior assumptions about the number of attributes, the names of the
attributes, or the values of the attributes.
What’s next?
The next lesson in this series will show you how to take what you have learned in
this and the previous lesson and to apply that knowledge to the generation of
XHTML files containing in-line SVG/XML code. The next lesson will also teach
you how to apply that knowledge to the writing of servlets that produce XHTML
output containing in-line SVG/XML code.
Future lessons will teach you how to write servlets that:
- Produce XHTML output containing references to external SVG files, some
of which are created on-the-fly during the execution of the servlet. - Create and re-use graphic elements.
- Use SVG symbols.
- Deal with stroke caps in SVG in comparison with similar caps in Java 2D.
- Use the switch element in SVG.
- Deal with bit-mapped images in SVG.
- Deal with text in SVG.
- Deal with other features such as animation in SVG.
Complete program listings
Complete listings of the programs discussed in this lesson are shown in
Listing 22 and Listing 23 below.
Listing 22. The program named Svg07.
/*File Svg07.java Copyright 2006, R.G.Baldwin One of the most frustrating things about using Java to create elements in XML, XHTML, or HTML is having to deal with the escape characters for the many required quotation marks that surround attribute values. This program demonstrates a method that can be used to easily create elements without having to deal with those escape characters. The element produced by the method may or may not be empty, and may or may not have attributes. This program produces the following output: Demo empty element with attributes. <ElementName color="Color.RED" width="10" height="20" /> Demo empty element with no attributes. Probably doesn't make any sense. <ElementName /> Demo non-empty element with attributes. <ElementName color="Color.RED" width="10" height="20" > Content of element. </ElementName> Demo non-empty element with no attributes. <ElementName > Content of element. </ElementName> Tested using J2SE 5.0, Firefox v1.5.0.9, and WinXP. *********************************************************/ import java.io.*; public class Svg07{ public static void main(String argv[]){ String name = "ElementName"; System.out.println( "Demo empty element with attributes."); String emptyElement = makeElement(true,name, new String[]{"Color","Color.RED", "width","10", "height","20"}); System.out.println(emptyElement); System.out.println(); System.out.println( "Demo empty element with no attributes."); System.out.println( "Probably doesn't make any sense."); emptyElement = makeElement(true,name,null); System.out.println(emptyElement); System.out.println(); System.out.println( "Demo non-empty element with attributes."); String elementStartTag = makeElement(false,name, new String[]{"Color","Color.RED", "width","10", "height","20"}); System.out.println(elementStartTag); System.out.println("Content of element."); //Create end tag for element System.out.println("</" + name + ">"); System.out.println(); System.out.println( "Demo non-empty element with no attributes."); elementStartTag = makeElement(false,name,null); System.out.println(elementStartTag); System.out.println("Content of element."); //Create end tag for element System.out.println("</" + name + ">"); }// end main() //----------------------------------------------------// /* One of the most frustrating things about using Java to create elements in XML, XHTML, or HTML is having to deal with the escape characters for the many required quotation marks. This method constructs an element, which may or may not have attributes. Also, the element may or may not be empty. The user of this method does not have to deal with the required quotation marks surrounding attribute values and the corresponding escape characters The first incoming parameter must be true if the element is empty and false if the element is not empty. If the first parameter is true, the element is sealed off in the required manner for an empty element. If the first parameter is false, the method returns the complete start tag for the element but does not return a complete element. It is the responsibility of the calling method to provide the content and the end tag for the element. The second parameter to the method must be a String that specifies the name of the element. The third parameter to the method must be a reference to an array object of type String. This array must contain an even number of elements. Each pair of elements constitutes the name and the value of an attribute, in the order name, value, name, value, etc. If the reference to the array object is null and the first parameter is false, the method returns the start tag for an element that has no attributes and is not empty. If the reference is null and the first parameter is true, the method returns a complete empty element with no attributes (which probably doesn't make any sense). An example of the recommended usage of the method follows: String newElement = makeElement( true/false, name, new String[]{"name","value", "name","value", "name","value", }); */ static String makeElement( boolean empty,String elementName,String[] data){ //Begin constructing the start tag. String element = "<" + elementName + " "; //Deal with elements that have no attributes. if((empty==false) && (data == null)){ //Return a complete start tag. return element + ">"; }else if((empty==true) && (data == null)){ //Return a complete empty element. return element + "/>"; }//end if for(int cnt=0;cnt<data.length;cnt+=2){ String name = data[cnt]; String value = data[cnt+1]; element += name + "=" + """ + value + "" "; }//end for loop if(empty){ //Terminate the element appropriately for an // empty element. A complete empty element will // be returned. element += "/>"; }else{ //End the start tag for an element that is not // empty. In this case, only the start tag will // be returned. The calling program must provide // the content for the element as well as the end // tag for the element. element += ">"; }//end else return element; }//end makeElement }//end Svg07 |
Listing 23. The program named Svg08.
/*File Svg08.java Copyright 2006 R.G.Baldwin The purpose of this program is to demonstrate the use of a new graphics method that can be used 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 program creates a DOM tree describing a specific graphic image in SVG format and writes it out into an XML file named junk.svg. The program illustrates three different ways to use the methods of the SVG graphics library to create ellipses having different attribute values. The output file produced by this program can be rendered by loading it into Firefox 1.5. Tested using J2SE 5.0, Firefox v1.5.0.8, and WinXP. *********************************************************/ import javax.xml.parsers.*; import org.w3c.dom.*; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.*; public class Svg08 { public static void main(String[] args){ //Begin by creating a DOM tree that represents the XML // code that will render to produce the image of // interest. //Get the Document object. Document document = SvgGraphics.getDocument(); //Create the root node named svg and append it to the // document. Specify the parent as null for the // special case where the parent is the document. // Otherwise, specify the actual parent node. Element svg = SvgGraphics.makeNode( document, null,//parent "svg",//node type new String[]{"xmlns","http://www.w3.org/2000/svg", "version","1.1", "width","220", "height","440", "position","absolute", "top","0", "left","0", });//end makeNode method //Show outline of canvas using 'rect' element Element outline = SvgGraphics.makeNode(document, svg,//parent "rect",//type new String[]{"x","0", "y","0", "width","220", "height","440", "fill","none", "stroke","black", "stroke-width","1", });//end makeNode method //Create a node named g, which will be the parent for // an ellipse. Pass null for the reference to the // array object for the special case where the node // has no attributes. Element g = SvgGraphics.makeNode(document, svg,//parent "g", null); //Draw a blue line 12 pixels wide. First define the // line by providing values for the four default // attributes of the makeLine method that specify the // end point locations of the line. Then supplement // those attributes by invoking the setAttribute // method twice in succession on the reference to the // line to set the stroke and stroke-width attributes, // causing the line to be blue with a width of 12 // pixels. Element line = SvgGraphics.makeLine(document, g, //owner 0, //x1 0, //y1 220, //x2 440);//y2 line.setAttribute("stroke","blue"); line.setAttribute("stroke-width","12"); //Draw a black ellipse by providing values for the // four default attributes of the makeEllipse method // that specify the location and size of the ellipse. // The ellipse is black because the attribute that // controls the color was not set. SvgGraphics.makeEllipse(document,//This document g,//Owner 110,//Center x-coordinate 100,//Center y-coordinate 100,//Width 40); //Height //Draw an ellipse by providing values for the four // default attributes of the makeEllipse method that // specify the location and size of the ellipse. Then // supplement those attributes by invoking the // setAttribute method on the reference to the ellipse // twice to fill the ellipse with red color and to // cause the filled ellipse to have 60% opacity. Element ellipse2 = SvgGraphics.makeEllipse( document, g,//Owner 110,//Center x-coordinate 200,//Center y-coordinate 100,//Width 40); //Height ellipse2.setAttribute("style","fill:red"); ellipse2.setAttribute("opacity","0.6"); //Draw a green ellipse with 60% opacity rotated by // 15-degrees clockwise, using the makeNode method, // for which there are no default attributes. //This method lets you set all, none, or any // combination of attribute values in order to // take advantage of none, any, or all of the // available attributes with no assumptions being // made about any of the attributes. This is the // most general of the three approaches. Element ellipse3 = SvgGraphics.makeNode( document, g,//parent "ellipse",//node type new String[]{"cx","110", "cy","300", "rx","100", "ry","40", "style","fill:green", "opacity","0.6", "transform","translate(110,300) " + "rotate(15) " + "translate(-110,-300)" });//end makeNode method //Transform the DOM and write the output file. SvgGraphics.transformTheDom(document,"junk.svg"); }// end main() //----------------------------------------------------// }// class Svg08 //======================================================// //This is an abbreviated library used solely to support // this program for the purpose of demonstrating the // method named makeNode. class SvgGraphics{ //----------------------------------------------------// /* The purpose of this method is to create a general node having any name, and any number of attributes with any attribute names and any String values for the attributes, or no attributes at all. The first parameter is a reference to the document to which the new node belongs. The second parameter is a reference to the parent node to which this node is to be appended so as to become a child of that node. If this parameter is null, the new node is appended to the document. Otherwise, it is appended to the specified parent node. The third parameter is a String that specifies the type of node. The fourth parameter to the method must be a reference to an array object of type String. This array must contain an even number of elements. Each pair of elements constitutes the name and the value of an attribute, in the order name, value, name, value, etc. An example of the recommended usage of the method follows: Element abc = SvgGraphics.makeNode( document, def,//parent could be null "ghi",//node type new String[]{"name","value", "name","value", "name","value", });//end makeNode method */ static Element makeNode(Document document, Element parent, String nodeType, String[] data){ Element element = (Element)document.createElement(nodeType); if(parent == null){ //For the special case of parent equal to null, // append the new node to the document. document.appendChild(element); }else{ //Otherwise, append the new node to the specified // parent. parent.appendChild(element); }//end else //Deal with elements that have no attributes. if(data == null){ return element; }//end if for(int cnt=0;cnt<data.length;cnt+=2){ String name = data[cnt]; String value = data[cnt+1]; element.setAttribute(name,value); }//end for loop return element; }//end makeNode //----------------------------------------------------// //This method returns a reference to an ellipse. The // xCoor and yCoor parameters specify the center of the // ellipse. The xRadius and yRadius parameters specify // the width and height of the ellipse respectively // while it is in the horizontal plane before being // rotated. Numeric attributes are set at type String. static Element makeEllipse(Document document, Element parent, int xCoor, int yCoor, int xRadius, int yRadius){ Element ellipse = (Element)document.createElement("ellipse"); parent.appendChild(ellipse); ellipse.setAttribute("cx",""+xCoor); ellipse.setAttribute("cy",""+yCoor); ellipse.setAttribute("rx",""+xRadius); ellipse.setAttribute("ry",""+yRadius); return ellipse; }//end makeEllipse //----------------------------------------------------// //This is a utility method that is used to execute code // that is the same regardless of the graphic image // being produced. This method is not new to this // program. static Document getDocument(){ Document document = null; try{ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.newDocument(); document.setXmlStandalone(false); }catch(Exception e){ e.printStackTrace(System.err); System.exit(0); }//end catch return document; }//end getDocument //----------------------------------------------------// //This is a utility method that is used to execute code // that is the same regardless of the graphic image // being produced. This method transforms the DOM into // raw XML code and writes that code into the output // file. This method is not new to this program. static void transformTheDom(Document document, String filename){ try{ //Get a TransformerFactory object. TransformerFactory xformFactory = TransformerFactory.newInstance(); //Get an XSL Transformer object. Transformer transformer = xformFactory.newTransformer(); //Sets the standalone property in the first line of // the output file. transformer.setOutputProperty( OutputKeys.STANDALONE,"no"); //Get a DOMSource object that represents the // Document object DOMSource source = new DOMSource(document); //Get an output stream for the output file. PrintWriter outStream = new PrintWriter(filename); //Get a StreamResult object that points to the // output file. Then transform the DOM sending XML // code to the file StreamResult fileResult = new StreamResult(outStream); transformer.transform(source,fileResult); }//end try block catch(Exception e){ e.printStackTrace(System.err); }//end catch }//end transformTheDom //----------------------------------------------------// //This method returns a reference to a line. x1 and y1 // specify the starting point of the line before it is // rotated. x2 and y2 specify the end point. By // default, the stroke is set to black one pixel wide. // This can be overridden to specify other colors and // other widths if you need to do so. static Element makeLine(Document document, Element parent, int x1, int y1, int x2, int y2){ Element line = (Element)document.createElement("line"); parent.appendChild(line); line.setAttribute("x1",""+x1); line.setAttribute("y1",""+y1); line.setAttribute("x2",""+x2); line.setAttribute("y2",""+y2); line.setAttribute("stroke","black"); line.setAttribute("stroke-width","1"); return line; }//end makeLine //----------------------------------------------------// }//end class SvgGraphics |
Copyright
Copyright 2007, Richard G. Baldwin. Reproduction in whole or in part in any
form or medium without express written permission from Richard Baldwin is
prohibited.
Resources
Java 2D Graphics
300 Java 2D Graphics,
Nested Top-Level Classes and Interfaces
302 Java 2D Graphics,
The Point2D Class
304 Java 2D Graphics,
The Graphics2D Class
306 Java 2D Graphics,
Simple Affine Transforms
308 Java 2D Graphics,
The Shape Interface, Part 1
310 Java 2D Graphics,
The Shape Interface, Part 2
312 Java 2D Graphics,
Solid Color Fill
314 Java 2D Graphics,
Gradient Color Fill
316 Java 2D Graphics,
Texture Fill
318 Java 2D Graphics,
The Stroke Interface
320 Java 2D Graphics,
The Composite Interface and Transparency
322 Java 2D Graphics,
The Composite Interface, GradientPaint, and Transparency
324 Java 2D Graphics,
The Color Constructors and Transparency
Java 2D API
Specification
Java 2D API
Java API for XML Processing (JAXP)
2200 Java
API for XML Processing (JAXP), Getting Started
2202 Getting
Started with Java JAXP and XSL Transformations (XSLT)
2204 Java
JAXP, Exposing a DOM Tree
2206 Java
JAXP, Implementing Default XSLT Behavior in Java
2208 Java
JAXP, Writing Java Code to Emulate an XSLT Transformation
2210 Java
JAXP, Transforming XML to XHTML
Links to numerous XML tutorials
by Richard G. Baldwin
Scalable Vector Graphics (SVG)
Java JAXP, Creating graphics using Java and SVG
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
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.