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

客户端与服务器端的SVG

文章索引
客户端与服务器端的SVG
服务器端的SVG
所有页面

来源:SVG中国(ChinaSVG.COM)
翻译:刘彦青
英文:http://www.xml.com/pub/a/2002/02/27/batik/index.html

客户端与服务器端的SVG

  Batik是基于Java技术的工具包,适用于SVG各种应用。我们使用Batik是因为它能够生成矢量图形的servlet。如果客户端支持SVG,servlet将返回一个SVG文档,否则它将根据客户端支持的图像格式返回一个JPEG或PNG图形。

  如果曾经使用过SVG,也许听说过Adobe SVG Viewer或Apache Batik工程。尽管Apache Batik是以其SVG阅读器而著名的,但它又不仅仅只是SVG阅读器。据其网站所称,Batik是一个基于Java技术的工具包,它适用于使用可升级矢量图形(SVG)格式的各种应用。
  Batik阅读器应用程序使用JSVGCanvas组件,该组件是一个Java类,它接受SVG格式的文件作为输入,并在屏幕上显示它。在本篇文章中,我们将讨论Batik的其他二个组件:SVGGraphics2D、Batik transcoders。SVGGraphics2D类与JSVGCanvas的作用相反,使用Java的标准2维图形方法,我们可以进入 SVGGraphics2D环境,其输出是一个SVG文档。而JSVGCanvas则将SVG文档作为输入,输出则是JPG或PNG格式的图形。
  我们使用这些工具的环境是一个能够生成蒙特里安风格的几何图画艺术的servlet。如果客户端支持SVG,servlet将返回一个 SVG文档,否则它将根据客户端支持的图像格式返回一个JPEG或PNG图形。

 图1 Batik与SVG图形原理及用途

客户端

  我们向用户展示的网页将让用户选择自己作品的定位和色彩设计。下面的图就是一个例子:

图2 客户端例子

  下面是HTML代码:

<html>
<head>
<title>Art-O-Matic</title>
</head>
<body>
<h2>Art-O-Matic</h2>
<p>Yes, you too can become a famous artist! With a few simple clicks
of the mouse, you can generate geometric art in the style of
Piet Mondrian.</p>
<form id="artForm" action="http://jde:8080/artmaker/servlet/ArtMaker">
<p>What kind of picture would you like?<br />
<input type="radio" name="picType" value="landscape"
    checked="checked" /> Landscape
<input type="radio" name="picType" value="portrait" /> Portrait
<input type="radio" name="picType" value="square" /> Square</p>
<p>Which color scheme would you like?<br />
<input type="radio" name="scheme" value="bright"
    checked="checked" /> Vivid colors
<input type="radio" name="scheme" value="pastel" /> Soft pastel</p>
</body>
</html>

  对客户是否支持SVG的判断工作必须在客户端进行,我们将添加一段从Sun公司的资料中借鉴的脚本代码来处理这一判断工作。

<script type="text/javascript">
<!-- <![CDATA[
var hasSVGSupport = false;  // does client have SVG support?
var useVBMethod = false;    // use VBScript or JavaScript?
/*
    Internet Explorer returns 0 as the number of MIME types,
    so this code will not be executed by it. This is our indication
    to use VBScript to detect SVG support.
*/
if (navigator.mimeTypes != null
    && navigator.mimeTypes.length > 0)
{
    if (navigator.mimeTypes["image/svg+xml"] != null)
    {
        hasSVGSupport = true;
    }
}
else
{
    useVBMethod = true;
}
// ]]> -->
</script>
<!--
    Visual Basic Script to detect support of Adobe SVG plugin.
    This code is not run on browsers which report they have MIME types,
    and it is also not run by browsers which do not have VBScript support.
-->
<script type="text/vbscript">
On Error Resume Next
If useVBMethod = true Then
    hasSVGSupport = IsObject(CreateObject("Adobe.SVGCtl"))
End If
</script> 

  为了让互联网网页将这一信息返回给服务器,我们有必要修改标记,并在表格上添加一个隐藏字段:

 <form id="artForm"
    action="http://jde:8080/artmaker/servlet/ArtMaker"
    onsubmit="return setSVGStatus();">
<input type="hidden" name="imgType" value="jpg" />

  上面的代码需要添加一段如下的代码,使在我们向服务器发送数据之前设置隐藏字段的值:

<script type="text/javascript">
<!-- <![CDATA[
function setSVGStatus()
{
    if (hasSVGSupport)
    {
        var theForm = document.getElementById("artForm");
        theForm.imgType.value = "svg";
    }
    return true;
}
// ]]> -->
</script>

  读者可以从这里下载整个HTML文件的源代码


服务器端

  首先,我们需要在 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)

 

将要更新