Aokashi Room

なんでも書き続けるAokashiの部屋

句読点を末尾に持っていける文章表示プログラム

前に、GitHubのGistにこんなものを投稿しました。

句読点がメッセージの末尾にきた場合、折り返さずに詰めるプログラム · GitHub

これは、文章を表示する際に、横末尾*1の文字の次が句読点*2だった場合に、その句読点を折り返さずに末尾に持っていくプログラムです。

実際に聞いてわからないと思うので、動いている様子を見てみましょう。

f:id:aokashi:20170810181655p:plain

句読点の「、」が3行目にはみ出していることが確認できますでしょうか。

以下は、Gistのソースコードです。長いので動く仕組みとかどうでもいい方はそのままスクロールしてください。

句読点がメッセージの末尾にきた場合、折り返さずに詰めるプログラム

注意

ただし、このプログラムをうまく動かすには幾つか条件があります。

  • フォントは固定幅フォントを利用すること
  • 文字数として全角文字は1文字で、半角文字は0.5文字でカウントします
  • CSSletter-spacing は利用しないこと(利用すると半角文字2つ分の幅が全角文字1つ分の幅と合わなくなるため)

そもそもなぜ作ったのか

多分ここが本編かもしれません。

役に立ちそうだけど、実際に使われている場面は想像しにくいと思います。

これは、JavaアプレットWWAのメッセージの表示方法を再現したかったからです。なお、Javaアプレットで実装されているWWAのことをこれからは原作と言います。

現在、JavaScript移植のWWA Wingを原作っぽく再現するようにしています。原作では、メッセージが横16文字分収まりますが、句読点をその横にはみ出すこともできます。

f:id:aokashi:20170810182826p:plain

原作では、上の図のように動いています。最終行に句読点がメッセージとメッセージ枠の隙間に付いていることが確認できます。

WWA Wing ならではの問題

WWA Wingは原作と違って、メッセージがHTML上でテキストがそのまま表示されます。わかりにくいと思いますが、この実装により、メッセージの内容をそのままテキストとして選択やコピーができます。試しにテキストのカーソルを載せると、文字が選択できる状態になっていることが下の図で確認できます(参考)。

f:id:aokashi:20170812140212p:plain

原作では、文字列を1文字ずつ描画して、折り返す必要になったときには描画位置を決めている変数を操作します。わかりやすく説明すると、xpyp が存在していて、それぞれ文字を描画する画面のX座標、Y座標を表しています。折り返す場合は xp を0にして、 yp に1行分足す仕組みになっています。ただし、次の文字が句読点だった場合はその操作をスキップする仕組みになっています*3

ただし、WWA Wingでは、HTMLのテキストが表示される都合上、Javaアプレットでは一切関わらないであろうHTMLの親子関係が存在しています。メッセージ枠の要素にメッセージが入るため、文字の位置はここで!とかのように細かく調整することができません*4

そこで、原作の仕様を似た形として、折り返す場合は br 要素を挿入して改行する方法もありますが、下の図のようにメッセージ全体をコピーして、貼り付けた結果に改行が含まれてしまいます。ページ内検索や、貼り付けたテキストへの検索を行う方もいますから、なるべく必要以上に改行されたくないのです。

f:id:aokashi:20170812142237p:plain

解決策と実装方法

このような問題を解決するために改行の代わりに実際に表示する1行分それぞれspan 要素として設ける形にしました。

  • 実際に表示する1行分は、折り返した場合に表示される1行分です。

はみ出た句読点を置くために、1行の領域に複数の span 要素が入らないために、その要素の幅はメッセージボックスいっぱいに指定しています(width プロパティが 100%)。

しかし、インライン要素は幅が指定できないため、その span 要素にはCSSdisplay プロパティには inline-block を指定しています*5

f:id:aokashi:20170812195737p:plain

説明した通りに、要素の仕組みを上の図で表す事ができます。赤い枠が span 要素になります。ここでは6つあります。

こんな感じで、今回制作したプログラムはこのように利用しています。気になる方は、プログラムのHTMLファイルを開いてみて、開発者ツールで要素を確かめてみてください。

このような実装はCSSには存在しない

残念なことに、今回のプログラムのような実装はCSSでは用意されていません。まあ、用意したらここまで苦労する必要はないと思うんですがねー…。

似たものとして font-feature-settings プロパティがありますが、句読点のみならず、カギ括弧も対象だったり、そもそも末尾に置かなくても詰めてしまい本来の目的と違っていたりするので、使おうとは思いませんでした。

最後に

とりあえず「できた」のですが、今後の課題とかは現在思いつきません。もしあるとしましたら、横幅の文字数を指定しなくてもいいようにしたいこと、計算量を減らすためにアルゴリズムを最適化すること、でしょうか。

ちなみに、原作っぽく再現したWWA Wingはもう少しユーザーに体験できるようになります。

*1:折り返す直前の文字の、次の文字で、文章全体の一番後ろではありません。

*2:ここで指す句読点は「。」と「、」の2つです

*3:ただしメッセージには枠や余白も存在しますので、必ずこの方法で描画するとは限りません

*4:しかし、その分文字を置くだけというメリットは存在しますが…

*5:幅が 100% なら単純に div 要素使ったほうが楽かもしれないですが、ブロック要素になると先程あげた br 要素の挿入と同様に貼り付けたテキストに改行が生じます。