2011年2月27日日曜日

Rhinoでネームスペース付きのxmlファイルを読み込む

どうでもよかった女.svg」の省サイズ化をしたとき、「スクリプトを作っておいた方が楽かも」とか書いてました。 簡単に言うと、

  1. inkscapeでsvg画像を描いてウェブサイトに投稿したい。
  2. inkscapeは色々不要なデータも吐き出してくれる。
  3. ウェブサイトにsvg画像を上げるときはサイズが小さい方がいい。
  4. svg画像はxmlドキュメントなのでxmlを読んで不要なデータを消すスクリプトが組みたい。

ってことです。 今svg画像を作っていないのですぐに必要はないのですが、とりあえずなにでスクリプトを組むか考え中。 javascriptについて調べてみたらRhinoに行き当たりました。

簡単なサンプルコードを書いてみました。 Rhinoのバージョンは1.7R2です。 「inkscapeで線対称のパス」の投稿で描いたsvgをファイル名RubinsVase.svg、文字コードutf-8で保存して、javascriptで読み込んでいます。

// test.js
print('◆arguments[0] = ' + arguments[0] );
var input_file = arguments[0];
var svg_text = readFile(input_file, 'utf-8');

// XML宣言があるとパースエラーになるので消す。
// 文字列の先頭がルート要素の「<」でないと読み込んで
// くれないので、XML宣言に続く空白も消す。
svg_text = svg_text.replace(/^<\?xml.*\?>\s*/, "");

// ルート要素に素のxmlnsがあると読み込んでくれない
// PHPのregisterXPathNamespaceに当たる関数は...?
svg_text = svg_text.replace('xmlns="http://www.w3.org/2000/svg"', '');
//svg_text = svg_text.replace('xmlns:svg="http://www.w3.org/2000/svg"', '');

var svg = new XML(svg_text);

// ルート要素の属性を列挙
var attrs = svg.attributes();
for(var i in attrs)
{
    print(attrs[i].name() + ' = ' +  attrs[i] );
}

やってることはルート要素の属性を列挙するだけなんですが、スンナリいきませんでした。 コードのコメントにあるとおり、XML宣言があるとパースエラーになります。 文字列からXMLオブジェクトを作るときはXML宣言を消して、さらに文字列の先頭にルート要素の左カッコ(<)が来るように空白を消さなくてはなりません。 これに気づくのに時間がかかりました。 とりあえずこちらのサイトの投稿で「XML宣言はダメ」ってことは分かったんですが、それだけじゃダメだったんですよね。

で、さらにそのままXMLオブジェクトを作っても個別の要素にアクセスできませんでした。 XML全体のprintはできても個別の要素は引っかからない状態。 「ナニゴト?」と悩んで色々やってみたら名前空間が問題だったようです。 XMLドキュメントにデフォルト名前空間の記述があるとそうなるみたい。 厳密な処理がしたいわけではないので、今回はデフォルト名前空間の記述を文字列置換で消してから処理してみました。

出力はこうなりました。

D:\~\rhino1_7R2>java -jar js.jar -w -strict -encoding utf-8 test.js RubinsVase.svg
◆arguments[0] = RubinsVase.svg
height = 486
id = svg2
version = 1.1
width = 492
http://www.w3.org/2000/xmlns/::svg = http://www.w3.org/2000/svg

どうやら接頭辞付きの名前空間に関する属性名も変なことになっているようです。 今やろうとしていることは、inkscapeがプレーンSVGとして吐き出したファイルの省サイズ化なので、厳密に名前空間の処理をする必要はありません。 趣味でやってるだけですしね。 というわけで、

  1. svg画像(xmlドキュメント)を文字列として読み込む。
  2. 文字列のときに名前空間の部分だけ空白に置換。
  3. 文字列からXMLオブジェクトを作成。
  4. XMLオブジェクトで色々編集。
  5. XMLオブジェクトを走査して文字列で出力用XMLを組み立てる。そのとき名前空間も書き込む。
  6. 出力。

っていうのがいいかな? 「正式な手続きじゃない」と全力で主張するようなコードになりますね。 名前空間について厳密じゃなくていいなら、svg以外でもこんな感じで。

出力は、RhinoだとJavaのクラスにアクセスできるようなのでそれを使うか、標準出力に出してリダイレクトかですね。 もし次のsvg画像を作ったら、そんな感じで省サイズ化スクリプトを書いてみるかな? (今やる気は、なくなりました...)

それにしても、日本語のRhinoの情報少ないですね。 「コードが煩雑すぎるjavaよりはjavascriptの方がいいか?」と思ってRhinoに手を出したんですが、もしかしたら調べ物に手間取らない分javaの方が楽かも...