HOME >
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のコード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の高さを求めることが容易にできます。横方向に注目すれば、文字列の表示幅を求めることもできます。是非トライしてください。
2016/10/24 大坂 哲司