簡易・縦書きエディタの作り方
- テキストファイルがバイナリデータであることを理解する
- 文字を表示するためにはフォントが必要であることを理解する
- ここまで理解できればあとは作るだけです
- 文字列に対応するグリフをビットマップデータにするプログラムを書く
- ビットマップデータをいい感じに配置するプログラムを書く
- マウスやキーボードにいい感じに反応するプログラムを書く
- 各OSのIMEといい感じにやり取りするプログラムを書く
- 細かいところを作る
- 完成
実際の内容
1.~2.と6.の内容はまた別の機会に譲ることにします。
今回は3.から5.までの内容をざっくりと書きます。
さらに、ここでは複雑な機構は考えないことにします。
具体的には次のような状況を想定します。
- フォントはOpenTypeを想定
- OpenTypeはフォントファイルの仕様のひとつです
- OpenTypeについての簡単な解説はまた後日
- 日本語的な縦書きを想定
- 書字方向は上から下(アラビア語を入れると下から上になりそうですね)
- 改行方向は右から左(モンゴルだと左から右ですね)
- ルビや傍点といった装飾はなし
- 折り返しはなし
- スクロールはなし
- 描画のキャッシュはなし
- 文字は白地の上に黒(選択しても変化しない)
- テキストの編集履歴はなし
- 縦書き時の回転は行わない(英単語が横倒しになるやつ)
プログラムは大まかに一文字一文字を描画、文字を並べる、選択・編集に分かれます。
最低限状態として持たないといけないのは選択中の文字列です。
3. 文字列に対応するグリフをビットマップデータにする
このステップは大雑把に、各行で以下の手順を実施します。
(ここでの行とは、文字列を改行文字で分割したときの各部分文字列のこと)
- 使用するフォントを決める
- おおよそのOSにはフォントダイアログからフォント名を取得できる
- 使用するフォントを読み込む
- 各OSでフォント名からフォントファイルを取得する方法は異なる
- 使用するフォントを見て文字に対応する横書き用のグリフIDを取得する
- 主にフォントの中の
cmap
という場所のお仕事- コードポイントをフォント内のグリフのIDへ射影するものです
- FreeTypeに任せることが可能
- グリフというのはフォントに格納されている各シンボルのことです
- 基本的にはひとつのコードポイントがひとつのグリフに対応します
- 主にフォントの中の
- 縦書き用のグリフIDに変換する
- フォントの中の
vrt2
もしくはvert
とvrtr
という場所のお仕事- 縦書きと横書きで違うのはここと文字の並ぶ方向だけです
- 頑張ってね(要望があれば詳しく書きます)
- GSUB lookup type 1なので簡単です
- フォントの中の
- グリフIDに対応するグリフデータからビットマップを生成する
- 基本的にはアウトラインデータを所望のサイズに変換してラスタライズすることになる
- FreeTypeに任せることが可能
- フォント内にビットマップがあればそれを使用しても良い
- MS 明朝などには白黒画像がフォント内に含まれている
- 基本的にはアウトラインデータを所望のサイズに変換してラスタライズすることになる
4. ビットマップデータをいい感じに配置する
グリフのビットマップデータ列を取得できました。
次にこれを各行で上から下に向かって並べます。
各行は適切な間隔をあけて、右から左に並べます。
- 先頭のグリフのビットマップデータを行頭に配置します
- 配置したビットマップデータの送り幅を取得する
- 送り幅は横書きと縦書きで別に存在します
- 次のグリフののビットマップデータを配置する
- 配置するグリフは前のグリフの位置から送り幅分だけ下方になる
- 2.と3.を改行文字まで続ける
- グリフの位置をリセットして左に移動
- 左への移動は等間隔にするのが良いでしょう
- 次の行があれば1.に戻って新しい行の処理をする
- なければ終了
5. マウスやキーボードにいい感じに反応する
- 文字列の選択
- 左クリックの押したときと上げたときの座標を取得します
- それぞれの座標が対応するグリフの位置を計算します
- 水平方向の座標から行を特定
- 行の配置が等間隔なら簡単です
- その行が全体の何文字目から始まるかを調べる
- 垂直方向の座標から行内の何個目のグリフ描画位置が近いか特定
- 行頭が1個目のグリフ描画位置
-
i
個目ならi-1
が選択位置 - 特別に最後のグリフの後ろも選択位置になり得ます
- 座標から全体の何個目のグリフかがわかる
- 水平方向の座標から行を特定
- グリフの位置から文字列のインデックスに変換します
- ここで書いたことよりも正しい描画結果を求めない場合は「何個目のグリフか」と「文字列のインデックス+1」は一致します
- 選択中の文字列をプログラム中の状態として持ちます
- 初期値は0文字目から0文字目でいいと思います
- 文字列の編集
- 編集は置換にだけ対応すれば十分
- 文字列の
n
文字目からm
文字目までを文字列X
で置換する - UTF-8やUTF-16なら
n
やm
はコードポイントを壊さないよう注意します- ここも後日詳しく書きます
- 文字列の
- 選択中の文字列を入力されたキーの文字で置き換える
- Delete, BackSpaceキーで文字列を削除
- 選択文字列があるときはそれを削除
- 選択文字列を選択開始位置に変更
- 選択文字列がないときにDeleteなら後方の文字を削除
- 選択文字列は変更なし
- 後方に文字がなければ何もしない
- 選択文字列がないときにBackSpaceなら前方の文字を削除
- 選択文字列は1つ前に移動
- 前方に文字がなければ何もしない
- 選択文字列があるときはそれを削除
- 編集後には再描画する
- 編集は置換にだけ対応すれば十分
6. 各OSのIMEといい感じにやり取りする
後日のアドベントカレンダーで取り上げる予定です。
おわりに
そもそもなぜ縦書きエディタを作ったのか。
事の発端:Windows XPでフォントが綺麗に表示されてUnicodeが使える縦書きエディタがない。
〜3年後〜
C言語とかいうものを講義で知る。
→FreeTypeでフォントから文字を描画できることを確認。
→wxWidgetsでUnicodeを扱えることを確認。
→縦書きエディタが作れるということがわかる。
→作った。
記事はほぼそらで書いてるので間違い等ありましたらお知らせください。
なお、記事中で「文字」と言っていますが大雑把にはコードポイントひとつという認識で問題ありません。詳しいことはまた後日。