-- 作者:supremeweb
-- 发布时间:4/2/2007 1:48:00 PM
-- Batik 入门(四)[转帖]
SVG 生成器: SVGGraphics2D 在java平台中所有的绘图,都通过Graphics2D抽象类,这个类提供象drawRect,fillRect,drawString这样的方法。对于每个类型的输出这个抽象类都有指定的执行,比如屏幕或者打印机。SVGGraphics2D是这个接口的一个新的执行生成SVG内容,以用来替代画到屏幕或者打印机上。 SVGGraphics2D 拥有以下特性: 1.它允许应用程序输出图形到SVG格式 2.它输出图形到svg格式,不需要修改任何图形代码 3。它提供用户使用DOMApi操作生成文档的能力 此主题相关图片如下:
以上图形显示生成器是怎样用DOMAPI工作的。W3C已经定义了一个API用来显示带有java对象的XML内容。这个API允许程序员在内存中操作,创建或者修改XML内容。DOMAPI包含象document,Element,Attr的接口,这些接口等价于java语言中的XML documents, elements 和attributes。 生成器管理一个DOM对象树用来显示相应于SVGGraphics2D实例的svg内容。换句话说,每次一个程序调用一个绘图方法,比如fillRect,在一个SVGGraphics2D实例中,一个新的DOM对象,描绘等价SVG,被添加到DOM树中。举个例子,一个矩形元素将在fillrect方法调用后被添加。 程序员使用这个生成器,可以存储DOM树到更深层的操作,或者可以直接写内容到一个输出流中,就象我们下面选项中看到的那样。 怎样使用SVGGraphics2D 从上面部分的描述我们可以看到为了使用一个SVGGraphics2D实例来构建SVG内容,一个文档类实例是必须的。DOM树是svg文档的内存内表现,它可以被用户使用DOMAPI深入操作,或者通过一个Write对象,流输出。下面的例子证明怎么从java图形中怎么生成SVG内容 import java.awt.Rectangle; import java.awt.Graphics2D; import java.awt.Color; import java.io.Writer; import java.io.OutputStreamWriter; import java.io.IOException; import org.apache.batik.svggen.SVGGraphics2D; import org.apache.batik.dom.GenericDOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.DOMImplementation; public class TestSVGGen { public void paint(Graphics2D g2d) { g2d.setPaint(Color.red); g2d.fill(new Rectangle(10, 10, 100, 100)); } public static void main(String[] args) throws IOException { // Get a DOMImplementation. DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); // Create an instance of org.w3c.dom.Document. String svgNS = "http://www.w3.org/2000/svg"; Document document = domImpl.createDocument(svgNS, "svg", null); // Create an instance of the SVG Generator. SVGGraphics2D svgGenerator = new SVGGraphics2D(document); // Ask the test to render into the SVG Graphics2D implementation. TestSVGGen test = new TestSVGGen(); test.paint(svgGenerator); // Finally, stream out SVG to the standard output using // UTF-8 encoding. boolean useCSS = true; // we want to use CSS style attributes Writer out = new OutputStreamWriter(System.out, "UTF-8"); svgGenerator.stream(out, useCSS); } } 我们可以看到在我们的TestSVGGen实例中生成SVG内容包括三个步骤: 1.创建一个org.w3c.dom.Document实例,以便生成器用来构建它的XML内容,并且使用Document实例创建一个SVG生成器 // Get a DOMImplementation. DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); // Create an instance of org.w3c.dom.Document. String svgNS = "http://www.w3.org/2000/svg"; Document document = domImpl.createDocument(svgNS, "svg", null); // Create an instance of the SVG Generator. SVGGraphics2D svgGenerator = new SVGGraphics2D(document); 2.调用SVG生成器上的描绘代码。在我们的例子中,我们调用了TestSVGGen的paint方法 // Ask the test to render into the SVG Graphics2D implementation. TestSVGGen test = new TestSVGGen(); test.paint(svgGenerator); 3.流输出svg内容。svg生成器可以流输出它的内容到任意的java.io.Writer中。在我们的例子中,我们流输出内容到标准的输出流中 // Finally, stream out SVG to the standard output using // UTF-8 encoding. boolean useCSS = true; // we want to use CSS style attributes Writer out = new OutputStreamWriter(System.out, "UTF-8"); svgGenerator.stream(out, useCSS); svg有两种方式来指定风格属性,比如填充颜色,显示属性或者CSS类型属性。useCss 参数允许用户使用这个属性 svg生成器定制 在前面的段落中,我们已经看到SVG生成程序可以被定制来输出svg类型属性作为显示属性,或者css在线属性。在这个部分里我们将讨论一些高级定制的例子。 为了替代创建SVGGraphics2D只使用象创建SVG元素的工厂那样的document,我们可以使用一个SVGGeneratorContext 实例的构造器。通过提供你拥有的SVGGeneratorContext 实例,你将可以进行高级定制。你将发现如下的例子可以被定制。 1.在生成的SVG文件中存在你自己的注释 下面我们开始简单可行的例子。如果你在自己的java应用程序中结合Batik SVG生成器,你将希望在XML代码中生成专门的注释。 DOMImplementation impl = GenericDOMImplementation.getDOMImplementation(); String svgNS = "http://www.w3.org/2000/svg"; Document myFactory = impl.createDocument(svgNS, "svg", null); SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory); ctx.setComment("Generated by FooApplication with Batik SVG Generator"); SVGGraphics2D g2d = new SVGGraphics2D(ctx, false); 2.在生成的SVG文件中使用嵌入式svg字体 要有一个不使用系统字体来显示的独立的svg文件,你可以自己在svg文件中定义字体。 DOMImplementation impl = GenericDOMImplementation.getDOMImplementation(); String svgNS = "http://www.w3.org/2000/svg"; Document myFactory = impl.createDocument(svgNS, "svg", null); SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory); ctx.setEmbeddedFontsOn(true); SVGGraphics2D g2d = new SVGGraphics2D(ctx, true); 3.定制图片存储路径 每次你调用一个通过SVGGraphics2D接口提供的drawImage方法时,默认图片绘制被创建并且放到一个默认文件中。举个实例,一个base64编码被创建,并且通过默认嵌入到svg文件中去。作为选择,你可以选择将图片写到事先定义好的文件夹中的一个单独的文件里。文件格式是svg规范要求的两种光栅格式图片:jpeg,png 你可以改变默认行为,通过在svg生成器中提供明确的提供图片句柄来使用。下面例子中所有图片将转换为png格式,同时存放到res/images文件夹中 DOMImplementation impl = GenericDOMImplementation.getDOMImplementation(); String svgNS = "http://www.w3.org/2000/svg"; Document myFactory = impl.createDocument(svgNS, "svg", null); SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory); GenericImageHandler ihandler = new ImageHandlerPNGEncoder("res/images", null); ctx.setImageHandler(ihandler); SVGGraphics2D g2d = new SVGGraphics2D(ctx, false); 为每个单独drawimage调用在一个新的被写到SVG文件或者一个扩展文件的图片数据拷贝中使用默认的图片处理结果。如果你一遍又一遍的使用相同的图片,这样做的结果就是在一个SVG文件中包含许多冗余信息。在初始化SVGDOm树的时候将会有微小的性能损失,你可以选择将图片重复使用。为了这个原因你要使用一个指定的图片处理器,如下: DOMImplementation impl = GenericDOMImplementation.getDOMImplementation(); String svgNS = "http://www.w3.org/2000/svg"; Document myFactory = impl.createDocument(svgNS, "svg", null); SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory); // Reuse our embedded base64-encoded image data. GenericImageHandler ihandler = new CachedImageHandlerBase64Encoder(); ctx.setGenericImageHandler(ihandler); SVGGraphics2D g2d = new SVGGraphics2D(ctx, false); 用隐藏的图片处理器,你甚至可以使用几个不同的SVG文档图片数据的相同拷贝的重用。只要保持图片处理的参考,并且传输它到为生成SVG DOM树使用的SVGGraphics实例中。下面简单的一个例子用来论证不同的SVG树通过单独的SVG生成器被创建,以便有效的存储任何通用图片。 class MySVGGenerator { // The image handler will write all images files to "res/images". private static ImageHandler ihandler = new CachedImageHandlerPNGEncoder("res/images", null); public void generateSVG(JPanel myCanvas, OutputStream outStream) { DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); Document myFactory = domImpl.createDocument(svgNS, "svg", null); SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory); ctx.setGenericImageHandler(ihandler); SVGGraphics2D svgGenerator = new SVGGraphics2D(ctx, false); // Create the SVG DOM tree. myCanvas.paintComponent(svgGenerator); Writer out = new OutputStreamWriter(outStream, "UTF-8"); svgGenerator.stream(out, true); } } 定制生成的SVG类型 你的需要相关风格可以不同于提供的两个选项(XML presentation attributes or CSS inline stylesheets)。举个例子,你可以希望将CSS属性方知道一个SVG风格的元素不分离,并且通过类属性提及到他们。那么你将需要定制一个如下所示的新的stylehandler public class StyleSheetStyleHandler implements StyleHandler { // The CDATA section that holds the CSS stylesheet. private CDATASection styleSheet; // Build the handler with a reference to the stylesheet section. public StyleSheetStyleHandler(CDATASection styleSheet) { this.styleSheet = styleSheet; } public void setStyle(Element element, Map styleMap, SVGGeneratorContext generatorContext) { Iterator iter = styleMap.keySet().iterator(); // Create a new class in the style sheet. String id = generatorContext.getIDGenerator().generateID("C"); styleSheet.appendData("."+ id +" {"); // Append each key/value pair. while (iter.hasNext()) { String key = (String) iter.next(); String value = (String) styleMap.get(key); styleSheet.appendData(key + ":" + value + ";"); } styleSheet.appendData("}\n"); // Reference the stylesheet class on the element to be styled. element.setAttributeNS(null, "class", id); } } 然后你可以创建并且使用一个有正确配置的SVGGeneratorContext的一个SVGGraphics 2D // Configure the SVGGraphics2D for a given Document myFactory. SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory); CDATASection styleSheet = myFactory.createCDATASection(""); ctx.setStyleHandler(new StyleSheetStyleHandler(styleSheet)); SVGGraphics2D g2d = new SVGGraphics2D(ctx, false); // Use the g2d to draw (e.g., component.paint(g2d)). // Add a stylesheet to the definition section. SVGSVGElement root = (SVGSVGElement) g2d.getRoot(); Element defs = root.getElementById(SVGSyntax.ID_PREFIX_GENERIC_DEFS); Element style = myFactory.createElementNS (SVGSyntax.SVG_NAMESPACE_URI, SVGSyntax.SVG_STYLE_TAG); style.setAttributeNS(null, SVGSyntax.SVG_TYPE_ATTRIBUTE, "text/css"); style.appendChild(styleSheet); defs.appendChild(style); // Dump the root content to a given Writer myWriter. g2d.stream(root, myWriter); 扩展paint对象到SVG元素转换 SVGGraphics2D可以为通用的java 2d对象生成SVG元素,但是有时候你可以用你自己的类,比如java 2D Paint的执行接口。因为这个原因你需要写一个你将要设置在你的SVGGeneratorContext中的ExtensionHandler。 在下边的例子中,我们定义ExtensionHandler的一个草图,这个草图允许转换一个Paint接口的Batik执行,名称叫LinearGradientPaint。 class MyExtensionHandler extends DefaultExtensionHandler { public SVGPaintDescriptor handlePaint(Paint paint, SVGGeneratorContext generatorCtx) { if (paint instanceof LinearGradientPaint) { LinearGradientPaint gradient = (LinearGradientPaint) paint; // Create a new SVG 'linearGradient' element to represent the // LinearGradientPaint being used. String id = generatorCtx.getIDGenerator().generateID("gradient"); Document doc = generatorCtx.getDOMFactory(); Element grad = doc.createElementNS (SVGSyntax.SVG_NAMESPACE_URI, SVGSyntax.SVG_LINEAR_GRADIENT_TAG); // Set the relevant attributes on the 'linearGradient' element. grad.setAttributeNS(null, SVGSyntax.SVG_ID_ATTRIBUTE, id); grad.setAttributeNS(null, SVGSyntax.SVG_GRADIENT_UNITS_ATTRIBUTE, SVGSyntax.SVG_USER_SPACE_ON_USE_VALUE); Point2D pt = gradient.getStartPoint(); grad.setAttributeNS(null, "x1", pt.getX()); grad.setAttributeNS(null, "y1", pt.getY()); pt = gradient.getEndPoint(); grad.setAttributeNS(null, "x2", pt.getX()); grad.setAttributeNS(null, "y2", pt.getY()); switch (gradient.getCycleMethod()) { case MultipleGradientPaint.REFLECT: grad.setAttributeNS (null, SVGSyntax.SVG_SPREAD_METHOD_ATTRIBUTE, SVGSyntax.SVG_REFLECT_VALUE); break; case MultipleGradientPaint.REPEAT: grad.setAttributeNS (null, SVGSyntax.SVG_SPREAD_METHOD_ATTRIBUTE, SVGSyntax.SVG_REPEAT_VALUE); break; // 'pad' is the default... } // Here we should write the transform of the gradient // in the transform attribute... // Here we should write the stops of the gradients as // children elements... return new SVGPaintDescriptor ("url(#" + ref + ")", SVGSyntax.SVG_OPAQUE_VALUE, grad); } else { // Let the default mechanism do its job. return null; } } } 然后你需要在SVGGeneratorContext上,通过使用setExtensionHandler方法设定它。 SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory); ctx.setExtensionHandler(new MyExtensionHandler()); SVGGraphics2D g2d = new SVGGraphics2D(ctx, false); 怎样查看生成的SVG文档 下面的代码详细描述怎么查看利用SVGGraphics2D对象生成的svg内容 import java.awt.*; import java.awt.geom.*; import javax.swing.*; import org.apache.batik.swing.*; import org.apache.batik.svggen.*; import org.apache.batik.dom.svg.SVGDOMImplementation; import org.w3c.dom.*; import org.w3c.dom.svg.*; public class ViewGeneratedSVGDemo { public static void main(String[] args) { // Create an SVG document. DOMImplementation impl = SVGDOMImplementation.getDOMImplementation(); String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI; SVGDocument doc = (SVGDocument) impl.createDocument(svgNS, "svg", null); // Create a converter for this document. SVGGraphics2D g = new SVGGraphics2D(doc); // Do some drawing. Shape circle = new Ellipse2D.Double(0, 0, 50, 50); g.setPaint(Color.red); g.fill(circle); g.translate(60, 0); g.setPaint(Color.green); g.fill(circle); g.translate(60, 0); g.setPaint(Color.blue); g.fill(circle); g.setSVGCanvasSize(new Dimension(180, 50)); // Populate the document root with the generated SVG content. Element root = doc.getDocumentElement(); g.getRoot(root); // Display the document. JSVGCanvas canvas = new JSVGCanvas(); JFrame f = new JFrame(); f.getContentPane().add(canvas); canvas.setSVGDocument(doc); f.pack(); f.setVisible(true); } }
|