svg要素の基本的な使い方まとめ

Written by defghi1977@xboxLIVE.この文書は全てテキストエディタで作成しています.えーと,そりゃもうゴリゴリと.

6.スタイル設定

HTMLと同様にSVGにおいてもスタイルシートを用いた見た目の変更をサポートする.本項ではこの操作を表すstyle要素とstyle属性の説明,及びその注意点について示す.また関連項目としてXLSをSVGに応用した例を示す.

style:スタイルの設定

CSSによるスタイル付けの前にスタイルそのものについて考えてみよう.一般にXMLやHTMLはデータ構造や文書構造を記述するためのものであり,これらを使って作成された文書はコンピュータがその内容を解釈するには都合が良いものの,人間の目には単にタグ付けされたデータに過ぎず直感的に判りにくい.ここで文書に何らかの見た目を与えるものをスタイルと呼ぶ.文書にスタイルを与えることで元のデータ構造を損なうことなしに見た目を判りやすくすることが出来る.WEBブラウザでHTMLファイルを開いた場合にある程度構造が判るように文書が整形されるのは,予め規定のスタイルが設定されているからである.また,文書の用途に応じスタイルを切り替えることでより適切な見た目を得ることも可能となる.文書構造とスタイルとは一纏めとすることもできるが,通常は複数に分離しておきそれぞれの再利用性を高める構造を摂ることが多い.

このスタイルを定義するものがスタイルシートである.代表的なスタイルシート言語としてはCSS(Cascading Style Sheets)とXSL(eXtensible Stylesheet Language)が挙げられる.SVGではこれらの言語によるスタイル付け機構を導入しているので,色などの情報を直接指定するよりも柔軟な運用を行うことが可能となっている.なお,一般的なスタイル付け機構では視覚的なものだけでなく聴覚的なユースケース(スクリーンリーダー等)をも想定しているが,SVGでは扱う対象が画像であることから,スタイル設定では主にグラフィック制御を行うこととなる.

SVGにおいてスタイルを指定するにはstyle要素を用いるstyle属性を用いる外部CSS文書を参照するの3つの方法がある.いずれも通常はCSSをスタイルシート言語として利用する.

※なお,本文書ではSVGでのスタイル付けに関わるトピックについてのみ解説するものとし,CSSやXSLの扱い方については他の解説サイトに委ねることとします.なお,SVGにおいてもスタイルの適用順等の基本的な動作はHTMLと全く同じです.

style要素

style要素を用いることでSVG文書にスタイルを定義することが出来る.

type
スタイルの形式.text/cssが規定値.
media
スタイルが適用される条件.all,screen,print等.メディアクエリに相当する.
title
html4との互換性のために定義されている.

style要素の内容は<![CDATA[〜]]>(cdataセクション)を使って記述するのが原則である.さもないと唯一「>」セレクタの記述をエスケープしなければならないが,html埋め込みのsvg要素の場合はそれほど意識しなくても良いかもしれない(なおサンプルではスクリプト処理の関係上cdataセクションの記述が落ちている).

style属性

図形要素のstyle属性に直接スタイルを指定することもできる.

style
図形要素にスタイルを指定する.

外部CSSの参照

SVGにおいてもスタイルシートを外部ファイルに書きだしておいて,それをSVGファイルから参照することができる.やり方は次の通り.なお,インラインSVGであればこれまで通りHTMLのlink要素でスタイルシートを参照すれば良い.

  1. XMLのスタイルシート参照(xml-stylesheet処理命令)を利用する.
    仕様書にも記載されている標準的な記法で,スタンドアロンのSVGファイルの場合はこちらを利用する.なおこの方法であればXSLによる属性値の指定も可能だ.
    <?xml-stylesheet href="参照先" type="text/css"?>
    この記述は併記することもでき,複数のスタイルシートを参照することが可能だ.
    なお,参照するCSSファイルの文字コードがutf-8と異なる場合は@charset規則を使って明示する必要がある.
  2. CSSの@import規則を利用する.
    cssのもつファイル参照機能を用いるもの.
    <style type="text/css"><![CDATA[ @import url([参照先].css); ]]></style>
  3. XHTMLのlink要素を利用する
    SVG文書内部でXHTMLのlink要素を宣言し,スタイルシートを参照するもの.
    XMLの記述法としては正しいが,なぜこれで動作するのか若干気持ち悪い為,参考程度に留めておくと良い.
    <link xmlns="http://www.w3.org/1999/xhtml" href="[参照先].css" rel="stylesheet" type="text/css"/>

実際には1と2の方法を組み合わせて用いることとなるだろう.なおSVGの参照モードによっては外部スタイルシートの参照が無効となる場合がある.ブラウザ毎に動作が異なるので注意すること.

補足)代替スタイルシートの指定

HTMLにおける代替スタイルシートの指定はlink要素のrel属性にalternate stylesheetを指定するが,xml-stylesheet処理命令ではalternate="yes"と記述する.すると,代替スタイルシートに対応したWEBブラウザではメニューからスタイルの変更が可能となる.

<?xml version="1.0" standalone="no"?>
<?xml-stylesheet href="a.css" type="text/css" title="main"?>
<?xml-stylesheet href="b.css" type="text/css" alternate="yes" title="sub"?>
<svg width="100px" height="100px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
    <rect width="100%" height="100%"/>
</svg>

SVGにおけるCSSの記述法

CSSは通常HTMLに対するスタイル付けを念頭に入れており,そのほとんどの機能はHTMLを整形するためのものである.従ってSVGにおけるCSSは構文こそ同じであるが,指定できるプロパティが異なるため混同しないようにしたい.

プレゼンテーション属性

SVGにおいて図形の色や見た目に伴う属性群をSVGのプレゼンテーション属性と呼ぶ.これらの属性は図形要素に直接記述する他にCSS から指定することが可能となっている.それ以外の図形の形状,配置に関わる属性(x,y,width,height…)等は設定できない.これはSVGの本質が図形の形状であることを暗に示している.詳しくはこちらを参照のこと.なおHTMLでのCSS属性と同じ名称でも,SVGに適用するにあたり若干の意味合いの変更を伴ったもの(overflow属性など)も存在している.ちなみに埋め込みSVGの場合,SVG要素に対してwidth,height属性値をCSS属性として指定することができるが,これはHTMLの世界でのスタイル値が有効となっているものと考えられる.

プレゼンテーション属性(SVGの属性のうち,CSSで設定可能なプロパティ値)
font,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,direction,letter-spacing,text-decoration,unicode-bidi,word-spacing,clip,color,cursor,display,overflow,visibility,
SVG固有の属性:clip-path,clip-rule,mask,opacity,enable-background,filter,flood-color,flood-opacity,lighting-color,stop-color,stop-opacity,pointer-events,color-interpolation,color-interpolation-filters,color-profile,color-rendering,fill,fill-opacity,fill-rule,image-rendering,marker-end,marker-mid,marker-start,paint-order,shape-rendering,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-mitterlimit,stroke-opacity,stroke-width,text-rendering,alignment-baseline,baseline-shift,dominant-baseline,glyph-orientation-horizontal,glyph-orientation-vertical,kerning,text-anchor,writing-mode
(transform属性はこのカテゴリに含まれない)※SVG1.1では

†HTMLでは見た目を制御するにはCSSからスタイルを指定せねばならなかったが,SVGでは複数の見た目の制御をサポートする.このことでスタイル付けの優先順等においてHTMLより厄介な問題を生ずるケースがある.

CSSの構文

CSSの構文は次の通り.
[セレクタ]{[プレゼンテーション属性]:[値]}
セレクタはカンマ「,」区切りで複数指定することが出来る.また,属性名と値のセットはセミコロン「;」区切りで列挙することが出来る.コメントは「/*コメント*/」として記述する.例を示す.ここではid属性がa,bのcircle要素に対しfill属性とstroke属性をcssから設定している.

構文に問題があった場合はその設定は無視される.非常に長いスタイルシートを記述する場合は間違いに気付きにくいため注意のこと.

セレクタ

CSSではセレクタと呼ばれるクエリ言語を使ってSVGの中身を検索する.このセレクタに関わる属性としては次のものがある.

class
クラス.要素に対するカテゴリを定義する.列挙可能.
id
要素の識別子.HTML文書と同様に文書の中で一意となるように設定する.(重複してもエラーとはならないものの,意図しない結果を生む原因となる.)
この属性はスタイル付けを行う他にも,use要素から図形を参照する場合やグラデーションや画像処理を行う際の処理要素を参照するためにも用いられる.

class属性を用いた例を示す.それぞれ2つのクラスが付与されており,その内容がミックスされていることが判る.

以下にセレクタ(selectors Level 3)で定義されているものを示す.なおHTMLのブロックモデルに関わる擬似要素についてはsvgにはそれに相当する機能が存在しないため無効である.インラインSVG要素に対する::before・::after擬似要素についても同様で,基本的にはimg要素と同じである.

記法説明種別
E[foo^="bar"] 「foo」属性の値が「bar」から始まる全てのE要素.属性セレクタ
E[foo$="bar"] 「foo」属性の値が「bar」で終わる全てのE要素.属性セレクタ
E[foo*="bar"] 「foo」属性の値が「bar」という部分文字列を含む全てのE要素.属性セレクタ
E[foo|="en"] 「foo」属性の値が「en」もしくは「en-」から始まる全てのE要素.属性セレクタ
E:root 文書のルートであるE要素.擬似クラス
E:nth-child(n) 親要素で数えてn番目の子であるE要素.擬似クラス
E:nth-last-child(n) 親要素で後ろから数えてn番目の子であるE要素.擬似クラス
E:nth-of-type(n) 親要素に対するE子要素でn番目の子であるE要素.擬似クラス
E:nth-last-of-type(n) 親要素に対するE子要素で後ろからn番目の子であるE要素.擬似クラス
E:first-child 親要素における先頭の子要素であるE要素.擬似クラス
E:last-child 親要素における末尾の子要素であるE要素.擬似クラス
E:first-of-type 親要素における先頭のE要素.擬似クラス
E:last-of-type 親要素における末尾のE要素.擬似クラス
E:only-child 親要素における唯一の子であるE要素.擬似クラス
E:only-of-type 親要素における唯一のE要素.擬似クラス
E:empty (テキストノードを含め)子要素を持たないE要素.擬似クラス
E:linkハイパーリンクを持つE要素のうち,未訪問のE要素.擬似クラス
E:visited ハイパーリンクを持つE要素のうち,訪問済みのE要素.擬似クラス
E:activeアクティブ状態にあるE要素.擬似クラス
E:hoverカーソルが当たっているE要素.擬似クラス
E:focus フォーカスが当たっているE要素.擬似クラス
E:target URIの参照ターゲットとなっているE要素.擬似クラス
E:lang(fr) 言語属性に「fr」を持つE要素全体.擬似クラス
E.warning クラスに「warning」をもつE要素.クラスセレクタ
E#myid idに「myid」をもつE要素.IDセレクタ
E:not(s) 単体セレクタsに合致しないE要素.擬似クラス
E F E要素の子孫であるF要素.子孫結合子
E > F E要素の子であるF要素.子結合子
E + F E要素の直後にあるF要素.隣接結合子
E ~ F E要素の後に表れるF要素.後続結合子

要素の出現順を意識したトリッキーなスタイル指定

SVGにおいては要素の順番とレイアウトとの間に(図形の重ね合わせの他に)明確な相関が存在しない.下の例では要素の順番とレイアウトを意図的にずらしておいたもので,一見描画のバグのように見えるが正常な動作である.

きちんとcircle要素が並んでいれば問題ないのだが…

擬似クラスと後続結合子

先程の特徴は実現したい機能に応じて適切な要素の構造を摂る事が可能ということでもある.特に擬似クラスと後続結合子「~」の組み合わせはSVGの表現の幅を大きく広げる.擬似クラスを適用する要素を前に記述しておき,その要素に連動する要素を後ろに記述しておくと,後続結合子により自由に要素を抽出することが可能となるのだ.例を示す.矩形要素にカーソルを合わせるとそれに連動して円要素の色が変化する.

この文書を変形し,矩形と円の位置を変更してみた.この時レイアウトを変更したとしても,SVG文書の構造を変化させなければ正しく動作する

もう一つ例を示す.HTML側のチェックボックスの状態を元に埋め込みSVGの内容を書き換えている.

このテクニックを応用することで,静的なSVG文書についてスクリプトを用いること無しにアプリケーション的な機能を付加することも可能である.文書構造が固定的なhtmlよりも積極的に利用できる.

スタイルの優先順位

XML属性(プレゼンテーション属性)として設定し値とスタイルシートで設定した値とではスタイルシートで設定した値のほうが優先される.そのため,不用意に全称セレクタ(*)などでスタイルを指定すると,SVGの属性値が全く意味を為さなくなる.また,図形要素にstyle属性が存在する場合はこの値が優先される.例を示す.この例では塗りつぶしの色をstyle要素・style属性に指定したものとstyle要素・style属性・fill属性の合計3箇所で設定したものを用意した.その結果として一つ目はstyle要素の内容が優先され緑色に塗られており,二つ目ではstyle属性の値が優先され円が青色で塗られている.

HTMLでのインラインSVGに対するスタイルの指定について

HTMLに直接記述されたSVG図形(インラインSVG)にスタイルを指定する場合は,親となるHTML部のstyle要素にSVGに対するスタイル定義を記述しても正しく動作する.このことでCSS定義を一つにまとめることが可能であるものの,一部HTMLとSVGとで意味合いの若干異なるスタイル値が存在するため,混在させる際に注意を要する.更にスタイル指定の有効範囲はHTML文書全体に及ぶため,複数のインラインSVGを定義してセレクタを適切に指定していなかった場合に意図せぬ箇所にスタイルが設定されてしまうケースがある.

CSS名前空間による制御

SVGとHTMLとを連携する場合,SVGのa要素とHTMLのa要素とを区別したい場合がある.例えば下ではセレクタにaのみを指定したため,スタイルがHTMLのa要素とSVGのa要素の両方に設定されてしまっている.

SVGのa要素 HTMLのa要素

この問題は,CSS名前空間モジュールにおける@namespace規則を用いることで解決する.予めSVGの名前空間を@namespace規則で宣言しておくことで,それぞれ実体の異なるa要素に対するスタイルが指定可能となる.

SVGのa要素 HTMLのa要素

ルートsvg要素に対するスタイル指定

SVGにおいては通常プレゼンテーション属性のみをスタイルシートで指定できるが,SVGグラフィックのルートとなるsvg要素についてはHTMLにおけるスタイルシート設定が有効である.つまり,widthやheight,backgroundのみならず,margin,padding,border,transform等の設定を施すことが出来る.

<?xml version="1.0" standalone="no"?>
<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
    <style><![CDATA[
        svg:root{background-image:url(img.png);}
    ]]></style>
    <circle cx="100" cy="100" r="80" fill="blue" opacity="0.5"/>
</svg>

この時,CSSによるbackground画像の配置はsvg要素におけるズーム・パンの影響を受けない点に注意しよう.すなわち,グラフィックの一部を固定位置に表示すると言った事が可能となる.

メディアクエリ

CSSに定義されているメディアクエリ機能はSVGでも利用することが出来る.HTMLの場合と同様にSVGの描画範囲に応じてその内容を切り替えると言った処理を実現することが出来る.下の例では200pxを境界として描画内容を切り替えている例である.下の例は背景を指定したスタンドアロンSVGをobject要素で参照している.

<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
    <title>メディアクエリによる見た目の切り替え</title>
    <style>
        #wide{
            display:none;
        }
        @media (min-width: 200px){
            #narrow{
                display:none;
            }
            #wide{
                display:inline;
            }
        }
    </style>
    <g id="narrow">
        <text y="20">幅が200pxより小さい</text>
    </g>
    <g id="wide">
        <text y="20">幅が200pxより大きい</text>
    </g>
</svg>

以下にメディアクエリで指定可能な値を示す.なお,この全てがsvgで利用可能という訳ではない点に注意する.

width
表示幅が指定した幅の場合(max-width/min-width)
height
表示高が指定した高の場合(max-height/min-height)
device-width
表示デバイスのスクリーン幅が指定した幅の場合(max-device-width/min-device-width)
device-height
表示デバイスのスクリーン高が指定した高の場合(max-device-height/min-device-height)
orientation
表示の方向
portrait
表示幅≦表示高
landscape
表示幅>表示高
aspect-ratio
アスペクト比(表示幅/表示高)が指定値の場合※16/9といった形式で定義(max-aspect-ratio/min-aspect-ratio)
device-aspect-ratio
デバイスアスペクト比が指定値の場合(max-device-aspect-ratio/min-device-aspect-ratio)
color
デバイスの色深度(色の量子化ビット数)※0で白黒.r(3bit)g(3bit)b(2bit)の場合は2bitとして解釈する.(max-color/min-color)
color-index
デバイスの色数(max-color-index/min-color-index)
monochrome
デバイスがモノクロの場合の階調bit数※0でモノクロ以外(max-monochrome/min-monochrome)
resolution
デバイスの解像度が指定したdpi/dpcm(max-resolution/min-resolution)
scan
テレビの表示形式
progressive
プログレッシブ形式
linterlace
インターレース形式
grid
デバイスの表示形式
0
グリッド形式(テキストベースと考えてよい)
1
ビットマップ形式

補足)レスポンシブウェブデザインとsvg

昨今話題となっているテクニックとしてレスポンシブウェブデザイン(responsive web design)がある. そもそも「〜がレスポンシブである」とは単一の文書が実行される環境毎に最適なレイアウトでスクリーン表示されることを指す.従って元々HTMLはデフォルトスタイルでレンダリングされるという意味でレスポンシブである.ただしこのままでは実用的でないため何らかのデザインを適用する必要があるが,その際にHTMLが元来持っているレスポンシブ性を活かす形でスタイル定義を行う様をレスポンシブウェブデザインと呼ぶ.先ほど示したメディアクエリはこのレスポンシブ性を制御するために用いられる.

ではSVGにおいてはどうかというと同様のレスポンシブ性を考えることが出来る.例えばwidth/height属性を指定せず,viewBox属性のみを指定したSVG画像はレスポンシブである.なぜなら,画像がスクリーンのサイズに自動拡大されるからだ.単純な用途であればこれで事足りるが,ではより高度なレスポンシブ性の制御を行うにはどうすればよいか.HTMLでは文書構造を固定しレイアウトをCSSで記述していたが,SVGにおいては文書構造とレイアウトが密接に関わり合っている.その為,高度にレスポンシブなSVGを構成する場合はHTMLと異なるアプローチを探る必要がある.

例えば次のような方法が挙げられる.共通的に利用するグラフィック部品をdefs要素内で定めておき,環境ごとのレイアウトに合わせuse要素でコピーした上で並べ変えておく.その上で複数のレイアウトの中からメディアクエリを使って一つだけ画面に表示させるようにするのだ.図で表すと次のようになる.

以下にレスポンシブなSVGを構成する上で役に立つ機能を挙げてみよう.

とは言え多くは固定的なレイアウトにならざるを得ず,HTMLのCSSが提供する強力なレイアウト機構(段組や文字列処理)には到底敵わない.そもそもSVG単体で実現するメリットが無いため止めたほうが良い.

例を示す.結局の処,3つのファイルを一つにまとめたものとそう変わらない.

<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
    <style><![CDATA[
/*全体を非表示としておく*/
svg:not(:root){
    display:none;
}
/*小さいデバイスの為のスタイル*/
@media (max-width: 125px) , (max-height: 175px){
    :root{
        width:100px;
        height:150px;
        display:block;
        margin:0 auto;
        background-color:yellow;
    }
    svg#miniDevice{display:inline;}
}
/*縦長のデバイス向け*/
@media (orientation: portrait) and (min-width: 125px) and (min-height: 175px){
    :root{background-color:red;}
    svg#portrate{display:inline;}

}
/*横長のデバイス向け*/
@media (orientation: landscape) and (min-width: 125px) and (min-height: 175px){
    :root{background-color:green;}
    svg#landscape{display:inline;}
}
    ]]></style>
    <!--部品を定義する-->
    <defs>
        <image id="img" xlink:href="img.png" width="200" height="200"/>
        <g font-size="18" font-weight="bold" fill="black" id="msg" text-anchor="middle">
            <g id="text">
                <text x="100" y="220">This is responsive svg!</text>
                <text x="100" y="240">Is it usefull?</text>
                <text x="100" y="260">I don't know, but...</text>
                <text x="100" y="280">it's too difficult!</text>
            </g>
            <use xlink:href="#text" fill="white" x="-1" y="-1"/>
        </g>
    </defs>
    <!--デバイスごとのレイアウト-->
    <svg id="miniDevice" width="100" height="150" viewBox="0 0 200 300">
        <use xlink:href="#img"/>
        <use xlink:href="#msg"/>
    </svg>
    <svg id="portrate" width="100%" height="100%" viewBox="0 0 200 300">
        <use xlink:href="#img"/>
        <use xlink:href="#msg"/>
    </svg>
    <svg id="landscape" width="100%" height="100%" viewBox="0 0 300 200">
        <use xlink:href="#img"/>
        <use xlink:href="#msg" transform="translate(0,200),rotate(-90)"/>
    </svg>
</svg>

target擬似クラスを用いたマルチページグラフィックの実現

target擬似クラスを用いると,現在urlで参照している要素(urlの#で表されるハッシュ値に該当するidを持つ要素)に対するスタイルを設定することができる.この仕組みを応用すると,単一のSVG文書が複数のページから構成されているような構造を摂ることができる.

  1. ページ毎にg要素を設定する.適宜class名を設定(例えばpage等)しておくと良いかもしれない.
  2. g要素毎にページ番号に相当するidを付与する.
  3. 上記のg要素にdisplay:noneを設定し隠してしまう.
  4. 更にtarget擬似クラスを使ってdisplay:inlineを設定し再表示する.
  5. SVG文書を参照する際に,ファイル名に続けて「#ページ名」を記述する.

なお,ブラウザによってobject要素,img要素,background-imageプロパティでの動作が異なる.確実に動作させたい場合はobject要素(及びiframe要素)に表示させると良い.

multiPage.svg#page1multiPage.svg#page2multiPage.svg#page3
object
img
background-image
<?xml version="1.0" standalone="no"?>
<svg width="200px" height="200px" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<style>
    g{display:none;}
    g:target{display:inline;}
</style>
    <g id="page1">
        <circle cx="100" cy="100" r="80" fill="red"/>
    </g>
    <g id="page2">
        <rect x="20" y="20" width="160" height="160" fill="blue"/>
    </g>
    <g id="page3">
        <polygon points="100,30 30,160 170,160" fill="green"/>
    </g>
</svg>

※これと同じような効果を得る方法としてview要素を用いたものが考えられる.詳しくはview要素の項を参照のこと.

テンプレート図形とuse要素とセレクタ

セレクタによる要素の抽出はdefs要素配下の図形に対しても有効で,図形のテンプレートに対するスタイルの指定を行うことも可能だ.しかしこの部分においてはブラウザごとにuse要素の実装が異なる点について注意しなければならない.

use要素はテンプレートとなる図形を複製する効果を持つことは前に示したが,この機能を実現するとなると実装の方針としては次の2つの方法が考えられる.

  1. テンプレートとなる要素からなるツリーのクローンを生成し,use要素の配下に(こっそり)挿入する.
  2. use要素の配下にテンプレートとなる要素を参照するプロキシ(代理)オブジェクトを生成し,描画処理時の都度参照する.

前者はFireFoxの実装方針であり,後者はChrome,Operaの実装方針である(これは筆者の推測である).ここでdefs要素で定義された図形要素にスタイルシートを適用したとしよう.するとFireFoxではuse要素以下に元のテンプレートとは異なる図形要素が(暗黙的に)生成されているので,use要素により実際に出力される図形にはスタイルが設定されていない.一方Chrome・Operaではuse要素を描画する際にテンプレートの内容を参照するため,描かれる図形にはスタイルが適用される.

※なおここで説明しているのはuse要素に対するスタイルの指定ではなく,use要素内部(に有るように見える)の図形要素に対するスタイル指定であり,より細やかなスタイルの指定が可能である点に注意しよう.

※この実装の違いはセレクタだけでなく,animate要素の動作についても影響を及ぼしている.

FireFoxではuse要素配下にテンプレートとなる図形要素が存在しているようにセレクタを記述することでスタイルを指定することが可能だ.(id指定も問題なく動作する)

このようにブラウザにより動作が異なるため,CSSの記述に気を付ける必要がある.もし可能であるなら,テンプレートを定義するdefs要素とuse要素に同じクラスを定義し,スタイルの指定を共通化する方法もある.

図案とレイアウトとを分離する

先ほどのマルチページSVGの構成とテンプレートに対するスタイルの指定とを適切に組み合わせることで冗長なコードを削減することが可能だ.例えばトランプにおいては,マークが異なるだけで同じレイアウトのカードが存在するが,これをマークとレイアウトとに分離することで,52枚全てをデザインせずに動的にカードを表示させることができる.下記の例は全て単一のSVG文書で構成されている.

詳しい説明は省くが,サンプルコードを見れば大体どのような事をしているか判るはずだ.

<?xml version="1.0" standalone="no"?>
<svg width="200px" height="300px" viewBox="0 0 200 300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<style>
/*スートの選択(chrome,opera)*/
.spade:target~defs #spade{
display:inline;
}
.hart:target~defs #hart{
display:inline;
}

/*スートの選択(firefox)*/
.spade:target~.card #spade{
display:inline;
}
.hart:target~.card #hart{
display:inline;
}

/*カードの選択*/
.ace:target~#ace{
display:inline;
}
.two:target~#two{
display:inline;
}
</style>
<!--カード選択用のダミー要素-->
<rect id="s1" class="spade ace"/>
<rect id="h1" class="hart ace"/>
<rect id="s2" class="spade two"/>
<rect id="h2" class="hart two"/>

<!--カードの表面-->
<rect x="5" y="5" width="190" height="290" rx="10" ry="10" stroke="black" fill="white"/>

<!--レイアウト-->
<g id="ace" class="card" display="none">
<use xlink:href="#sute" y="50" width="200" height="200"/>
</g>
<g id="two" class="card" display="none">
<use xlink:href="#sute" x="50" y="25" width="100" height="100"/>
<use xlink:href="#sute" x="50" y="150" width="100" height="100"/>
</g>

<!--シンボル-->
<defs>
<symbol id="sute" viewBox="0 0 100 100">
<path display="none" id="spade" d="m 35.848886,93.252387 c 2.417956,-6.2823 4.5221,-12.7304 …" fill="black"/>
<path display="none" id="hart" d="M 51.336451,91.104936 C 40.659695,82.468763 …" fill="red"/>
</symbol>

</defs>
</svg>

:target擬似クラスを用いたsvgの印刷時の注意点

WEBブラウザにおいてurlを指定する際にurl文字列の後ろにハッシュ(#[id])を指定することができるが,この値を印刷時に無視するブラウザ(FireFox等)が存在する.この値は一般的なHTML文書においては単なる見出しへのジャンプに相当するため,印刷時にその内容を無視しても文書全体が出力対象となるだけで問題無かった.

が,ハッシュ値の指定機能を自由に拡張可能とする:target擬似クラスを用いる場合,HTMLの内容をダイナミックに書き換えているケースもあり,スクリーンでの見た目と印刷時の見た目が全く異なるといった弊害が発生する.SVGにおいても同様であり,直接ブラウザで表示しようが,object要素から参照しようが全く同じ結果となる.従ってSVGの印刷を念頭に入れている場合は:target擬似クラスを用いるよりもjavascriptによる書き換えを検討したほうが良い.

XSLのSVGへの適用

XSL(eXtensible Stylesheet Language)はXMLで定義されたスタイル付け言語であり,一般にデータの羅列であるXML文書に対する見た目の整形を行う.従ってXMLをベースとしているSVGにおいてもこの機能を使えばCSSと同じような見た目の変更を行うことが出来る.以下スタンドアロンのSVG文書におけるXSLの利用方法について示す.

SVG書き換えによるスタイルの設定

例を示す.まずベースとなるSVGファイルを見てみよう.各図形要素にはfill色の指定が為されていないので,描画結果は黒く塗りつぶされるはずだ.

<?xml version="1.0" standalone="no"?>
<?xml-stylesheet href="mystyle.xsl" type="application/xml"?>
<svg xmlns="http://www.w3.org/2000/svg"
    width="200px" height="200px" viewBox="0 0 200 200" version="1.1">
    <rect class="a" x="20" y="20" width="100" height="100"/>
    <rect x="80" y="80" width="100" height="100"/>
    <g>
        <circle cx="120" cy="120" r="50"/>
    </g>
</svg>

ここでXMLスタイルシート宣言部で「mysvg.xsl」を指定すると,SVGの中身が書き換わり,結果として図形領域が塗りつぶされる.下のコードはmysvg.xslの中身である.CSSではセレクタにより要素を検索するが,XSLではXPathにより要素を検索する.xsl:attribute要素でsvgの属性値を書き換えている.この時,CSSでは書き換えることができない位置に関わる属性がXSLによって書き換わっている点に注意する.

<?xml version="1.0" standalone="no"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:svg="http://www.w3.org/2000/svg">
    <xsl:output
        method="xml"
        encoding="utf-8"
        doctype-public="-//W3C//DTD SVG 1.1//EN"
        doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"/>
    
    <xsl:template match="*">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="svg:rect[@class='a']">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:attribute name="fill">red</xsl:attribute>
            <xsl:attribute name="stroke">blue</xsl:attribute>
            <xsl:attribute name="stroke-width">3</xsl:attribute>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="svg:rect">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:attribute name="fill">green</xsl:attribute>
            <xsl:attribute name="stroke">aqua</xsl:attribute>
            <xsl:attribute name="stroke-width">3</xsl:attribute>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="svg:circle">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:attribute name="fill">purple</xsl:attribute>
            <xsl:attribute name="stroke">yellow</xsl:attribute>
            <xsl:attribute name="stroke-width">5</xsl:attribute>
            <xsl:attribute name="cx">60</xsl:attribute>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

但し正しく動作させるにはSVGをインラインフレームで表示させる(つまりiframe要素・object要素を用いる)必要がある.img要素からSVGを参照した場合はブラウザ毎に動作が異なる.FireFoxでは元々のSVGが表示され,Chromeではエラーとなり,Operaでは正常に動作する.

object要素 img要素

SVGへのデータの挿入

XSLは見た目の変更に留まらず,SVGの構成を変更することも出来る.下の例はSVGファイルのスタイルシートの中で名刺データXMLファイルを読み込み,それらをマージして名刺画像を生成するものである.

<?xml version="1.0" standalone="no"?>
<?xml-stylesheet href="namecard2.xsl" type="application/xml"?>
<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="100px" viewBox="0 0 200 100" version="1.1">
    <rect x="15" y="15" width="180" height="80" fill="red"/>
    <rect x="10" y="10" width="180" height="80" stroke="black" fill="yellow"/>
    <text x="15" y="35" font-size="20">
        <tspan id="first"></tspan>-<tspan id="last"></tspan>
    </text>
    <text id="tel" x="180" y="65" text-anchor="end" ></text>
    <text id="bio" x="180" y="80" text-anchor="end" font-size="15"></text>
</svg>
※データの参照先xml
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="namecard.xsl" type="application/xml"?>
<person>
    <name>
        <first>Shigoroku</first>
        <last>Hifumi</last>
    </name>
    <tel color="red">123-456-7890</tel>
    <bio>Svg is very exciting!</bio>
</person>
※適用したスタイルシート
<?xml version="1.0" standalone="no"?>
<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:svg="http://www.w3.org/2000/svg">
    <xsl:output
        method="xml"
        encoding="utf-8"
        doctype-public="-//W3C//DTD SVG 1.1//EN"
        doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"/>
    
    <xsl:template match="*">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[@id='first']">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:value-of select="document('data.xml')/person/name/first"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[@id='last']">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:value-of select="document('data.xml')/person/name/last"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[@id='tel']">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:attribute name="fill">
                <xsl:value-of select="document('data.xml')/person/tel/@color"/>
            </xsl:attribute>
            <xsl:value-of select="document('data.xml')/person/tel"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[@id='bio']">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:value-of select="document('data.xml')/person/bio"/>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
※処理結果のsvg
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="100px" viewBox="0 0 200 100" version="1.1">
    <rect x="15" y="15" width="180" height="80" fill="red"/>
    <rect x="10" y="10" width="180" height="80" stroke="black" fill="yellow"/>
    <text x="15" y="35" font-size="20">
        <tspan id="first">Shigoroku</tspan>-<tspan id="last">Hifumi</tspan>
    </text>
    <text id="tel" x="180" y="65" text-anchor="end" fill="red">123-456-7890</text>
    <text id="bio" x="180" y="80" text-anchor="end" font-size="15">Svg is very exciting!</text>
</svg>

XMLからSVG画像を生成する

本来XSLはXML文書の構造を変更するために定義されたものであり,SVGの見た目を変更するだけでなく,(XHTMLを含む)任意のXMLから別のファイル形式に内容を変換する事が出来る.下の例では先ほどのXMLファイルをXSLにより名刺画像に変換するものである.この場合も先ほどと同様にiframe要素やobject要素を用いて表示する必要がある.

<?xml version="1.0" standalone="no"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="utf-8"
        doctype-public="-//W3C//DTD SVG 1.1//EN"
        doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"/>
    <xsl:template match="/">

<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="100px" viewBox="0 0 200 100" version="1.1">
    <rect x="15" y="15" width="180" height="80" fill="red"/>
    <rect x="10" y="10" width="180" height="80" stroke="black" fill="yellow"/>
    <text x="15" y="35" font-size="20">
        <xsl:value-of select="person/name/first"/>-<xsl:value-of select="person/name/last"/>
    </text>

    <xsl:element name="text">
        <xsl:attribute name="x">180</xsl:attribute>
        <xsl:attribute name="y">65</xsl:attribute>
        <xsl:attribute name="text-anchor">end</xsl:attribute>
        <xsl:attribute name="fill"><xsl:value-of select="person/tel/@color"/></xsl:attribute>
        <xsl:value-of select="person/tel"/>
    </xsl:element>
    
    <text x="180" y="80" text-anchor="end" font-size="15">
        <xsl:value-of select="person/bio"/>
    </text>
</svg>

    </xsl:template>
</xsl:stylesheet>

このことからWEBサーバーの設定の都合上SVGを正しく扱えない環境においても,理屈上はXMLを経由してグラフィックを描ける可能性がある.XMLとXSLが動作する必要はあるが,SVGを一旦XMLとして保存しそれをXSLを使ってクライアントサイドでSVGに再構築するのだ.

また冗長になりがちなSVGの記述であるが,用いる要素や属性が予めわかっている場合はそれらに別名をつけたXMLを用意しておき,XSLを適用して実際のSVGに戻すと言った方法も考えられる.下の例ではpath情報が格納されているXMLをXSLを使ってSVGに変換しているものである.

<?xml version="1.0" standalone="no"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="utf-8"
        doctype-public="-//W3C//DTD SVG 1.1//EN"
        doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"/>
    <xsl:template match="s">
        <svg viewBox="0 0 200 200" width="200px" height="200px" xmlns="http://www.w3.org/2000/svg">
            <xsl:apply-templates/>
        </svg>
    </xsl:template>
    <xsl:template match="p">
        <path xmlns="http://www.w3.org/2000/svg">
            <xsl:attribute name="d"><xsl:value-of select="@d"/></xsl:attribute>
            <xsl:attribute name="fill"><xsl:value-of select="@f"/></xsl:attribute>
        </path>
    </xsl:template>
</xsl:stylesheet>
※元となるxml(metasvg.xml)
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="tosvg.xsl" type="application/xml"?>
<s>
    <p d="M10,50A40,40 0 1,1 90,50A40,40 0 1,1 10,50Z" f="red"/>
    <p d="M65,65h70v70h-70z" f="green"/>
    <p d="M140,90l40,80h-80z" f="blue"/>
</s>

パスの記述が若干短くなっているため,多量のpath要素が定義されていた場合に若干ではあるがファイルサイズの軽減を見込むことができる.(だが,ぎりぎりチューニングの範疇と言えるのでおすすめはしない.)

処理結果の取得

ブラウザではXSLでの変換結果をスクリーンで確認するだけでファイルに保存することはできないが,専用のツールを使えば可能だ.windows環境では「msxsl.exe」を,ubuntuを始めとしたlinux及びmac環境では「xsltproc」を用いると良いだろう.

コマンド実行例
windows環境:msxsl
msxsl namecard2.svg namecard2.xsl -o result.svg
linux/mac環境:xsltproc
$ xsltproc namecard2.xsl namecard2.svg > result.svg

このように,特に難しいところもなく比較的簡単に差し込み処理が行えた.XSLはJavaScriptと異なり関数言語的な特性を持っているため,本格的に習得するのは意外に面倒ではあるが,画像にテキストを挿入すると言った用途であれば大方決まったパターンで実装できる(様な気がする).何よりスクリプトを書かずともXMLプロセッサのみで動作するのが強みである.その一方でXSLの記述量は,CSSやJavaScriptで実装する場合と比較するとかなり膨らんでしまう.記述ミスの危険性が大きく使い勝手は今ひとつである.

DTDを用いたスタイルの共通化

スタンドアロンのSVGやXHTMLはXMLであることからDTDを使ってエンティティを定義することが出来る.つまり,文書内部で共通的に用いられる色などの設定を一箇所にまとめることが出来る.

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg[
    <!ENTITY color "red">
]>
<svg width="200px" height="200px" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
    <rect x="5" y="5" width="90" height="90" fill="&color;"/>
    <circle cx="100" cy="100" r="60" fill="&color;"/>
    <rect x="105" y="105" width="90" height="90" fill="&color;"/>
    <path d="M10,190L190,10" stroke="&color;" stroke-width="10" stroke-linecap="round"/>
</svg>

なお将来的にはSVG2で導入される予定のsolidColor要素を使うことで色の共通化を図ることができる.

DTDによる初期値の設定

DTDの属性リスト宣言を用いることで特定要素の属性の規定値を定義することが出来る.大量の要素を挿入する場合に有効な方法である.

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg[
    <!ATTLIST rect  width CDATA "80"
                    height CDATA "80"
                    rx CDATA "20"
                    ry CDATA "20"
                    transform CDATA "translate(10,10)">
]>
<svg width="200px" height="200px" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
    <style>
        rect{
            stroke:white;
            stroke-width:20;
            stroke-opacity:0.6;
        }
    </style>
    <rect fill="red"/>
    <rect fill="blue" x="100"/>
    <rect fill="green" y="100"/>
    <rect fill="orange" x="100" y="100"/>
</svg>