SVGの概要

JAGAT客員研究員

岸 和孝

SVGとは
SVGの座標系
SVGの基本図形(直線成分)
SVGの基本図形(曲線成分)
図形の定義・参照
図形の移動,回転,変形
SVG図形を動かす(SMIL機能)
SVG図形を動かす(JavaScript)
SVGで表とビジネスグラフを描く
XSL変換で円グラフを描く
SVGからPDFへ変換
数式表示への応用
今後の課題

SVGとは

 「SVG」とは,オープンシステム環境におけるデータ交換を目的とした,XMLに基づくボキャブラリ(タグ集合)であり,W3Cによって開発・公開された規格である。SVGの仕様を携帯電話と携帯情報端末に向けて制限した「Mobile SVG」という規格も公開されている。文書記述言語として「XML」が「SGML(ISO/IEC 8879,JIS X 4151)」の後継として,さらに「XSL」が「DSSSL(ISO/IEC 10179,JIS X 4153)」の後継として開発されたように,SVGもまたコンピュータ・グラフィックス・メタファイルの「CGM(ISO/IEC 8632,JIS X 4211)」の後継として開発されたものである。

 SVGは「Scalable Vector Graphics」の頭辞語で,その意味は「拡大・縮小可能なベクトル図形」である。ベクトル図形(幾何学図形とも言う)は,GIFやJPEGなどの画像(ラスター図形とも言う)が拡大・縮小によって形状が崩れるのに対して,本来の形状を保ったままで拡大・縮小できる。さらにSVGは,アニメーションの記述に用いられる「SMIL」と一部互換性がある[後述]。そのことからSVGは,設計図や地図といった静的なコンテンツだけでなく,動的なコンテンツも実現できると期待されている。

 SVGは,DTPで扱う「EPS」によく似ており,文書の構成部品となる性質を持っている。周知の通り,EPSは「Encapsulated PostScript」の頭辞語で,その意味は「カプセル化したポストスクリプト」である。つまり,設計図や地図を表したSVG文書は,HTML文書やPDF文書における段落や画像などから独立した構成部品として個別に埋め込むことができる。とりわけ,HTML文書においては,SVGの描く個々の図形要素とその属性(座標,大きさ,色など)は,DOM(文書オブジェクトモデル)で扱われJavaScriptの操作対象となりうる[後述]。

 SVGの応用に積極的なAdobe Systems社のホームページでは,次のように,SVGの特長をまとめている。

(1) データ駆動グラフィックス
 SVGの内容はXMLで書かれているので,電子商取引や企業データベースのような業務処理と連携できる。
(2) 対話的グラフィックス
 JavaScriptやMicrosoft Visual Basicといったプログラミング言語を使って,洗練されたウェブのユーザーインターフェースを作れる。
(3) カスタム・グラフィックス
 SVGの内容は,各種のフォントが扱えるので様々な読者向けにカスタマイズできる。また,データベースから集めた情報とユーザーとのやりとりを通じて動的にSVGを生成できる。

 現在,多くのSVG対応ソフトウェアが開発され公開されている。しかし,現状のウェブにおいてはSVGは余り普及していないようである。その普及には,SVG文書が簡単に作れるソフトウェアと,エンドユーザーの環境で閲覧できるソフトウェアが整備されることが肝要である。何と言っても,普及の決め手はコンテンツ制作者の意欲にかかっている。そうしたことから,SVGが一般のエンドユーザーに周知されるようになるには,まだ暫く時間がかかると思われる。

 SVG文書またはSVG文書を埋め込んだHTML文書を閲覧するには,ウェブブラウザーのInternet Explorerでは,プラグイン「SVG Viewer」をインストールする必要がある。それはAdobe Systems社から無償で入手できる。またNetscapeあるいはMozillaでは,ネイティブにSVGを表示する。HTMLのEMBEDタグによってSVG文書は外部参照される。それは,IMGタグによる外部画像の外部参照と同じような働きである。なお,ストリーミングソフトの「RealPlayer」には「SVG Viewer」が組み込まれている。

 描画した結果をSVG文書へ変換するドローイング・ツールには,商用製品の「Adobe Illustrator」「花子」などがあるが,フリーウェアでは,MacOS X向けの「Sketch+」と,Windows向けの「Dynamic Draw」が利用できる。ちなみに「Sketch+」はApple Computer社の開発キットに含まれているサンプルソースの「Sketch」から木下 誠氏が開発した「Sketch BP」を基に私が開発したものである。Sketch+は,描画結果をSVG形式,TIFF形式,PDF形式で出力できるので,さまざまな用途に利用できると考えている[図1]。

▲図1 Sketch+におけるデータの流れ

 通常,Illustratorのユーザーは描画結果がどのようにポストスクリプトで表現されているかを意識することはないだろう。同様に,SVG対応のドローイング・ツールがあれば,SVGの知識は不要かもしれない。しかし,動的なコンテンツを制作するには,簡便なツールが得られない限り,SVGの知識は必須となる。ここが肝心なところである。

 ここでは,SVGの表現について説明する。それは,ポストスクリプトの説明と同様に,どうしても専門的で煩雑な話となる。あらかじめご了承いただきたい。

SVGの座標系

 初めに小さなSVG文書の例として「日の丸」を描いてみる。[flag.svg]。xml宣言の次にあるsvg要素は,このXML文書がSVG文書であることを意味する。SVGの名前空間は「http://www.w3.org/2000/svg」である。そのwidth(幅)属性とheight(高さ)属性は「日の丸」を描画するキャンバスの指定である。キャンバスの座標系(x, y)は,左上隅が原点(0,0)となる[図2]。ちなみにポストスクリプトの原点は左下隅である。座標の単位は,とくに指定しなければピクセル(px)となる。

 この例では,「日の丸」の旗の部分をrect(方形)要素で,円の部分をcircle(円)要素でそれぞれ表している。

 rect要素のx属性とy属性は方形の左上隅のX座標とY座標を,width属性とheight属性は方形の幅と高さをそれぞれ表す。style(スタイル)属性はstroke(線の色)とfill(塗りつぶしの色)をカスケード・スタイル・シート(CSS)の形式で表す。色の種類は,CSSで規定された色名かRGB値で指定する。

 circle要素のcx属性とcy属性は円の中心のX座標とY座標を,r属性は円の半径をそれぞれ表す。

▼flag.svg 日の丸

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
     width="110px" height="80px">
<!-- 日の丸 -->
  <rect x="10" y="10" width="100" height="70" 
        style="stroke:black;fill:white"/>
  <circle cx="60" cy="45" r="21" 
          style="stroke:none;fill:red"/>
</svg>

▲図2 キャンバスの座標系

SVGの基本図形(直線成分)

 SVGの基本図形のうち直線成分を表す要素を使った例として「縦棒グラフ」を描いてみる[図3]。

 この例では,注釈宣言に示したように,縦棒グラフの縦棒をrect要素,目盛りをline(直線)要素,縦棒の値やラベルの文字をtext(テキスト)要素でそれぞれ表している[vbargraph.svg]。

 text要素の内容は表示する文字列を,x属性とy属性は文字列のテキストの左下隅(ベースライン)のX座標とY座標を,font-size(フォントの寸法)属性は表示する文字の大きさを,stroke属性は文字の色をそれぞれ表す。なお,現時点のSVG Viewerには,font-family(フォントファミリー名)属性に漢字フォントを指定しても解釈されないという不具合がある。

 line要素のx1属性とy1属性は直線の始点のX座標とY座標を,x2属性とy2属性は直線の終点のX座標とY座標を,stroke属性は直線の色をそれぞれ表す。

▲図3 縦棒グラフ

▼vbargraph.svg 縦棒グラフ

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
     width="400px" height="600px">
<!-- 縦棒グラフ -->
  <!-- X軸とY軸のラベル -->
  <text x="252" y="220" font-size="8pt" stroke="black">年</text>
  <text x="55"  y="10"   font-size="8pt" stroke="black">円</text>
  <!-- 縦棒グラフの背景 -->
  <rect x="50" y="15" width="200" height="195" fill="yellow" stroke="black"/>
  <!-- Y軸の目盛り -->
  <text x="40" y="210" font-size="8pt" stroke="black">0</text>
  <text x="32" y="145" font-size="8pt" stroke="black">100</text>
  <text x="32" y="80"  font-size="8pt" stroke="black">200</text>
  <text x="32" y="15"  font-size="8pt" stroke="black">300</text>
  <line x1="50" y1="210" x2="54" y2="210" stroke="black"/>
  <line x1="50" y1="145" x2="54" y2="145" stroke="black"/>
  <line x1="50" y1="80"  x2="54" y2="80"  stroke="black"/>
  <!-- X軸の目盛り -->
  <text x="50"  y="218" font-size="8pt" stroke="black">1950</text>
  <text x="100" y="218" font-size="8pt" stroke="black">1960</text>
  <text x="150" y="218" font-size="8pt" stroke="black">1970</text>
  <text x="200" y="218" font-size="8pt" stroke="black">1980</text>
  <!-- 縦棒 -->
  <rect x="50"  y="190" width="10" height="19"  fill="blue" stroke="blue"/>
  <rect x="100" y="171" width="10" height="38"  fill="blue" stroke="blue"/>
  <rect x="150" y="132" width="10" height="77"  fill="blue" stroke="blue"/>
  <rect x="200" y="30"  width="10" height="180" fill="blue" stroke="blue"/>
  <!-- 縦棒の値 -->
  <text x="52"  y="188" font-size="8pt" stroke="black">30</text>
  <text x="102" y="169" font-size="8pt" stroke="black">60</text>
  <text x="152" y="130" font-size="8pt" stroke="black">120</text>
  <text x="202" y="28"  font-size="8pt" stroke="black">280</text>
  <!-- 縦棒のラベル -->
  <rect x="260" y="15" width="8" height="8" fill="blue" stroke="blue"/>
  <text x="270" y="23" font-size="8pt" stroke="blue">コーヒー</text>
</svg>

SVGの基本図形(曲線成分)

 SVGの基本図形のうち曲線成分を表す要素を使った例として「円柱」を描いてみる[図4]。

 この例では,注釈宣言に示したように,斜上から見た円柱の上部をellipse(楕円)要素で,そして円柱の側面を直線と楕円弧からなる閉区間とみなしてpath(パス)要素でそれぞれ表している[cylinder.svg]。

 ellipse要素のcx属性とcy属性は楕円の中心のX座標とY座標を,rx属性とry属性はX軸方向とY軸方向の楕円の半径をそれぞれ表す。

 path要素のd属性は「パスデータ」を指定する。パスデータは,コマンドの並びからなる。コマンドは,座標上におけるペンのさまざまな動きを指示する。コマンドの並びは順に実行される。

 M(移動)コマンドは,ペンを現在点から指定座標へ移動する。L(直線)コマンドは,現在点から指定座標へ直線を描く。Zコマンドは,図形を閉じ,閉じた図形の内側の塗りつぶしを有効にする。A(円弧)コマンド「A rx,ry t f1,f2 x,y」は,現在点から(x,y)へ楕円弧を描く。楕円の大きさと方向はX軸の半径(rx),Y軸の半径(ry),X軸の回転(t)で指定し,楕円弧を描く範囲はフラグ(f1,f2)で指定する。楕円の中心はそれらのパラメータから自動的に計算される[図5]。

▲図4 円柱

▼cylinder.svg 円柱

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
     width="200px" height="250px"> 
<!-- 円柱 -->
  <!-- 円柱の上部 -->
  <ellipse cx="100" cy="50" rx="50" ry="30" 
           fill="lightgray" stroke="black"
           stroke-width="4" />
  <!-- 円柱の側面 -->
  <path d="M 50,50
           L 50,200
           A 50,30 0 1,0 150,200
           L 150,50
           A 50,30 0 0,1 50,50
           Z"
      fill="lightgray" stroke="black" 
      stroke-width="4" />
</svg:svg>
 

▲図5 Aコマンドによる楕円弧の描画

 現在点の座標を(100,50)とするとき,

A1M 100,50 A 50,40 0 1,1 150,90
A2M 100,50 A 50,40 0 0,0 150,90
B1M 100,50 A 50,40 0 1,0 150,90
B2M 100,50 A 50,40 0 0,1 150,90

図形の定義・参照

 SVG図形の定義・参照を表す要素を使った例として「スマイルマーク」を描いてみる[図6]。

 この例は,注釈宣言に示したように,「スマイルマーク」の定義と参照という二つの部分に大きく分かれている[smilemark.svg]。

 先ず「スマイルマーク」を基本図形の組み合わせを表し,それらをsymbol(記号)要素でまとめてid属性で「smilemark」と名付ける。さらに,それをdefs(定義)要素の中に定義する。

 「スマイルマーク」の基本図形として顔の部分をcircle要素で,目の部分を二つのrect要素で,そして口の部分をpath要素のd属性のパスデータの中のC(ベジェ3次曲線)コマンドでそれぞれ表している。

 Cコマンド「C x1,y1 x2,y2 x3,y3」は,現在点(x,y)と終了点(x3,y3)を結ぶサブパスで,その曲線の形は任意の制御点(x1,y1)と(x2,y2)で指定する[図7]。描きたい曲線の制御点を求める方法は,幾何学計算によるが,実際はドローイング・ツールの描画結果から必要な数値を得ることになる。

 具体的に「スマイルマーク」を描画しているのは,use(適用)要素のxlink:href属性で「#smilemark」として参照している箇所である。このように,元になる図形を一度「部品」として定義しておき,後でそれを参照する書き方によって,複雑な合成図形を何回も描く場合でもSVG文書全体を簡略に表現できる。

▲図6 スマイルマーク

▼smilemark.svg スマイルマーク

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="160px" height="160px"> 
<!-- スマイルマーク -->
  <!-- スマイルマークの定義 -->
  <defs>
   <symbol id="smilemark">
  <!-- 顔 -->
     <circle cx="70" cy="70" r="70" fill="yellow"
             stroke="black" stroke-width="4"/>
  <!-- 目 -->
     <rect x="50" y="30" width="4" height="15" fill="black"
           stroke="black"/>
     <rect x="90" y="30" width="4" height="15" fill="black"
           stroke="black"/>
  <!-- 口 -->
     <path d="M 30,70 
        C 30 92.091390 47.908610 110 70 110
        M 70,110
        C 92.091390 110 110 92.091390 110 70"
       fill="none" stroke="black" stroke-width="4"/>
    </symbol>
  </defs>
  <!-- スマイルマークの参照 -->
  <use xlink:href="#smilemark" x="10" y="10" />
</svg>
 

▲図7 Cコマンドによるベジェ3次曲線の描画

図形の移動,回転,変形

 SVG図形の変形,移動,回転を表す要素を使った例として「様々な矢印」を描いてみる[図8]。図8の黒丸は,図形個々の原点を示している。

 この例は,「スマイルマーク」の場合と同様に,「矢印」の定義と参照という二つの部分に大きく分かれている[arrow.svg]。

 図8のAは,定義した「矢印」をそのまま参照しており,それ以外の「矢印」は,use要素のtransform(変換)属性によって何らかのtranslate(移動),rotate(回転),X軸方向への斜体化(skewX),Y軸方向への斜体化(skewY)を指定している。

 図8のBは,scale(2)によってX軸,Y軸方向へ共に2倍に拡大した矢印である。一方,図8のCは,scale(4,1)によってX軸方向へ4倍に拡大しY軸方向はそのままの矢印である。言うまでもなく,同じ倍率でなければ「矢じり」の形は相似形にならない。相似形を保つのであれば,「矢じり」と「矢の軸」を別々に扱わなければならない。

 図8のDは,skewX(45)によってX軸方向へ45度斜体化した矢印である。同様に,図8のEは,skewY(45)によってY軸方向へ45度斜体化した矢印である。

 図8のFは,座標(100,250)に「矢じり」の先端を置き,rotate(45,0,10)によって45度右回りした矢印である。部品の「矢印」は縦20,横50の方形とみなせるので,「矢じり」の先端を合わせるための移動は,translate(100,250-10)となり,回転の中心点は「矢じり」の先端(0,10)となる。

 同様にして,図8のGは,座標(100,350)に「矢じり」の先端を置くために,translate(100,350-10)によって移動し,skewY(30)によってY軸方向へ30度斜体化(skewY)した矢印を表したものである。

 図8のHは,矢印を移動し回転した後に,さらに斜体化した例である。

▲図8 様々な矢印

▼arrow.svg 様々な矢印

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="450pt" height="450pt"> 
<!-- 矢印 -->
  <!-- 矢印の定義 -->
   <defs>
    <symbol id="arrow">
      <path d="M 0,10 
        L 10,0 10,7 50,7 50,13 10,13 10,20 0,10 Z" />
    </symbol>
  </defs>
  <!-- 矢印の参照 -->
  <text x="25" y="30">A</text>
  <use xlink:href="#arrow" x="0" y="0" stroke="gray" fill="gray" />
  <circle cx="0" cy="0" r="3" stroke="black" fill="black"/>
  <text x="160" y="40">B</text>
  <use xlink:href="#arrow" stroke="gray" fill="gray" transform="translate(100,0) scale(2)"/>
  <circle cx="100" cy="0" r="3" stroke="black" fill="black"/>
  <text x="210" y="90">C</text>
  <use xlink:href="#arrow" stroke="gray" fill="gray" transform="translate(100,50) scale(4,1)"/>
  <circle cx="100" cy="50" r="3" stroke="black" fill="black"/>
  <text x="160" y="130">D</text>
  <use xlink:href="#arrow" stroke="gray" fill="gray" transform="translate(100,100) skewX(45)"/>
  <circle cx="100" cy="100" r="3" stroke="black" fill="black"/>
  <text x="125" y="175">E</text>
  <use xlink:href="#arrow" stroke="gray" fill="gray" transform="translate(100,150) skewY(45)"/>
  <circle cx="100" cy="150" r="3" stroke="black" fill="black"/>
  <text x="125" y="240">F</text>
  <use xlink:href="#arrow" stroke="gray" fill="gray" transform="translate(100,240) rotate(45,0,10)"/>
  <circle cx="100" cy="250" r="3" stroke="black" fill="black"/>
  <text x="125" y="340">G</text>
  <use xlink:href="#arrow" stroke="gray" fill="gray" transform="translate(100,340) skewY(30)"/>
  <circle cx="100" cy="350" r="3" stroke="black" fill="black"/>
  <text x="225" y="240">H</text>
  <use xlink:href="#arrow" stroke="gray" fill="gray" transform="translate(250,240) rotate(90,0,10) skewY(30)"/>
  <circle cx="250" cy="250" r="3" stroke="black" fill="black"/>
</svg>

SVG図形を動かす(SMIL機能)

 SVGは,アニメーションの記述に用いられる「SMIL」と一部互換性がある。SMILは「Synchronized Multimedia Integration Language」の頭辞語で,その意味は「同期的マルチメディア統合言語」である。SVGにおけるSMIL機能を使うと,アニメーションを簡単に実現できる。あるSVG図形を動かすには,その要素に対して,開始時間や周期などに関する属性を指定したアニメーション要素を組み合わせるだけである。

 アニメーション要素には,animateTransform(変形に関するアニメーション)要素,animateColor(色に関するアニメーション)要素,animateMotion(移動に関するアニメーション)要素,set(それらの要素に対する各種属性の設定)要素がある。

 SVG図形にアニメーション要素を組み合わせた例として「八の字に動き回るスマイルマーク」を実現してみる[図9]。

 この例では,前述した「スマイルマーク」をanimateMotion要素で移動させている[loop.svg]。なお,symbol要素で部品を定義し,use要素でそれを参照するという仕組みではなく,g(グループ)要素による表現をとった。

 animateMotion要素によって移動対象となる図形は,g要素の中の基本図形のすべて,すなわち「スマイルマーク」である。移動のdur(周期)属性は10秒,repeatCount(繰り返し)属性はindefinite(無限)としている。「スマイルマーク」の中心はpath属性で指定したパスデータ,すなわち「八の字」の上を移動する。さらに,rotate(回転)属性をauto(自動)によって「スマイルマーク」そのものが回転することになる。図形が移動する軌道はパスデータで指定するので,直線成分と曲線成分が混じった自由な形が指定できる。

▲図9 八の字に動き回るスマイルマーク

▼loop.svg 八の字に動き回るスマイルマーク

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
     width="500pt" height="600pt"> 
<!-- 八の字に動き回るスマイルマーク -->
  <g>
  <!-- スマイルマーク -->
  <circle cx="0" cy="0" r="70" fill="yellow" stroke="black" stroke-width="4"/>
  <rect x="-20" y="30" width="4" height="15" fill="black" stroke="black"/>
  <rect x="20" y="30" width="4" height="15" fill="black" stroke="black"/>
  <path d="M 0 -40
        C -22.091390 -40 -40 -22.091390 -40 0
        M 0 -40
        C 22.091390 -40 40 -22.091390 40 0"
        fill="none" stroke="black" stroke-width="4"/>
  <!-- 八の字に動き回る -->
  <animateMotion dur="10s" repeatCount="indefinite"
    path="M 150 250
        C 205.228475 250 250 205.228475 250 150
        C 250 94.771525 205.228475 50 150 50
        C 94.771525 50 50 94.771525 50 150
        C 50 205.228475 94.771525 250 150 250
        C 205.228475 250 250 294.771525 250 350
        C 250 405.228475 205.228475 450 150 450
        C 94.771525 450 50 405.228475 50 350
        C 50 294.771525 94.771525 250 150 250"
        rotate="auto" />
  </g>
</svg>

SVG図形を動かす(JavaScript)

 SVG図形は,SVG文書自身に組み込んだJavaScriptのプログラムによって自在に動かすことができる。JavaScriptによるアニメーションでは,SMILよりも一層自由な制御が実現できる。JavaScriptは,HTML文書の表示制御を目的として,サンマイクロシステムズ社によって開発された言語である。

 SVG図形をJavaScriptによって制御する例として「アナログ・デジタル時計」を実現してみる[図10]。

 この例は,JavaScriptプログラムの部分とSVG図形の部分に大きく分かれている[clock.svg]。script要素の内容は,「」で終わる。この区間にJavaScriptプログラムが記述されている。この「マーク区間」は,XML文書としてはエスケープされる。

 JavaScriptプログラムとSVG図形の連係は,オブジェクト名を介して行っている。すなわち,「doc」はSVG文書,「seconds」は「秒針」,「minutes」は「分針」,「hours」は「時針」,「DigitalClock」が「デジタル時計」をそれぞれ表す。これらのオブジェクトは,JavaScriptプログラム側では変数であり,SVG図形側ではid属性でその名前を持つ要素である。オブジェクトとしての個々の図形要素とその属性は,DOMインターフェースによって操作されている。

▲図10 アナログ・デジタル時計

▼clock.svg アナログ・デジタル時計

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
     width="200" height="250" onload="init(evt)">
  <script language="Javascript">
    <![CDATA[
      var doc, seconds, minutes, hours, DigitalClock;
      // 初期化
      function init(evt) {
        doc  = evt.getTarget().getOwnerDocument();
        seconds = doc.getElementById("seconds")
        minutes = doc.getElementById("minutes")
        hours   = doc.getElementById("hours")
        setInterval("currentTime()",1000);
        try {
          DigitalClock = doc.getElementById("digitalClock").firstChild();
        } catch(e) { return; }
      }
      // 現在の時刻の表示
      function currentTime() {
        current  = new Date();
        seconds.setAttribute('transform', 'rotate(' + (current.getSeconds() * 6) + ')');
        minutes.setAttribute('transform', 'rotate(' + (current.getMinutes() * 6) + ')');
        hours.setAttribute('transform', 'rotate(' + (current.getHours() * 30) + ')');
       try {
          DigitalClock.setData(Math.round(current.getHours()) + ':' + Math.round(current.getMinutes()) + ':' + Math.round(current.getSeconds()));
        } catch(e) { return; }
      }
    ]]>
  </script>
  <!-- 時計 -->
  <g transform="translate(100,100)">
    <!-- アナログ表示 -->
    <circle cx="0" cy="0" r="80" style="stroke-width:6;stroke:black;fill:white"/>
    <line id="hours" x1="0" y1="0" x2="0" y2="-35" style="stroke-width:4;stroke:black"/>
    <line id="minutes" x1="0" y1="0" x2="0" y2="-55" style="stroke-width:2;stroke:black"/>
    <line id="seconds" x1="0" y1="0" x2="0" y2="-75" style="stroke-width:1;stroke:black"/>
    <!-- デジタル表示 -->
    <text id="digitalClock" x="15" y="100" style="font-size:12pt;stroke:black;fill:black">hh:mm:ss</text>
  </g>
</svg>

SVGで表とビジネスグラフを描く

 文書にデータが含まれている場合,それを表やビジネスグラフで表現するのが一般的である。しかし,表とビジネスグラフのどちらが理解しやすいかには,個人差があるので,その選択は一概に決められない。そこで,SVGをこの問題解決に応用し,新しい表現方法を実験してみた。

 この例では,ある統計データについて可逆的(交替可能)な「表としての表現」と「ビジネスグラフとしての表現」が,表示画面をクリックする度に交替するようになっている[図11]。

 このSVG文書は,JavaScriptプログラムを含み,それによってSVG図形を操作する構造になっている[table-graph.svg]。このSVG文書は,表示の基となるデータをJavaScriptプログラムに定数として含める必要があるので,サーバー側で生成しなければならない。

▲図11 ビジネスグラフとしての表現

▼table-graph.svg 交替可能なデータ表現

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
     height="800" width="800" onload="Draw(evt)">
<script type="text/javascript">
<![CDATA[
var title   = new String("コーヒー"); // データの表題を表す文字列
var x_label = new String("年"); // X軸のラベルを表す文字列
var y_label = new String("単価"); // Y軸のラベルを表す文字列
var statistic = new Array(     190,      171,      132,       30); // データ(Y軸方向の量)を表す数値の配列
var x_axis    = new Array("1950年", "1960年", "1970年", "1980年"); // X軸の目盛り(項目名)を表す文字列の配列
var y_axis    = new Array("   0円",   "50円",  "100円",  "150円"); // Y軸の目盛り(項目名)を表す文字列の配列
var y_scale   = 1; // Y軸の倍率を表す数値
var obj = "table"; // 表示するオブジェクト(表またはグラフ)の区別を表す文字列
// ユーザーによって枠内をクリックされた時と,このSVG文書が最初にロードされた時に実行された時に実行される
function Draw(evt) {
  var doc = evt.getTarget().getOwnerDocument();
  if (doc == null) { alert("no doc"); return; }
  ClearNodes(doc);
  switch(obj) {
    case "graph":
      DrawGraph(doc); obj = "table"; break;
    case "table":
      DrawTable(doc); obj = "graph"; break;
  }
}
// データに基づいて「縦棒グラフ」を表示する
function DrawGraph(doc) {
  for (var y_max = 0, i= 0; i < statistic.length; i++) {
    if (y_max < statistic[i]) { y_max = statistic[i]; }
  }
  var y_height = y_max * y_scale;
  DrawText(doc, 150, 10, title, 14, "green");
  DrawText(doc, 80 + x_axis.length * 50, 50 + y_height, x_label, 8, "black");
  DrawText(doc, 36, 35, y_label, 8, "black");
  DrawRect(doc, 50, 40, 25 + x_axis.length * 50, y_height, "black", "none");
  for (var i = 0; i < x_axis.length; i++) {
    DrawText(doc, 70 + 50 * i, 50 + y_height, x_axis[i], 8, "black");
  }
  for (var i = 0; i < y_axis.length; i++) {
    DrawText(doc, 20, 50 + y_height - 50 * i, y_axis[i], 8, "black");
  }  
  for (var i= 0; i < statistic.length; i++) {
     DrawRect(doc, 80 + 50 * i, 40 + y_height - (statistic[i] * y_scale), 10, statistic[i] * y_scale, "black", "green");
     DrawText(doc, 80 + 50 * i, 38 + y_height - (statistic[i] * y_scale), String(statistic[i]), 8, "black");
  }
}
// データに基づいて「表」を表示する
function DrawTable(doc) {
  DrawText(doc, 150, 30, title, 14, "green");
  DrawRect(doc,  30, 40, 50, 20, "black", "pink");
  DrawText(doc,  50, 55, x_label, 8, "black");
  for (var i = 0; i < x_axis.length; i++) {
    DrawRect(doc, 80 + 50 * i, 40, 50, 20, "black", "none");
    DrawText(doc, 90 + 50 * i, 55, x_axis[i], 8, "black");
  }
  DrawRect(doc,  30, 60, 50, 20, "black", "lime");
  DrawText(doc,  50, 75, y_label, 8, "black");
  for (var i = 0; i < x_axis.length; i++) {
    DrawRect(doc,  80 + 50 * i, 60, 50, 20, "black", "none");
    DrawText(doc, 100 + 50 * i, 75,  statistic[i], 8, "black");
  }
}
// SVG図形としてのテキストを描く
function DrawText(doc, x, y, str, size, color) {
  var canvas = doc.getElementById("canvas");
  if (canvas == null) { alert("no canvas"); return; }
  var txt = doc.createElement("text");
  txt.setAttribute("x", x);
  txt.setAttribute("y", y);
  txt.setAttribute("font-family", "Osaka");
  txt.setAttribute("font-size", size);
  txt.setAttribute("stroke", color);
  txt.setAttribute("fill", color);
  canvas.appendChild(txt);
  var tstr = doc.createTextNode(str);
  canvas.lastChild.appendChild(tstr);
}
// SVG図形としての方形を描く
function DrawRect(doc, x, y, width, height, stroke, fill) {
  var canvas = doc.getElementById("canvas");
  if (canvas == null) { alert("no canvas"); return; }
  var rct = doc.createElement("rect");
  rct.setAttribute("x", x);
  rct.setAttribute("y", y);
  rct.setAttribute("width", width);
  rct.setAttribute("height", height);
  rct.setAttribute("stroke", stroke);
  rct.setAttribute("fill", fill);
  canvas.appendChild(rct);
}
// すでにキャンバスに描かれている複数のSVG図形を消去する
function ClearNodes(doc) {
  var canvas = doc.getElementById("canvas");
  if (canvas == null) { alert("no canvas"); return; }
  if (! canvas.hasChildNodes()) { return; }
  var n = canvas.childNodes.length;
  if (n == 0) { return; }
  for (var e = n; e > 0; e--) {
    var child = canvas.lastChild;
    if (child == null) { alert("no item"); return; }
    canvas.removeChild(child);
  }
}
]]>
</script>
<!--「表」または「縦棒グラフ」を描くキャンバス -->
<rect onmouseup="Draw(evt)" x="10" y="10" width="400" height="280" stroke="black" fill="white"/>
<g id="canvas" transform="translate(10,30)"></g>
</svg>

XSL変換で円グラフを描く

 棒グラフのような直線成分だけで構成されるビジネスグラフを表すSVG文書は,XSL変換で生成できる。しかし,円グラフのような曲線成分を含むビジネスグラフを表すSVG文書は,XSL変換仕様に適切な数学関数がないために,XSL変換だけでは生成できない。ちなみに,XSL変換における関数型の再帰処理を利用するという案もあるが,まだ実現していないようである。

 円グラフを,パイ(直線と楕円弧からなる閉じた区間)の集まりとして描いてみる[図12]。こうしたSVG文書は,前述の「交替可能なデータ表現」と同様に,サーバー側で生成することになる。

▲図12 円グラフ

▼piegraph.svg 円グラフ

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
     width="450px" height="400px" onload="DrawPieGraph(evt)">
<script type="text/javascript">
<![CDATA[
var graph_title = new String("大陸の面積");
var core_title  = new String("単位:10,000平方メートル");
var items   = new Array("アジア", "アフリカ", "ヨーロッパ", "北アメリカ", "南アメリカ", "オセアニア", "旧ソ連", "南極");
var numbers = new Array(2757, 3033, 493, 2151, 2056, 851, 2240, 1361);
var colors  = new Array("yellow", "gray", "white", "magenta", "green", "aqua", "red", "white");
var graph_width = 300;
var graph_height = 200;
var core_x = 40;
var core_y = 60;
var rx = graph_width / 2;
var ry = graph_height /2;
var cx = core_x + rx ;
var cy = core_y + ry ;

function DrawPieGraph(doc) {
  var sum, i, d, angle;
  
  var doc = evt.getTarget().getOwnerDocument();
  if (doc == null) { alert("no doc"); return; }

  for (sum = 0, i= 0; i < items.length; i++) {
    sum += numbers[i];
  }
  for (d = 0, i = 0; i < items.length; i++) {
    angle = numbers[i] / sum * 360;
    DrawPie(doc, d2r(d), d2r(angle), colors[i]);
    d += angle;
  }
  for (d = 0, i = 0; i < items.length; i++) {
    angle = numbers[i] / sum * 360;
    DrawPieItem(doc, d2r(d + angle / 2), d2r(angle), items[i], numbers[i]);
    d += angle;
  }
  DrawText(doc, cx, core_y - 20, graph_title, 16, "black");
  DrawPieCoreTitle(doc, cx, cy, rx / 3, ry / 3, "white", core_title)
}

function DrawPie(doc, d, angle, color) {
  var a  = d + angle;
  var ah = d + angle / 2;
  var x1 = Math.sin(d) * rx;
  var y1 = - Math.cos(d) * ry;
  var x2 = Math.sin(a) * rx - x1;
  var y2 = - Math.cos(a) * ry - y1;
  var flag = (angle >= Math.PI)? 1 : 0;
  DrawPath(doc, "M " + cx + "," + cy + " l " + x1 + "," + y1 + " a " + rx + "," + ry + " 0 " + flag + " 1 " + x2 + "," + y2 + " L" + cx + "," + cy, "black", color) 
}

function DrawPieItem(doc, d, angle, item, number) {
  var a  = d + angle;
  var ah = d + angle / 2;
  var xt = cx + Math.sin(d) * rx;
  var yt = cy - Math.cos(d) * ry
  DrawText(doc, xt, yt, item + "(" + number + ")", 12, "black");
}

function d2r(d) {
  return d * Math.PI / 180;
}

function DrawPath(doc, d, stroke, fill) {
  var canvas = doc.getElementById("canvas");
  if (canvas == null) { alert("no canvas"); return; }
  var ph = doc.createElement("path");
  ph.setAttribute("d", d);
  ph.setAttribute("stroke", stroke);
  ph.setAttribute("fill", fill);
  canvas.appendChild(ph);
}

function DrawText(doc, x, y, str, size, color) {
  var canvas = doc.getElementById("canvas");
  if (canvas == null) { alert("no canvas"); return; }
  var txt = doc.createElement("text");
  txt.setAttribute("x", x);
  txt.setAttribute("y", y);
  txt.setAttribute("font-family", "Osaka");
  txt.setAttribute("font-size", size);
  txt.setAttribute("stroke", color);
  txt.setAttribute("fill", color);
  canvas.appendChild(txt);
  var tstr = doc.createTextNode(str);
  canvas.lastChild.appendChild(tstr);
}

function DrawPieCoreTitle(doc, cx, cy, rx, ry, color, core_title) {
  var canvas = doc.getElementById("canvas");
  if (canvas == null) { alert("no canvas"); return; }
  var elp = doc.createElement("ellipse");
  elp.setAttribute("cx", cx);
  elp.setAttribute("cy", cy);
  elp.setAttribute("rx", rx);
  elp.setAttribute("ry", ry);
  elp.setAttribute("stroke", "black");
  elp.setAttribute("fill", color);
  canvas.appendChild(elp);
  DrawText(doc, cx - rx + 2, cy, core_title, 8, "black");
}
]]>
</script>
<g id="canvas" transform="translate(10,30)"></g>
</svg>

SVGからPDFへ変換

 XML文書からHTML文書への変換では,XSL変換あるいはXSL変換を内蔵したウェブブラウザーが使われ,XML文書からPDF文書への変換では,XSL変換を内蔵したXSLフォーマッターが使われる。変換対象となるXML文書は,先ずXSLにおける「フォーマッティング・オブジェクト(FO)」というボキャブラリへXSL変換され,引き続いてFO文書からPDF文書へ変換される。FOのinstream-foreign-objectタグによって,HTMLのEMBEDタグによるSVG文書の参照と同じように,SVG文書は外部参照される。

 この例では,「名刺」のデータを表したXML文書[金太郎.xml]をXSL変換によってHTML文書へ変換している[図13]。また,XSLフォーマッターによってPDF文書へ変換している[図14]。その変換では,それぞれの目的に応じたスタイルシートが必要となる。なお,ロゴ要素の画像属性が示す形式がJPEGのような外部画像の場合は,external-graphicタグによって外部画像を参照するように変換を切り替える必要がある。

▼金太郎.xml

<?xml version="1.0" encoding="Shift_JIS" ?>
<?xml-stylesheet type="text/xsl" href="名刺.xsl" ?>
<名刺>
<名前><姓>金</姓><名>太郎</名></名前>
<会社名>金時山茶屋</会社名>
<役職>店主</役職>
<住所 郵便番号="250">金時山大字峠之垰</住所>
<電話番号>12-6543</電話番号>
<ホームページ>www.kintokiyama.com</ホームページ>
<ロゴ 画像="富士.svg"/>
</名刺>

▲図13 HTML文書へ変換された名刺

▲図14 PDF文書へ変換された名刺

数式表示への応用

 分数や根号のある数式[図15]は,HTMLタグでは表示できないので,GIFなどの画像で代用的に表されている。その問題を解決するために「MathML」という規格がW3Cによって開発・公開された。MathMLは「Mathematical Markup Language」の略で,文字通り「数式を表すためのマーク言語」である。このMathMLで表した数式をSVGへ変換する「CUSTARD」というツールをSchemaSoft社が実現している。なお,同社はAtimi Software社へ合併されたようであり,このツールは現在ウェブで閲覧することはできない。

 ここに図15の数式についての例を引用しよう。CUSTARDは,MathMLで表した数式[eqn.mml]をSVGで表した数式[eqn.svg]へ変換する。言うまでもなく,このSVG文書は,HTML文書やFO文書から外部参照できる。

 MathMLをネイティブに表示できるウェブブラウザーが登場してきたので,こうした変換は不要に思えるかもしれない。しかし,それはウェブの場合であって,高解像度が求められるXSLフォーマッターでは重要なツールと思われる。

▲図15 数式

▼eqn.mml MathMLで表した数式

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE math PUBLIC "-//W3C//DTD MathML 2.0//EN"
    "dtd/mathml2.dtd">
<math xmlns="http://www.w3.org/1998/Math/MathML">
  <mi>x</mi>
  <mo>=</mo>
  <mroot>
    <mfrac>
      <mrow>
        <mi>a</mi>
        <mo>+</mo>
        <mi>b</mi>
      </mrow>
      <mi>3</mi>
    </mfrac>
    <mi>3</mi>
  </mroot>
</math>

▼eqn.svg SVGで表した数式

<?xml version="1.0"?>
<!DOCTYPE svg
  PUBLIC "-//W3C//DTD SVG 20001102//EN"
 "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink" id="svg-root" width="100%" height="100%" viewBox="0 0 82.10564668241815 56.431">
  <title>Custard SVG</title>
  <desc>Copyright 2001, Schema Software, Inc. All rights reserved. 
   SVG Generated from MathML 2 XML document, using XSLT 1.1.</desc>
  <text class="mi1" x="20" y="30.415499999999998" font-size="10" fill="black">x</text>
  <text class="mo" x="27.666668" y="30.415499999999998" font-size="10" fill="black">=</text>
  <use x="35.333336" y="20" width="12.098979242418153" height="16.431" xlink:href="#SquareRoot"/>
  <line x1="47.057315242418156" y1="20.2" x2="61.730646682418154" y2="20.2" style="stroke:black; stroke-width:0.4"/>
  <text class="mi1" x="47.057315242418156" y="27.964399999999998" font-size="7.1" fill="black">a</text>
  <text class="mo" x="52.26398096241815" y="27.964399999999998" font-size="7.1" fill="black">+</text>
  <text class="mi1" x="57.470646682418156" y="27.964399999999998" font-size="7.1" fill="black">b</text>
  <line x1="47.307315242418156" y1="30.680149999999998" x2="61.480646682418154" y2="30.680149999999998" style="stroke: black; stroke-width: 0.426"/>
  <text class="mi1" x="52.26398096241816" y="36.732899999999994" font-size="7.1" fill="black">3</text>
  <text class="mi1" x="35.399163695709376" y="28.2813775" font-size="5.041" fill="black">3</text>
  <defs>
    <style type="text/css"><![CDATA[
      .mi1{ font-family: 'Courier New', monospace; font-style: italic }
      .mi2{ font-family: 'Courier New', monospace }
      .mo{ font-family: 'Courier New', monospace }
      .mn{ font-family: 'Courier New', monospace}
      .mtext{ font-family: 'Lucida Console', monospace }
]]></style>
    <symbol id="SquareRoot" viewBox="0 0 421.429 572.321" preserveAspectRatio="xMinYMin meet">
      <path d="M0,359.821l73.214-36.606l112.5,220.535L403.571,0h17.857L192.857,572.321h-25L57.143,350.893L7.143,375L0,359.821z"/>
    </symbol>
  </defs>
</svg>

今後の課題

 SVGの技術的基盤はまだ発展途上にある。現行のウェブブラウザーなどのツールには制約が少なくない。仕様通りに制作しても,プラットフォームによっては実現できない場合もある。とは言え,ドッグイヤー(日進月歩)のウェブ技術ゆえに,コンテンツビジネスに従事する人々は,SVG,JavaScript,XSL変換,FOといった関連技術の研究と習得は,不断に続けていかなければならないだろう。

 SVGの応用は近い将来に広範な領域に及ぶに違いない。とりわけ,SVGによるアニメーションは,広告,広報,娯楽,教育,技術支援などのさまざな分野に応用できる。とりわけ,端境期にあるコンテンツビジネスをさらに前進させるために,顧客を魅了する需要の掘り起こしが大きな課題である。

初出:テキスト&グラフィックス研究会会報 2005年6月号(Vol.11,No.10,通巻233号)「SVGの概要」


(c)2006, KISI