文字単位のレンダリング走査で
日本語組版を自動検査する
日本語の改行制御を実装しても、それで終わりではありません。文言をひとつ直せば折り返し位置は変わり、画面幅が変われば新しい分断が生まれます。全ページ×全幅を目視で確認し続けるのは現実的ではない。そこで弊社では、ブラウザのレンダリング結果を文字単位で走査し、組版の問題を機械検出する検査系を作り、文言修正のたびに全数検査を回す運用にしています。本記事ではその設計を、判定ルールや閾値の実値まで含めて解説します。
なぜ「実際のレンダリング」を見るのか
折り返し位置を静的に予測するのは、ほぼ不可能です。フォントごとの字幅、letter-spacing、禁則処理、インライン要素の境界。これらの相互作用は実装依存で、CSSの仕様書から答えは導けません。だから発想を変えて、ブラウザに実際にレンダリングさせ、その結果を観測することにしました。予測をやめて観測に寄せる。この転換が検査系全体の土台です。
行の復元: Range API による文字単位の走査
検査対象のページを、検査したい幅(例えば375px)の iframe に読み込みます。フォントの読み込み完了を待ってから、各テキスト要素を TreeWalker で辿り、一文字ずつ Range を張って getBoundingClientRect() を取ります。文字の上端座標が直前の文字より大きく下がった箇所が、実際に行が折れた位置です。
閾値の設計にはひとつ工夫があります。単純に「top が変わったら改行」とすると、ルビや上付き・字間の揺れで誤分割します。弊社の実装では「直前の文字の高さの0.6倍を超えて下がったら新しい行」と判定しています。この0.6という係数は、見出しと本文が混在するページ全体を走査しても誤分割が出ない値として、試行で決めたものです。
検出ルール: 三つの異常パターン
復元した行の配列に対して、次の判定を機械的にかけます。
- カタカナ語の泣き別れ: 行末の文字と次の行頭の文字がともにカタカナまたは長音符。熟語の漢字が割れるのは許容し、カタカナに限定するのが誤検知を抑えるコツです
- 見出しの孤立行: 見出し要素の最終行が3文字以下。本文の短い最終行は自然なので、対象は見出しに限定します
- 行頭禁則: 行頭が句読点・閉じ括弧・小書き仮名・長音符。ブラウザの禁則が効かない例外パターン(後述)を捕まえます
このうち行頭禁則の検出には実益がありました。折り返し制御に使う inline-block 要素の直後に句読点が続くと、ブラウザの禁則処理が働かず「。」が行頭に落ちることがあります。仕様の隙間のような挙動で、目視ではまず気づけません。機械検査が最初に見つけてくれたのはこのパターンでした。
検査対象の幅は7つ
走査はモバイルの 360・375・390・414px、PCの 768・1280・1440px の計7幅で行います。狭い順に潰せばよいわけではないのが厄介なところで、375pxでは問題のない見出しが414pxでだけ孤立行になる、という事例が実際に何度もありました。行の折れ方は幅に対して非線形に変わるため、代表幅の網羅が必要です。
運用: 検出は広めに、判断は人間が
検出ルールは誤検知を恐れず広めに設計しています。たとえば語単位の折り返し制御で意図的に作った「語の単位で折れる位置」も、機械的にはカタカナ分断として検出されます。これらは意図的な改行として個別に許容します。自動修正まで踏み込まないのは意図的な設計判断で、組版の最終判断には文脈(その見出しで何を伝えたいか)が要るためです。機械は網羅を、人間は判断を。この分担が最も生産性が高いというのが現時点の結論です。
効果と限界
この検査系を入れてから、文言修正に伴う組版の劣化は「修正のたびに全数検査→検出箇所だけ手当て」という数分のループで抑えられるようになりました。特に効くのが、修正の副作用の検出です。ある語を折り返し制御で包むと行の埋まりが変わり、離れた場所に新しい孤立行が生まれる。この玉突きは目視ではまず見逃しますが、機械走査なら確実に捕まります。
一方で限界もあります。検査はブラウザエンジンのレンダリングに依存するため、フォントの置換が起こる他のOS・実機での見え方までは保証しません。また、美的な「なんとなく間延びして見える」といった問題はルール化できていません。検査系はあくまで下限の保証であり、最終的な組版の品位は人間の目で仕上げる。その前提で使うのが正しい距離感だと考えています。