What is really ambiguous?

UAX#11: East Asian Width という、なんとも適当なドキュメントがある。

いわゆる全角・半角という概念を Unicode 上に持ってきて、どの文字が Wide でどの文字が Narrow か云々、というのを定義してたりしてなかったりする実に適当なドキュメントである。

まず幅についてのクラス分けをしている。

  • Wide: 常に全角の字。「あ」とか「漢」とか
  • Full width: 半角と全角両方ある字のうち、全角の方。全角の「A」とか
  • Narrow: 常に半角の字
  • Half width: 半角と全角両方ある字のうち、半角の方(この分け方だと U+0020-U+007F は Half width になる気がするが、実際にはそれらは Narrow となっている。Basic Latin は特別扱いなのかもしれない)
  • Ambiguous: よくよく考えてみれば全角にする理由は特にないけれど、東アジアの野郎どもが彼らの文字セットに入れてたので、慣例的に全角になるような雰囲気の字。つまりこれは、東アジアの野郎の慣例を抜きにすれば別に半角でも構わない──よって、曖昧
  • Neutral: 東アジアの野郎どもが知らない字

Unicode のすべてのコードポイントにこのクラスのいずれかを割り当てる。それにより、端末のような縦横のグリッドの中に文字を収めていくような表示デバイスについて、グリッドいくつ分を消費するのかの判断のもとになる、はずだった。Wide/Full width/Narrow/Half width については悩む必要はないのだが、問題は Ambiguous と Neutral なのだ。

Ambiguous な文字の幅の問題は、たとえば vim で「●」とかの表示が変なんですけど! → set ambiwidth=double しなさい的なお約束がググれば出てくるアレである。これについては、Unifont においては更に厄介な問題となっている。

いずれの端末エミュレータにしても、あるいは vim のようにアプリケーション側で Ambiguous な字をハンドリングするにしても、だいたいは A 属性の文字全体を半角として扱うか、それとも全角として扱うかの二者択一になると思う。ところがこれに対して Unifont では、半角のつもりの A 属性の字と全角のつもりのそれが混在しているのだった。たとえば U+2460、丸数字の1は Ambiguous 属性だが、Unifont はこれを全角幅としてデザインしている。一方、U+0391 からの Greek アルファベットもまた Ambiguous であり、そのグリフは半角幅である。

すなわち、端末で Unifont を使っている限り、Ambiguous な字の取り扱いを全角にしようが半角にしようが、その半分は常に正しく表示できない。

なんだこれ。どうしろってんだ。

次に Neutral な文字で、こちらはさらに輪をかけて混沌としている。

ぶっちゃけた話全角・半角の違いって、プロポーショナルフォントを描画できるデバイスとか、描画を自分で完全に制御できるプログラムではそれほど重要ではない。むしろ端末のような表示デバイスで「どう表示するか?」に直結してくる問題だと思うのだけど、冒頭の UAX #11 はどういうわけか端末に対する記述は全くない。「文字を幅によってクラス分けしてみたよ! やったね!」以上のことを何も教えてくれない。

なんとか判断してみるにしても、Neutral はつまるところ Neutral である。幅の情報は一切含んでいない。それを半角・全角のいずれかと判断するためのいずれの理由も見出すことはできない。とりあえず実際のところ、おそらく既存の端末エミュレータはそれを Narrow 文字として取り扱うと思う。それが楽そうなので。

一方、Unifont はどうなっているかというと、たとえば Devanagari の文字はすべて Neutral だが、なんとすべて Wide としてデザインしてあるのである。16 ピクセルでよく描いたなーと思わせる良いグリフなのだけど、しかし以上のような状況を勘案すると、今のところ既存の端末エミュレータの中のテキストエディタ上でそれを正しく表示したり編集したりはできないんじゃないかと思う。

えーと…なんだこれ。どうしろってんだ。

crisped edge #2

Linux 向けの、実用に足る unicode ベースのビットマップフォントが、もしかして未だにないんじゃないのか? という問題を解決したい。

実のところを言えば、unicode ベースのビットマップフォントというのはすでにある。

これは Basic Multilingal Plane の全域をサポートした 8×16/16×16 ドットのビットマップフォントである。

しかしながら、いくつか問題があるのである:

  • Latin な文字について、x height が高すぎる。そのため大文字と小文字の判別がつきづらい
  • 0 に斜め線が入ってない
  • CJK な文字について、それらは Wen Quan Yi Project が作成しているフォントから提供されているのだが、漢字部分はともかくひらがなやカタカナがけっこう見づらい

こういうわけで、日常的に端末で使えるかどうかというと微妙なのであった。

一方で、GNU のプロダクトである。グリフの元データとなる hex ファイルや、さまざまなユーティリティプログラムも込ですべてが公開されている。フォークして、これらを元にさせていただきつつ新しいのを作ればよいのではないか。そうだそうだ、そうしよう。

* * *

ということで、新しくグリフをデザインしたり、懐かしの jiskan16 を持ってきたり、東雲フォントをもってきたりいろいろした結果、できた。名前は特に意味はないが、UFO とした。地球のフォントに飽きたところよ。

Latin 文字

Latin 文字

ソースコード

ソースコード

日本語の文

日本語の文

もちろんこれで完成というわけではまったくない。既知の問題として CJK のグリフがバウンディングボックスを目一杯使っているのでスクリーンにぎっしり日本語の文章を表示するとかなり見にくい。もしかしたら CJK グリフは一回り小さい wqy なビットマップフォントから改めて持ってくる必要があるかもしれない。

とりあえず個人的に使ってみつつ微調整したい。

crisped edge

日常の作業は端末エミュレータ上でのそれがほとんどなのである。

したがって、一日の大半は黒い画面上の白い文字を睨んでいるわけなのだが、ここに問題がある。ナウい OS というものはなんでもかんでもアンチエイリアスがガンガン効いたフォントをレンダリングする。一方で、端末は 10pt とか 12pt とか、けっこうこまい文字で使っている。するとこのサイズでのアンチエイリアスの効いた文字はむしろ見にくいのだ。これははっきり言ってストレスがたまる。

つまり、端末ではいわゆるビットマップフォントを使いたいということだ。しかしこれは意外なことに Linux のほうが面倒くさい。Windows(というか cygwin の mintty)では単に Terminal のような、fon 形式のフォントを選択するだけなのに、Linux の場合はそもそもビットマップフォントを選択できなかったりする。

しかしながら、さすがにフラストレーションが溜まってきたので、なんとかしたい。ここで言うなんとかしたいというのは、端末に bdf フォントを認識させるにはどうしたらいいの? 的なレベルではなく、Linux 向けの、実用に足る unicode ベースのビットマップフォントが、もしかして未だにないんじゃないの? というソレに対するアレである。

RDP 8.0 #3

以前、Windows 7 上で RDP 8.x を有効にする更新を入れたら満足に動かなかったという記事を書いたのだが、今月になって Windows Update の中にそれの更新版が来ていたようだ。以前リンクしたMSDN のドキュメントも更新されているが、 KB2574819 KB2857650 KB2830477 KB2913751 KB2913751(optional) が入ってれば良いという。うちのマシンには全て入っていた。

ということで、ホスト側 PC で改めて RDP 8.x を有効にした上で適当な別のマシンから接続し、こうやって記事を書いている。以前書いたような不具合は今のところ見られないようだ。

ありがとう MS さん!

exodus from presto opera

Blink Opera が version 20 に達した今もなお、うちの PC のメインブラウザは Opera 12.16 である。

2014 年 3 月の今現在においても、とりあえず、Presto Opera でもそれほど困る場面は多くはない。もちろん CSS3 をふんだんに使って Opera じゃ全然満足に表示できないとか、javascript の JIT がらみで Opera ごとすぽーんとプロセスが落ちたりするサイトはじわじわと増えてきている感はある。しかしメインブラウザの地位を脅かすほどのストレスをもたらすほどでは、まだない。

とは言ってもこれは時間の問題なのも確か。やがては、Presto Opera から移行しなければいけない時期が来るのは言うまでもないのである。その時のために、先立ってなぜ Presto Opera でなければいけなかったのか、の個人的な理由をまとめておこう。つまりこの要件を満たしてくれるブラウザがなければ将来であっても移行できないし、満たしてくれさえすれば今すぐにでも移行できる。

  • メーラがブラウザと一体化している: 正確にはブラウザと一体化しているのがメリットなのではなく、さまざまなアカウントのメールや、メーリングリストや、RSS フィードや、IRC や、その他もろもろをひとまとめにしてくれる。これはまあ必ずしもブラウザと一体化している必然性は実はあまりない。つまりまとめて面倒見てくれさえすればブラウザと一体化してなくても別によい
  • キーバインディングの自由度が高い: vi/vim に魂を捧げた人たちにはついていけないけど、hjkl にページスクロールを割り当てたり程度はする。また ctrl+H に [cci]Back | Close page[/cci] といった条件判断を伴うバインディングをする。あるいは、コンテンツ領域外、例えばアドレスフィールドに対して [cci]ctrl+N/P/F/B[/cci] を割り当てたりする。この全てを柔軟に聞き届けてくれるブラウザは今もなおそれほど多くはない
  • 充実したデバッガ: Dragonfly は様々な改善を経て、今では十分に使えるデバッガになっている。特にけっこう自由にキーバインディングできるのが非常によい。他のブラウザが持つデバッガはいずれもここまでの自由度は持っていない。持っていたら教えて下さい

願わくば、数年後にこれらの要件を Blink Opera が満たしていてくれればとても素晴らしいのだけど。

moon-crystal-power

赤福プラスをエクステンションのパッケージにビルドするための準備をした。

wasavi のビルドは ant を使っている。これは junit と selenium を使った機能テストと統合しているためである。一方で赤福プラスの場合はその手のテストが難しい。つまりやることの大半がふたばのサーバに対する応答なわけで、継続的な自動テストを行わせるにはふたばのサーバとして振る舞うダミーが必要になるのだが、それを真面目にやるのはなかなかな大仕事だということだ。もしかしたら junit & selenium とか、そういう話じゃなくなって、まったく別の仕組みを使う必要が出てくる可能性すらある。

となるとビルドに ant を使う必要もないわけで、赤福プラスのビルドは make で行うようにしてみた。ant でダメというわけでもないのだけれど、ビルドの手順を xml で書くのは、やっぱりとっても冗長なのである。まあきっと一般的には build.xml は直接触らず GUI でぽいぽい手順を指示するタイプのツールがメジャーなのであって、xml が冗長だ的な批判は的はずれなのだろう。xml を直接書くほうが頭おかしいのである。

しかし、それにしても、(シンプルなプロジェクトの)Makefile はやはりシンプルで、素朴で、いいなあと思いました。

hello, onedrive

OneDrive にも対応するようにした。OneDrive というのはかつては SkyDrive とよばれていた、Microsoft が提供するオンラインストレージだ。

そのためにドキュメントをいろいろ参照するわけなのだけど、まあいつもの MSDN クオリティというか、量自体は豊富なんだけど痒いところに手が届かない感じがしてあんまり使いやすくない。

あと、OAuth での認可を受けるためのエンドポイントのドメインが login.live.com なのに OneDrive(というか Live Connect)の API は apis.live.net だったりちぐはぐなのがなんか全体的にこう……なんと言ったらいいのか MS らしいですね!

かつて apple の親分だったおじさんが MS を評して曰く、「彼らはセンスが足りないんじゃない。『ない』んだ」とかなんとか、と聞いたことがあるけれど、もしかしたら、まったくもって、その通りなのかもしれない。

mkdir -p

[cci]$ mkdir -p foo/bar/baz[/cci] とすると、パスを構成する各ディレクトリがなかったら、自動的に作ってくれるという賢い動作をしてくれるじゃないですか。

オンラインストレージの API でそういう動作をしてくれるかという話。

dropbox は、まさにそういう動作をする。というより、それ以外の動作(存在しないパスが指定されたらエラーにする的な)を指定できない。ファイルをアップロードする REST エンドポイントの構造は
[cci]https://api-content.dropbox.com/[VERSION]/files_put/[ROOT IDENTIFIER]/[PATH][/cci]
というものだ。VERSION は現在は [cci]1[/cci]、ROOT IDENTIFIER はユーザーに割り当てられた空間のどこをルートにするかという指定で、[cci]dropbox[/cci] は空間全体のルートを表し、[cci]sandbox[/cci] は登録したアプリケーションごとの専用のディレクトリを表す。

実際には、[cci]sandbox/foo.txt[/cci] は 例えば単に [cci]dropbox/app/akahukuplus/foo.txt[/cci] と同じ場所を示す。sandbox を指定している限り、誤っておかしなディレクトリのファイルを読み書きしてしまうことはないというシンプルだけど確かに効果のあるからくりだ。

そして、PATH で目的のファイルのパスを指定する。つまりエンドポイント URL に対象となるパスの、その階層構造も含まれている。

一方で、Google Drive はどうかというと、そもそも階層構造の中の特定の位置を文字列で示すという概念がない(気がする)。[cci]/foo/bar/baz.txt[/cci] というパスの baz.txt の情報を得るには、ルート上のファイルの中で [cci]foo[/cci] を検索し、そのディレクトリの下にある [cci]bar[/cci] を検索し…という処理を再帰的に行う必要があり、そのつど REST のネットワークアクセスが発生する。非常に、恐ろしく無駄だ。なぜ、パス文字列という概念を google が導入しないのかはわからない。そういう環境なので、そもそも mkdir -p 的な動作を期待できない。

 * * *

というわけで、akahukuplus のバックエンドの gdrive ファイルシステムモジュールでは、例えば [cci]/dat/b/1234567890.jpg[/cci] というパスにファイルを保存したい際は mkdir オプションを指定できるようにした。この値が [cci]auto[/cci] で、かつ指定のパスが存在していなかった場合は mkdir -p 相当の動作をエミュレートしてから保存処理を再度実行する。

OAuth 2.0 in a browser extension

アプリケーションが OAuth 2.0 における認可を得る際の手順は、そのアプリケーションの性格によっていくつかの系統に分かれる。このあたりは google のドキュメントなどにまとまっているのだが、つまり

  • Web サーバが主体になる場合
  • ネイティブアプリケーションが主体になる場合
  • ブラウザが主体になる場合

と大別される。いずれにしても、基本的には

  1. 認可を得るための一時的なコードを得るための最初のリクエスト(これは正確には認可を得るためのページを web ブラウザで開くだけの処理だ。認可を得るかどうかはそのページを見ているユーザが判断する)
  2. そのコードをアクセストークンへ交換するための 2 度目のリクエスト
  3. 得たアクセストークンが正しいものであるかの確認のリクエスト(これは認可を得るための必須の処理ではないけれど、普通は行う)

を順に行うという流れに従うのだが、何が主体になるかで微妙なところが微妙に違う。具体的には、一時的なコードを得るための経路や、アプリケーションごとに割り当てられるクライアント ID、クライアントシークレットの扱いなどだ。

  • Web サーバが主体になる場合、リクエスト 1 の際にリダイレクト URL を指定する。この URL として、Web サーバの管理が及ぶ適当なものを与える。アプリケーションを利用するユーザが承認すると、ユーザが見ているブラウザのページは自動的にリダイレクト URL に転送される。このとき、URL に追加されているクエリ文字列に一時コードが含まれているので、Web サーバはそれを得ることができる。
  • ネイティブアプリケーションが主体になる場合、Web サーバのようには行かないので、2 つの特殊なリダイレクト URL のどちらかを使用する。[cci]urn:ietf:wg:oauth:2.0:oob[/cci] を指定した場合は、何やら呪文のようなアドレスだが、つまりリクエスト 1 の時点で単にページ上に一時コードを表示して一旦終了ということだ。これをユーザが手作業でアプリケーションにコピペする。[cci]http://localhost[/cci] を指定した場合は、あらかじめネイティブアプリケーションがローカルマシンの適当なポートをリスンしておき、その経路を通して一時コードを得る。
  • ブラウザが主体になる場合は特殊で、一時コードをすっ飛ばしていきなりアクセストークンを得ることができてしまう。ただしこれで得たアクセストークンは非常に短い有効期間しか持っていない。

と、ここまでは、上のドキュメントに書いてあることそのままなので、いいのである。問題は、ブラウザに組み込まれるエクステンションの場合はどのシナリオを選択すればいいの? ということだ。

エクステンションは単なるブラウザ上のスクリプトよりも多くの機能を持っているので、ブラウザだからといってブラウザ主体のシナリオのみ許されるわけではない。それどころか、とても興味深いことに、ブラウザのエクステンションは工夫次第によって 3 つのシナリオを全てを実装できてしまう。そして同時に、いずれの方式にも若干の気をつけるポイントも出てくる。どれを選択するべきなのか?

 * * *

Web サーバシナリオを選択した場合、適当な外部の Web サーバを用意し、リダイレクト URL を作る。エクステンションはリクエスト 1 によるアクセスにまつわるブラウザのページ遷移を監視し、リダイレクト URL に達した時点でクエリ文字列を得ればいい。これに嫌な点があるとすると、一時コードを含んだ URL が認可サーバからブラウザに来るパスの他にもう一度、ネットワークを流れてしまうということだ。そのためリダイレクト URL を SSL 経由にしたりする必要があるが、これはなかなかコストのかかる作業である。

ネイティブアプリケーションシナリオを選択した場合、外部 Web サーバは不要だ。ブラウザのリダイレクトを [cci]oob[/cci] にし、ページに一時コードが表示された時点でエクステンションからそれを読み取ればいい(このへんがちょっとハックでストレートじゃないのが気になるが)。一方で、これは Web サーバシナリオでも実質的に同じだが、クライアント ID とクライアントシークレットをエクステンションのパッケージに内包させなければならないのが気になる。全然シークレットでもなんでもなくなってしまうのである(これは、上のドキュメントにも書いてあることであるが、ネイティブアプリケーションシナリオにおいてはどうにもならない副作用だ)。

wasavi は dropbox のクライアント情報に関しては、エクステンションのパッケージ内に暗号化して含めるようにしている。これはプレーンな状態で含めようとしたら dropbox の審査で暗号化しろよ! と怒られたのでそうした経緯があるのだが、しかしまあ、ブラウザのエクステンションである。すなわち javascript コードがそのまま入ってるのである。コードを読めば、どう復号するのかはすぐわかっちゃうわけで、意味があるのかどうかはいまいちわからない。

最後のブラウザシナリオでは上に書いたような問題点は一見ない。アクセストークンを得るためにクライアントシークレットは不要だ(クライアント ID は必要なのでこのあたりの問題が根本的に解決するわけではない)。しかしこの形式で得たアクセストークンは前述したとおりすぐ切れる。wasavi では Google Drive との接続はこの方式を用いているのだが、だいたい 1 時間もするとすぐに再認可のページに飛ばされてしまう。これは使い勝手が良いとはちょっと言えない。