blob: 0a10cdcaac826186697e54125c44bfa2cc33cf2f [file] [log] [blame]
<?xml version="1.0"?>
<!DOCTYPE document SYSTEM "./dtd/document-v10.dtd">
<!-- ====================================================================== -->
<!-- Copyright (C) The Apache Software Foundation. All rights reserved. -->
<!-- -->
<!-- This software is published under the terms of the Apache Software -->
<!-- License version 1.1, a copy of which has been included with this -->
<!-- distribution in the LICENSE file. -->
<!-- ====================================================================== -->
<!-- ====================================================================== -->
<!-- author Thomas.DeWeese@kodak.com -->
<!-- version $Id$ -->
<!-- ====================================================================== -->
<document>
<header>
<title>Extending Batik</title>
<subtitle>Possible extensions</subtitle>
<authors>
<person name="Thomas DeWeese" email="thomas.deweese@kodak.com"/>
</authors>
</header>
<body>
<s1 title="Introduction">
<p>The goal of this section is to provide an overview of
the built in extension mechanisms in Batik. As an open
source project, people can of course make any extension
they feel is interesting, however Batik has been
designed with several forms of extension in mind.</p>
<p>The main areas extension are anticipated are:</p>
<ul>
<li><link href="#customXMLTags">
Support for custom XML Tags
</link></li>
<li><link href="#imageTagFormats">
Support for new file formats in the 'image' tag.
</link></li>
<li><link href="#urlProtocols">
Support for new protocols in URL references.
</link></li>
</ul>
<p>
In general extensions are done through the Service
Provider Interface mechanism as described in the
<link href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service Provider">
Jar file</link> documentation.
</p>
<p>
This allows for the extension of Batik simply by adding a
new jar file(s) to the class path, no modification of
Batik source is required!
</p>
<note>
If you feel that the Batik team has overlooked an
important area for extension please let your feelings be
known on the mailing-lists.
</note>
</s1>
<anchor id="customXMLTags" />
<s1 title="Custom XML Tags">
<p>
First one must ask "What does it mean to support custom
XML tags?"
</p>
<p>
There are three basic options Batik considers:
</p>
<dl>
<dt> Having your tags appear in the DOM tree. </dt>
<dd><br/>
As long as your custom tags are well formed (and
preferably valid) XML they will appear in the SVG DOM
tree. When rendering Batik will skip branches of the
tree that use a tag it doesn't know about (so even if
standard SVG tags are child nodes they will not be
displayed).
<br/>
It is strongly suggested that you make use of XML
namespaces for your personal tags even if you are not
planning on validating the XML.
<br/>
This can be useful if you want to add extra pieces of
data into the standard SVG drawing. These might be
annotations, or other application specific data. In
general this wouldn't be particularly useful with the
svgbrowser or rasterizer, but might be very useful if
you were writing a custom browser, rasterizer, or
pre/post processing tools.
</dd>
<dt> Tags use a custom element in the DOM tree</dt>
<dd><br/>
If you need your tags to use a custom element subclass
in the DOM tree (for behavioral or performance
reasons) then you need to provide an <link
href="#domExtension"> extension to the Batik
DOM.</link> <br/>
Doing this gives you the opportunity to override the
standard methods on DOM elements, or to provide
additional methods to your DOM elements. For example
all of the SVG elements in Batik implement the
getAttribute methods to respect the CSS styling
properties. They also provide additional methods to
implement the SVG DOM.
</dd>
<dt> Tags that get rendered.</dt>
<dd><br/>
Probably the most common reason to develop custom tags
is to add new rendering primitives to the SVG
language. In this case you must provide an <link
href="#bridgeExtension"> extension to the Batik
Bridge</link>. The bridge extension is resposible for
constructing the class(es) that will handle the
rendering of the new primitive in Batik. <br/>
In most cases it will also be necessary to write a DOM
extension to make the tag behave like other SVG tags
(most notably for support of styling).
</dd>
</dl>
<anchor id="domExtension"/>
<s2 title="Writing a Batik DOM Extension">
<p>
The ability to extend the elements used in the SVG DOM
tree allows users to provide implementations for nodes
that can be used in place of Batik's default
implementation of a node. This may be done for a
variety of reasons but is most commonly done to extend
the behavior of standard node calls (such as to
include styling in attribute lookup), or to implement
the DOM interface for a Tag.
</p>
<p>
The key class for building the DOM tree is the
<code>SVGOMDocument</code> class. When an instance of this class
is constructed it searches for instances of the
<code>org.apache.batik.dom.svg.DomExtension</code>
Service Provider interface. It then calls the
'registerTags' method with it's self for each one.
This method typically calls
<code>registerCustomElementFactory</code> for each tag that it
wishes to handle.
</p>
<p>
With Batik the most likely reason to extend a node is
to provide proper CSS styling of the node attributes.
To this end Batik provides a class you can extend:
<code>org.apache.batik.extension.PrefixableStylableExtensionElement</code>.
If you derive off this class you are only required to
implement three methods: <code>getLocalName</code>,
<code>getNamespaceURI</code>, and <code>newNode</code> (plus constructors). If
all you want is proper style support then you are
done implementing your elements at this point.
</p>
<p>
The distribution comes with two examples
<code>org.apache.batik.extension.svg.BatikStarElement</code>,
and
<code>org.apache.batik.extension.svg.BatikRegularPolygonElement</code>.
These examples also include the required <code>DomExtension</code> instance to
register the elements with the <code>SVGOMDocument</code>.
</p>
</s2>
<anchor id="bridgeExtension"/>
<s2 title="Writing a Batik Bridge Extension">
<p>
Before you write a bridge extension it may be useful
to understand what the role of the bridge package is
in Batik. The bridge package is responsible for
creating and maintaining elements in the Graphics
Vector Toolkit (GVT) tree from the corresponding
element in the SVG DOM. This is done because for a
variety of reasons the SVG DOM is not well suited for
rendering directly off of. The GVT tree is used for
all rendering and transcoding operations.
</p>
<p>
The key class for managing this link is the
<code>BridgeContext</code> class. This class maintains an
association between a tag name with namespace and a
particular bridge instance that will handle it. The
work of constructing the proper entities in the GVT
tree is then deferred to the registered instance of the
bridge class.
</p>
<p>
New associations can be added by subclasses of the
<code>BridgeExtension</code> Service Provider Interface. This
interface has a number of methods that provide
information about the particular extension being
registered (including contact information, and the
list of implemented extensions). It also has a
'registerTags' method which is responsible for
registering the bridge instances with a <code>BridgeContext</code>.
The built in bridges are bundled with a
BridgeExtension as well as the two example extension
tags (in <code>org.apache.batik.extension.svg</code>),
so these are both good places to start.
</p>
<p>
The <code>Bridge</code> interface itself is very simple. It only
includes methods to get the namespace and local name
of the tag the bridge is responsible for. This
interface is then extended for each of the major
concepts present in SVG:
</p>
<dl>
<dt><link href="#graphicsNodeBridge">
GraphicsNodeBridge</link></dt>
<dd>
These are probably the most common SVG elements
they represent graphic elements in what I'll call
the "visible" SVG tree. These are the elements
most other bridges modify in some way (by clipping,
masking, filtering, etc).
<br/>
<em>Example tags:</em> svg, g, path, rect.
</dd>
<dt>FilterBridge</dt>
<dd>
Handles the SVG 'filter' element. If you wanted
to implement a new tag that could be referenced
from the 'filter' attribute on an SVG graphics
node then you would need to subclass this bridge.
However adding new types of filters to the
existing SVG 'filter' element is accomplished via
the FilterPrimitiveBridge.
<br/>
<em>Example tag:</em> filter
</dd>
<dt><link href="#filterPrimitiveBridge">
FilterPrimitiveBridge</link></dt>
<dd>
Constructs an element in the filter chain applied
to a SVG graphics node.
<br/>
<em>Example tags:</em> feBlend, feCompose, ...
</dd>
<dt><link href="#paintBridge">PaintBridge</link></dt>
<dd>
Constructs a java Paint object to be used
in filling or stroking graphic elements.
<br/>
<em>Example tags:</em> gradient, pattern.
</dd>
<dt>ClipBridge</dt>
<dd>
Constructs a ClipRable to apply to a Graphics Node.
This provides a path that data is clipped to.
<br/>
<em>Example tag:</em> clip-path.
</dd>
<dt>MarkerBridge</dt>
<dd>
Constructs a Marker for annotating the path of a
GraphicsNode.
<br/>
<em>Example tag:</em> marker
</dd>
<dt>MaskBridge</dt>
<dd>
Constructs a Mask Filter to apply to a Graphics
Node. Mask Filters typically modify the alpha
channel of the Graphics Node output to make
portions fully or partially transparent that
wouldn't be otherwise.
<br/>
<em>Example tag:</em> mask.
</dd>
</dl>
<p>
Extension writers are free to work with any of the
above bridges, however the three most common are
likely to be the <code>GraphicsNodeBridge</code>, the
<code>FilterPrimitiveBridge</code>, and the <code>PaintBridge</code>.
Each of these interfaces has several extremely useful
subclasses that handle much of the common behavior
among elements.
</p>
<p>
In some simple cases it is possible to only provide an
extension to the Bridge and get your desired effect,
however in most cases you will find that for your
element to behave like a normal SVG element (for
example supporting styling) you will need to provide a
DOM extension as well.
</p>
<anchor id="graphicsNodeBridge"/>
<s3 title="GraphicsNodeBridge">
<p>
The graphics node bridge is oriented around
constructing a new <code>GraphicsNode</code> in the GVT tree.
The <code>GraphicsNode</code> is the basic element that makes
up the GVT tree. Each <code>GraphicsNode</code> has a <code>paint</code>
method that is responsible for painting the object
(including considering clipping, masking,
filtering, and opacity for the node).
</p>
<p>
If you want to you can implement
<code>bridge.GraphicsNodeBridge</code> interface
directly or subclass
<code>bridge.AbstractGraphicsNodeBridge</code>.
This gives you the most flexibility since you can
construct a new subclass of
<code>gvt.GraphicsNode</code> and implement the
paint method to do essentially anything you want,
this is also a lot of work (and I'm not going to
try and explain everything needed to pull this
off).
</p>
<p>
However, if you just want to generate a custom
filled or stroked shape the easiest way is to
subclass either of the following classes. In this
case you are essentially only responsible for
constructing a standard java <code>Shape</code> object to
describe the desired area to operate on:
</p>
<dl>
<dt><code>SVGShapeElementBridge</code></dt>
<dd>
Subclasses of this class only need to implement
buildShape, getNamespaceURI, and getLocalName.
buildShape generally constructs a
<code>java.awt.Shape</code> object and sets it on
the provided <code>shapeNode</code> object,
however it may adjust other features of the given
shape node.
</dd>
<dt><code>SVGDecoratedShapeElementBridge</code></dt>
<dd>
This is very similar to SVGShapeElementBridge,
except it also handles the standard marker
properties. Markers will be place at each the
end of each segment of the path.
</dd>
</dl>
</s3>
<anchor id="filterPrimitiveBridge"/>
<s3 title="FilterPrimitiveBridge">
<p>
The Filter primitive bridge is concerned with the
construction of individual elements of the filter
chain. Unlike graphics nodes which generally just
draw new objects on top of the destination, filters
take existing image data and modify it to apply
effects.
</p>
<p>
This part of GVT rendering is based on the Java2D
<code>java.awt.image.renderable.RenderableImage</code>
and <code>java.awt.image.RenderedImage</code>
interfaces. This provides a convenient framework to
handle image processing (an inherently resolution
dependent operation) in the resolution independent
system defined by SVG.
</p>
<p>
The majority of classes for part of Batik are present
in the <code>batik.ext.awt.image.*</code> package hierarchy which
contains a set of generally useful extensions to the
core JDK classes and methods.
</p>
<p>
Note that the <code>FilterPrimitiveBridge</code> is invoked once
for each reference to the <code>&lt;filter&gt;</code> tag that the filter
primitive is part of. So if a filter effect is used a
half dozen times the <code>createFilter</code> method will be
called a half dozen times, even though the tag may
only appear once in the file. This means that it is
safe for the Filters returned to be 'fixed' for a
particular <code>GraphicsNode</code> being filtered.
</p>
<p>
You will notice that Batik uses extended versions of
the standard <code>Renderable</code> and <code>Rendered</code>
image classes to provide additional information about surround
requirements for operations as well as a few
convenience methods. These interfaces are called:
<code>java.ext.awt.image.renderable.Filter</code> and
<code>java.ext.awt.image.rendered.CacheableRed</code>.
Batik contains simple wrapper classes that can take
the default JDK <code>Renderable</code> and <code>Rendered</code> Image
interfaces. Within the code base the convention 'Red'
for classes implementing <code>RenderedImage</code>, and 'Rable'
for classes implementing <code>RenderableImage</code> is commonly
used ('Red' is to be pronounced like the color, and
'Rable' is to be pronounced like 'horrible' with a
silent 'h').
</p>
<p>
The <code>FilterPrimitiveBridge</code> really has only
one method right now: <code>createFilter</code> that must construct
an instance of <code>Filter</code> to perform the required
operation. This is still a fairly complex task given
the general need to support accessing the various
standard sources of image data. To this end there is
a provided subclass,
<code>AbstractSVGFilterPrimitiveElementBridge</code>
This provides convenience methods to handle many common
tasks.
</p>
<p>
Generally the bulk of the work in writing a filter
extension is the writing of the Filter instance not
tying it into the GVT tree. Batik does contain
several base classes that make this processes a bit
easier.
<code>ext.awt.image.renderable.AbstractRable</code>,
<code>ext.awt.image.rendered.AbstractRed</code>, and
<code>ext.awt.image.rendered.AbstractTiledRed</code>.
<code>TiledRed</code> ties into the Batik tile cache
(use this with caution as it is a complex area of
the Batik code and that code may be inadvertently broken).
</p>
<p>
The <code>ext.awt.image.rendered</code> and
<code>renderable</code> packages contain quite a
number of fairly general examples covering most common
cases, please refer to them for more detail.
</p>
</s3>
<anchor id="paintBridge"/>
<s3 title="PaintBridge">
<p>
The <code>PaintBridge</code> constructs an instance of the
<code>java.awt.Paint</code> to be used to fill or stroke
shapes/text (part of the paint server architecture of
SVG).
</p>
<p>
Like the filter primitive bridge the <code>PaintBridge</code> is
invoked for each reference to the paint. This makes
it possible to customize the Paint returned for the
particular element to be painted.
</p>
<p>
This is how the gradients and patterns are implemented
in Batik, so it is possible to construct rather
complex paint effects through this mechanism.
</p>
<p>
For paints you are mostly on your own, because unlike
the cases there aren't any really generally useful
base classes to derive off, the closest is the
<code>AbstractSVGGradientElementBridge</code> which is
used to handle most of the radial and linear gradient
attributes.
</p>
<p>
The existing gradient paint implementations are in
<code>ext.awt</code>, the pattern implementation is in
<code>gvt</code> since it requires access to gvt
internals.
</p>
</s3>
</s2>
</s1>
<anchor id="imageTagFormats" />
<s1 title="New File Formats">
<p>
When Batik encounters an 'image' element and it determines
the element does not reference an SVG file. It defers the
loading of the referenced image to
<code>org.apache.batik.ext.awt.image.spi.ImageTagRegistry</code>. This
class maintains a list of <code>RegistryEntries</code>, generally one
for each format.
</p>
<p>
Since the formats supported natively by Batik are also
implemented through this mechanism. The <code>JPEGRegistryEntry</code>
and <code>PNGRegistryEntry</code> should be used as good references for
extensions.
</p>
<s2 title="RegistryEntry">
<p>
There are currently two flavors of <code>RegistryEntry</code>:
</p>
<dl>
<dt>URLRegistryEntry</dt>
<dd>
These take a <code>ParsedURL</code> and try to decide if the URL
is intended for them. This group is mostly
intended to handle alternate network protocols. It
can also be useful for interfacing with libraries
that want a URL instead of a stream.
</dd>
<dt>StreamRegistryEntry</dt>
<dd>
These work with a markable <code>InputStream</code>. This is the
preferred form of registry entry as it generally
avoids opening a potentially expensive connection
multiple times, instead it opens the stream once
and relies on mark and reset to allow entries to
check the stream.
</dd>
</dl>
</s2>
<s2 title="Helper classes">
<p>
There exists quite a number of classes to assist in
implementing a <code>RegistryEntry</code>. It is strongly
recommended that you review these classes and make use
of them where appropriate. They will likely save you
time and improve the integration with Batik.
</p>
<dl>
<dt>MagicNumberRegistryEntry</dt>
<dd>
An abstract class that can handle the
<code>isCompatibleStream</code> method for formats that make use
of "magic numbers". Magic numbers are a well known
sequence of bytes at a well known offset in the
file.
</dd>
<dt>RedRable</dt>
<dd>
This takes any <code>java.awt.image.RenderedImage</code> and
wraps it into a <code>Filter</code> (Batik's subclass of
RenderableImage). This is extremely useful for
<em>single resolution</em> file formats.
</dd>
<dt>DeferRable</dt>
<dd>
This allows one to load the image in a background
thread, rather than hold up the construction of the
GVT tree while reading the image (useful since
reading the image is generally I/O bound, so it
makes a good background task). This is used by all
the current Image readers.
</dd>
<dt>AbstractRable</dt>
<dd>
An abstract base class that makes it relatively
easy to implement the Filter interface.
</dd>
<dt>AbstractRed</dt>
<dd>
An abstract base class that makes it relatively
easy to implement the CacheableRed interface
(Batik's subclass of RenderedImage).
</dd>
</dl>
</s2>
</s1>
<anchor id="urlProtocols" />
<s1 title="New URL Protocols">
<p>
For a variety of reasons (not the least of which is the
heavy use of the 'data:' protocol in SVG). Several parts
of Batik use a <code>util.ParsedURL</code> instead of the
JDK's <code>java.net.URL</code> class.
</p>
<p>
<code>ParsedURL</code> offers a few advantages over the JDK's URL class.
First, it is designed to make minimal use of exceptions, so it
is possible to use it to parse a malformed URL and get "the
good parts". Second, it is extensible, so support for new
protocols can be added, even protocols that change the normal
parsing rules for URLs (such as our friend the 'data'
protocol). Third it can automatically check a stream when
opened for common compression types and decode them for you
(this behavior can also be bypassed if needed).
</p>
<p>
The service class is
<code>org.apache.batik.util.ParsedURLProtocolHandler</code>. This
interface consists of three methods, one returns the protocol
to be handled, one is for parsing an absolute URL string and
one is for parsing relative URL strings. Both the parsing
methods return an object of type ParsedURLData (the instance
may of course be a subclass of ParsedURLData).
</p>
<p>
The <code>ParsedURLData</code> class holds all the data and implements the
all the stream handling commands for the ParsedURL class.
This allows ProtocolHandlers to return custom subclasses for
particular protocols.
</p>
</s1>
</body>
</document>