サロゲートペアへの対応
今後の Unicode 絵文字への対応なども考えるとサロゲートペアへの対応方針を考える必要があります。
Python 3 に移行すると文字列の内部表現を 32 ビット化できるようですが、 NVDA にとって適切なアプローチかどうか検討が必要です。
まずは、独自拡張した文字説明データを UTF-16 ではなく UTF-32 で記述するようにしてみて、サロゲートペア文字が文字レビューできるか、入力メソッドで読み上げできるか、といった具体的な検証をしたいと思います。
このチケットの最初に書いた「ケイコツノ ケイ:ニクヅキニ マタノシタ ツチ」という文字は、UTF-32 では下記のように 2667e になります。
>>> u"\ud859\ude7e" u'\U0002667e'
このような文字を文字説明辞書や記号辞書に登録してうまく扱えるかどうかを(たぶん問題が起きるでしょうが)まず検証してみます。
現状の jpbranch で把握した状況は以下の通りです。
後述のように文字説明データをUTF-8で記述。
音声合成は Windows 8.1 の SAPI5 Haruka を使用。エンジンはこの文字を読むことができない。
(1) メモ帳でこの文字が書かれた行に上下矢印キーで移動すると、symbols.dic の情報が適用されて「ケイ」と読み上げる。
(2) メモ帳でこの文字を左右矢印キーで移動すると、サロゲートペアの1文字目だけが扱われてしまうらしく、文字を読み上げない。 現在位置文字の説明(ラップトップでNVDA+ピリオド2回)を押すと、サロゲートペアの1文字目 u+d859 が通知される。 ちなみにカーソルをどう左右に動かしてもサロゲートペアの2文字目を読み取ることはできない。
(3) Microsoft IME で "2667e" + F5 を押してUnicode文字入力を行うと、候補文字列の読み上げは u+d859 u+de7e のようにサロゲートペアの1文字目と2文字目を読み上げる。
なお JTalk の辞書はこの文字を「ケイ」と読み付与できており、symbols.dic の登録をしなくても(1)の操作で「ケイ」と読み上げています。
以下、文字説明データ:
#souce/locale/ja/symbols.dic 𦙾 ケイ none # 𦙾 U+2667e #souce/locale/ja/characters.dic 𦙾 2667e [ケイ] ケイコツノ ケイ:ニクヅキニ マタノシタ ツチ
本家のサロゲートペア対応について、 XML パーザー関連だけ下記のチケットとコミット(NVDA 2012.1)で暫定対応されています。
http://community.nvda-project.org/ticket/2090
http://community.nvda-project.org/changeset/2dae1e4b03767259afb44b469701744627eb0c5b
本家チケット 3805 で Unicode 文字の読み上げテーブルの話が出ていたので本件に関するコメントを書きました。
日本語のサロゲートペアと関係がある気がするのでここに紹介。
Hindi 言語の compound character で「文字の詳細説明」がちゃんと動くようにする作業が進んでいる。
本家チケット 4582
On using NUMPAD 2(twice) in desktop layout for character description, the compound character is not described instead only its first contained character is described
http://community.nvda-project.org/ticket/4582
他の関連チケット #34890 記号読み上げ辞書の整理と「読み方モード辞書」の統合
#34899 の作業をするついでに、本家の 4582 で行われた変更を読んでみました。
サロゲートペア文字の文字説明の実装に役立ちそうな気がします。
現在は以下の言語コードのみが対象になっている:
#Set containing locale codes for languages supporting conjunct characters LANGS_WITH_CONJUNCT_CHARS = {'hi', 'as', 'bn', 'gu', 'kn', 'kok', 'ml', 'mni', 'mr', 'pa', 'te', 'ur'}
getCharDescListFromText() の処理をちょっと抽象化した例で説明すると:
characterDescription (charDesc) の内容 abc C1 d D ef C2 getCharDescListFromText の入力文字列 "abcdef" 入力文字列を先頭から最長一致で charDesc テーブルのキーとマッチングしていく。 "abcdef", "abcde", "abcd" とスキャンして、該当なし。 "abc" で "C1" が見つかる。返り値の配列に ("abc", "C1") というタプルが追加される。 残った文字列 "def" でもう一度同じアルゴリズムを繰り返す。 "def", "de" で該当なし。 "d" で "D" が見つかる。返り値の配列に ("d", "D") のタプルが追加される。 残った文字列 "ef" で "C2" が見つかる。これがタプルに追加される。 この結果、返り値は以下のようなタプルの配列になる。 [ ("abc", "C1"), ("d", "D"), ("ef", "C2") ]
このアルゴリズムで非BMPの漢字やユニコード絵文字も分割できそうな気がします。
ちなみに abcdef に相当する文字コードの分割は Python の Unicode オブジェクトのイテラブル性に依存した処理になっています。
将来 NVDA が Python 3.x に移行したときにはこんな処理は必要なくなるのか、あるいはこのままなのか。。
もうすこし speech.py を追ってみたのですが、 メモ帳に U+2667e の文字を入力して、左右矢印で移動すると speech.speakSpelling() には u+d859 (サロゲートペアの前半)しか来ていません。
しかしこの文字だけが書かれた行で NVDA+L を2回押し(現在行をスペル読みでレビュー)すると、 ちゃんと speech.speakSpelling() に u+d859 u+de7e の2文字が来ているようです。
なので、この speech モジュールの対応に加えて、textInfos での現在文字の取得処理 (場合によってはアプリケーションごとの NVDAObject の処理) に 手を入れる必要がありそうです。
もうちょっとシンプルなテストケースを作って、 本家にフィードバックしたほうがよさそうな気がしてきました。
手元のテストコードで Unicode U+1F300 (サロゲートペアだと D83C + DF00 の2文字になる)を 「うずまきの絵文字」と読むようになった。
Windows 10 の Microsoft IME で「うずまき」を変換した場合にも候補として読み上げる。
残った作業
本家のコードの textInfos.offsets.py で1文字移動のポインタ計算に手を入れたので、 副作用がないかどうかを調べる必要がある。
branch jpunicode https://github.com/nvdajp/nvdajp/tree/jpunicode
mecab-ipadic-NEologd の Unicode 絵文字対応
http://www.slideshare.net/overlast/mecab-ipadicneologdpydatatokyo05pub-48560060
Unicode 絵文字の読み定義が入っているという話。
https://github.com/neologd/mecab-ipadic-neologd/blob/master/README.ja.md
ちょっと見てみた:
$ xzcat seed/mecab-user-dict-seed.20160526.csv.xz | grep "エガオ" 😀,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😁,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😃,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😄,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😅,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😆,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😇,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😈,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😊,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😋,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😌,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😍,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😎,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😏,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😸,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😺,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😻,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ 😼,1285,1285,3996,名詞,一般,*,*,*,*,笑顔,エガオ,エガオ
TSE ワンライナーで抽出:
$ xzcat seed/mecab-user-dict-seed.20160526.csv.xz | tse -F "," -s ".*" "if len(L1) == 2 and 0xd800 <= ord(L1[0]) <= 0xdfff: print(L1 + ',' + repr(L1) + ',' + L11 + ',' + L12)" > _emoji_out $ wc _emoji_out 1758 1983 78488 _emoji_out
最初の数行。文字ごとに複数の品詞・読みがあるようだ:
🀄,u'\U0001f004',麻雀,マージャン 🀄,u'\U0001f004',中,チュン 🃏,u'\U0001f0cf',ジョーカー,ジョーカー 🃏,u'\U0001f0cf',ジョーカー,ジョーカー 🅰,u'\U0001f170',A,エイ 🅰,u'\U0001f170',A,エイ
約850個の絵文字の辞書を作成。
元にしたデータは下記(mecab-ipadic-NEologd から作成):
https://github.com/nvdajp/nvdajp/blob/9d7b47e2cf59e1c7a4eff264bea98b37484707d8/jpchar/emoji.txt
GitHub issue https://github.com/nvdajp/nvdajp/issues/7
サロゲートペア文字(絵文字ではなく漢字)の辞書整備に使えそうなのでメモ:
MJ文字情報一覧表 独立行政法人情報処理推進機構(IPA) クリエイティブ・コモンズ 表示 – 継承 2.1 日本
本家がサロゲートペア対応パッチを作ったのでマージしようかと検討中。
https://github.com/nvaccess/nvda/compare/offsetsUnicodeBeyond16
Unicode 文字のなかには1文字を表現するのに16ビットのコード2つを必要とします。
現在 locale/ja/characters.dic の 7488 行に書かれている以下の行
は、サロゲートペアなので、32ビットのコードポイントを記述する必要がありますが、NVDA がそもそもサロゲートペアを処理できているかも確認できていません。