Javascript createElement: bug in batik?

Batikを使ってscript付きのSVGを読み込むことを考えます。そのとき、JavaScriptcreateElementを使って要素を生成してもうまくいきません。要素の生成にはcreateElementNS()を使う必要があります。参考資料1を参照。

var e = document.createElementNS(namespace,attrName);

font要素の生成?

Batikを使ってscript付きのSVGを読み込むことを考えます。JavaScriptcreateElementNSを使ってfont要素を生成します。同様にfont-face要素、glyph要素も生成します。すべての要素に属性を付します。font要素内にfont-face要素、glyph要素を格納します。documentにfont要素を加えます。既存のtext要素のfont-family属性を新たに生成したfont-face要素内のfont-family名に変更します。文字を更新します。glyphに指定した字体に変わるはずですが変わりません。ASV3では字体は変わります。

hogehoge.js
// document.getElementById
function $(tagId){
	return document.getElementById(tagId);
}

function myFunc(){

   	var svgNS = "http://www.w3.org/2000/svg";
	var font =document.createElementNS(svgNS,"font");

	font.setAttributeNS(null,"id","bunafont02");
	font.setAttributeNS(null,"horiz-adv-x","2048");

	var fontface = document.createElementNS(svgNS,"font-face");

	fontface.setAttributeNS(null,"font-family","bunafont2");
	fontface.setAttributeNS(null,"font-weight","500");
	fontface.setAttributeNS(null,"font-stretch","normal");
	fontface.setAttributeNS(null,"units-per-em","2048");
	fontface.setAttributeNS(null,"panose-1","2 0 6 9 0 0 0 0 0 0");
	fontface.setAttributeNS(null,"ascent","1638");
	fontface.setAttributeNS(null,"descent","-410");
	fontface.setAttributeNS(null,"x-height","-1e+10");
	fontface.setAttributeNS(null,"cap-height","1560");
	fontface.setAttributeNS(null,"bbox","25 -389.298 1408 1608.05");
	fontface.setAttributeNS(null,"underline-thickness","102");
	fontface.setAttributeNS(null,"underline-position","-204");
	fontface.setAttributeNS(null,"unicode-range","U+000C-U+004D");

	var glyph = document.createElementNS(svgNS,"glyph");

	glyph.setAttributeNS(null,"glyph-name","G");
	glyph.setAttributeNS(null,"unicode","G");
	glyph.setAttributeNS(null,"horiz-adv-x","1098");
   	glyph.setAttributeNS(null,"d","---path---");

	$("defs1").appendChild(font);
   	$("bunafont02").appendChild(fontface);
	$("bunafont02").appendChild(glyph);

	$('t1').setAttributeNS(null,"style","font-family:bunafont2; font-size:18;fill:black");
	$('t1').firstChild.data ="G";
}
test.svg
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<script type="text/ecmascript" xlink:href="hogehoge.js" />
<defs id="defs1">
</defs>
<g id="layer1" onclick="myFunc();">
 <rect x="50" y="50" width="50" height="20" style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.09839252;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"/>
 <text x="55" y="65" id="svgfontid01">ボタン</text>
</g>
<text x="0" y="20" id="t1">A</text>
</svg>

SVGでテキストを扱う

text要素

SVG1.1でテキストを表示したい時、一般的に使用されるのはtext要素である。text要素は文字をグラフィックと同じように描画する。そのため文字のグリフ(形)をSVG側で微調整することが可能である。要素の詳細については参考資料1を参照のこと。

テキストの入力

上述のtext要素は静的なテキストを表示することを目的としている。SVG1.1にはHTMLのtextarea要素のように入力可能な要素は用意されていない。ただし、javascriptを使って中のテキストを書き換えることは可能である。参考資料2のEtch-A-Sketchというサンプルに使われている小さなテキストボックスは大変よく出来ている。(ASV3でしか動かない点を除いて)参考資料3はjavascriptでテキストの座標を知る際に役に立つインターフェースである。参考資料4のCharacterDataインターフェースはDOMであり、テキストの操作に積極的に活用したい。

SVG1.2

SVG1.2ではtext要素は入力できるように拡張された。(参考資料5)editable属性をtrueにすることで可能となる。キャレットの表示やコピー、ペーストが行える。スクロールバーが表示されるのかは不明。

SVG+XForms

参考資料6,7の一連のドキュメントはとても興味深い。SVGの中でHTMLのform要素郡を使えるとすれば、JavaScriptSVGWidgetを一から作るといったことからプログラマは開放される。ただ、SVGの中でテキストエリアを使ってテキストにSVGFontのグリフが宛がわれる保証はない。

SVG 1.2

仕様書

Scalable Vector Graphics (SVG) Tiny 1.2 Specification
http://www.w3.org/TR/SVGMobile12/

Scalable Vector Graphics (SVG) Full 1.2 Specification
http://www.w3.org/TR/2005/WD-SVG12-20050413/

JSON

postURL関数を使ってJSONを送る

postURL*1を使ってデータを送信する場合、データに名前を付けて送信することが出来ない。そこでデータをJSONの文字列として送り、サーバー側で配列に直すことを考える。JSONについては参考資料1に詳しい。

JSONを文字列に直す

JavaScriptJSONを文字列に直すには参考資料1のサイトで配布されているjson.jsを使用するのが簡単でいい。以下の様にしてJSONを文字列に直すことが出来る。ただし日本語は使用すると文字化けが発生する。

var requestData = [
	{
		"fontName":"msp_gothic",
		"unicode":["87","101","98","12392","25991","23383"],
	},
	{
		"fontName":"ms_gothic",
		"unicode":["87","101","98","12392","25991","23383"],
	}
]

var myJSONText = requestData.toJSONString();
/*(実際には改行はない)
[{"fontName":"msp_gothic","unicode":["87","101","98","12392","25991","23383"]},
{"fontName":"ms_gothic","unicode":["87","101","98","12392","25991","23383"]}]
*/

JSONを配列に直す

JSON文字列をJSONに戻すライブラリは多数の言語で提供されている。*2ここではPHPJsphonというライブラリを使ってJSON文字列をJSONに戻してみる。Jsphonは参考資料2のサイトで公開されている。

<?
require_once('Jsphon.php');

/* ------- エンコード ------- */
$json = $HTTP_RAW_POST_DATA;
try {
    $json = Jsphon::decode($json);
} catch (Jsphon_Exception $e) {
    die($e->getMessage());
}

echo $json[0]['unicode'][0];
//87
?>

*1:postURLについては2007-09-09の記事を参照のこと

*2:参考資料1の一番下を見ること

postURLについての追記

2007-09-09の記事でjavascriptの通信用関数が2つあること-getURLに対してpostURLがあること-を書いた。そのpostURLを使う上で注意すべきことを書く。

送信するデータについて

postURL関数の第2引数に入れられたデータはそのままサーバーにpostされる。すなわちdata1=xxx&data2=yyyといった文字列はdata1=xxx&data2=yyyのまま受け取られる。

phpでデータを受信する

phpでpostされたデータを取得する際、通常は$_POST['elementname']といった方法を使う。しかしpostURLから送信されたデータは$HTTP_RAW_POST_DATAに格納されている。

参考資料

  1. Dynamically updating SVG based on serverside information.

Batik+JWSでサーバとの通信を行う

2007-09-04の記事でBatik+JWSを使うとサーバーとの通信にエラーが出ることを書いた。今回はそのエラーを回避する方法を書く。

JWSのセキュリティ

JWSから起動されるJavaアプリケーションはデフォルトではローカルファイルにアクセスできない。また他のコンピューターへのネットワーク接続も出来ない。前回サーバーとの通信が出来なかった原因もここにあると思われる。(参考資料3を参照)

回避

ローカル・マシンに対する完全アクセスを確保するためにJWSでは、2つのことをする必要がある。

  1. セキュリティ証明書ですべてのJARファイルに署名する
  2. JNLPファイルに"all-permissions" を指定する

1に関していうとBatikのJarファイルはすべて電子署名されていない。2に関しては前回作成したJNLPファイルにall-permissions要素は存在しなかった。

電子署名

jarファイルに電子署名を施す場合、Java2SDKツールのjarsignerとkeytoolを使用する。まずkeytoolを使って証明書を作成する。DOSプロンプトに以下のように打ち込み、設問に答えていくだけでよい。自分が入力したパスワードやエイリアスを忘れないこと。

keytool -genkey -keystore myKeyStore -alias myAlias
  • myKeyStore:そのままファイル名になる

証明書のファイルが出来たらjarsignerを使用してjarファイルに電子署名を施す。DOSプロンプトに以下のように打ち込む。

jarsigner -keystore myKeyStore -storepass pass -keypass pass -signedjar jar1.jar jar2.jar myAlias
  • myKeyStore:jarsignerで作成したmyKeyStoreと同じものを指定
  • pass:jarsignerで答えたパスワードと同じものを指定
  • jar1.jar:署名後のファイル名
  • jar2.jar:署名するファイル名

Batikで使用するjarファイルは全部で25個である。一つ一つ処理していくのは幾分面倒くさい。そこで下記のようなバッチファイルを作った。

for %%I in ( *.jar ) do jarsigner -keystore myKeyStore -storepass pass -keypass pass -signedjar signedjar\%%I %%I  myAlias

このバッチファイルはカレントディレクトリからjarファイルを見つけ出し、電子署名を施し、signedjarフォルダに同名ファイルとして保存する。署名入りのjarファイルが出来上がったら、元のjarファイルと入れ替える。

JNLPファイルの作成

JNLPファイルは前回のものを流用する。それに今回はsecurity要素とその子要素all-permissions要素を加える。

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="http://www3.pf-x.net/~project-x/web_mozi/batik/batik1_7" href="batik.jnlp">
  <information>
    <title>batik</title>
    <vendor>Apache XML Project</vendor>
    <homepage href="http://xmlgraphics.apache.org/batik/" />
    <description>Batik for java web start</description>
    <description kind="short">batik</description>
    <offline-allowed />
  </information>
  <update check="timeout" policy="always" />
  <security>
   <all-permissions />
  </security>
  <resources>
    <java version="1.4+" />
    <jar href="batik-squiggle.jar"/>
    <jar href="lib/batik-ext.jar"/>
    <jar href="lib/batik-dom.jar"/>
    <jar href="lib/batik-css.jar"/>
    <jar href="lib/batik-svg-dom.jar"/>
    <jar href="lib/batik-gvt.jar"/>
    <jar href="lib/batik-parser.jar"/>
    <jar href="lib/batik-script.jar"/>
    <jar href="lib/batik-bridge.jar"/>
    <jar href="lib/batik-swing.jar"/>
    <jar href="lib/batik-anim.jar"/>
    <jar href="lib/pdf-transcoder.jar"/>
    <jar href="lib/batik-transcoder.jar"/>
    <jar href="lib/batik-gui-util.jar"/>
    <jar href="lib/batik-awt-util.jar"/>
    <jar href="lib/batik-codec.jar"/>
    <jar href="lib/batik-util.jar"/>
    <jar href="lib/batik-xml.jar"/>
    <jar href="lib/xerces_2_5_0.jar"/>
    <jar href="lib/xalan-2.6.0.jar"/>
    <jar href="lib/xml-apis.jar"/>
    <jar href="lib/xml-apis-ext.jar"/>
    <jar href="lib/js.jar"/>
  </resources>
  <application-desc  main-class="org.apache.batik.apps.svgbrowser.Main" />
  </application-desc> 
</jnlp>

サンプル

http://www3.pf-x.net/~project-x/web_mozi/batik/batik1_7/batik.jnlp
自前の証明書であるため警告が出る。問題はない。発行者の名前は特に意味はない。ローカルに置いてあるBatikと同じように使えるはずである。postURL、getURLも問題なく使用できる。

Batik+JWS その2

Batik 1.7beta

Batikのバージョンが1.6から1.7に上がっていた。1.6の時に使用できなかったpostURLが使用可能になっている。これでデータ長を気にしないでサーバーと通信が行えるようになった。全体のデザインもよくなっている。前よりXPライクになった感じだ。ただ、外部スクリプトファイルの文字コードはやはりSJISでなければ読み込めなかった。*1

*1:2007-09-04の記事参照