Although you might imagine that the editing of an XForms form and having an SVG image in the browser window update automatically, it is not that easy. But that doesn't mean it can't be done. This article shows you how to create an XForms-based logo generator using SVG.
In order to get the most out of this article, you should be familiar with the basics of XForms. Familiarity with SVG also helps, but isn't crucial.
The code in this article has been tested with Firefox and the Mozilla XForms extension (see Resources to download), but the concepts should apply to any XForms implementation.
What you're trying to accomplish
Let's start by looking at what the finished product looks like. Ultimately, you want a form that enables you to edit the properties of existing shapes, or to remove those shapes altogether. You also want to be able to add new shapes, edit those shapes, and save the data to a logo file. Once you've done that, you want to be able to see the changes right there on the page. Your final logo generator looks something like Figure 1.
Figure 1. The final logo generator
To accomplish all of this, you will need to use several techniques, including inserting and deleting elements, copying elements from one instance to another, and dynamically updating the contents of an iframe.
A logo to edit
Before moving on to the editor itself, let's look at the Scalable Vector Graphics, or SVG, we're going to edit (see Listing 1).
Listing 1. The basic SVG image file
<?xml version="1.0" encoding="UTF-8"?> <svg:svg xmlns:svg="http://www.w3.org/2000/svg"> <svg:g><svg:text stroke="red" stroke-width="1" x="350" y="85" font-family="Verdana" font-size="36" font-style="italic" fill="blue">Bombast, Inc.amp;lt;/svg:text></svg:g> <svg:g><svg:rect x="25" y="25" width="300" height="100" stroke="black" rx="0" ry="0" stroke-width="3" fill="green"/></svg:g> <svg:g><svg:circle cx="115" cy="100" r="75" fill="red" stroke="blue" stroke-width="10"/></svg:g> </svg:svg>
Create a new file with this data and save it as logo.xml. Notice that it includes three different shapes, each with attributes that control its appearance. Each is part of a "group" element. Displayed by the browser, it looks like Figure 2.
Figure 2. The SVG image
Adding the SVG image to the page
So to take care of it, you'll need to add an iframe you can update with a button (Listing 2).
Listing 2. The general page
<?xml version="1.0"?> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:my="http://example.com/my" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:XForms="http://www.w3.org/2002/XForms" xmlns:svg="http://www.w3.org/2000/svg"> <head> <title>XForms Logo Generator</title> </head> <body> <h1 align="center">Logo Generator</h1> <form style="display: inline"> <input type="button" value="Refresh View" onclick="logoframe.document.location.reload()" /></form> <div style="width:100%;text-align: center; margin-top: 20px"> <iframe id="logoframe" src="http://localhost/logo.xml" width="640" height="200"></iframe> </div> </body> </html>
The button enables the user to refresh the iframe after saving. Save this file as logogenerator.xhtml.
Editing the existing logo
In order to edit the XML, you first need to create an instance that includes the data (Listing 3).
Listing 3. Adding the data instance
<?xml version="1.0"?> <html ...> <head> <title>XForms Logo Generator</title> <xforms:model> <xforms:instance src="http://localhost/logo.xml" /></xforms:model> </head> <body> <h1 align="center">Logo Generator</h1> ...
Here you are creating the instance by referencing an external file, which just happens to include the SVG you created earlier. Now you need to create the controls that will enable you to edit the data (Listing 4).
Listing 4. Adding the editing controls
There seems to be a lot of code here, but it boils down to this: Each type of shape has its own set of properties (and therefore attributes), so you create a section for each of your three major types: circle, rectangle, and text. For each one, include controls to edit the relevant attributes. For the text, you also include a control for editing the first child of the element, which is the actual text to display. You can format your XForms controls using CSS, but in this case, to keep things simple, table elements have been used. The result should look something like Figure 3.
Figure 3. Editing controls
You can, of course, provide facilities for editing whatever shape you want. For example, you could include the ability to add and edit polylines, or other SVG shapes. The process is the same, so let's keep the article a little simpler by sticking to these three major shapes.
If you make changes to the data in the controls, the XForms engine will make changes to the instance, but if you click the Refresh View button, you won't see any changes. This is because the iframe is referencing the file, not the "in memory" version of the data. In order to see the changes, you are going to have to save the new data to the logo.xml file.
The mechanics of saving the file are going to depend on where you have the file in the first place. If the file is local (or if your Web server supports it), you can use the
PUT method, but this article assumes that the file is remote and that you will need a script to do the actual saving. The form of the actual script doesn't matter, as long as it outputs the XML back to the instance. I've included an example PHP script in the source code for this article. To call that script, you need to create a submission (Listing 5).
Listing 5. The submission
You create the
submission element; in this case, a submission that sends the data to the PHP file, in the model. Rather than replacing the entire page, you only want the processor to replace the instance. This way, when the user submits the form, the page remains in place. This enables the user to save frequently in order to see small incremental changes without the process becoming overly cumbersome. To enable the user to actually save the data, you also added a XForms submit button that calls that submission.
To see this in action, refresh the page and make changes to the data in the controls. Click the Save Logo button and then click the Refresh View button to see the changes. The result is a page that should look something like Figure 4.
Figure 4. Saving the data
Using this technique, you can make any changes you want to existing elements. Now you need to know how to add new elements to the image.
Inserting new elements
Up to now, things have been fairly straightforward. You have essentially been doing what XForms was designed to do, and doing it fairly simply. Now you are going to see the differences between how XForms was designed and the reality of what it can do. For example, you want to create a button that adds a new element to the document. In order to do that, you need to construct something like what is shown in Listing 6.
Listing 6. Adding the trigger
What you're telling the editor to do is add a copy of the last
g element at the top of the file, before element number one. That means if you reload the form and click the Add New Element button, you'll see a new Circle element, as shown in Figure 5.
Figure 5. Adding a new element
Obviously, this is not what you want; you want to be able to add an empty element. To make that happen, you can add a new element to the end of the logo.xml file (Listing 7).
Listing 7. Adding a new blank element
... <svg:g><svg:circle cx="115" cy="100" r="75" fill="red" stroke="blue" stroke-width="10"/></svg:g> <svg:g>NEW ELEMENT -- CHOOSE SHAPE</svg:g> </svg:svg>
g element doesn't define text as its body, so this text won't affect the actual image. It will, however, affect whether this is a "valid" SVG XML file. The Mozilla SVG implementation doesn't care, but if yours does, you'll want to adapt this technique so that you don't cause yourself problems. The actual text is completely arbitrary. You can then adapt to the editor to detect these elements (Listing 8).
Listing 8. Detecting text in the SVG elements
Here you have created a new control that displays a message only when it finds a
g element that contains as its content a text node. You can see it displayed in Figure 6.
Figure 6. Displaying a message regarding the text-containing elements
Now if you click the Add New Element button, you will see an additional message. This is because it is this new element you are cloning and adding to the document, as you can see in Figure 7.
Figure 7. Adding a new element
If you save the file and look at it, you can see the new element in logo.xml, at the top of the file.
Copying elements from one instance to another
All right, you've got the ability to edit existing elements, and the ability to add new elements, but those new elements are pretty useless in terms of and into the actual image. In order to make them useful, you need the ability to populate them with one of your basic shapes.
To start with, you will create an instance that includes templates for the elements you wish to add (Listing 9).
Listing 9. Templates
First notice that the new instance includes data in more than one namespace. Each template has been added in a generic
my:shape element so that you can list the various available shapes. Second, notice that now that you have more than one instance, a reference has been added to the appropriate instance to each of your XPath statements. The
instance() function replaces the root element in the XPath expression. (Note that technically you didn't have to specify the content instance, as, being the first instance in the model, it is the default. Consistency, however, says you should.)
Next you can add the control that enables you to choose from among these templates (Listing 10).
Listing 10. Copying existing elements
This one is a little complicated, so let's take it from the top. First, you are dealing with a
select element, used to add data to a specific node based on the users choices. The note to which this data will be added is the first
g element in the content instance. Remember, this is the element you added with Add New Element. The actual data that's added to this node depends on the selected
itemset items. In this case, those items come from the templates instance, and consist of each of the shape elements. For each one, the label is the
label attribute of the
But where you would normally follow the label element with a
value element, you are instead using a
copy element, which references the child of the shape element, or the circle, rect, or text template. From the user standpoint, the result looks like Figure 8.
Figure 8. The select control
When the user clicks on one of these labels to select the element, the XForms engine copies that element to the target
g element. You can see the results in Figure 11, where you now have an extra Rectangle. If, on the other hand, the user deselects that item, perhaps by clicking a different item, the copy is removed and, in this case, replaced by the new copy. For example, if you now click the Circle item and save the logo, refreshing the view shows the results in Figure 9.
Figure 9. Adding a circle
There is still one more problem. If the user simply loads the page and clicks a new element type before any new element, you can get some fairly terrible results, because you are simply copying the template into the first
g element -- which in this case is actual data, and not a placeholder. To solve that problem, you can add an empty
g element to the top of the file (Listing 11).
Listing 11. Adding an empty placeholder
<?xml version="1.0" encoding="UTF-8"?> <svg:svg xmlns:svg="http://www.w3.org/2000/svg"> <svg:g></svg:g> <svg:g><svg:text stroke="red" stroke-width="1" x="350" y="85" font-family="Verdana" font-size="36" font-style="italic" ...
To afford further confusion, you can also change the editors so that it only displays an empty element if one has been added by the user (Listing 12).
Listing 12. Limiting the display of new elements
... <xforms:repeat id="logonew" nodeset="instance('content')/svg:g[text()]"> <xforms:label>NEW ELEMENT -- CHOOSE SHAPE</xforms:label> </xforms:repeat> ...
This way, the form only displays a message for a second text-containing element.
Deleting existing elements
You are almost done, but you need to add one more piece of very important functionality. To do that, you can use the delete action (Listing 13).
Listing 13. Adding delete buttons
It's possible to create a delete button that then leaves the "current" element, but to keep things simple, you are adding a delete button for each individual element. Clicking that button removes the current element from the instance. The result looks like Figure 10.
Figure 10. Delete buttons
Now you can add, edit, and delete elements at will.
Starting from scratch -- the "empty" logo file
You started this project with an existing logo.xml file, so the last step is to make sure that you can replicate your success with a file that does not contain a pre-existing image. There are, however, very specific requirements for pre-existing elements, so you can't actually start with an empty file. Instead, you need the following (Listing 14).
Listing 14. The "empty" file
<?xml version="1.0" encoding="UTF-8"?> <svg:svg xmlns:svg="http://www.w3.org/2000/svg"> <svg:g></svg:g> <svg:g>NEW ELEMENT -- CHOOSE SHAPE</svg:g> </svg:svg>
Essentially, you need two pre-existing elements. The first is the placeholder into which you can add new elements. The second is the element to clone for when the user clicks the Add New Element button. Replace the contents of your logo to an XML file with the text of Listing 14 and save the file. Refresh the editor so you will have the ability to create an image from scratch, such as the one seen in Figure 11.
Figure 11. Creating a new image
In this article, you looked at what it takes to create an XForms form capable of creating and editing an SVG image. What you learned is that you can use XForms' ability to insert new elements and copy information into them in order to add new content. You can also use XForms' traditional element- and attribute-editing capabilities to alter the properties of SVG objects. From there, you can save the data to a file and view changes to the file.
In a production environment, you will want to make several changes. For one thing, you'll want each individual user to have his or her own logo.XML file, probably based on a unique username. You might want to expand the users' choice of shapes, or the properties available for editing. You might also provide a way to download a non-SVG version of the logo, such as in a PDF or PNG file.
But the basic technique is right here.
|Logo generator source code||logogenerator_source.zip||3KB|
- Get a basic introduction to XForms in Introduction to XForms, Part 1: The new Web standard for forms (developerWorks, September 2006).
- Learn more about XForms in the IBM developerWorks XML zone.
- Validate your SVG files here.
- Get the basics of Scalable Vector Graphics in Introduction to Scalable Vector Graphics (developerWorks, March 2004).
- Learn more about XForms submission events in XForms tip: Using form submission events (developerWorks, November 2006).
- Find out how to accept XForms data in Java (developerWorks, October 2006), Perl (developerWorks, October 2006), and PHP (developerWorks, October 2006).
- Learn how to Create JPEGs automatically with SVG in Tip: Create JPEGs automatically with SVG (developerWorks, September 2003).
- IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
- SVG and XForms, a Primer (developerWorks, November 2003) provides an overview of the two technologies and highlights the potential synergies between them.
- Visit XForms.org, a clearinghouse of XForms-related information.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
Get products and technologies
- The XForms Recommendation is maintained by the W3C.
- You can download Firefox here.
- You can download the XForms extension here.
- The SVG Recommendation is also maintained by the W3C.
- Download a trial version of Lotus Forms (formerly Workplace Forms product).
- See other XForms-related downloads.
- Get MozzIE, an open-source control that allows you to render XForms in Internet Explorer.
- Participate in the discussion forum.
- developerWorks blogs: Get involved in the developerWorks community.