SVG——新一代Web设计及互动媒体的革新

客户端与服务器端的SVG - 服务器端的SVG

文章首页
客户端与服务器端的SVG
服务器端的SVG

服务器端

  首先,我们需要在 Graphics2D环境中绘制实际图像的Java代码,读者可以参阅Artist.java的资料(http://www.xml.com/2002/02/27/examples/Artist.java),里边没有什么特别的,它只是一些draw和fill调用。唯一需要注意的是constructor,它要求一个宽、高、调色板(其值可能是BRIGHT或PASTEL)参数:

public Artist( int width, int height, int colorType );

  现在再回到servlet上,我们将在doPost方法中完成所有的工作。我们首先发送头部信息,以阻止结果被缓冲:

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");
  接下来我们获取参数,使用它们设置一些局部变量,并创建一个适当的 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 );

  一旦这些设定完成后,我们需要创建一个SVG文档和SVG图形环境。由于每个解析器的实现有其自身的一套存储对象的方法,我们必须请求 SVGDOMImplementation类给出详细情况。一旦知道了详细资料,我们就可以请求解析器实现创建一个带有合适名字空间的文档。 createDocument的第三个参数是为文档定义的一系列实体的引用。由于在本例没有为文档定义的实体,因此这一参数为 NULL。

DOMImplementation domImpl =
    SVGDOMImplementation.getDOMImplementation();

String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;
Document document =
    domImpl.createDocument(svgNS, "svg", null);

  接下来我们创建SVGGraphics2D对象,并将它与它将创建创建的文档进行关联,然后将图象环境设置为期望的宽、高值和填充的像素。

SVGGraphics2D svgGraphicsEnvironment = new SVGGraphics2D(document);
svgGraphicsEnvironment.setSVGCanvasSize(
    new Dimension( width + 10, height + 10 )
);

  OK,现在所有的准备工作都已经完成,可以让艺术家在图形环境中开始绘制图画了,系统会自动地建立相应的SVG文档。

mondrian.paint( svgGraphicsEnvironment );

  现在的文档仍然是存储在内存中的,但仍然需要将它以文本的格式返回给客户端,我们可以通过对文档进行串行化处理为一个 ByteArrayOutputStream对象,在初始化时它的大小为8192个字节,对于大多数的图画产生的文档而言,这一大小已经足够了。流式化的第二个参数被设置为"TRUE",这表示输出应当采用CSS样式而不是样式属性。

ByteArrayOutputStream baos = new ByteArrayOutputStream( 8192 );
Writer svgOutput = new OutputStreamWriter( baos, "UTF-8" );
svgGraphicsEnvironment.stream( svgOutput, true );

  现在我们决定是否需要返回一个返回一个SVG、JPEG或PNG格式的图像。客户端已经向我们表明它是否支持SVG,有关PNG支持的信息在HTTP请求的Accept头部中。我们获得SVG输出的字节矩阵,并将它转换为一个字符串,传递给一个合适的表达函数。

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() );
    }
}

  doPost方法的其余部分是错误捕捉功能,无论发生什么样的错误,我们都将向客户端发送一个带有错误信息的HTML网页。

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>");
}

  发送的方法有三种,其中emitSVG比较简单。我们只需将带有适当的MIME类型的字符串发送回客户端即可:

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();
    }
}

  译码

  要向客户端发回一个JPEG或PNG文件,我们就必须使用Batik的transcoder,transcoder的输入可以是一个文档、流或一个阅读器,输出可以是一个URI、流或收书写器。对于JPEG译码器而言,我们可以通过调用addTranscodingHint方法来设置图像的质量。由于我们向客户端发送的是一连串的字节而不是一个字符串,因此我们的输出内容是输出到response.getOutputStream()而不是 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();
    }
}

  发送PNG输出的代码与此相似。如果想让透明的象素点转换为白色的象素,我们可以使用KEY_FORCE_TRANSPARENT_WHITE参数,对于不支持PNG格式的透明度的老浏览器而言,这一点十分有用。

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();
    }
}

  我们可以将整个servlet集成为一个文件。如果在一个合适的 servlet容器(例如Jakarta Tomcat)中安装Artist和Artmaker,就可以绘制如下所示的图像了。

图3 效果演示

  总结

  我们在本篇文章中没有讨论的其他Batik工具可以将TrueType字体转换为SVG格式,对SVG进行扩展,使其包括自定义的标记。如果读者需要一个能够开发浏览、建立、转换SVG文档的功能强大、跨平台的开发工具,那就非Batik莫属了。

(THE END)