2013年8月30日金曜日

wpf : 文字列の形からsvgのパスを作成

試しに文字列からsvgのパスを作成するコードを作ってみました。 やり方は簡単で、

  1. System.Windows.Media.FormattedTextに文字列を渡す。
  2. FormattedText.BuildGeometryで文字の形を現すジオメトリを作る。
  3. ジオメトリを再帰で探索して、PathGeometryならToStringする。それを連結。
  4. svgのpath要素のd属性にできた文字列を格納する。

コレだけです。

PathGeometry.ToStringはジオメトリグループの階層をsvgのpathとよく似た構文で表現してくれるようです。

これは少なくとも今回試した範囲では、先頭にF1などのfillRuleが入っている以外はsvgのpathと同じのようですね。 なのでPathGeometry.ToString().Substring(2)でF1を削ればそのまま使えそうな気配です(詳細未確認)。 そのままだとノードの座標を表す数字の小数点以下の桁数が多すぎるので、これも正規表現で削ります。 単純に切り捨てです。

そんな事をするコードはこんな感じ。

svgのpathにはidとclassを埋め込めるようにしています。 pathをdefsに書いておけばuse要素で簡単にずらせます。 classの埋め込みは文字の修飾をまとめて扱えるようにするためです。

pathのノードの座標は入力文字列を(0,0)に描画したときの座標です。 pathのサイズはTextWidth、TextHeightのプロパティで取得できます。 これは標準的な余白を含んだサイズです。 普通はコチラを使うといいでしょう。 それに対してGeometryOffsetX、GeometryOffsetY、GeometryWidth、GeometryHeightのプロパティはノードの上下左右の最も外側の座標を元に計算されています。 余白の一切無いデータを使いたい場合はコチラを使うことになりそうですね。 どちらもpathのストロークについて考慮していないので注意。 例えば太字にするときなどは一工夫いりそうです。

PathGeometry2Pathメソッド以下、コードの大部分がコメントアウトされていますが、これは自分でSegmentを読むときのコードです。 長い割りにたいしたことはやってません。 正規表現で有効桁数以下を切り捨てるより速いかと思ったんですが、気のせいかも? まぁ性能問題にぶち当たるようなネタは無いので放置で。

動作チェック用の適当なMainWindow.xaml

適当なMainWindow.xaml.cs

textBoxOutputへの出力をsvgという拡張子のテキストファイルに保存すれば、結果が見れます。 例えば「動作テスト!」という文字列をpathにするとこうなります。

なんでこんなのを作ったかというと、ブログのネタとして簡単な図表(出力はsvg)を書くスクリプトを思いついたからなんですよね。 svgのtextはブラウザによって全然見た目が違います。 あるブラウザでちょうどいいtextが別のブラウザではみ出しているとか、よくあること。 なので色んな環境で見れる物を作るならtextではなく、textをpathに変換したものを使う必要があります。

ホントはjavascriptで同じ事をできればよかったんですけど、検索してもそれっぽい情報はありませんでした。 というわけで思いついた単語はsilverlight。 共通するライブラリも多いという事でまずwpfで作ってみたんですけど、silverlight、どうなんでしょうね? silverlightが使われていた例ってのは、以前Gyaoで使われていたのを見ただけなんですけど、今はflashになっちゃってるし。 やっぱりMFCと同じ運命に...?

まぁ、個人ブログのネタで使うんならいいんでしょうけど、無駄なことをしようとしてるような気がします。

================

2013/09/02 追記。 System.Windows.Media.FormattedTextがsilverlightでは使えないことを確認。 というわけで図表ネタはボツに。 まぁ、小さなネタなんでいいんですけどね。