使用Batik开发SVG应用程序(二)

    技术2022-05-11  70

    使用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 URISVG DOM时(通过setURIsetSVGDocument方法),相关文档首先被解析(在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.w 3c .dom.Document;

    import org.w 3c .dom.Element;

    import org.w 3c .dom.events.Event;

    import org.w 3c .dom.events.EventListener;

    import org.w 3c .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 W 3C "

    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.

    [4] "Creating a GUI with JFC/Swing"Swing小组(Sun Microsystems) ,网址http://java.sun.com/docs/books/tutorial/uiswing/overview/threads.html.


    原文地址:

    http://www.svgopen.org/2002/papers/kormann__developing_svg_apps_with_batik/

     

    最新回复(0)