Web ページにおける「ぶら下げ組み」の実装

技術、というよりは現状の実装方法をまとめておくくらいの備忘記事です。

# そもそもぶら下げ組みとは

[Wikipedia](https://ja.wikipedia.org/wiki/ぶら下げ組み "ぶら下げ組み") より
> ぶら下げ組み（ぶらさげぐみ）とは、和文の組版において、版面の行の末尾で句読点をほかの文字よりもはみ出させる組みかた。単にぶら下げ、あるいはぶら下がりとも呼ぶ[1][2]。このような処理をしない組みかたを、追い込み（おいこみ）または追い込み組みと呼ぶ。

原稿用紙に文章を書いたときような、点「、」や丸「。」を最上のマスではなく、最下のマスをはみ出して書く組み方のことを言います。

CSS で言うところの `text-indent` などを使って表現するような「ぶら下げインデント」のことではありません。

## 禁則処理

ついでに、もう少し馴染みのあるであろう「禁則処理」についても紹介しておきます。
[Wikipedia](https://ja.wikipedia.org/wiki/禁則処理 "禁則処理") より
> 句読点（。、）や閉じ括弧（」』)】など）と言った約物は、当該文章の行頭に位置させてはならない。

これらの禁則処理の一部として、ぶら下げ組みという仕組みの処理があります。

## 実装する意味

とても細かな違いですが、小説など、文章を読ませるうえでは重要な仕組みのようです。
ぶら下げ組みについては、私もクライアントからの要望で初めて知ったのですが、話を訊くと「物書き側から見ると、とても大切な処理」なのだそうです。

ちなみに、「TeraPad」や「サクラエディタ」など、ごく一般的に広く使われているエディタには、ぶら下げ組みの機能や設定が搭載されています。
やはり、物書きの方々には欠かせない仕組みのようです。

ではここから、Web ページでぶら下げ組みを表現するにはどうすれば良いか考えていきましょう。

# CSS プロパティ `hanging-punctuation`

CSS プロパティに `hanging-punctuation` というものがあります。
[W3C 仕様書](https://drafts.csswg.org/css-text-3/#hanging-punctuation-property "hanging-punctuation") によると、いくつかの値を指定すると、その規則に沿ってぶら下げ組み表示を行ってくれるのだとか。

ただし、このプロパティは現状（2019/12 現在）Safari しか対応しておらず、ほとんどのブラウザでこのプロパティが有効ではありません。
他のブラウザでぶら下げ組みを表現するには、別の方法を取るしか無さそうです。

# 愚直に改行

CSS プロパティで改行を無効にしたうえで、文章を `<p>` タグや `<br>` タグで無理やり改行させます。
The・力技です。

以下のサンプルのような完成形を想定します。

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="result" data-user="crwlr-sdk" data-slug-hash="jOEbpye" data-preview="true" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Japanese hanging-punctuation .1">
  <span>See the Pen <a href="https://codepen.io/crwlr-sdk/pen/jOEbpye">
  Japanese hanging-punctuation .1</a> by crwlr-sdk (<a href="https://codepen.io/crwlr-sdk">@crwlr-sdk</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

上記のぶら下げ組みを実装するためには、いくつかの前提条件があります。

- 文章があらかじめ決まっている
- 一行の文字数が決まっている
- 等幅フォント

これらの条件を満たしていれば、サンプルのような HTML をゴリゴリと作っていけば良いでしょう。

# JavaScript で改行

さすがに手作業で文章を改行していくのは手間なので、ぶら下げ組みの改行を行う関数を作ります。
言語は Web っぽく JavaScript 由来の TypeScript で書きます。

## 文字数が決まっている場合

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="result" data-user="crwlr-sdk" data-slug-hash="GRgoEgN" data-preview="true" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Japanese hanging-punctuation .2">
  <span>See the Pen <a href="https://codepen.io/crwlr-sdk/pen/GRgoEgN">
  Japanese hanging-punctuation .2</a> by crwlr-sdk (<a href="https://codepen.io/crwlr-sdk">@crwlr-sdk</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

文章と一行の文字数を入力し、文章を行毎に Split する関数 `splitInHangingPunctuationFormat()` を作っています。
ぶら下げ組みを含む禁則処理を行います。
とりあえずぶら下げ組みを実装するためだけの関数なので、いくつか注意点。

- 一部の文字の禁則処理しかしていません
- 全角、半角文字の区別をしていません
- スクリプトインジェクションは回避していません

## 文字数が決まっておらず、動的に改行させたい場合

昨今はレスポンシブデザインが主流であろうと思いますので、一行の文字数が決まっていない場合も考えていきます。

無難な落とし所は、breakpoints などを使って画面サイズ毎に文字サイズや文字数、文章範囲の横幅を調整するのが良いと思います。

文章を読むのに特化したアプリであれば、もう少し踏み込んで、文字サイズ、横幅のマージンなどを設定で変更できるとなお良いでしょう。
一行の文字数は `横幅 / 文字サイズ` で求めることができます。
さらに、各値を Subscribe し、変更の度に文字数を計算できれば、シームレスなレスポンシブデザインにもできるでしょう。
（後日余裕があれば、サンプルを作って掲載します）

# 終わりに

言ってしまえば、ぶら下げ組みは Web サービス全体においては細部です。
ぶら下げ組みのためにどこまでやるかは要件次第です。
ですが、上記で紹介した機能がひとつある毎に、文章の読みやすさが変わります。
また、私たちがユーザインタフェースをこだわるように、物書きの方々は、私たちが思っている以上に文章の読みやすさを重視しています。
もし、文章を読むためのサービスを作ることがあれば、この記事を参考にしていただけると幸いです。