引自http://blog.csdn.net/firefight/archive/2007/02/06/1503103.aspx
使用 Batik 创建 SVG 应用程序
Batik 工具集提供的 JSVGCanvas 模块是一个 swing 组件,用于显示静态或动态 SVG 文档。通过 JSVGCanvas 模块,开发人员可以轻松显示 SVG 文档(通过 URI 地址或 DOM 树)并对其进行操作,例如旋转、缩放、摇动、选择文本或激活超级链接等。首先介绍如何创建 JSVGCanvas 并集成到一个 swing 应用程序中。接下来解释如何完成与 SVG 画布相关的常用功能,例如如何跟踪 SVG 文档渲染时发生的所有事件,以及如何通过 JavaTM 语言操作 SVG 文档。
创建 JSVGCanvas
JSVGCanvas 是一个 swing 组件并遵循 swing 设计规则 (Swing design rule [4] ) 。这意味着组件不是线程安全的,而且所有操作应当参照 swing 教程描述使用。 JSVGCanvas 也是一个 JavaBean 组件,因此可以在可视化应用程序开发工具中使用。下例中演示了如何轻松创建和使用 JSVGCanvas 组件(见图 3 )。
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.swing.gvt.GVTTreeRendererAdapter;
import org.apache.batik.swing.gvt.GVTTreeRendererEvent;
import org.apache.batik.swing.svg.SVGDocumentLoaderAdapter;
import org.apache.batik.swing.svg.SVGDocumentLoaderEvent;
import org.apache.batik.swing.svg.GVTTreeBuilderAdapter;
import org.apache.batik.swing.svg.GVTTreeBuilderEvent;
public class SVGApplication {
public static void main(String[] args) {
JFrame f = new JFrame("Batik");
SVGApplication app = new SVGApplication(f);
f.getContentPane().add(app.createComponents());
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.setSize(400, 400);
f.setVisible(true);
}
JFrame frame;
JButton button = new JButton("Load...");
JLabel label = new JLabel();
JSVGCanvas svgCanvas = new JSVGCanvas();
public SVGApplication(JFrame f) {
frame = f;
}
public JComponent createComponents() {
final JPanel panel = new JPanel(new BorderLayout());
JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
p.add(button);
p.add(label);
panel.add(p, BorderLayout.NORTH);
panel.add(svgCanvas, BorderLayout.CENTER);
// Set the button action.
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JFileChooser fc = new JFileChooser(".");
int choice = fc.showOpenDialog(panel);
if (choice == JFileChooser.APPROVE_OPTION) {
File f = fc.getSelectedFile();
try {
svgCanvas.setURI(f.toURL().toString());
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
});
// Set the JSVGCanvas listeners.
svgCanvas.addSVGDocumentLoaderListener(new SVGDocumentLoaderAdapter() {
public void documentLoadingStarted(SVGDocumentLoaderEvent e) {
label.setText("Document Loading...");
}
public void documentLoadingCompleted(SVGDocumentLoaderEvent e) {
label.setText("Document Loaded.");
}
});
svgCanvas.addGVTTreeBuilderListener(new GVTTreeBuilderAdapter() {
public void gvtBuildStarted(GVTTreeBuilderEvent e) {
label.setText("Build Started...");
}
public void gvtBuildCompleted(GVTTreeBuilderEvent e) {
label.setText("Build Done.");
frame.pack();
}
});
svgCanvas.addGVTTreeRendererListener(new GVTTreeRendererAdapter() {
public void gvtRenderingPrepare(GVTTreeRendererEvent e) {
label.setText("Rendering Started...");
}
public void gvtRenderingCompleted(GVTTreeRendererEvent e) {
label.setText("");
}
});
return panel;
}
}
图 3: 运行中的 SVG 应用程序
事件处理机制
如上例所示,每当设置 JSVGCanvas 的 URI 或 SVG DOM 时(通过 setURI 或 setSVGDocument 方法),相关文档首先被解析(在 URI 的情况),然后创建、渲染和有选择的更新。为了接收这些不同的阶段的事件通知,正确的方法是实现一个侦听器并附加到该组件。有五种类型的侦听器:
- SVGDocumentLoaderListener – 提供了用于跟踪 SVGDocumentLoaderEvent 事件的一组方法。它描述了SVG装载阶段,即使用 SVG 文件创建 SVG DOM 树。
- GVTTreeBuilderListener – 提供了用于跟踪 GVTTreeBuilderEvent 事件的一组方法。它描述了创建阶段,即通过 SVG DOM 树创建一个 GVT (图形矢量工具集),然后 GVT 树 被用于渲染文档。
- SVGLoadEventDispatcherListener – 提供了用于跟踪 SVGLoadEventDispatcherEvent 事件的一组方法。它描述了 DOM 'SVGLoad' 的事件派发阶段。该事件仅在动态类型文档中触发。
- GVTTreeRendererListener – 提供了用于跟踪 GVTTreeRendererEvent 事件的一组方法。它描述了渲染阶段,即使用一个 GVT 树创建图像。在动态文档中该事件只在最初渲染时被触发一次。
- UpdateManagerListener – 提供了用于跟踪 UpdateManagerEvent 事件的一组方法。它描述了运行阶段,即显示 更新管理器启动,然后显示线程可能被挂起、恢复或停止。该侦听器可用于跟踪图像更新情况。只有动态文档触发该事件。
使用 JavaTM 脚本
Batik 工具集提供了简便的,基于 JavaTM 语言的 SVG 文档脚本。在前一节中,我们学习了如何显示一个 SVG 文档;本节描述如何操作当前在 JSVGCanvas 中显示的 SVG 文档。下面的例子中演示了如何操作 SVG 文档。注意开发人员不需要考虑图像的更新问题,在事件侦听器激活后画布根据需要进行自动更新。
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.swing.svg.SVGLoadEventDispatcherAdapter;
import org.apache.batik.swing.svg.SVGLoadEventDispatcherEvent;
import org.apache.batik.script.Window;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
public class SVGApplication {
public static void main(String[] args) {
new SVGApplication();
}
JFrame frame;
JSVGCanvas canvas;
Document document;
Window window;
public SVGApplication() {
frame = new JFrame();
canvas = new JSVGCanvas();
// Forces the canvas to always be dynamic even if the current
// document does not contain scripting or animation.
canvas.setDocumentState(JSVGCanvas.ALWAYS_DYNAMIC);
canvas.addSVGLoadEventDispatcherListener
(new SVGLoadEventDispatcherAdapter() {
public void svgLoadEventDispatchStarted
(SVGLoadEventDispatcherEvent e) {
// At this time the document is available...
document = canvas.getSVGDocument();
// ...and the window object too.
window = canvas.getUpdateManager().
getScriptingEnvironment().createWindow();
// Registers the listeners on the document
// just before the SVGLoad event is dispatched.
registerListeners();
// It is time to pack the frame.
frame.pack();
}
});
frame.addWindowListener(new WindowAdapter() {
public void windowOpened(WindowEvent e) {
// The canvas is ready to load the base document
// now, from the AWT thread.
canvas.setURI("doc.svg");
}
});
frame.getContentPane().add(canvas);
frame.setSize(800, 600);
frame.show();
}
public void registerListeners() {
// Gets an element from the loaded document.
Element elt = document.getElementById("elt-id");
EventTarget t = (EventTarget)elt;
// Adds a 'onload' listener
t.addEventListener("SVGLoad", new OnLoadAction(), false);
// Adds a 'onclick' listener
t.addEventListener("click", new OnClickAction(), false);
}
public class OnLoadAction implements EventListener {
public void handleEvent(Event evt) {
// Make some actions here...
// ... for example start an animation loop:
window.setInterval(new Animation(), 50);
}
}
public class OnClickAction implements EventListener {
public void handleEvent(Event evt) {
// Make some actions here...
// ... for example schedule an action for later:
window.setTimeout(new DelayedTask(), 500);
}
}
public class Animation implements Runnable {
public void run() {
// Insert animation code here...
}
}
public class DelayedTask implements Runnable {
public void run() {
// Make some actions here...
// ... for example displays an alert dialog:
window.alert("Delayed Action invoked!");
}
}
}
在 SVG 文档中注册的 DOM 侦听器在画布更新线程中被激活。为了避免冲突的情况,开发人员不应该在另一个线程中操作 DOM 树,而应当切换到画布更新线程进行更新操作。从外部线程切换到画布更新线程的方法如下:
// Returns immediately
canvas.getUpdateManager().getUpdateRunnableQueue().
invokeLater(new Runnable() {
// Insert some actions on the DOM here
});
// Waits until the Runnable is invoked
canvas.getUpdateManager().getUpdateRunnableQueue().
invokeAndWait(new Runnable() {
// Insert some actions on the DOM here
});
与常规的事件侦听器相似,当 Runnable 从更新线程激活时,图形被更新。
Batik 同时提供了 SVG<script> 元素的扩展,以便在 SVG 文档中执行 Java 程序。所有使用 bridge 模块的 Batik 应用程序都可以使用该功能(例如 JSVGCanvas 和 ImageTranscoder 模块)。为了使用该扩展, <script> 元素中的 'type' 属性必须设置为 application/java-archive 。另外, xlink:href 属性应设置为执行程序所在的 jar 文档 URI 地址。该 jar 文件的表述文件( manifest )必须包括下表中的入口项:
Script-Handler: <classname>
<classname> 必须是实现 org.apache.batik.script.ScriptHandler 接口的类的名称。该类可以直接放在 jar 文件中,也可以位于表述文件 Class-Path 入口项所包含的其它 jar 文件中。
总结
在本文中,我们了解到开发人员如何使用 Batik 工具集创建、操作和显示 SVG 内容。 Batik 的模块具有良好的扩展性而且易于使用,通过本文的学习, JavaTM 开发人员现在可以着手编写客户端或服务器端的 SVG 应用程序。另外, Batik 项目是 Apache 软件基金会 (ASF) 倡导的开源志愿项目。这就意味着有很多方式对该项目贡献自己的力量,包括直接参与(编码、写文档、回答问题、提供想法、报告错误、错误修改建议等等),或资源捐献(公开代码、硬件、软件、出席会议、演讲等)。本项目特别关注使用 Batik 模块的应用程序,包括各种工具和扩展,因此请积极的通过 Batik 邮件列表为本项目作出贡献,邮件列表为 batik-users@xml.apache.org 。
参考资料
[1] "The official SVG page at W3C" ,
SVG 工作组,网址 http://www.w3.org/Graphics/svg .
[2] "The Document Object Model" ,
DOM 工作组,网址 http://www.w3.org/DOM .
[3] "The Batik SVG Generator Tutorial" ,
Batik 小组,网址 http://xml.apache.org/batik/svggen.html .
http://www.svgopen.org/2002/papers/kormann__developing_svg_apps_with_batik/