使用 Scalable Vector Graphics 为 ASP.NET 构建基于 XML 的灵活、轻量的图像


Dennis Forbes

本文假设您熟悉 HTML、ASP.NET 和 C#

下载本文的代码:ScalableVectorGraphics.exe (123KB)

摘要

Scalable Vector Graphics (SVG) 是围绕 XML 建立的 W3C 图形标准,它是使快速轻量图(比如图表和图形)能在适当的查看器上迅速呈现的几种矢量图形技术之一。这样的矢量图形有许多优势,包括节省带宽和存储媒体、灵活性强。本文将解释这些优点,并向您介绍如何轻松地在 Web 应用程序中添加强大、动态和交互式的可视化元素。

Scalable Vector Graphics (SVG) 是使用 XML 文档制作和部署二维矢量图形的一种标准。它是由大量业界组织制订和认可的 WWW 联合会 (W3C) 建议。SVC 具有高质量且丰富的矢量图形、简单性及 XML 基础广泛的工具兼容性,以及功能丰富的文档对象模型 (DOM) 接口的动力,从而为 DHTML 项目带来了强大的新成员。将它与创造力(以及一点别出心裁)相结合,就可以使无数的交互式 Web 可能变为现实,从而改善您的 Web 应用程序的外观、可用性和功能性。

各种矢量图形格式

矢量图形(包括 SVG、矢量标记语言 (VML)、Windows® 图元文件(.wmf 和 .emf)和 TrueType 字体)是以指令列表的形式存储和表述的,这些指令详细描述了如何使用一连串的形状、线和转换来重新创建图像。这种做法好比通过准确记录、然后重新创建原先用来产生绘图的画笔笔划序列来复制该绘图。矢量图形通常数据量很小(特别是当它经过压缩后),所以最大程度地降低了对存储空间和带宽的要求。当它们打印或缩放,甚至是扩大或放大时,都不需要像素化就能呈现。因为允许将它们分解为基本的形状和曲线,所以对于制作精度有限的任何图形是一种很好的选择。但另一方面,因为重新创建一幅图像需要的步骤是数以千计的,所以呈现矢量图形需要的计算量很大。矢量图形一般不适合表示实际拍摄、高度细节化的图像,但通常很适合表示由基本形状组成的图形、图表或者图像(比如卡通风格的图像)。

光栅图形 — 例如可移植网络图形 (PNG)、GIF 和 JPEG — 是通过对以特定的分辨率和缩放比例预先呈现的、最终图像的逐像素的表示方式进行比较而绘制的。这好比等待绘图完成,然后以特定用途和输出设备需要的分辨率和大小将它画下。光栅图像很容易跨平台移植,可以最大程度地降低对计算量的要求。它们用于表示高度细节化的、实际拍摄的图像是很理想的,但通常缩放和打印的效果较差。因为没有对庞大的文件大小进行处理,所以它们的动态可编程性很差,而且通常存储和传输的效率很低。

细心的读者可能已经注意到本部分前面提到的 VML,的确有必要对它进行详细介绍:VML 具备 SVG 的许多同样的优点,如轻松的基于文本制作和根据矢量图形构建的基础。然而,其他格式极为排斥 VML,而 SVG 在去年获得了一些 VML 的矢量图形要素。有工具可以在 SVG 和 VML 之间进行双向转换,所以可以相信经过很小的努力就可以使这两者同时得到支持。

SVG 入门

一个 SVG 实质上是一个 XML 文档,这种文档遵循定义为 SVG 批准进程的一部分的架构。规范中定义的是各种基本几何形状,例如矩形、圆、椭圆、直线、折线、多边形和字形元素(例如文本)。复杂些的形状可以使用路径元素创建,允许您定义诸如一连串直线、弧线和复杂的贝赛尔曲线等形状。使用这些基本形状,外加一点巧妙设计,即使最复杂的几何结构都可以创建出来。图 1 阐述了一个基本的 SVG 文档的 XML 构成,图 2 显示在 SVG 查看器中呈现的 SVG 的样子。


图 2 查看器中的 SVG

虽然有些应用程序(例如 CorelDraw、Adobe Illustrator 和 Batik SVG 工具集)可以直接显示或处理 SVG,但在大多数情况下,SVG 可以作为 HTML 文档的一种元素使用,与其他 HTML 文本和多媒体内容共存。典型方法是通过原始的嵌入标记加以实现:

<html>
<body>
<h1>A Page Containing an SVG</h1>
<embed name="ExampleSVG"
pluginspage="http://www.adobe.com/svg/viewer/install/"
src="figure1.svg" width="400" height="300"
type="image/svg-xml" />
</body>
</html>

pluginspage 属性可将用户引向可用的内容处理程序(如果还没安装)。在本示例中,我引用的是 Adobe SVG Viewer。

值得注意的地方是嵌入标记的 width 和 height 属性之间的相关性,对于所引用图形中的根 SVG 元素也是一样(这个值有时会以各种方式表示,包括像素、厘米等等)。如果这些数字不匹配,所封装的图形就会以某种不合要求的方式被裁剪或修改。通常使用的一种可避免这种问题并提供更大的灵活性的技术就是防止将该 SVG 限制为特定的大小。图 3 显示了一个经过稍加修改的 SVG,它使用 viewBox 属性,而不是 width 和 height。它固定了绘画时的画板边界,并因此固定了 width 和 height 之间的比例;然而,它并不固定呈现大小。如果您更改了嵌入元素的 width 和 height,但保持 width 和 height 的相对比例不变,则 SVG 图形就会依此平滑改变大小。

该非标准 HTML 嵌入元素的一种替代方案(特别是当要求与 XHTML 兼容)就是使用对象或 iframe 元素:

<html>
<body>
<object data="figure1.svg"
codetype="image/svg-xml"
style="width:400;height:300;">
</object>
<iframe src="figure3.svg" width="600" height="450" />
</body>
</html>

因为当遇到不支持的类型时允许层叠 (cascade),这种对象标记提供了一个特别强大的功能。例如,以下文档告诉浏览器首先试着呈现 SVG,如果失败(例如,SVG 功能不可用),就显示备选的光栅 PNG。这样的结构允许创建同时支持 SVG 和光栅内容的单个页面集,而不需要根据客户端浏览器的能力而有不必要的分支,如下所示:

<html>
<body>
<object data="figure1.svg" codetype="image/svg-xml"
style="width:400;height:300;">
<img src="figure1.png" width="400" height="300" />
</object>
</body>
</html>

关于对象标记有一点告诫需要说明。在开发 Microsoft® Internet Explorer 时,Microsoft 致最大的努力使它具有很高的可扩展性。这表现在浏览器实例化大量关联程序组件以显示多媒体 Web 页面的能力上(通过在 HTML 上直接引用,例如通过 CODEBASE/CLASSID 对引用特定的 ActiveX® 对象,或者通过 MIME 类型并查找 HKEY_CLASSES_ROOT\MIME\Database\Content Type)。这带来了极大的通用性,但是当有恶意的应用程序劫持内容类型并导致适时不能正确呈现时,或者当它们在卸载时不能将 Internet Explorer 重新关联为处理程序时,就会产生问题。另外,当我将对象标记和 SVG 一起使用时遇到一些不一致和不规则的问题 — 当使用嵌入标记或 iframe 标记时不会有这样的问题。然而,嵌入标记将不被支持。通常,您的用途可能会有不同,但是当您解决客户端安装大量帮助应用程序的棘手情况时,应该记住这个告诫。

动态内容

SVG 的一个强大功能就是具有通过内部或外部脚本以编程方式操作 SVG 的图形元素的能力。图 4是一个封装了 figure3.svg 图形的 HTML 文档(可从位于本文顶部的链接处的下载文件中获得)。本示例使用适用于 Internet Explorer 的 Adobe SVG Viewer 插件。本示例说明为了自动操作由 SVG 查看器公开的 DOM 接口,在用作容器的 HTML 文档中 ECMAScript (JavaScript) 的用法。通过单击如 5 所示的 Web 页面中的一个按钮,用户可以切换在基本形状中使用的颜色或者控制一组对象的旋转速度。但是这个特例显示的只是经过改头换面的不提倡使用的 BLINK 标记,它的确暗示 SVG提供的可能性非常广泛。可以在图形中添加新的对象,也可以完全修改现有的内容。这就允许您创建用动态解释提示支持的图形,例如,给用户一个更好的工具以帮助他完全理解信息。


图 5 操作形状

SVG 部署

因为 SVG 的尖端特性以及缺少当前所有主流浏览器的本地支持,所以 Web 站点访问者通常需要下载和安装专用的查看器(比如 Adobe SVG Viewer 或即将推出的 Corel SVG Viewer)才能看到 Web 页面内的 SVG 内容(这种状况至少要持续到浏览器像支持大多数其他图形格式一样本身就本地支持 SVG 时为止)。Adobe Viewer 本身可在几种平台上运行,包括 Windows、Macintosh 和 Linux,以及几乎所有主流 Web 浏览器。考虑到 SVG 是一个经广泛证明的标准,所以我感觉它并不是对一种专用格式悄悄下的一场赌注,而是为将来能够发挥积极的作用而在目前所作的一种投资。

尽管安装了 SVG 的客户端普及得很广泛,但大多数站点仍需要继续支持可能无法安装 SVG 特定的软件的旧式客户端。通过可以产生 SVG(同时又能为旧式客户端创建静态光栅图形)的图形基础结构可以轻松实现这样的解决方案。本文后面将详细介绍能实现这种目的的一种强大方法。当然,即使 SVG 从不直接服务于 Web 客户端,也可以在后端将 SVG 与 Microsoft .NET Framework 中强大的 XML 类结合使用。

迅速创建 SVG

VG 的杰出图形能力与 NET Framework 的包罗万象的功能集相结合为 Web 开发人员提供了很好的机会。通过利用数据后端与自动的 SVG 文档创建,Web 站点可以提供实时、美观、交互式的图形,帮助用户更好地领会和理解信息。还能使 Web 应用程序有一个更加专业、优美的外观。

为了使根 SVG 文档的动态创建更为轻松,同时为了演示具有基本 SVG 智能的 XmlDocument 派生类,我创建了一个很普通的类,如图 6 所示。以下的示例将用到这个助手类,它应该与放在 Web 应用程序的 bin 目录(例如,C:\inetpub\wwwroot\bin)中的结果 DLL 一起编译:

csc /target:library figure5.cs
copy figure5.dll C:\inetpub\wwwroot\bin

图 7 显示了一个由 SVG 生成的 Web 页面的基础。它包含一个简单的服务器端 ASP.NET 脚本,这个脚本通过返回包含以 ISO 8601 格式表示的当前时间的 SVG 文档来响应请求。尽管这个示例的实用性有限,但它的确显示了在服务器中动态创建 SVG 文档的简单性。这个示例对字符串进行连接以形成 XML 文档。它也可以通过以编程方式添加每个使用恰当 DOM 方法的元素和属性来实现。这个示例设置了 Response 对象的 ContentType 属性(它也可以使用 @Page 指令的 ContentType 属性)。这可以使客户端不用依靠烦琐且不可靠的文件扩展名就可以识别内容类型。尽管如此,有时在 URL 中包含一个假的参数后缀(例如 ?.svg)是有好处的,因为客户端(例如 Opera 6)会盲目地寻找一个扩展名以确定内容类型。

本示例还有一点值得注意的是 XML 文档直接流式传输到输出流,中途没有任何不必要的驻留,从而保持了高性能。当页面需要满足大量用户的实时需要时,这就十分值得考虑了。

接下来是一个在调用 ASP.NET 页的过程中生成的结果 SVG,以及带有取决于何时调用页面的日期/时间:

<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"[]>
<svg width="400" height="200" xmlns="http://www.w3.org/2000/svg">
<text x="100" y="100" font-family="Verdana" font-size="15"
fill="blue">2003-03-28T122002-10-29T12:02:14</text>
</svg>

如前面所述,可以使用对象标记将这个动态 SVG 文档包含在一个基本的 HTML 页面中:

<html>
<body>
<center>
<h1>SVG Test</h1>
<object name="ExampleSVG" codebase="http://www.adobe.com/svg/viewer/
install/" data="figure7.aspx?.svg" width="400" height="200"
type="image/svg+xml"/>
</center>
</body>
</html>

采用SVG 绘图

许多 SVG 可以按要求根据数据库中数据的更改动态生成,包含区域性边界和划分或最新的月份销售数额的 GIS 信息就是极好的例子。作为通过数据动态生成 SVG 文档的技术示例,命名空间 yafla.yaflaSVG(通过本文顶部的链接获得 svg_graphing.zip,并编译和安装其中的 yafla.yaflaSVG.cs 文件就可以使用该命名空间)提供了各种类来帮助将数据转化成图形。这些图形类型包括饼图(参见 8)以及垂直和水平条形图(参见 9)。该类层次结构已经以一种容易扩展的面向对象方式进行构建,并提供大量设置来控制生成的图形的 UI。既然生成的 SVG 图形是以 XML 文档的形式返回的,则在将文档返回给调用者之前,可以利用 .NET Framework 中丰富的 XML 能力对它们进行修改。虽然这些图形无法取代商业画图产品,但它们是 SVG 系列中优秀的入门级产品,能实际为一些读者提供切实可行的解决方案。Barchart_example.aspx 和 piechart_example.aspx(也在下载中提供)演示了使用这些类动态生成图形的基础知识(这里创建的是条形图和饼图)。虽然这些示例是用手动方式创建和填充 DataTable 的,但这与作为查询结果检索 DataTable 的方式一样简单,因此实现了真正的动态矢量图形。


图 8 饼图

节省带宽

虽然 SVG 文档通常比相应的光栅图形小了许多,但当表示基本图形和图表时,XML 的固有详细特性还是会为复杂图形产生很大的文件。幸运的是,SVG 标准包含对 GZIP 压缩(在 RFC 1952 中经证明的标准)的本地支持,从而极大减少了传输和存储大小 — 通常压缩率达到 80% 或更多。虽然 GZIP 因为压缩和解压缩而确实增加了一些额外的计算开销,但在服务器端通过适当的缓存能够得以缓解。在客户端,这种担心很大程度上就没有必要了,因为现代高性能处理器的速度远远超过了大多数网络连接的速度。考虑到这些因素,可以很明显地看出,压缩是一举两得,既能降低主机的带宽成本,又能给客户端一个更快更灵活的 Web 站点。


图 9 条形图

对于静态 SVG 文档,压缩是很简单的,只需对服务器端的文件进行 GZIP 操作(经过压缩后得到的文件的扩展名是 .svgz;.svg 是未压缩的 SVG 文档的扩展名),确保在 Microsoft Internet 信息服务 (IIS) 中将 .svg 和 svgz 与适当的 MIME 类型 (image/svg+xml) 相关联,并确保引用指向压缩过的副本。通过这样就可以只用到原来所需带宽的一小部分。您的客户端,特别是拨号上网的 Internet 用户,会喜欢更快速的体验。

压缩动态生成的内容时会稍微复杂一点。进行动态压缩的第一步是确定它是否已经生成。您的 Web 环境能否将压缩过的输出流式传送到安装有相应解压缩设备的客户端? 10 是一个简单的脚本,它将 URL 作为参数(例如,wscript figure9.js http://127.0.0.1/mydynamicpage.aspx)并使用 MSXML ServerXMLHTTP 对象来报告:对于该 URL 而言,是否支持流式传送压缩内容。另外一个优点是,它还报告了服务器返回给请求的 MIME 类型,通过这样来让您确认服务器是否正确配置。进行测试很重要,因为压缩可以通过负载均衡前端或 IIS ISAPI 筛选器(收到请求时压缩输出)透明地进行,在这样的情况下,双重压缩就是没有必要的浪费。

如果您想要启用动态 SVG 压缩,在市场上有各种 NET 可访问的压缩组件,包括功能丰富、使用可靠的商业软件包、用于现有 DLL 的 COM Interop 包装,以及优秀的开放源代码项目。这些解决方案有许多实现为从 .NET 流对象派生的类,这样在使用流对象的地方就可以很容易地使用。从演示角度考虑,我选择的是采用 C# 编写、开放源代码的 SharpZipLib 库,可在http://www.icsharpcode.net/OpenSource/SharpZipLib下载。

将二进制的 DLL 放入全局程序集缓存或 Web 应用程序的 /bin 目录后,就可以十分容易地实现流式传送 GZIP 压缩内容,如图 11所示(它是图 7 的变体)。通过使用 GZIP 流接口作为 XML 文档和 HTTP 响应对象之间的传输桥梁,数据就可以在发送的时候压缩,这样极大地降低了带宽需求。对于如此小的 SVG 文档,GZIP 压缩所节省的带宽是很小的,与增加的计算成本相比很可能得不偿失。然而,对于实际使用的文档,它所带来的好处可能非常大。Compressed_ barchart_example.aspx(下载中提供)对图表组件使用压缩。在该示例中,文档从 7,722 降到 1,235 字节(减少了 84%)。

保持向后兼容

期待每个用户都采用专用于您的 Web 站点的特定组件来升级他们的系统是不现实的。使用深奥的技术会因为用户担心被排除在外而导致无数支持电话,或者对于公共 Web 站点,不可避免会有大量电子邮件来自极个别对 HTML 1.0 以后的一切都很狂热的拥护者。为了避免这种情况,一种可能就是在服务器上将 SVG 呈现为位图化的图形,并向支持的客户端返回 SVG 本身,而对其他客户端返回位图化的图形。如果 SVG 本身从不服务于客户端,这种方法是可行的,但仅仅是作为动态图形制作技术使用,它需要进行光栅化才能呈现。

捕获它的 GDI 输出以进行光栅化所需要的计算量可想而知。但是 Adobe SVG Viewer 能否用于在服务器端呈现在它们的许可协议中可能会提到,可以访问http://www.adobe.com来核实。另一个可行的办法是采用 Batik 项目 (http://xml.apache.org/batik/svgrasterizer.html),这个实用工具可以将 SVG 转换成各种格式,包括 PNG、TIFF、PDF 或 JPEG 等光栅格式(虽然有损耗的 JPEG 格式通常只适合实际拍摄的图像或具有平滑的颜色过渡的图像)。GIF 格式从它的输出格式排除大概是因为相关的许可问题。不幸的是,Batik 需要您将 SVG 保存到硬盘(如临时文件夹)中,然后它在那里进行处理并根据传递的参数生成光栅图形。Barchart_example_rasterize.aspx(下载中提供)演示了如何携带 width 输入参数并通过它生成期望大小的光栅 PNG 条形图(例如,http://localhost/barchart_example_rasterize.asp?width=750)。添加附加逻辑以携带一个可以选择目标文件类型并允许生成各种类型和大小的图形输出的参数是很容易做的。比较棘手的情况是添加逻辑以将缓存整合到系统中。这种情况需要将 SVG 本身和一个只要源 SVG 更改就会重新生成的附属光栅图形保存到缓存对象中(即使只是短期保存)。通过这样的更改就可以启用一个功能强大的服务器端图形子系统,它可以满足动态矢量和光栅二者的需要,同时又能使用 .NET Framework 的缓存基础结构,在即使负载很高的情况下,也可实现很大的伸缩性和很短的响应时间。

出于性能和集成考虑,开发一个 .NET 本地 SVG 光栅化器 (rasterizer) 是一个很好的选择;但此项目不在本文的讨论范围内。也许随着 SVG 得到越来越多的关注,实现这一点会成为现实。SVG 目前也缺少 DHTML 行为(而 VML 支持)。这将会是另一个添加到 SVG 规范的很好的领域。

小结

通过将 SVG 的图形威力和 .NET Framework 的强大功能与通用性相结合,您可以将 Web 应用程序变成一个处理图形的无敌金刚。通过添加动态图形元素,您可以使数据更加清晰,实现更好的组织结构化支持,并向外界展示更加完美、更加专业的图像。希望本文能够提供足够的信息以满足您的需要,并能鼓励您在 Web 开发中更进一步体验和探索矢量图形令人激动的实现可能。

相关文章请参阅:How to use VML on Web Pages
Create Snazzy Web Charts and Graphics On the Fly with the .NET Framework
Manipulate XML Data Easily with Integrated Readers and Writers in the .NET Framework
背景资料请参阅:
http://www.w3.org/TR/SVG
http://www.w3.org/TR/NOTE-VML
http://www.w3c.org/Graphics/WebCGM

优质内容筛选与推荐>>
1、提高每天的工作效率_成就更辉煌的人生
2、【NOIp复习】dp复习列表
3、Django在用models时出现__init__() missing 1 required positional argument: 'on_delete'
4、Mercurial - 分布式版本控制系统
5、解决Resharper在Core项目中无法执行单元测试的问题


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn