SVGで、テキストの縦位置(baseline)を揃える処理を書こうとしてはまったので、覚え書き。
経緯
コピィ・ライターを作成していてふと、
「あれ? 基本的にテキストを配置してバナーを作るものだし、SVGと相性良いんじゃ? 設定ファイルを JSON で書き出しているところをちょっといじれば、SVG で書き出すこともできるようになるかも?」
と思い立ったので、処理を追加してみたところ、テキストの baseline を揃えるところで苦労したというもの。
そもそも、SVGをいじるのはこれが初めてなので、誤解をしているところはあるかも……変なところがあればご指摘願いたい。
どこではまったか? 「え……またIEかよっ!」
HTML5 Canvas の場合、textBaseline プロパティを指定すればテキストの baseline が指定できていた。
同じようなものが SVG でも無いか探してみると、text 要素等で指定できる dominant-baseline プロパティがこれに相当するようだ。
HTML5 Canvas と SVG のテキストの baseline の対応(今回対象としたもののみ)
baseline(基底線) | Canvas (textBaselineプロパティ) |
SVG (dominant-baselineプロパティ) |
---|---|---|
The top of the em square | top | text-before-edge |
The middle of the em square | middle | central |
The bottom of the em square | bottom | text-after-edge |
em square=文字の構造的な大きさを表す四角形、EM box・em quad 等とも。
■参考
textBaseline プロパティ - Canvasリファレンス - HTML5.JP
svg要素の基本的な使い方まとめ
それで、実際やってみたところ……
1枚目はChromeで、2枚目はIE11で、それぞれ見たイメージ。
IE11はそもそもdominant-baseline未サポートなのか、それとも設定の仕方がまずいのか。代替手段はあるのか…? pic.twitter.com/7g6FRrLovE
— 風柳 (@furyutei) 2015, 7月 13
のように、IE では効いていないように思われた。
それで調べてみると、確かにサポートされていないようだ。
IE9 Mode, IE10 Mode, IE11 Mode, and EdgeHTML Mode (All
Versions)The dominant-baseline attribute is not supported.
[MS-SVG]: [SVG11] Section 10.9.2, Baseline alignment properties
ではどうするか?
とりあえず、IE でなんとかする方法を探してみたところ、
The specification defines
central
like that:
central
This identifies a computed baseline
that is at the center of the EM box.We can take an
internet explorer 9 - How to center SVG text vertically in IE9 - Stack OverflowEM box
of known font size and measure its bounding box to compute the center.
とあり、具体的なソースコードが書かれている。
どうやら、SVG中の script 要素にて、EM box の座標と大きさを元に central 相当の位置を決めて、transform 属性にて位置を調整する、ということらしい。
- SVG 中の text 要素に指定した座標を (Tx, Ty)
- 実際に描画された当該 text 要素の EM box(外枠) の左上の座標を (Rx, Ry)、高さを Rh
とすると、
text-before-edge 相当位置 | Ry + (Ty - Ry) |
---|---|
central 相当位置 | Ry + ( (Ty - Ry) - (Rh / 2) ) |
text-after-edge 相当位置 | Ry + ( (Ty - Ry) - Rh ) |
になると思われる。
transform 属性には、
座標系, 変換, 単位 – SVG 1.1 (第2版)
- translate(<tx> [<ty>])
- tx, ty による 並進 を指定する。
<ty> が与えられていない場合、 0 とみなされる。
のような変換定義を指定してやればテキストの移動が可能なので、例えば central の場合には
tx = 0, ty = (Ty - Ry) - (Rh / 2)
を指定してやればよい。
tx, tyは、EM box左上端からの相対位置なので注意。
コピィ・ライターでは、具体的には、以下のようなスクリプトを SVG 内に挿入した。
var useragent = window.navigator.userAgent.toLowerCase(); if (useragent.indexOf('msie') < 0 && useragent.indexOf('trident') < 0) { return; // IE 以外は何もしない } window.onload = function() { var elm_text_list = document.getElementsByTagName('text'); for (var ci=0, len = elm_text_list.length; ci < len; ci++) { var elm_text = elm_text_list[ci] , text_y = parseInt(elm_text.getAttribute('y')) , rect = elm_text.getBBox() , rect_y = rect.y , rect_height = rect.height , offset_y = (text_y - rect_y) - (rect_height / 2); // central の場合 elm_text.setAttribute('transform', 'translate(0, ' + offset_y + ')'); } };
なお、IE 以外の場合には、別途 SVG の text 要素に対して dominant-baseline プロパティを設定してあるため、スクリプトでは処理を行わない。
結果
実際に上記手法を適用して作成したSVG画像が、Chrome と IE でどう見えるかを示す。
赤線は基線(SVGのテキスト要素で指定したY座標)、テキストの周囲の四角はEM box(スクリプトで描写)
注意点
- SVG ファイルを HTML中に貼り付ける際、IMG要素 を使用すると、SVG内のスクリプトも実行されないため、上記の小細工が適用されない。
これはCSSのbackground-image等で指定する場合でも同様。 - OBJECT要素や IFRAME要素で指定してやれば、意図通りに表示される。
ただし、IFRAMEだと扱いが比較的面倒であるため(CSSで拡大縮小しづらい等)、OBJECT要素を使用するのがベター。