タグ「No penguin no peace」が付けられているもの

JavaアプレットでSVGを再生する(2)

  • 投稿日:
  • by
  • カテゴリ:

以前にBatikを使ってJavaでSVGを描画したり書き換えたりしてみたが、特にWebブラウザ上で動かすJavaアプレットを作成する目的ではあまり満足のいく結果が得られなかったので、もう1つ気になっていた、軽量が売りのSVG Salamanderも使ってみることにした。

SVG書き換え再生テストの起動ページ

ソースコード
svgSalamanderTest1.java アプレット本体
SVGIconOnJPanel.java アイコン描画用にダブルバッファリングを有効にしたJPanel
BouncingIcon.java
SVGファイル
txtanitest1s.svg 文字列書き換えテスト用SVG
p_icon.svg アイコンのSVG

前回と同様に、SVGの文字列を動的に書き換えて再生する。このSVGの表示には、JPanelを継承していてJAppletに組み込みやすいSVGDisplayPanelクラスを使っている。
それとは別に、SVGIconクラスはjava.awt.Graphicsに描画できるので、Swingのコンポーネント上も含めて、アイコンを動かしながら表示してみた。
ちなみに、Popボタンを短時間に連打するとアイコンが初期サイズまで戻らなくなるのは、仕様である。


以下、今回何か作ってみてわかった、SVG Salamanderを使う上での注意事項をまとめる。

まず、再生可能なSVGはかなり限られると思っておいた方が良い。SafariやBatikのSquiggleでは思い通りに再生できるのにSVG Salamanderでは思い通りに再生されないということは頻繁に起こった。それがSVG的に正しくない(SafariやBatikは気を利かせて補間している)からなのかSVG Salamanderの対応が不十分なのかはあまり追究していないが、何せ、SVG Salamanderを使うなら、svgSalamander.jarを実行すると起動するSVGプレーヤーで再生確認しながら、SVGファイルを編集することになる。

SVG Salamanderの描画がSafariやBatikと違ってしまう要因の1つは、ECMAScriptが使えないことである。<script>タグが含まれるSVGファイルはSVG Salamanderでは正しく再生されないのである。動的なSVGコンテンツにはよくECMAScriptが使われるので、どこかから拾ったSVGはこれが原因で再生されないことが少なくない。

他に、SVG SalamanderのSVGの扱いに関して筆者が気づいたこととして、パスデータ(<path>タグのd属性などで与えられるもの)に、"C"/"c"(curveto)や"Q"/"q"(quadratic Bézier curveto)が先行せずに"S"/"s"(smooth curveto)や"T"/"t"(smooth quadratic Bézier curveto)があると、まず期待した動作にならない。SafariやBatikでは1つ目のcontrol pointを始点(またはその閉パスの最後のcontrol point?)とすることで突拍子も無い表示になることを避けているようだが、SVG Salamanderは2つ目のcontrol point或いは終点の、始点からの反対方向の鏡像点を1つ目のcontrol pointとしている節がある。


次に、Javaライブラリに関して気付いたことをまとめる。

SVGのテキスト要素の文字列の先頭や終端に余分な空白があると、描画時とText#getBoundingBox()実行時とで空白の扱いが異なってしまうようで、内部の計算位置と表示位置にずれが発生してしまう。これはSVGファイルの<text>〜</text>で括られたテキストの先頭や末尾の改行や空白についても同じことが起こるので、Javaコード内だけでなくSVGファイル内でも空白文字を消しておく必要がある。

SVGにフォントサイズのアニメーションがあると、SVGUniverse#updateTime()では表示に反映されないようだ。内部的に値は更新されるようで、Text#rebuild()すれば文字サイズに反映されるが、透明度のアニメーションと組み合わせると、ちらつく上、Text#rebuild()時に変化中の透明度が無視されて不自然な表示になった。

SVGElementクラスにhasAttribute(), setAttribute()があるのにgetAttribute()が無いのが謎である。現在の値からいくらか変化させるということができないことを意味する。
また、現実的な問題として、printfデバッグのために、その時点での属性の値を取得したいということはよくあったが、その手段が無かったのが結構辛かった。

それから、色々なクラスのset系のメソッドの引数にある、attribTypeの意味がわからない。JavadocのAPI referenceには与え得る値も何も書かれていない。今回は"Using SVG Salamander"のページのサンプルコードに書かれているAnimationElement.AT_XMLを一律に指定しているが、これで正しいのかどうかはわからない。

BatikのJSVGCanvasはSVGの表示位置が自動的に中央寄せになるが、SVG SalamanderのSVGDisplayPanelでは左寄せになってしまう。しかも、中央寄せにするメソッドが無い。SVGファイルの<svg>タグのviewBox属性を書き換えれば位置調整ができるが、メモリにロードしたviewBox属性を更新する手段がない。SVGDiagram#getRoot()で取れるSVGRootのprivate属性にはあることがわかったが、getAttribute()メソッドが無いので、値を取得することすらできない。
今回は、SVGDiagramのgetViewRect()とgetDeviceViewport()で得られた座標を元にSVGのviewBox属性を直接書き換えるという強引な方法で、何とか中央寄せができた。(svgSalamanderTest1.javaのcenterViewBox()参照)


最後に、Webサーバーに配置する時の注意点として、svgSalamander.jarはSVGプレーヤーが内蔵されていたり署名されていたりするので、そのSVGプレーヤーも署名する必要も無ければ、ライブラリとしての実行用にはsvgSalamander-tiny.jarを使うと良い。