異体字…IVS、フォント、GSUB

 世の中には姓名にちょっとでも違う字を使うとうるさく言う奴がいて、そんなこんなでUnicodeに字形切り替えの仕組みが備わった。これをIVSという。特に「ナベ」さんはうるさかったらしく、字形も沢山登録された。

上の図はUnicodeに登録されている異体字リスト(IVD:http://unicode.org/ivd/data/2007-12-14/IVD_Charts.pdf*1)から引っ張ってきた。実は上に挙げたのは一部である。日本人しか使わない、日本人のための表である。ああ素晴らしい( ´∀`)。


 こういう規格が何で出てきたのかとか、以下のサイトが詳しかった。
  IVSとフォントの関係 - ちくちく日記
  IVSとGSUBはどう違うのか - Mac OS Xの文字コード問題に関するメモ

ともかく、私も日曜フォントプログラマーとして、フォント内部のIVS構造がどうなっているのかを話したい。

仕様

 フォントの仕様の部分はここ(Character/Glyph Index Mapping)のFormat 14: Unicode Variation Sequencesに当たる。バイナリのデコンパイル実装は自分で頑張ってください。私が作ったのはこれ*2Chromeブラウザで閲覧することをおすすめする。で、デコンパイルした結果が下。フォントのサンプルにはHanaMinOTPr6N-Regular.otfを使用させていただいた。


http://fontconverter.appspot.com/cmap?platformID=0&encodingID=5&fontname=HanaMinOTPr6N-Regular.otf


見て分かる通り、VS(varSelector)に対して、レコードが幾つといったように、通常とは逆のデータ構造に成っている。defaultUVSOffsetは気にしなくてもいい。nonDefaultUVSOffsetがUnicodeとグリフIDを結びつけているレコードになる。defaultUVSOffsetとnonDefaultUVSOffsetの数値をクリックすると表示を展開するので見て欲しい。

対応表

 上のデコンパイル結果を見ても、どのVSがどの表示に対応しているか分からないと思うので、並び替えてリストを作った。ソートされていないのはご愛嬌。


http://fontconverter.appspot.com/cmap2?platformID=0&encodingID=5&fontname=HanaMinOTPr6N-Regular.otf


Unicode#VSとなっている。対応しているグリフIDは右に表示される。クリックしたらグリフに飛ぶ。リストの一番下に37001の定義がある。キャプチャした画像が以下。


http://fontconverter.appspot.com/glyph?fontname=HanaMinOTPr6N-Regular.otf&GID=7947

http://fontconverter.appspot.com/glyph?fontname=HanaMinOTPr6N-Regular.otf&GID=7948


点が有ったり無かったり。極めてどうでもいい、些細な違いのあるグリフがフォント内には別々のIDを振られて格納されている。

GSUB

GSUBとは字形置き換えの仕組みである。IVSの仕組みと何が違うのといわれれば、IVSはUnicodeの仕様、GSUBはOpenTypeFontの仕様である。だからGSUBは字形を置き換える用途ならなんでもありになる。縦書き用のグリフを置き換えたり、アラビア文字の置き換えを定義したり、大体なんでもできる。GSUBの中味を見てみよう。仕様のテーブルの解説はいつかまた。


http://fontconverter.appspot.com/GSUB2?fontname=HanaMinOTPr6N-Regular.otf


featureというのが用途だと思えば良い。14のjp78のmaptableのリンクを見て欲しい。


http://fontconverter.appspot.com/GSUB3?subtableindex=0&lookupindex=4&fontname=HanaMinOTPr6N-Regular.otf


original GlyphIDが置き換え前のグリフID、substitute GlyphIDが置き換え後のグリフIDである。試しに、index:3の行を見て欲しい。以下キャプチャ。


http://fontconverter.appspot.com/glyph?fontname=HanaMinOTPr6N-Regular.otf&GID=23
http://fontconverter.appspot.com/glyph?fontname=HanaMinOTPr6N-Regular.otf&GID=6210


なんだかゴチャゴチャっとしたグリフに置き換わっているのがお分かりいただけただろうか。こんな感じで言語や用途を指定した上で、使用するグリフをすげ替えてしまうのがGSUBテーブルである。もちろん、今見せたような1対1の置き換えだけではなくて1対多、逆に多対1、特定のグリフ列担った場合に置き換えるなど色々できる。

まとめ

 日曜フォントプログラマーとして、IVSとGSUBをフォントフォーマットの観点から語ることが出来た。残念ながら「ナベ」さんだけが異状にうるさい理由は分からなかったが、まあよしとしよう。

お詫び

 公開しているプログラム(http://fontconverter.appspot.com/)はバグフィックス中です。エラーコードの表示が出ても許してください☆(ゝω・)vキャピっ

気になったAAをすぐ登録できるブックマークレットを作った

動機

 ネットを巡回して気になるアスキーアートを見つけたとき、とりあえず保存しておこうと考えるのだが、どうも都合の良い方法が見つからなかった。昔はFireFoxにQuickNoteというメモ帳アドオンを入れてテキストファイルに保存しておいたのだが、検索が面倒だし確認がなによりも面倒くさい。そこで以前に作ったアスキーアートのデータベース(アスキーアートのデータベースを作る - Webと文字)にタグを付けて放り込むようなブックマークレットを書いた。

 使い方は簡単。気になるAAを選択してブックマークレットを呼び出すだけ。あとは登録画面が出てくるのでタグを全角スペースで区切り、OKを押す。これで登録終わり。確認はASCII and Shift_JIS Art DataBaseで見る。詳細はAA登録ブックマークレット-α解説を見て欲しい。

技術的メモ

postで投稿

 AAの中にはかなり大きなものもあるのでgetで送ると文字数制限に引っかかる。その為、iframe+formでpostで投稿するようにした。実際にはiframeを使用したクロスドメインPOST - 文殊堂スクリプトを少し改造したものをそのまま使用している。IEではクロスサイトスクリプティング警告に引っかかる時がある。

真ん中に表示

 ブラウザのウィンドウの真ん中に表示させるにはウィンドウの幅と高さ、スクロール量、要素の大きさなどを考慮しなくてはならない。ウィンドウや要素の大きさブラウザのスクロール量を取得するには? | Diaspar Journalユーザーのウインドウ表示幅を取得するjavascript - WEBデザイン BLOGを参考にした。

var bp = getBrowserParam(w,d); //windowとdocumentを与えて、その幅と高さを返す。
div.style.left =( d.body.scrollLeft || d.documentElement.scrollLeft) + bp[0]/2 - (div.offsetWidth/2) +"px";
div.style.top  = (d.body.scrollTop  || d.documentElement.scrollTop )+ bp[1]/2 - (div.offsetHeight/2) + "px";
選択範囲を取得

 基本的にはJavaScript Rangeの使い方 - とみぞーノートを参考にした。以下のように取得できる。改行も考慮される。追記:2009:12:16 そんなに単純には行きませんでした。AAがpreで入っているのか、タグで改行を表現しているのかで違ってきます。この判定はうまい方法が思いつかなかったので、改行コードの数で判定しています。詳しくはコード*1を見てください。ページによって改行が表現されない不具合はこれで解消したと思います。←Operaでは上手くいかない。

//IE
var range=document.selection.createRange();
var selectionText = range.text;

//IE以外
var selectionText = window.getSelection();
FireFoxのバグ?

 document.createElementで要素を生成してappendChildで追加したあとに、innerHTMLで要素を追加するとバグる?

var parentElement = document.createElement('div');
var div1 = document.createElement('div');
var div2 = document.createElement('div');

parentElement.appendChild(div1);
parentElement.innerHTML +="<br>";//createElementで生成してappendChildする
parentElement.appendChild(div2); 
IEのバグ?

 preタグに入ってしまった文字はinnerTextで取得しても改行は消滅する。range.textを使用する。

通信の流れ

 アスキーアート及びタグはencodeURIComponentでエンコードし、php側でrawurldecodeでデコードする。MySQL文に追加する時はaddslashesでエスケープし、取り出すときはstripslashesでエスケープを解除する。←論外でした。このせいでいくつか登録したAAが変に登録されてしまいました(´・ω・`)。
 JavaScriptから送る文字はencodeURIComponent関数でURLエンコードし、最初に書いたようにiframeを使ってpostで投稿する。php側では$_POST['aa']で受け取ったあとにget_magic_quotes_gpc関数を実行して返り値を確認する。これがtrueならばget、postで受け取った引数は既に「'」が「\'」の様にエスケープされているのでstripslashes関数を使用して元に戻す。

$aa = get_magic_quotes_gpc() ? stripslashes($_POST['aa']) : $_POST['aa'];

その後にURIデコードをするためにrawurldecode関数を実行する。

$aa = rawurldecode($aa);

MySQLに接続し、クエリの構成前に引数をmysql_real_escape_string関数を使用してMySQLエスケープをする。この関数はmysqlに接続している間にしか使用できない。

$aa = mysql_real_escape_string($aa);

これでデータベースに無事AAが登録された。タグの登録も同様の経路を辿る。これらのAAを取り出すときは特にエスケープやエンコードは必要ない。

文字化け

 確認画面が文字化けするけどそれは仕様ですsuccess!若しくはfailure...と英語で表示することで誤魔化した。

フレームを使ったサイト:追記:2009/12/23

 AAのサイトにはフレームを使ったサイトが結構多い(AA大辞典や2ch全AAイラスト化計画など)。しかし範囲選択を取得するためにはそのフレームのdocumentとwindowが必要で、厄介な問題である。特にフレームの中にフレームを含むようなサイトは単純な方法では上手くいかない。
以下のコードは再帰関数を使用してセレクションが存在するフレームのdocumentとwindowを取得するものである。ただし、セレクションが複数ある時は最後のフレームが優先される。

function frameSelection(document,window){
	var dw=null;
	var farr = document.getElementsByTagName('frame');
	if(farr.length >0){
		for(var i=0;i<farr.length;i++){
			var w= farr[i].contentWindow;
			var d=farr[i].contentWindow.document;
			if(dw = frameSelection(d,w)){
				
			}else{
				var s= !!(window.attachEvent && !window.opera) ? farr[i].contentWindow.document.selection.createRange().text : farr[i].contentWindow.getSelection().toString();
				if(s != "") {
					dw= [d,w];
				}
			}
		}
	}
	if(dw){
		return dw;
	}else{
		return false;
	}
}

 

Googleが日本語のIMEを出した


Google Japan Blog: 思いどおりの日本語入力 - Google 日本語入力

思いどおりの日本語入力 - Google 日本語入力
本日、Google 日本語入力 (ベータ) をリリースしました。

 いやぁすごい(´・ω・`)。ギコもモナーもやる夫もジョルジュ長岡もちゃんと一発で変換できる。そのくせに長文を打ってもちゃんと文節変換されるから普通の使用にも全然問題ない。TabでSuggest選択もいい。地味に便利だ。スペースキーは遠いもの。数字を進数変換する機能も誰が考えたのか冴えていると思う。
 でも私が一番すごいと思ったところは、このIMEが明示的な辞書作成を必要としない所だ。ユーザーが意識的に言葉を登録する必要がない。SocialIMEのように個人の登録を集める必要も、MSIMEのように流行に合わせて辞書を作り直す必要もない。辞書の元となるのは普段使ってるGoogle検索だ。つまり私たちが検索した言葉がそのまま辞書の素材になるわけで、言い換えればGoogleで検索することがすなわち辞書作成になるのだ。そうしてできた辞書をソフトの更新と同時に配布すれば、流行も、一般的な語句も、すべて含んだ完璧なIMEができる。コレはすごい。
 でもまぁ不満がないわけではない。Suggestの機能は長文を打つ時はウザく感じる。顔文字がないのも残念でならない。AAで検索したら画像がアスキーアートになる日本Google法人とは思えない。あとはそう、こんな面白そうなプロジェクトに俺も参加させろって事ぐらいかな。

どうやらデフラグさんが本気出してきた

           [゚д゚] デフラグカイシシマス。
           /[_]ヽ
            | |

ちょっと高機能なデフラグさんだよ!文字をデフラグするよ。JavaScriptだからさくさく動く…と思うよ!IEでは動かないよ!文字列の値をcharAtでとるとIE7でも動くようになりました。

[゚д゚]:http://www28095u.sakura.ne.jp/defrag/

本来の用途としては

WebFontを日本語で - Webと文字においてある文章内で使用しているすべてのUnicode値が必要になったために作成した。

予測変換とリクエストの件数

Social IME: 予測変換API


1. 予測変換
 引数stringとして変換したいかな文字列を次のように渡します(実装時はURLエンコードしてください)。
 http://www.social-ime.com/api2/predict.php?string=よそくす
 レスポンスの形式はプレーンテキストで、次のようになります。

   / ̄\
  |  ^o^ | < これでぷろぐらむつくります
   \_/
   _| |_
  |     |

   / ̄\
  |  ^o^ |  計算中
   \_/
   _| |_
  |     |

   / ̄\
  |  ^o^ | < できました
   \_/
   _| |_
  |     |


=>JavaScript IME for 日本語(Japanese)


**
予測変換では幾つかの動作がソフトと異なります。候補が表示されている状態でShift + q,w,e,r,t,a,s,d,f,gを押すとその候補をセレクトします。Shift + zで次の10個分に行きます。Shift + cでEnterと同じように確定できます。スペースキー押すとかな漢字変換になります。矢印キーやEnterキー、BSキーもそのまま使えます。
**

でも…

   / ̄\
  |  ^o^ | < リクエスト件数が10倍になりました
   \_/
   _| |_
  |     |
         / ̄\
        |     | < なんというF5アタック
         \_/
         _| |_
        |     |

ちなみにスクリプトが送ったリクエストは20日(-22:30)に約1600件、21日(−22:30)に約5500件でした。

〜次に実装すること

  • バックスペース
  • 暗号化→aSSLを使う

ウェブベースのかな漢字変換まとめ

1.まとめ

ChaIME: Term-based Yet Another Input Method Editor
開発者のページ:生駒日記
WebAPI:×
Social IME 〜 みんなで育てる日本語入力 〜
開発者のページ:nokunoの日記
WebAPI:○
Ajax IME: Web-based Japanese Input Method
開発者のページ:Taku Kudo
WebAPI:×
Yahoo!デベロッパーネットワーク - テキスト解析 - かな漢字変換
提供している企業:Yahoo!Japan
WebAPI:○

3つは個人で開発していて、そのうち一つがAPIを公開している。残りの一つはYahoo!が公開しているものでAPIも公開している。公開していないところもブラウザ上で変換する形式を取っているから勝手に使えということなのかもしれないけど、アナウンスしてくれると助かる。

2.こういうのはできるのだろうか

 アラビア語なんかは子音字が無いそうです。(参考資料1)子音字なしのローマ字を打って日本語の文章に変換することはできるんでしょうかね?

きょうはいい天気です。
 あり-> kyouhaiitennkidesu.
 なし-> kyhiitnnkds.

3.中国語の変換にも応用できないだろうか

 ピン音文字列を飛ばしたら漢字文になって戻ってくるようなフリーの変換サーバーがどこかに無いかな。Microsoft Pinyin IME3.0見たいのをWeb上でやりたい。

ブラウザでかな漢字変換

1.『はてブが付いたよ!』「やったねたえちゃん!」

               , r::':::::::::::::..ヽ 、
             /::::::::::::::::.:::::..::::::::ヽ、
                /:::イ::N、..iv、..:..:..:.:.::::::::vr−、
/⌒ヽr−、  _    /i::;;;:/十ヾN-、Ni:::i!:::::/    i
たや i! ::.'´ / ・ヽ  ' Y:::i ⌒ , ⌒`}::N)::i 付は .!
え っ i!     ,, `●,/N:::!.'' r-v, '' ,}rrノ::| い.て |
ち.た i!  r−、 ーrイ:/ .i!N;;>`ニ イ/ト;;;< た.ブ |
.ゃね > ヽ :::、==´=}   _,.r'r'| ,./ ` >! .よ が.|
ん  i!ヽr=>   `ヘ>V⌒)|| ,iト、_,/  ノ/、 !!  /
! ./  |'/,フ7   /⌒i:/ト、ト` ヾ三テイ'  .iー'
`ー'   、' ' レi     ノ'::i`ーi  ヽ´ ./.:   |

拙作のJavaScript IMEにブックマークが4つ付きました。詳しくはSocialIMEがちょっとヤバイ - Webと文字を見てね。

2.SocialIMEの人がAPIを考えてくれるようです。

Javascrit IMESocial IME APIに対応! - nokunoの日記


予測変換機能も近々API公開します。あと、

文節のひらがなを返すようにしてほしいです。

なるほど。これは必要そうですね。

    ___
   ,;f     ヽ         
  i:         i   ありがたやありがたや
  |        |  ///;ト,
  |    ^  ^ ) ////゙l゙l;   
  (.  >ノ(、_, )ヽ、} l   .i .! |   
  ,,∧ヽ !-=ニ=- | │   | .|
/\..\\`ニニ´ !, {   .ノ.ノ
/  \ \ ̄ ̄ ̄../   / .|

3.バグリスト

  • FireFoxではエディタ領域の中身が空っぽだとおかしくなる。
  • IEだと下線が付かないときがある。

4.その他やりたいこと

 AAの変換なんて面白そう。台詞を入れれるようにしたり。phpSQLの勉強しよう。

             ∧∧<br>
ギコ →  〜′ ̄ ̄(,,゚Д゚)<br>
         UU ̄U U<br>

          ,,,,,,,,,,,,,,,∧,,∧  <br>
フサギコ → 〜′,,,,,,,,,,ミ,,゚Д゚彡<br>
         UU"""" U U   <br>

               ,,,,,,,,,,,,,,,∧,,∧   / ̄ ̄ ̄<br>
フサギコ::ゴラァ → 〜′,,,,,,,,,,ミ,,゚Д゚彡<  ゴラァ<br>
              UU"""" U U    \___<br>

**改行は<br>で帰ってくるのでinnerHTMLでそのまま要素に入れればいい。

 Google AJAX Language APIなんかを使って多言語変換したり

明日は晴れるでしょう

Tomorrow will be cleared.
Mañana se borrará.
غدا سوف يتم سدادها.
明天将被清除。
Morgen zal worden gewist.
Demain, il sera effacé.
내일은 삭제됩니다.
Завтра будет очищен.
Domani verrà cancellata.
Morgen werden gelöscht.
明天將被清除。
明天将被清除。