参考のために Chrome での見え方を次に示す。
これは次のように書かれている。
#encoding: utf-8 import svg c = svg.Canvas(0,0,400,400,map=(-1.5,1.5,1.5,-1.5)) d = c.define() shadow = """ <!--まず図形の影をSourceAlphaで取得し,ぼかす.結果をblurとして保持する.--> <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/> <!--先ほどのぼかした結果をずらして,offsetBlurとして保持する.--> <feOffset in="blur" dx="4" dy="4" result="offsetBlur"/> <!--offsetBlurの上に元のグラフィックを重ねる.→結果ドロップシャドウ効果となる.--> <feMerge> <feMergeNode in="offsetBlur"/> <feMergeNode in="SourceGraphic"/> </feMerge>""" # http://www.h2.dion.ne.jp/~defghi/svgMemo/svgMemo_11.htm d.filter(-1,-1,1,1,id="F1",filter=shadow) d.end() c.rect(-1,-1,1,1) e = c.group(filter="url(#F1)",font="size:128") e.text(0,0,text="SVG",anchor="C",fill="red") c.close() # Safari: NG # Firefox: OK # Opera: OK
このフィルターは
http://www.h2.dion.ne.jp/~defghi/svgMemo/svgMemo_11.htm
Shadow フィルターはよく使われそうなので辞書に登録しておいた。次のように使う。
#encoding: utf-8 import svg c = svg.Canvas(0,0,400,400,map=(-1.5,1.5,1.5,-1.5)) d = c.define() p = d.filter(-1,-1,1,1,filter="shadow") c.rect(-1,-1,1,1) e = c.group(filter=p,font="size:128") e.text(0,0,text="SVG",anchor="C",fill="red") c.close()
"shadow" の中には "<
" が含まれていないので、辞書を見に行く。
例1のファイルターを、3ステップに分けて、各々の効果を示す。
この SVG ファイルを生成する Python のコードは
#encoding: utf-8 import svg shadow1 = """ <!--まず図形の影をSourceAlphaで取得し,ぼかす.結果をblurとして保持する.--> <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/> """ shadow2 = """ <!--まず図形の影をSourceAlphaで取得し,ぼかす.結果をblurとして保持する.--> <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/> <!--先ほどのぼかした結果をずらして,offsetBlurとして保持する.--> <feOffset in="blur" dx="4" dy="4" result="offsetBlur"/> """ shadow3 = """ <!--まず図形の影をSourceAlphaで取得し,ぼかす.結果をblurとして保持する.--> <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/> <!--先ほどのぼかした結果をずらして,offsetBlurとして保持する.--> <feOffset in="blur" dx="4" dy="4" result="offsetBlur"/> <!--offsetBlurの上に元のグラフィックを重ねる.→結果ドロップシャドウ効果となる.--> <feMerge> <feMergeNode in="offsetBlur"/> <feMergeNode in="SourceGraphic"/> </feMerge>""" c = svg.Canvas(0,0,400,400,map=(-1.5,1.5,1.5,-1.5)) d = c.define() p1 = d.filter(-1.2,1.2,-0.2,0.2,filter=shadow1) p2 = d.filter(1.2,1.2,0.2,0.2,filter=shadow2) p3 = d.filter(-1.2,-1.2,-0.2,-0.2,filter=shadow3) d.end() e = c.group(filter=p1,font="size:128") e.rect(-1.2,1.2,-0.2,0.2,stroke="white;width:4",fill="none") e.end() e = c.group(filter=p2,font="size:128") e.rect(1.2,1.2,0.2,0.2,stroke="white;width:4",fill="none") e.end() e = c.group(filter=p3,font="size:128") e.rect(-1.2,-1.2,-0.2,-0.2,stroke="white;width:4",fill="none") e.end() c.close()
フィルターは Firefox が一番しっかりしているようだ。次に Firefox での見え方を示す。
次の図はフィルターをかけた場合の効果を示す例である。
現状では、ブラウザ依存性が強く、この図の場合には
の順で、効果が現れている。(IE9 は試していない)
Chrome は Firefox レベルの効果を出しているのだが、ひどいバグがあり、「SVG」の文字と中央の円が見たり見えなかったりする(安定しない)。これではフィルターに関しては使い物にならない!
参考のために Firefox での見え方を次に示す。
Firefox による見え方
この SVG ファイルを生成する Python のコードは
#encoding: utf-8 import svg c = svg.Canvas(0,0,400,400,map=(-1.5,1.5,1.5,-1.5)) d = c.define() blur = """ <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/> <feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>""" lighting = """ <feSpecularLighting in="blur" surfaceScale="5" specularConstant=".75" specularExponent="20" lighting-color="#bbbbbb" result="specOut"> <fePointLight x="-5000" y="-10000" z="20000"/> </feSpecularLighting>""" composite = """ <feComposite in="specOut" in2="SourceAlpha" operator="in" result="specOut"/> <feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="litPaint"/>""" merge = """ <feMerge> <feMergeNode in="offsetBlur"/> <feMergeNode in="litPaint"/> </feMerge>""" d.filter(-1,-1,1,1,id="F1",filter=blur+lighting+composite+merge) d.end() c.rect(-1,-1,1,1) e = c.group(filter="url(#F1)",font="size:45") e.text(0,0,text="SVG",anchor="C") e.rect(-0.5,-1.2,0.5,1.2, stroke="red;width:10", fill="none") e.circle(0,0,r=0.5,stroke="red;width:10", fill="none") c.close()
このプログラムでは
d.filter(-1,-1,1,1,id="F1",filter=f1)
-1,-1,1,1
)と、フィルター(f1
)を指定している。
フィルター f1
は、SVG の形式そのものであり、ここに使われたものは
http://www.w3.org/TR/SVG/filters.html#FilterElement
この例のフィルターは4種から構成されている。それらを文字列 blur
,lighting
,composite
,merge
としている。
プログラムの内容を読むと、各々のフィルターへの input と output が、in
と result
で示されている。それらがお互いに絡み合っているので、独立したフィルターの定義(文字列)を単に足し合わせれば良いものではない。
このフィルターの効果を見るに、結構苦労して作られたのでないかと想像する。フィルルターはプログラム並みにソフトウェア資産と考えるべきであろう。
取りあえず、フィルターの効果が見えるようにしたが、フィルターも、ライブラリ化できるようにすべきではないかと思っている。その場合、多少の仕様の変更が発生するかも知れない。
だいたい僕は
filter="url(#F1)"
今回、この問題を扱っていて2つの事に気づいた。
Python/Tk の anchor
で "C
" のように、簡潔に文字列を書く位置を決められるのは非常に楽である。他方では SVG の baseline
は、ブラウザ依存性が強く、僕も使い方が分からない部分が多すぎる。こうした問題に関してネット上にも問題提起がある。
http://nelsonslog.wordpress.com/2011/09/12/svgtext-baseline-considered-harmful/
僕は、これまでは、text
メソッドで直接フォントサイズを指定して、それを基に anchor="C" のように指定した時のテキストの位置を決められるようにしていたが、このやり方はグループに対してフォントサイズが指定される場合には使えない。
そこで今回は方針を転換して、Nelson さんのアドバイスに従う事とした。
このプログラムでは
e = c.group(filter="url(#F1)",font="size:45")
僕は
<text font-size="45" font-family="Times">SVG</text>
<text style="font-size:45;font-family:Times">SVG</text>
前者は実験した4つのブラウザでは機能していたが、後者は Firefox と Opera では font-size
の指定が働いていない! この例では text
タグを挙げたが、g
タグの場合も同様である。
こんな基本的な所で、どうして?!
ところで、最終的な SVG を生成する時に、style の形式が良いのか、それとも attribute の形式が良いのか、迷う所であるが、このようなブラウザ依存の問題があれば style の形式を使う訳にはいかないかと思う。僕は、これまでは style に統一していたが、修正する事にした。