背景
私は LiveSuggest という会議アシスタントを作っています。画面上に表示しておくタイプのツールで、通話に bot は参加しません。この記事では、会議セッションを PDF に書き出す機能で多言語対応がうまくいかなかった件と、その対処をまとめます。
もともとの構成
セッションのエクスポートでは、テキストを選択できる PDF を生成しています。生成には pdf-lib を使い、フォントには Noto Sans のサブセットを埋め込んでいます。
ラテン文字のセッションでは、この構成で問題は出ていませんでした。
原因
埋め込んでいるサブセットが対応している文字体系は、ラテン文字、キリル文字、ギリシャ文字です。CJK、ハングル、アラビア文字、タイ文字、デーヴァナーガリーは入っていません。
フォントに含まれていない文字は、そのフォントでは描画できません。そのため、日本語、韓国語、中国語のセッションは、テキスト PDF の経路では正しく書き出せない状態になっていました。
対処として、Unicode 判定で経路を分けた
エクスポートのときに本文の文字を Unicode で判定して、どちらの経路を使うか決めるようにしました。
日本語、韓国語、中国語が含まれていれば、html2pdf で画像 PDF を生成します。この分岐にはユニットテストを書いてあります。
トルコ語とポーランド語はラテン文字系なので、テキスト PDF のまま出力します。キリル文字とギリシャ文字もサブセットに含まれているので、同じくテキスト PDF です。テキストを選択できる出力を残せる言語では、そちらを優先しています。
レンダラーが失敗したときのフォールバック
テキストレンダラーが失敗した場合も、同じ画像 PDF のフォールバックに切り替えます。判定とは別にもう一つ逃げ道を用意しておいて、テキスト経路で何か起きてもエクスポート自体は完了するようにしています。
フォールバック先を一つにまとめたので、画像 PDF の経路は CJK の分岐とレンダラー失敗時の両方から使われます。
トレードオフ
画像 PDF にすると、文字の選択ができなくなります。日本語、韓国語、中国語のセッションについては、テキストとして選択できることより、とにかく文字が表示されることを優先しました。
アラビア文字、タイ文字、デーヴァナーガリーもサブセットには入っていません。これらの扱いは今回の CJK の分岐とは分けて考える必要があり、この記事では結論を書いていません。
振り返り
フォントをサブセット化するときは、どの文字体系が残るのかを最初に把握しておくべきでした。サブセットの範囲がわかっていれば、範囲外の言語を判定で振り分ける設計は最初から組めます。
もう一つは、分岐の判定ロジックにテストを付けておくことです。どの言語がどちらの経路に行くかは、コードを読むだけでは見落としやすいので、テストで固定しておくと安心できます。