http://xml.apache.org/http://www.apache.org/http://www.w3.org/

Home

Overview
FAQ
License
Download
Install
Demo

In the news

Tools and Apps
Browser
Rasterizer
Font Converter
Pretty-printer

Architecture
Generator
DOM API
Scripting
JSVGCanvas
Transcoder API

Extensions

Testing

Contributors
Mail Lists

CVS Repository
Bug Database

Status

Glossary


Introduction

Batik SVG Generator

As SVG(Scalable Vector Graphics) is emerging as a promising graphics format for a wide range of domains and applications, bridging it with Java becomes important. This page explains how Batik's SVGGraphics2D, referred to as the SVG Generator, makes this possible. It is divided into three parts:


What's SVGGraphics2D

On the Java platform, all rendering goes through the java.awt.Graphics2D abstract class, which offers methods such as drawRect, fillRect, or drawString. There are specialized implementations of this abstract class for each type of output, such as a monitor or a printer. SVGGraphics2D is a new implementation of that interface that generates SVG content instead of drawing to a screen or a printer.

SVGGraphics2D provides the following:

  • Allows applications to export their graphics into SVG format.
  • Does not require any modification of the graphics code to export to SVG.
  • Offers the user the ability to use the DOM API to manipulate the generated document.

High level architecture

The above figure shows how the generator works with the DOM API. The W3C has defined an API for representing XML content with a Java programming language object. That API allows programmers to manipulate, create, and/or modify XML content in memory. The DOM API contains interfaces such as Document, Element, and Attr, which model the Java programming language equivalent of XML documents, elements and attributes.

The generator manages a tree of DOM objects that represent the SVG content corresponding to the rendering calls made on the SVGGraphics2D instance. In other words, every time a program invokes a rendering method, such as fillRect, on a SVGGraphics2D instance, a new DOM object, representing the SVG equivalent, is appended to the DOM tree (for example a <rect> element will be appended after the fillRect method has been invoked).

The programmer using this generator can then access the DOM tree to further manipulate it or can directly write the content to an output stream, as we see in the following section.


How to use SVGGraphics2D

From the figure in the previous section we can see that in order for an instance of SVGGraphics2D to build the SVG content(the DOM tree), an instance of DOM's Document class is needed. The DOM tree is an in-memory representation of the SVG document, which can be further manipulated by the user using DOM API or be streamed out into any java.io.Writer.

The following excerpted code example shows how to generate SVG content from Java graphics.


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
        Document document = domImpl.createDocument(null, "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
        // character to byte encoding
        boolean useCSS = true; // we want to use CSS style attribute
        Writer out = new OutputStreamWriter(System.out, "UTF-8");
        svgGenerator.stream(out, useCSS);
    }
}

We can see that generating SVG content from our TestSVGGen instance is a three step process:

1. Create an instance of org.w3c.dom.Document that the generator will use to build its XML content; create a SVG generator using the Document instance.


// Get a DOMImplementation
DOMImplementation domImpl =
    GenericDOMImplementation.getDOMImplementation();

// Create an instance of org.w3c.dom.Document
Document document = domImpl.createDocument(null, "svg", null);

// Create an instance of the SVG Generator
SVGGraphics2D svgGenerator = new SVGGraphics2D(document);

2. Invoke the rendering code on our SVG generator. In our example, we invoke TestSVGGen's paint method:


// Ask the test to render into the SVG Graphics2D implementation
TestSVGGen test = new TestSVGGen();
test.paint(svgGenerator);

3. Stream out the SVG content. The SVG generator can stream its content into any java.io.Writer. In our example, we stream the content to the standard output stream:


// Finally, stream out SVG to the standard output using UTF-8
// character to byte encoding
boolean useCSS = true; // we want to use CSS style attribute
Writer out = new OutputStreamWriter(System.out, "UTF-8");
svgGenerator.stream(out, useCSS);

SVG has two ways of expressing properties, such as the fill color: either XML attributes or CSS inline properties. The 'useCss' parameter allows the user to control that option.


SVG Generator Customization

In the previous section, we have just seen that the SVG generation process can be customized to output SVG style as XML attributes or CSS inline properties. In this section we will talk about some examples of more advanced customizations.

Instead of creating the SVGGraphics2D just by using the Document that will be used as a factory for creating the SVG elements, we can use the constructor that use an SVGGeneratorContext instance. By providing your own SVGGeneratorContext instance, you will be able to do advanced customization. You will find below several examples of possible customizations.

Have your own comment in the generated SVG file

We begin with the simpliest possible example. If you integrate the Batik SVG generator in your own Java application, you may want to specialize the comment generated in the XML code. You can proceed as below.


SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory);
ctx.setComment("Generated by FooApplication with Batik SVG Generator");
SVGGraphics2D g2d = new SVGGraphics2D(ctx);
  

Use Embedded SVG Fonts in the generated SVG file

In order to have a self-contained SVG file that doesn't have to use system fonts to be displayed, you can embed the fonts you used for drawing strings in the SVG file through the SVG fonts facility.


SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory);
ctx.setEmbeddedFontsOn(true);
SVGGraphics2D g2d = new SVGGraphics2D(ctx);
  

Customizing the generated SVG style

Your needs in matter of styling may be different from the two provided options (XML attributes or CSS inline properties). For example you may want to put the CSS properties in a stylesheet SVG element section and reference them through the class attribute. In this case you will need to define a new StyleHandler as below.


public class StyleSheetStyleHandler {
    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 id in the style sheet
        String id = generatorContext.getIDGenerator().generateID("C");
        styleSheet.appendData("."+id+" {");
	// append each key/value pairs
        while (iter.hasNext()) {
            String key = (String)iter.next();
            String value = (String)styleMap.get(key);
            styleSheet.appendData(key+":"+value+";");
        }
        styleSheet.appendData("}\n");
	// reference the class id of the style sheet on the element to 
	// be styled
        element.setAttribute("class", id);
    }
}

Then you can create and use an SVGGraphics2D with a correctly configured SVGGeneratorContext.


// 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);

// use the g2d to dump drawings (component.paint(g2d))

// add a style sheet to the definition section
Element root = 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);

  

Extending Paint object to SVG element translation

The SVGGraphics2D is able to generate SVG elements for generic Java 2D objects, but you sometimes have your own classes such as implementations of the Java 2D java.awt.Paint interface. In this case, you will need to write an ExtensionHandler that you will set on your SVGGeneratorContext.

In the following example we define the first draft of an ExtensionHandler allowing to translate a Batik implementation of the java.awt.Paint interface named org.apache.batik.ext.awt.LinearGradientPaint.

  class SubExtensionHandler extends DefaultExtensionHandler 
  {
    public SVGPaintDescriptor handlePaint(Paint paint,
                                          SVGGeneratorContext generatorCtx) 
    {
      if (paint instanceof LinearGradientPaint) {
          LinearGradientPaint gradient = (LinearGradientPaint)paint;
	  String id = generatorCtx.getIDGenerator().generateID("gradient");
          Element grad = generatorCtx.getDOMFactory().
               createElementNS(SVGSyntax.SVG_NAMESPACE_URI, 
                               SVGSyntax.SVG_LINEAR_GRADIENT_TAG);
          grad.setAttributeNS(null, SVGSyntax.SVG_ID_ATTRIBUTE, ref);
          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
        return null; // let the default mechanism do its job
    }
  }

  

You should then set it on the SVGGeneratorContext by using the setExtensionHandler method.


SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(myFactory);
ctx.setExtensionHandler(new SubExtensionHandler());
SVGGraphics2D g2d = new SVGGraphics2D(ctx);



Copyright © 2000-2001 The Apache Software Foundation. All Rights Reserved.