textarea/divの、1行に入る文字列を求めます。
textarea/divのテキストを、text-tspanに変換します。

 秋の夜長は物思いするには絶好の季節です。よい思いもありますが、ちょっとどうでもよいようなことが気になり、急にいろいろなことを調べ始めたりしたことがありませんか。本稿も、そのような中で、急に気になり始めたことからスタート。どこで使うの?・・・・

 SVGのテキストを自動改行させる方法として、foreignObject要素の中にHTML-DIV要素を設け、このDIV要素の中にテキストを流し込めば、HTMLと同じイメージで、テキストの折り返し表示ができます。尚、SVG1.1のtext要素では、自動改行できません。SVG1.2からtext-tbreakによってテキストの折り返しが行えるそうです。またtextareaもサポートされるそうです。

 下記のコードの通り、SVGのforeignObject要素の使い方は簡単で、SVG-foreignObject要素の中に通常のHTMLが埋め込めばよいのです。この例は丁寧なコードですが、簡略して<div xmlns="http://www.w3.org/1999/xhtml"・・・から</div> までをforeignObject要素に埋め込んでも、同様な結果となります。

test.svgのコード













<foreignObject>
SVG 要素は、異なるユーザーエージェントによって描画されるグラフィックコンテンツを持つ外部XML名前空間を含めることを可能にします。
含まれた外部グラフィックコンテンツは、SVG 変換や合成の対象となります。
test.svgの実行

 下図のようにforeignObject要素中にHTMLが流し込まれていることが確認できます。

さて、話を進めます。
 たまたま、出来上がったSVGファイルをPDFに変換するため、Inkscapeを利用したところ、普通のSVG要素は描画できましたが、foreignObject要素の中身は描画されませんでした。どうやら現時点では、InkscapeはforeignObject要素の中身をレンダリングすることができないようです。
 そこで今回、oreignObject要素のtextarea/div要素のテキストをtext-tspanに変換することを試みます。

foreignObject-textarea/divをtext-tspanに変換
 textarea/divの中のテキストは、漢字かなや英数字を体裁良くするため、禁則処理に従ってテキストが折り返して表示されます。
 Wikipediaの禁則処理の説明では、”日本語の文書作成・組版において、句読点(。、)や閉じ括弧(」』)】など)と言った約物は、当該文章の行頭に位置させてはならない。理由は、これら約物が行頭に来ると見た目が悪くなるほか、読みにくくなったり、文意を取り違えるおそれがあるからである。”、とあります。
 さて、冒頭の通り、SVGのtext要素には折り返し機能がありません。textarea/divをtext-tspanに変換するためには、textarea/divの各行に入っている文字列を求め、それらをtspan要素で表示することで、見た目は同じになります。
 均等割付のフォントであれば、textarea/divの表示幅から、1行に入る文字列が簡単に求めることができます。一般にはプロポーショナルフォントを利用しているため、1行に入る文字列が簡単に求めることができません。また、禁則処理による折り返しなど、1行に入る文字列が簡単に求めることができません。フォントメトリックスや難しいルールを解析することも一案ですが、そこで案じたことが、”ブラウザーに聞け”にすることにしました。
 Chrome(Windows8.1)上にtextareaを設け、テキスト折り返しを調査しました(下図)。textareaに1文字づつ追加していくと次第に行末まで文字が埋まります。この状態で更に1文字を追加したとき、行末文字と追加される文字の関係によって、行末、行頭の挙動が異なります。行末が普通の文字で、普通の文字を追加した場合、行末文字はそのままで、追加された文字が次行の行頭に表示されます。しかし、行末が行末禁則文字の場合、その文字と追加される文字が次行の行頭に表示されます(下図、行末禁止文字によっても挙動が異なります)。

コード概略
 コードのポイントは、textareaの高さはフォントサイズとし、このtextareaに1文字づつ追加しながら、var h1 = node.scrollHeight;で、textareaの高さを監視します。textarea内でテキストの折り返しが生じた場合、この高さが変わります。
//1文字づつセットし、改行ポイントを求めるコード
       var node = document.createElement('textarea');
       document.body.appendChild(node);
       $(node).attr("id", id);
       var props = {
             "width": w + "px",
             "height": fs + "px",//1行の高さをフォントサイズ分とする
             "font-size": fs + 'px',
             "font-family": ff,
             "border-width": bw + "px",
             "padding": pd + "px",
             "word-wrap": "break-word",
             "visibility": "hidden"
       }
       $(node).css(props);
       var h0 = node.scrollHeight;//1行がフォントサイズ分の高さを求める

       var i = 1;
       for (; i <= str.length; i++) {//1文字づつセットする
             var s = str.substring(0, i);
             $(node).html(s);
             var h1 = node.scrollHeight;//現時点の行の高さを求める

console.log("h0=" + h0 + " h1=" + h1 + " " + s);

             if (h1 > h0) {//行数が増えた
                     break;
             }
       }
実行結果(ソースコードはこちらを、確認はchromeでお願いします。下はコンソール画面です)
      h0=22 h1=22 私
      h0=22 h1=22 私達
      h0=22 h1=22 私達の
      h0=22 h1=22 私達の田
      h0=22 h1=22 私達の田舎
      h0=22 h1=22 私達の田舎で
      h0=22 h1=22 私達の田舎で超
      h0=22 h1=22 私達の田舎で超超
      h0=22 h1=22 私達の田舎で超超超
      h0=22 h1=22 私達の田舎で超超超超
      h0=22 h1=22 私達の田舎で超超超超人
      h0=22 h1=22 私達の田舎で超超超超人気
      h0=22 h1=22 私達の田舎で超超超超人気の
      h0=22 h1=22 私達の田舎で超超超超人気の「
      h0=22 h1=40 私達の田舎で超超超超人気の「り
      h0=22 h1=22 「
      h0=22 h1=22 「り
      h0=22 h1=22 「りん
      h0=22 h1=22 「りんご
      h0=22 h1=22 「りんご」
      h0=22 h1=22 「りんご」が
 図の左側(青枠)がhtml-textareaで、右側(赤色字)がsvg-text-tspanで描画したものです。
 青枠内はtextareaで、いろいろな文字列を入力し、各行がそれぞれ求められているか、確認ができます。
 注意:禁則処理の確認テストが十分でないため、左右のテキストの改行位置が異なる場合があります。

参考:
 上記の通り、”ブラウザーに聞け”と言う考えに従って、textarea/div内の各行の文字列を求めることができました。今回は各行の文字列を求めるところで留めていますが、更に行数を求める、textarea/divの高さを求めることが容易にできます。横方向に注目すれば、文字列の表示幅を求めることもできます。是非トライしてください。

最後に
    Word2010:禁則処理とは
        http://office-qa.com/Word/wd272.htm
2016/10/24 大坂 哲司