
SVG Tips and Tricks: Adobe's SVG Viewer
Introduction
Since Adobe's SVG Viewer is the main SVG implementation, with 160 million deployed units and counting, I thought it would be a good idea to take a look at some special features that only the Adobe SVG Viewer offers to its users. Want to see what we can do with it? Grab a copy of version 3.0 of the viewer and we'll be off.
Using Adobe's Scripting Engine
With version 3.0, Adobe has added a significant feature to its SVG Viewer: a conformant
JavaScript engine. Before version 3.0, when you were using the Adobe SVG Viewer within your Web page as viewed in Internet Explorer or Netscape Navigator, you were relying on the browser's scripting engine to evaluate and run your SVG scripting. Using
Adobe's embedded scripting engine offers a few significant advantages. First and foremost it is a rock-solid, compliant implementation of the ECMA specification, and Adobe has used Mozilla's acclaimed SeaMonkey engine, opting for proven reliability. So, you can make sure that all your scripting code runs the same whether it's
viewed on IE or Netscape, and whether on a Mac or a Windows box. IE and Netscape have diverging views and implementations for some of the core JavaScript functionalities (for example, the String.split() method), and they don't allow JavaScript communications between the browser and a plug-in in the same way. All of which results in cases
where SVG authors had to do some browser-sniffing to serve either
IE- or Netscape-specific script libraries.
Although the Adobe SVG Viewer 3.0 has its own scripting engine, you can still use your
browser's engine if you want. You might actually want to author SVG content for use in a specific
browser, which would allow you to take full advantage of the browser's engine. Also, the
Adobe engine is not turned on by default, so existing content authored prior to version 3.0 will not break. Turning Adobe's scripting engine on is fairly easy and can be done in a clean way using an attribute in Adobe's namespace. This scripting-ready SVG template demonstrates the use of the Adobe scriptImplementation attribute:
<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:a3="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
a3:scriptImplementation="Adobe">
<script a3:scriptImplementation="Adobe" type="text/ecmascript">
<![CDATA[
// EcmaScript code
]]>
</script>
<!-- SVG contents -->
</svg>
|
Related Reading
|
To use the browser scripting engine, omit all Adobe-specific
declarations, or have the scriptImplementation attribute in the
Adobe namespace set to either browser, Microsoft, or
Netscape. Also, using the Adobe scripting engine will not prevent
you from calling some scripts in the browser environment, since Adobe also offers
a custom method, browserEval() located on its Window
object, to do exactly this.
Customizing the Context Menu
Every time I talk about SVG in a conference, I give a few use cases. I never forget to mention SVG's capabilities as a UI rendering layer. People often relate their experiences hacking UI stuff with SVG, and they often say how nice it is. Sometimes, when creating an SVG-based UI, you want certain things to look exactly the same as they would in your system's own look and feel. Adobe allows you to change the contents of its contextual menu, rendered using native UI code, and you can do so by specifying an XML representation of its contextual menu. The default menu XML is:
<menu> <header>Adobe SVG Viewer</header> <item action="Open" id="Open">Open</item> <item action="OpenNew" id="OpenNew">Open in New Window</item> <separator/> <item action="ZoomIn" id="ZoomIn">Zoom In</item> <item action="ZoomOut" id="ZoomOut">Zoom Out</item> <item action="OriginalView" id="OriginalView">Original View</item> <separator/> <item action="Quality" id="Quality">Higher Quality</item> <item action="Pause" id="Pause">Pause</item> <item action="Mute" id="Mute">Mute</item> <separator/> <item action="Find" id="Find">Find...</item> <item action="FindAgain" id="FindAgain">Find Again</item> <separator/> <item action="Copy" id="Copy">Copy Selected Text</item> <item action="CopySVG" id="CopySVG">Copy SVG</item> <item action="ViewSVG" id="ViewSVG">View SVG</item> <item action="ViewSource" id="ViewSource">View Source</item> <item action="SaveAs" id="SaveAs">Save SVG As...</item> <separator/> <item action="Help" id="Help">Help</item> <item action="About" id="About">About Adobe SVG Viewer...</item> </menu>
Like any other XML fragment loaded in the Adobe SVG Viewer, you can script this
using the standard XML DOM interfaces. The contextual menu XML representation is
actually another DOM Document instance, and Adobe has an accessor field,
contextMenu, for it on its Window
object. Once you access the contextMenu you can modify the
contextual menu any way you want, using the standard DOM scripting methods. As
you can see, <item> elements can have an action
attribute that specifies the built-in Adobe Viewer function it should fire upon
user activation. If built-in functions don't suffice, you can use the
onactivate attribute to call a custom-made JavaScript
function. Further menu customization also includes submenus with nested
<menu> elements. For more information, browse the SVG Wiki, especially its contextual
menu page.
Server Connections
One thing that the Flash Player prides itself on is server-connection facilities, which enable Flash developers to open XML sockets to load external data sources. But no SVG or DOM recommendations from the W3C specify any kind of load-and-save mechanisms. The DOM Level 3 Load and Save Working Draft aims to do just that. Adobe has actually anticipated a little bit, offering SVG content creators the means to do what the Flash Player can do, and a little more.
The relevant methods are getURL() and
postURL(),
both defined on the Window
object. These two methods allow you to retrieve data from, or post data to, a
URL. Both getURL() and postURL() require a callback
method to be passed as a parameter. The callback method is automatically passed to
an AsyncURLStatus
object, which gives some information on the status of the request and the data
you received. Let's see how we can call for data from a remote URL:
function loadFile (fileName) {
getURL(fileName, fileLoaded);
}
function fileLoaded (data) {
var msg = '';
if(data.success) {
msg = data.content;
} else {
msg = 'Loading has failed';
}
alert(msg);
}
The Adobe implementation handles data reception quite nicely. It will take care
of any necessary encoding and compression. If you want to receive gzipped XML or
SVG data, the viewer will decompress it before sending it back to you. The same goes
for ASCII and UTF-8 or UTF-16 encodings. You won't be able to pass any URL
outside of your SVG's document-originating server for security reasons. To see a
nice usage of these two methods, check out Kevin Lindsey's implementation of
a loading progress bar; it should give you a fair idea of how to integrate
it within your own applications. I have come up with a quick and dirty example myself: a minimal object
inspector and hide mechanism. Also, taking our previous discussions on
customizing the contextual menu further, you could use getURL() to
load a context-menu, XML-description file straight from a server rather than build
one on the client with some perhaps clumsy DOM code. One last notable point:
the SVG Working Group has received strong feedback from the SVG community to
make these methods available in a W3C standard. The Group is looking
into that and SVG 1.2 looks like a great candidate to have those two methods
defined (probably as convenient shorthands for DOM Level 3 Load and Save
methods).
|
Also in Sacré SVG |
Simplified XML Parsing and Serialization
Since it is only strings that can be sent or received with the methods above, you might well face two problems. If you receive SVG content from the server, and you want to append it to your existing SVG tree, you'll need a Document Fragment rather than a plain string. Or maybe you're sending SVG code generated on the client to the server so it can be archived in a database, in which case you will have to convert your Document Fragment into a string.
Rather than using getURL() or postURL(), you can use
parseXML() and
printNode(). These are exactly what you will need for your XML
parsing and serialization needs. printNode() takes a
node as a parameter and returns a formatted string with the XML
data. printNode() will most likely throw out some empty text nodes
and normalize contiguous text nodes. For example,
function printContextualMenu () {
var string = printNode(contextMenu);
alert(string);
}
The inverse operation, parsing, is done in a simple way, too. The
parseXML() method takes two parameters. The first one is the string
you want to have returned as a Document Fragment. The second argument, which is
optional, specifies the context document used for parsing. If you don't care to
provide a context, a new document will be created. If you want to parse SVG in
order to append it to an existing SVG DOM tree, you will want to parse your
string in the document, another field in the helpful Window
object that refers to your existing SVG document. Here's how you would parse a
string with SVG content and then append your new graphics to the document:
function parseAndAddData (string) {
var node = parseXML(string, document);
document.documentElement.appendChild(node);
}
Again, the SVG Working Group has received many requests to include these two
handy methods in an upcoming SVG recommendation. Similar to the
getURL() and postURL() methods, there's a good chance
you will see these two appear in SVG 1.2 as convenience shorthands to DOM Level
3 Load and Save methods.
Wrapping It All Up
You should now know how to get the best out of the most widely deployed SVG implementation out there. If you want to know more, look at the set of pages devoted to ASV3 on the SVG Wiki, which is maintained by Niklas Gustavsson. Think of Adobe's work as an anticipation of what's next with SVG. And don't hesitate to voice your concerns to the W3C or to Adobe.
