Server Side SVG
by J. David Eisenberg
|
Pages: 1, 2
The Server Side
|
|
| Post your comments |
First, we need Java code to draw the actual painting into a
Graphics2D context. You may see the
Artist.java source. There's nothing particularly
special about it; it's just a few draw and
fill calls in a recursive function. The only thing to
note is the constructor, which requires a width, height, and palette
(which will be BRIGHT or PASTEL):
public Artist( int width, int height, int colorType );
Now, on to the servlet. We'll do all the work in the
doPost method. We start by sending out header information
to keep the results from being cached:
public class ArtMaker extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
Artist mondrian = null;
try
{
response.setHeader("Cache-Control",
"no-cache, no-store, must-revalidate");
response.setHeader("Cache-Control",
"post-check=0, pre-check=0");
String agent =
request.getHeader("User-Agent").toLowerCase();
/* netscape chokes on Pragma no-cache so only
send it to explorer */
if (agent.indexOf("explorer") > -1){
response.setHeader("Pragma", "no-cache");
}
response.setHeader("Expires",
"Thu, 01 Dec 1994 16:00:00 GMT");
We then grab the parameters, use them to set some local variables,
and create an appropriate Artist.
String picType = request.getParameter( "picType" );
String colorScheme = request.getParameter( "scheme" );
String imgType = request.getParameter( "imgType" );
Artist mondrian = null;
int width;
int height;
int palette;
if (picType.equals("landscape"))
{
width = 400;
height = 300;
}
else if (picType.equals("portrait"))
{
width = 200;
height = 300;
}
else
{
width = 250;
height = 250;
}
palette = (colorScheme.equals("bright")) ?
Artist.BRIGHT : Artist.PASTEL;
mondrian = new Artist( width, height, palette );
Once this is set, we need to create an SVG document and an SVG
graphics environment. Since every parser implementation has its own
way of storing the objects, you must ask the
SVGDOMImplementation class to give you the details. Once
you know the details, you can ask the implementation to create a
document with the appropriate namespace, which is in
svgNS. The third argument to createDocument
is a reference to a list of entities defined for the document. There
aren't any in this case, so we set the third argument to
null.
DOMImplementation domImpl =
SVGDOMImplementation.getDOMImplementation();
String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;
Document document =
domImpl.createDocument(svgNS, "svg", null);
We then construct the SVGGraphics2D object and
associate it with the document that it will create. We set the graphic
environment's dimensions to the desired height and width with ten
extra pixels for padding around the edges.
SVGGraphics2D svgGraphicsEnvironment = new SVGGraphics2D(document);
svgGraphicsEnvironment.setSVGCanvasSize(
new Dimension( width + 10, height + 10 )
);
Everything is now set up. We just tell our artist to paint into that graphic environment, and the corresponding SVG document will be built up automatically.
mondrian.paint( svgGraphicsEnvironment );
Now the document is in memory, but we will need it in text form to
send back to the client. We accomplish this by streaming the document
to a ByteArrayOutputStream, whose initial size is 8192
bytes, which should be enough for most normal paintings that this code
will generate. The second parameter to stream is set to
true, indicating that the output should use CSS styles
instead of style attributes.
ByteArrayOutputStream baos = new ByteArrayOutputStream( 8192 ); Writer svgOutput = new OutputStreamWriter( baos, "UTF-8" ); svgGraphicsEnvironment.stream( svgOutput, true );
We now determine whether we need to send back an SVG, JPEG, or PNG
image. The client has told us whether we can use SVG or not. The
information about PNG support is in the HTTP request's
Accept header. In each case, we take the SVG output byte
array, convert it to a string, and pass it on to the appropriate
emit function.
if (imgType.equals("svg"))
{
emitSVG( request, response, baos.toString() );
}
else
{
if (request.getHeader("Accept").indexOf("png") >= 0)
{
emitPNG( request, response, baos.toString() );
}
else
{
emitJPG( request, response, baos.toString() );
}
}
The remainder of the doPost method is the error
trapping; in case anything fails, we send back an HTML page with an
error message.
catch (Exception e)
{
PrintWriter out = response.getWriter();
response.setContentType("text/html");
out.println("<?xml version="1.0" encoding="utf-8"?>");
out.println("<!DOCTYPE");
out.println("html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"");
out.println("\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");
out.println("<html><head><title>Error</title></head>");
out.println("<body>");
out.println("<p>Unable to create painting.</p>");
out.println("<pre>");
e.printStackTrace(out);
out.println("</pre>");
out.println("</body></html>");
}
This leaves the three emit...
methods. emitSVG is trivial; we just send the string back
to the client with an appropriate MIME type:
public void emitSVG( HttpServletRequest request,
HttpServletResponse response,
String svgString )
{
response.setContentType( "image/svg+xml" );
try {
response.getWriter().write( svgString );
response.getWriter().flush();
}
catch (Exception e)
{
e.printStackTrace();
}
}
Transcoding
To send back a JPEG or PNG file, we have to use Batik's
transcoder. Input to the transcoder can be a document, a stream, or a
Reader; output can be a URI, stream, or
Writer. For the JPEG transcoder, you may set the output
quality by calling the addTranscodingHint method. Since
we're sending a series of bytes back to the client rather than a
string, we're sending output to
response.getOutputStream() rather than
response.getWriter().
public void emitJPG( HttpServletRequest request,
HttpServletResponse response, String svgString )
{
response.setContentType("image/jpeg");
JPEGTranscoder t = new JPEGTranscoder();
t.addTranscodingHint(JPEGTranscoder.KEY_QUALITY,
new Float(.8));
TranscoderInput input =
new TranscoderInput( new StringReader(svgString) );
try {
TranscoderOutput output =
new TranscoderOutput(response.getOutputStream());
t.transcode(input, output);
response.getOutputStream().close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
Similar code sends PNG output; you may give it an additional hint
with KEY_FORCE_TRANSPARENT_WHITE if you want fully
transparent pixels to appear as white. This is good for older browsers
that don't support PNG transparency completely.
public void emitPNG ( HttpServletRequest request,
HttpServletResponse response, String svgString )
{
response.setContentType("image/png");
PNGTranscoder t = new PNGTranscoder();
TranscoderInput input =
new TranscoderInput( new StringReader(svgString) );
try {
TranscoderOutput output =
new TranscoderOutput(response.getOutputStream());
t.transcode(input, output);
response.getOutputStream().close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
You may examine the
entire servlet as one file. If you install Artist and
Artmaker in a suitable servlet container such as Jakarta Tomcat, you too can
produce art like this, which I've shrunk to half size to take up less
screen space.

Summary
Other Batik tools which we haven't covered here let you convert TrueType fonts to SVG and extend SVG to include custom tags. If you want a powerful, cross-platform toolkit for building applications that let you view, construct, and convert SVG documents, Batik is the answer.
- Altering SVG Document
2002-03-19 05:34:24 Phillip Larsen - trouble compiling ArtMaker.java
2002-03-18 12:42:10 Bill Carter