SVG Tips and Tricks: Adobe's SVG Viewer
July 3, 2002
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.