本文へジャンプ

Alpha の бесполезный な日記

2008-12-31

もうすこしがんばりましょう

今年は...、いろいろダメだったorz

Alpha 関係では 2 月に IVS 対応したのが一番印象に残ってる。その後はゴチャゴチャやりながら Python に手を出したりしたけど、本業の関係であまり時間が取れなかった

IVS 実装でお世話になった方々、Boost.Regex の翻訳を直してくださった方、ありがとうございました。来年はちゃんとします

ところで、Python やめて、やっぱ Active Script にします。詳しいことは来年...

thrown: 2008-12-31 11:31

帰省します

正月休み中のお供たち(画像貼るのマンドクセ):

2008-12-18

Boost.Regex のドキュメントを翻訳しますた \(°∀°)/ その 2

10 日に出した Boost.Regex の翻訳を読んだ人からコメントいただきました。翻訳上の誤り、日本語の誤り(ぇ)を修正 → 前と同じ名前のファイルです

thrown: 2008-12-18 0:53

2008-12-10

Boost.Regex のドキュメントを翻訳しますた \(°∀°)/

正直ほとんど時間が取れないんだが、毎日チマチマやって全部訳した。やっぱ Boost の中ではこのライブラリが一番好きだな。間違いとかあったら教えてください(ついでに直してください)

Dedicated to Dr. John Maddock.

thrown: 2008-12-10 0:28

2008-10-27

10 月ももう終わってしまう

もー無理。10 月末とか不可能なこと言ってごめんなさい。進んでないわけじゃないんだけど、あんまり面白くないというか(え)。他にやりたいことがあるんだが、Alpha じゃなくて Ascension の話ばっかだったりする

  • そろそろ Text Services Framework 対応させたい。もう十分に下調べしたし...
  • (ついでに Text Object Model も)
  • 強調表示とか折り返しに関連して CSS 3 を見ていたが、XSL のほうがいいのかな。というか、XSL いいな
  • Alpha/Ascension で使ってる Boost のライブラリドキュメントの翻訳をしてみようかと思っている。Python 、Regex 、Test 、(今は使ってないけど)Xpressive。cppll でも翻訳されてるけど、2 つ以上あってもいいだろ。cppll のは機械訳っぽいところがあるし、個人的にはなじまなかった
  • その翻訳データを XSL で作って PDF でばらまきたい。HTML/XHTML はもう嫌だ(JS20 で懲りた)
  • Boost.Regex の PDF 版ドキュメントが XSL-FO っぽかったのに影響されてるだけなんだが
  • そんな時間あるのかな? 本業のほうは、もうすぐ大阪冬の陣
thrown: 2008-10-27 21:33

2008-10-19

Ambient のコードをノロノロ書く

落ち着いてきたので Ambient のコードをノロノロ書く。Boost.Python が分からないから書けない、ということはなくなったのだが、メモリリークがどうしても消えない。気にしなくていいらしいという話もあるんだが、よく分からず。バージョンを上げたら直るかもしれないし、その前に私のコードがおかしいというのも考えられる。いずれにせよ数が多いんで、他のリーク箇所が埋もれて分からなくなってるのがまずい。早めに手を打つ

今は Python で実装可能なところを C++ から移植している最中で、進捗は 40% ぐらいかな。C++ と Python の配分は Emacs と KaaEdit を見ながら調整中。しかし、スクリプト書いてて思うのは「Lisp/Scheme いいなぁ」ってことばっかだな。まぁ Python も国際化がしっかりしてて面白いんだが。で、話が飛ぶんだが、Python で実装する部分はローカライズも Python でやるようにした。gettext を使う。↓ 参考ページ

ユーザがスクリプトを使って機能を拡張する場合、ローカライズの手段も提供すべきだと前から考えていた。Firefox なんかは結構凄いんだが、テキストエディタでそこまでやっているものはあまりない。Python + gettext だと Ambient では特に用意するものもないんで助かった

thrown: 2008-10-19 19:17

2008-10-11

転勤しますた

10 月になったらしいんで、予定通り大阪に移動しますた。前にいたのは三重県。(他人が買った)赤福を食べながら移動

開発用 PC はまだ段ボールから出してなかったり。マジで進まんなぁ

うーむ

あと、7 月末に買ったばっかの ThinkPad の BackSpace キーが効きにくい。なんか反発してくる。悲しい

thrown: 2008-10-11 16:13

2008-09-30

アンドゥ・リドゥと複合編集

テキストエディタの中でもずいぶんと基本的な機能だが...、最近設計を見直した部分について書いてみる。これは元々 Python に API を公開する段階で、インターフェイスの改良が必要と判断したところから始まった話で、結構深い。アンドゥ・リドゥというのはバッファに加えた変更のロールバック機能。実装はスタックを使ってどーのこーのとかいろいろあるだろうが、そういった実装の詳細ではなく、複合編集の設計について以下の 2 つを書いてみようと思う(ここで書く話は現時点での Alpha の最新バージョンで実験中なので、ひょっとしたら間違ってるかもしれないんで、参考にする場合はそこは自分で判断してください)

  • 2 つの戦略 : compound change と undo boundary
  • ロールバックが部分的にしか完了しない可能性

「複合編集」というのは “compound change” の和訳を私が勝手に作ったんだが、要するに複数の編集操作をまとめたものだ。例としてクリップボードからの貼り付け操作を考えよう:

void paste() {
  const std::string s(get_clipboard_content()): // クリップボードからデータを取る
  delete_selected_text();                       // 選択されているテキストを削除する
  insert_text(s);                               // テキストを挿入する
}

この実装はテキストの削除と挿入の 2 つの操作を実行するが、エンドユーザは単一の操作であると認識する。つまり貼り付け操作の直後にアンドゥを実行すると最後に挿入されたテキストが削除されるだけでなく、最初に削除したテキストも元に戻って欲しいと考える。この場合貼り付け操作は 1 回の削除と 1 回の挿入からなる複合的な操作として実装する必要がある

後で元に戻せるように、エディタは編集操作をリストやスタックに詰め込んでいく。実際のデータ構造は実装ごとに異なるが、ほとんどのエディタはアンドゥが実行されるとこのリストを逆にたどってバッファを以前の状態に戻す。リストやスタック各要素をどう編集操作に対応させるのか、私は既存のテキストエディタで使われている 2 通りのアプローチを調べた

連続したバラバラの編集操作をまとめて複合編集を構成する
この方法は多くのエディタで使われている(例えば JFace の IUndoManager や Qt の QUndoStack)。beginXXXXendXXXX のような関数呼び出しにはさまれた部分の操作が結合されて単一の編集操作となる。典型的な設計として、beginXXXX で内部カウンタを 1 つ増やし、endXXXX で 1 つ減らすようにし、カウンタが 0 以外のときに操作を結合する
編集操作の列を途中で分割して複合編集を構成する
この方法は Emacs で使われている。Emacs Lisp でバッファを編集する関数を呼び出すと、既定でそれらすべてが単一の編集操作として扱われる。その途中で undo-boundary を呼び出すと、そこが区切りとなり、前後の編集操作は別の複合編集となる

この 2 つの方法はまったく正反対のアプローチになっている。それぞれの欠点も正反対で、特に複数のオブジェクトから操作された場合に、

  • 1 番目の方法は、結合したくない操作が(他のオブジェクトの要求によって)結合される可能性がある
  • 2 番目の方法は、結合したい操作が(他のオブジェクトの要求によって)分割される可能性がある

という問題がある。Ascension は従来、この 2 つを折衷した設計にしていた。ただし基本的には 1 番目の方法で、内部カウンタを用いず、endXXXX で強制的に複合編集を終了させていた。また既に複合編集中の場合、beginXXXX は現在の複合編集を一度終了させて、新しい複合編集を開始するようにしていた

begin_compound_change();
command(); // (a)
command(); // (b)
end_compound_change();

begin_compound_change();
command(); // (c)
begin_compound_change();
command(); // (d)
command(); // (e)
end_compound_change();

この例だと [a, b], [c], [d, e] という 3 つの複合編集となる

当たり前だが、この設計では上に挙げた 2 つの欠点のどちらも解消されない。まだ私が 2 つのアプローチについて調べる前の話だ。コードを書くのが私だけであれば、注意していれば何となるが、ユーザの書いた Python のコードが動き始めるとどうにもならない

で、結構悩んだのだが、2 つの方法を折衷した設計で出直した。「基本的には Emacs が使っている 2 番目の方法を使い、必要に応じて 1 番目の方法を使う。ただし begin/end で作成された複合編集は undo boundary を無視する。」

command(); // (a)
command(); // (b)
insert_undo_boundary();
command(); // (c)
begin_compound_change();
command(); // (d)
insert_undo_boundary();
command(); // (e)
command(); // (f)
end_compound_change();

この例では、[a, b], [c, d, e, f] となる([a, b], [c], [d, e, f] の方が自然かもしれないが)

というか分析してみると、Ascension の中では 2 番目の方法だけでいけるようだ。選択範囲を置換(削除と挿入の結合)といった低水準機能は undo boundary を使って実装し、begin/end 式の複合編集は Python のコードみたいな高水準クライアントや、全置換コマンドを一気に元に戻すといったエンドユーザに近い部分で使うというポリシーで落ち着いた

やっぱり Emacs の設計は見ておいて正解だった。次の問題にも Emacs が関連する

もう 1 つの問題は「ロールバックが部分的にしか完了しない可能性」だ。ただし、これはナローイングが有効になっている場合だけだ。ナローイングでアクセス不能になったバッファ内の領域への編集操作は禁止されるが、アンドゥ・リドゥも編集操作であるのでこの制限が適用される。そういうわけでアクセス禁止領域を変更するアンドゥ・リドゥはエラーとなるべきだ。実際、Emacs と xyzzy はエラーを出す(やってみよう)

さて、このナローイングと複合編集が組み合わさると話がややこしくなる。複合編集の途中の操作がアクセス禁止領域を変更しようとしたらどうなるか。不可分な編集の列 e1, e2, ..., eN で構成される複合編集があるとして(eN が最初に元に戻される編集)、途中の編集 ei(ただし i < N)がアクセス違反を起こす場合、アンドゥはそこで停止してしまう。ei がアクセス違反を起こすかどうか最初から分かっていればいいが、ほとんどの実装では eN 以外については不可能か非常に難しい。これがロールバックが完全に完了しないケースで、この場合 Emacs 、xyzzy では ei の直前で停止し、エラーを出す。その後でナローイングを解除してアンドゥ・リドゥを繰り返すと、停止した部分で複合編集が分断されていることが分かる。つまり、e1, e2, ..., eiei+1, ei+2, ..., eN に分割される

この動作はプログラム的には変な感じがするが、アンドゥ・リドゥがエンドユーザ対話的な機能であることを考えれば、Emacs/xyzzy の動作も納得のいくものだと思う。そういうわけで Alpha もナローイングを実装した直後から同じ動作をするようにしてある

というようなことを、数週間やっていたのだった

thrown: 2008-09-30 02:30

2008-09-24

ごぶさたしておりました

1 か月近く空いてしまった。全然時間が取れん。でも、こんなもんかな...。以下、近況

  • テキストエディタの設計理論(本当はエディタに限らないが)として、アンドゥ・リドゥが完全に終了しない場合がある。特にナローイングが使えるエディタは。Emacs も Alpha もこの問題に対処しているが、コミットロールバックを保証するにはどうするのか
  • ↑ さっぱり分からんな。後で書くかも
  • throw()ASCENSION_NOFAIL/*throw()*/
  • 例外安全のためのリファクタリングがゴチャゴチャ
  • japanese.cpp のコンパイルに(私のマシンで)10 分かかるのはどうにかならんのか
  • MingW でのコンパイルはまだまだ。つーかヘッダが無いのはどうしたらいいのか
  • boost.python.class_.add_property とかで元の C++ 関数がオブジェクト返す場合エラーになっちゃうぜ → boost.python.make_function で戻り値のポリシーを設定したらいいのね
  • 強調表示とかの色指定で、RGB の他に HSV 、CMYK 、SVG 定義の色名が使えたら面白そうだな(ネタ元は Qt の QColor
thrown: 2008-09-24 11:50

2008-08-31

クリップボード機能の強化

遅い帰省中。片道電車 5 時間というのがなんとも...。 暇なんで Temple of Shadows から影響されて読み始めた『天使と悪魔』を読む。というか読んだ。Dan Brown というと『ダ・ヴィンチ・コード』の方が有名だが、順番はこっちが先なんで、こっちから読んでみることに。ANGRA の “Temple of Shadows” には 3 曲目にそのまんまのタイトルで入ってるが、Rafael Bittencourt がどんな風にインスパイアされたのか考えると興味深い(というかアルバム自体は『ダ・ヴィンチ・コード』に近いのだが)


本題

「強化」というほどでもないんだが、クリップボード周りはずっと前からいろいろ考えてたんで、その総括を。まぁ簡単にいうと、良くも悪くも Windows ベッタリにしてしまったというところかな。OS の機能なんでどうでもいいが

まず、クリップボードのデータを扱うときはすべて IDataObject を使うようにした。つまりデータの取得・設定に OleGetClipboardOleSetClipboardOleFlushClipboard も)を使用し、従来のクリップボード API は使わないようにした。IDataObject というとドラッグアンドドロップが出てくるのだが、D&D のコードがそのままクリップボード系の実装に使えるというわけだ。それで何が嬉しいのかというと、実はクリップボードのコードを見直しているときに Ascension でサポートする形式を増やそうということになって、それだと一本化したほうが便利という結論になった。今考えているのは、

  • CF_UNICODETEXT - UTF-16 テキスト
  • CF_TEXT (CF_OEMTEXT) - CF_LOCALE のコードページで符号化されたテキスト(か OEM 形式のテキスト)
  • CF_LOCALE - CF_TEXT のロカール(要するにコードページ)を表す LCID。Ascension が設定するときは常にユーザの既定ロカール
  • 矩形テキスト(Visual Studio と同じ形式)
  • CF_RTF - Rich Text Format
  • CF_RTFNOOBJS - CF_RTF のオブジェクト埋め込みなし版(たぶん)

の 6 つで、最後の 2 つ以外は既に実装してある。なんでリッチテキスト? と思うかもしれないが、Visual Studio もこの形式でコピーしてくるんだよね(まぁ、理由は大体分かるけど)。それに合わせたいと思っただけ。当分やらない

もう 1 つは D&D 中にドラッグしているテキストの半透明画像を表示する機能で、Explorer とかでファイルをドラッグすると出てくるよね。FireFox 3 も。やり方は分かってて、IDragSourceHelper(Vista で改良インターフェイスが登場したようだ)と IDropTargetHelper を使う。というか、手元のコードでは試験的なコードが動いてる。難しいのは、ヘルパオブジェクトの使用条件である「IDataObject.SetData をちゃんと実装すること」と「選択テキストのビットマップを作成すること」の 2 つ。両方とも気が向いたらちゃんと説明します。正直、機能的にはテキストエディタでここまでやる必要はないと思うが...

あとは例外のことを考えてリファクタリングとか、そんなんかな


Python の方は 8 月中に終わるかなと思ってたけど、やっぱり終わりませんでした \(^o^)/

さすがに 10 月末までには終わってると思うけど。なんか怪しくなってきた

thrown: 2008-08-31 23:49

2008-08-19

Boost.Python を使ってて思ったこと

Boost.Python に慣れてきて、エディタの基本的な部分はほとんど Python から制御できるようになった(強調表示とかはまだ)。で、今日(昨日だった orz)Boost.Python 使ってて思ったことを並べてみよう

  • どうも C++ で見た目が Python みたいなコードが書けるように工夫しているように見える。args(代入演算子で既定の引数値を指定)とか object からの関数呼び出し(括弧演算子)とか。最初は無茶な感じがしたけど、今はフツー
  • Python のメンバ関数を定義するのに C++ メンバ関数を使う必要がないのが嬉しい。class_.def はエクスポートするクラス型を第 1 引数にとる自由関数を受け付けるバージョンがある。これが無かったら泣いてた
  • プロパティと属性の定義も結構融通が利くから楽
  • クラスの中にクラスを作ったりとかも慣れたら簡単
  • オーバーロードのやり方が分からん。overloads.hpp 見たけど違うっぽい

あと同一性の問題については、

class my_object {
public:
  my_object() {self_ = boost::python::object(boost::python::ptr(this));}
  boost::python::object as_python() const {return self_;}
private:
  boost::python::object self_;
};

という風に、C++ と Python のオブジェクトを常にペアにしておくようにした(必要なクラスのみ)。Boost.Python が提供している C++ から Python への型変換機能を無視しているから、結構背徳的な気もするけどね

というわけで、ようやく光が見えてきた(ような気がする)。ただ、今のところ Python を動かすためにソースコードの構成を変えてるんで、コミットはほとんどしてないです。SF.jp の「活発さ」はあまり信用しないでください

thrown: 2008-08-19 00:34

2008-08-09

オブジェクトの同一性と懸垂参照

(「懸垂参照」ってたぶん使わんなぁ。死んだ参照のことね)この話は旧 Ambient のときにも出てきて、解決もしたから過去に同じことを書いてるかもしれない。まぁ、今回は Boost.Python 版ということで。C++ のオブジェクトを Python にエクスポートする場合、2 つの世界のオブジェクトがどの程度シンクロするかという話(実際はそんな大層な話ではない)

まずは同一性だが、例えばあるオブジェクトの属性(あるいはプロパティ)がさらにオブジェクトになっているような場合、

o.p == o.p
o.p is o.p

直感的に両方 True であると期待する。が、Boost.Python を使って実装するとそうならない場合がある。

namespace py = boost::python;

class hoge {};  // Python に(boost.python.class_ で)エクスポートするクラス

// (C++ 的に見て)毎回同じオブジェクトを返す関数
hoge& get_hoge() {
  static hoge instance;
  return instance;
}

...
  // モジュールをエクスポートするコンテキスト内
  // hoge クラスを Python に公開(コンストラクタは非公開)
  py::class_<hoge>("_Hoge", py::no_init);
  // get_hoge 関数を Python に公開。戻り値は自動的に Python の _Hoge クラスに変換される
  py::def("get_hoge", &get_hoge, py::return_value_policy<py::reference_existing_object>());
...

これを Python から使ってみると、

from testmodule import *
get_hoge() is get_hoge()  # False
get_hoge() == get_hoge()  # False

どうも get_hoge から返るときに毎回違うオブジェクトを作成しているようだ。ここで get_hoge を、

py::object get_hoge() {
  static hoge instance;
  return py::object(py::ptr(&instance));
}

というように自分で object を作って返すようにしてみたが、結果は同じ。さらに一歩進めて、

py::object get_hoge() {
  static hoge instance;
  static py::object wrapped_instance(py::ptr(&instance));
  return wrapped_instance;
}

これで本当に毎回同じオブジェクトが Python 側に渡る。結局のところ、C++ 側では最初から object で保持しておけということらしい。これは Active Script のときも同じだった(同一性対策だけであれば他にも方法はあるが)

もう一方の死んだ参照の問題というのはそのまんまで、すでに解体された C++ オブジェクトに Python からアクセスしてしまう可能性があるということだ。例えばバッファを表現するオブジェクトがあって、Python 側で後で使うためにインスタンスを変数に入れていたとする。しかし次にそのインスタンスを使おうとしたときに対象のバッファがエンドユーザによって閉じられて、C++ のバッファオブジェクトが破壊されていたらどうなるか。これは実際にやってみた。すると異常が補足されて MemoryError が投げられた。ちゃんと対応しているわけだ。これに関しては特に処理は必要ないようだ(しかし、どうやって...)

というわけで、まぁ、分かった

thrown: 2008-08-09 03:12

2008-08-07

Boost.Python と格闘

とりあえず、コマンドを Python の関数で書いたのをメニューから起動できるようになった。細かいところは後で変えると思うけど、旧 Ambient と違って大半のコマンドは Python で実装するので、現在は Alpha を制御するための API を設計・実装中。前に書いた所有権の問題は適当にかわしながらやっているが、正直 Boost.Python ってマニュアル読んだだけじゃ全然分からん。少なくとも、大元の Python C/API の知識は必要

暇があったらまた書くけど、Boost.Python のことを書いた記事って拡張モジュールの作り方ばっかで、組み込みの話があんまりない。が、それもまた罠(というほどでもないけど)で、組み込みでモジュールを公開するような使い方だとその辺のやり方が基本になってるんで注意が必要

大体分かったような気がするが、私が一番引っかかったのはモジュールのスコープだな。拡張モジュールだと BOOST_PYTHON_MODULE で定義した関数の中で、class_ とか def を並べるんだが、組み込みだとスコープがない状態でモジュールを初期化することがあって、そのまま enum_ とかを使うとエラーになっちゃう。分かってしまえば簡単だが、現在のスコープを手動で切り替える必要がある:

using namespace boost::python;
object module(handle<>(borrowed(::Py_InitModule("my_module", 0))));
{
  scope temp(module);
  enum_<MyEnum>("MyEnum");
  ...
}

もう 1 つは、これも簡単なことなんだが、C++ から Python への型変換が登録されている C++ オブジェクトを object に変換するには ptr() を使うと

ambientPackage.attr("buffers") = object(ptr(&Alpha::instance().bufferList()));

他にもいろいろハマってしまったのだが、恥ずかしくてここには書けん orz。また今度

thrown: 2008-08-07 03:13

2008-07-27

ThinkPad T61 (7658-A22) 購入

というわけで初めてノート PC 買っちゃいましたよと。偶然、先にあろはさんが X61s を購入されたけど、影響されたりとかじゃないです。ほとんど持ち出さないので A4 で適当なのを選んだ

  • CPU 2.2 GHz × 2 のメモリ 1 GB(今使ってる古いデスクトップのほぼ 10 倍)
  • 一応 Vista マシンなので、Vista での動作確認できるようになった
  • 最初から Consolas が入ってるぞ(普通そんなところは見ない)
  • 分かっちゃいるけど IBM のロゴは無くなってた orz

まぁ色々だが、フリーズしないのが最高だな

これからゆっくり開発環境を整えたいが、メンドいから MinGW (+ msys) + Meadow でもういいよという気もする。さすがにそれだけじゃほとんど何もできないが

っていうか、最近新出のテキストエディタが多いけど、作者の人ってエディタ何使ってます? 私は普段は xyzzy で C++ のときだけ VC という至って普通の感じなんだけど...

thrown: 2008-07-27 22:17

Python オブジェクトのモデリング

Boost.Python 使ったらオブジェクト(というかインスタンス)のエクスポートなんか楽勝だぜ、と思っていたが、いきなり壁にブチ当たった

new_buffer = ambient.Buffer(u'*scratch*')  # 結局、オブジェクトモデルの名前は "ambient" に戻りました
# 新しく作成した Buffer インスタンスを使ってゴチャゴチャ

Buffer インスタンスを作成して Alpha のバッファが新しく作成されるとしたら、このインスタンスが消滅したときに Alpha のバッファも削除されるのだろうか? 当たり前じゃんと思う人もいるだろうが、そのほうが直感的なのだが、それだと C++ からバッファの消失を監視する必要があるので一気に複雑になる。要するにバッファが削除されるときの処理を C++ デストラクタでやらなければならないということだ。逆にエンドユーザが GUI でバッファを始末したときは Python のオブジェクトは無効になる

これは結局所有権の問題で、究極的には Python 側でオブジェクトを作成する機会を与えず、C++ 側に所有権をまとめればずっと簡単になる。飛躍に聞こえるかもしれないが、実際、旧 Ambient ではその辺が嫌になって、Active Script 側にコンストラクタを公開しなかった。唯一の最上位オブジェクトから、システムに存在するすべてのオブジェクトにアクセスする手段を提供するというのが狭義の「オブジェクトモデル」だと Effective COM とかに書いてあるんだが、身近な例では Web ブラウザのスクリプトとか DOM がそうだ。基本的にクラスやコンストラクタはなく、新しいオブジェクトを作成するには別のオブジェクトを使う(プロパティとかファクトリ)

で、新 Ambient だが、いくつか Python を組み込んでいる他のプログラムを見てみた。大半が Python で記述されているテキストエディタ KaaEdit はコア機能にアクセスする関数を大量にエクスポートしているらしい。バッファとかウィンドウは Python のクラスで定義されていて、実装コードからは寿命管理を気にする必要がないように見える

もう 1 つ紹介するのは Gungnir という 3 次元モデリング・シーン構築ツールで、Boost.Python が使われている。こっちはクラスのエクスポートはしているがコンストラクタは公開していない(boost.python.no_init)。他のオブジェクトのメソッドがオブジェクトを作成する機能を提供している(ようだ)。同時にコア機能を実装する C++ クラスのラッパとして設計されている

もっと見たほうがいいのかもしれないが(というか Python を組み込んだプログラムはやっぱり多い!)、新 Ambient でもクラスはインターフェイスだけを公開することにする。KaaEdit のように大部分を Python で記述するのであれば寿命管理も Python にやらせたらいいと思うが、C++ から使うこと前提としている Ascension を使っていることから、それはかなり難しい(というか不可能)と考えた

[31 日追記] あー、やっぱ違うような。所有権を Python と C++ で共有したほうがいい場合もあるようだ。つーか、Boost.Python の理解と Alpha のアプリケーション層の再構築(← リファクタリング不足)と新 Ambient の設計が大変で、何が何だかもう分からん。他の新参のテキストエディタにはどんどん先越されるし orz

thrown: 2008-07-27 23:54
rethrown: 2008-07-31 12:38

2008-07-18

復活しますた

3 週間とか言ってたのが 3 か月になっちゃった! 今月、大体環境が整った (ネット繋げたり)。が、色々あって開発自体はあまり進んでないのだった

  • Python Cookbook (2nd. Ed.) 買った
  • というか、Python で本当にいいのか
  • 取り敢えず Python で色々やる方法は分かった。オブジェクトモデルの設計はこれからバージョンアップしながら
  • SF.jp にいくつか新しいテキストエディタのプロジェクトが登録されてる
  • Subversion が全然動かねーぞ
  • 新しい (といっても、まだ 2 台目) ノート PC 買おう
  • つーか、秋にまた転勤だし

といったところだ。Python 絡みの話は相当悩んだんだが、ほぼ固まった。基本的には、萌ディタみたいにキー入力に対して関数 (厳密には呼び出し可能オブジェクト) を起動するという形で。スクリプトで実装不可能なコマンドも Python のオブジェクトとして、スクリプトから参照できるようにしておく

# 名前は開発中のものです
ankh.define_key('C-o', ankh.intrinsics.open_file_dialog)

そんなわけで次から Python 必須なんで、その前に 0.7.93.5 をリリースした

thrown: 2008-07-18 14:19

2008-03-24

0.7.93.4

バグ修正だけ。直したと思ってたパーティションのバグが直ってなかったという...。あと、描画速度の改善も。プロファイルの結果を見ると、まだスピードアップできそうだ

thrown: 2008-03-24 14:39

2008-03-18

0.7.93.3

取り敢えずリリースした。つーか、0.7.93.2 って去年の 9 月だよね。どんだけペース遅いんだorz

パーティションのバグがずっと残ってたんで、それが取れたのがこのリリースの肝かな。もちろん異体字セレクタも

thrown: 2008-03-18 18:32

2008-03-14

予定

いきなりだけど、4 月から開発を中断します (たぶん)。引越しとかで、3 週間ぐらいネットも開発環境も無くなっちゃいます。メールも使えません。取り敢えず残りの 2 週間でできるとこまでやるけど

つーか、3 週間の中断なんて大したことないけどね。今までもノロノロやってたし

thrown: 2008-03-14 22:17

ハイパーリンク復活させた

全然伏線のない話だけど、ハイパーリンク (いわゆる、クリッカブル URL) を復活させた。ただ、設計としては他のエディタに比べてかなり抽象的にしてある (ascension.presentation.hyperlink)。Ascension の設計では単純に「起動可能なテキスト片」とした。クリックとか Web ブラウザとかは関係なしで、実装の 1 つとしてクリックして Web ページを表示というのを用意した感じだな。他の挙動が欲しかったら、スクリプトか C++ で何とかしてください

で、バッファから URL を抜き出す処理は RFC 3987 に従った。が、これだと何でもかんでもマッチするんで、スキーム (先頭の http とかの部分) として受け付ける文字列のリストをあらかじめ設定しておくようにした。まぁ、それでもマッチし過ぎるし、URL だと他の構文があるんだけど、まぁいいかな。どっちにしても大した話じゃない

まだやってないけど、ハイパーリンクみたいなのを実装する場合、MSAA のことも考えないと駄目だな。少なくとも、キーボードだけで起動できないと

抽象的な設計というのは Meadow のボタンみたいなのも想定してたんだが、そこまではできないだろうね

thrown: 2008-03-14 22:29

2008-03-13

新レイアウトエンジンの実装 その 50 : 異体字セレクタのその後

来週新しいのをリリースします。色々やってみて、まぁ動いてるかなということで、この話はこれで終わりかな。Uniscribe が IVS に対応するのは時間の問題だろうし

あと、unicmap の人が IVS 対応の gdi++ をリリースした (リンク先には置いてないよ)。Win32 テキスト描画プリミティブを利用するあらゆるプログラムで表示可能になるから、こっちの方が都合がいいというか自然だな。というかいつの間にか Uniscribe もフックするようになってたのね

今回の話は結構面白かった


このシリーズも 50 回目を迎えた。Unicode テキストレイアウトの話はまだまだ続く


Wikipedia の記事を見て今気付いたけど、UTS #37 の例文は「芦屋さん」じゃなくて「芦田さん」だった...orz。ずっと勘違いしてた。マジ恥ずかしいです

thrown: 2008-03-13 18:02

2008-03-04

新レイアウトエンジンの実装 その 49 : 異体字セレクタ実装再考

特定の状況でうまく動かないようだ。やり方が甘かったので、もう一度コードを見直す。IVS_OTFT について、今確認している不具合は 3 つあって、

  1. テキストの内容によってはシェーピングがオフになる場合があり、その場合は OpenType 異体字タグが全て効かない
  2. テキストの内容によっては、そもそも基底文字 (base character) が正しくレンダリングされない
  3. IVS の後ろにゴミが表示される場合がある

1. と 2. については UTF-16 の読み込みのバグで偶然気付いた。どうも Uniscribe は <U+FEFF, 漢字> を 1 つのランと処理してしまうようだ (バグとまでは言えないけど、ちょっと厄介だ)。まず 2. は実は IVS_OTFT と関係ないんだが、メインのフォントに漢字が入ってない場合にちゃんとフォールバックできずに欠損グリフが出力されてしまう。で、1. はフォールバックに失敗しまくると途中でシェーピングをオフにする仕様なので、その状態で ScriptSubstituteSingleGlyph を呼んでもダメということだ。これはどうしようもないと思う。まぁ、ユーザの方で最初からちゃんとしたフォントを選んでくれということかな。いや、2. に対処するためにフォールバックの精度は上げるつもりだが

3. は前に書いた字形選択に使った VS のグリフを潰す話で、空白グリフの取り方が間違っているかもしれない。MS UI Gothic でやってみたら、ScriptGetFontProperties が返す空白グリフは空白じゃないみたいで、謎なんだが。最初から ASCII の空白か ZWSP のグリフを取ってくる方がいいのかな、とか

thrown: 2008-03-04 21:44

2008-02-28

ivs-otft-20080228 出した

Active Script 関連のコードを全て除去すると、私の環境では起動できないバグが消えてしまった。結局、原因は分からなかった。少し前から起動しなかったらしく、ウィンドウが表示される前に止まってしまうので、やっぱり Active Script の絡みかもしれない。もちろん、完全に解決したわけじゃないけど

起動が速くなった気がする。当たり前だが

Active Script をやめてどうするのか。単一言語候補の片割れということだな。別に oedit の Scheme に対抗するわけじゃないけど、Python でほぼ確定した。あとはどんな風に利用できるようにするかだな。まぁ、これはまた後で


一方で Unicode 5.1.0 の話で、最近、UAX (ドラフト) がかなり変更されたようだ。名前まで変わってる。取り敢えず Ascension に関係の深い #14 、#24 、#29 、#31 について読んでみる

thrown: 2008-02-28 18:09

2008-02-26

IVS-OTFT 試験公開版がバグっとる

2 日前に出したやつがいきなりバグっとるらしいという...orz OTL。マシンによってはすり抜けて動いてしまうかもしれないが

早く直します

一瞬 ankh.tlb を疑ったんだが、関係なし。というか、型ライブラリ使うのやめます (= Active Script もやめます)

thrown: 2008-02-26 20:08

2008-02-24

IVS-OTFT 試験公開版出した

バグが取れてないけど、リリースしてきた。今日初めて 2000 でもテストしてみたけど、動いているようだ。SourceForge のページalpha-ivs-otft.zip です

中途半端なんで、表 (?) ではアナウンスしないことにした

これはこれで片がついた


ファイル入出力のバグってのは、使用可能なエンコーディングの管理方法を変えたり、変換方法をストリームベースに変えたりというところで、色々狙いはあったのだが、その分 (というわけでもないけど) 設計が複雑になってという感じだな。後で詳しく書くかも

まぁ、それはどうでもいいんだけど

スクリプトだな、やっぱ

thrown: 2008-02-24 21:39

2008-02-17

新レイアウトエンジンの実装 その 48 : 異体字セレクタのさらに続きの続き

15 日の続き。既に IVS のシェーピングに使った字形選択子をどうやって潰すかという話。やり方を列挙してたんだけど、もっと簡単な方法があった。というか、フォントのフォールバックとかで何度もシェーピングするようにしてるから、論理クラスタの調整は難しかった。やり方は構造体をいじってゼロ幅の設定をすれば大丈夫らしい。字形選択子を含めたシェーピングの後、字形選択子のグリフに該当する部分について、

  • グリフを空白のものに置換する (ScriptGetFontProperties で取れる)
  • SCRIPT_VISATTR.fZeroWidth を 1 にする
  • SCRIPT_VISATTR.uJustificationSCRIPT_JUSTIFY_BLANK にする (多分、均等揃えをしない場合は不要。念のため)

これで ScriptPlace は幅を 0 にして計算するようになる。構造体を勝手に書き換えていいのか微妙だが、色々試してビットマップフォントでも大丈夫だった

というわけで、実際にやってみたのがジャジャーン、右の写真。 「芦屋さんは芦屋のお嬢様だ」 UTS #37 の例文を Y.Oz さんの新フォントでレンダリングした。「芦」の字形が変化している。この字の変換規則は次の通り:

IVSCID異体字タグ
<U+82A6, U+E0100>CID+1142jp90
<U+82A6, U+E0101>CID+7961jp04

試してないけど、印刷もできると思う

そんなわけで、何とかできたっぽい。色々教えてくれた Y.Oz さん、S さん、505 さん、ありがとう! \(^o^)/


で、この機能をつけたバイナリを試験公開したいんだけど、まだ出せない。ファイル関係のバグが溜まってて、ちょっと...。来週中には何とか

thrown: 2008-02-17 16:52

2008-02-15

新レイアウトエンジンの実装 その 47 : 異体字セレクタのさらに続き

先月までの話で IVS を直接扱うことはできないが、jp90 とかの OpenType 機能タグを使ったアクセスはできるんで、IVS → <(機能タグ), (コードポイント)> の変換表があるといいねぇ、ということだった。で、Y.Oz さんに作ってもらっちゃったんだな、これが。ここまできたらやるしかないので、具体的な方法についてゴチャゴチャ考えた

例として「辻さん」という文字列を考える。今、フォントを抜きにして VS17 で一点辻を出したいとする。ユーザの入力は次のようになる

<U+8FBB, U+E0100, U+3055, U+3093>

ScriptItemize (ScriptItemizeOpenType も) はこれを次のように分割する

[U+8FBB] [U+E0100] [U+3055, U+3093]

IVS と機能タグの対応は <U+8FBB, U+E0100> → <'jp90', U+8FBB> だからランを超えて処理しなければならない。まず、字形選択子 (variation selector) が文字列に含まれているかどうか、変化させるベースの漢字はどれかを調べる方法だが、これは分割されたランの境界に存在する。今の例だと 1 番目と 2 番目のランの境界の前後 2 文字がそうだ。つまり字形選択子はランの先頭に、ベースの漢字はランの終端に来る。この 2 つのコードポイントの組み合わせを変換表から探して、あれば適用する機能タグを得る。ここまでは大して問題はない (が、そもそもの方法論にひねりがあって、Y.Oz さんの掲示板で議論が続いている)

次に機能タグを適用する。これには ScriptSubstituteSingleGlyph を使う。シェーピングが終わったら後は普通通りの処理が続く。ここまでは実際にコードを書いて異体字が表示されるのを確認した

問題は役目を終えた字形選択子をどうするかだ。今回の話で一番の難物はこれじゃないかと思うんだが...。例だと 2 番目のランは意味的に空っぽになる。字形選択子は default ignorable だから、これ以上のシェーピングは必要ない (というか禁止されている)

一般化して考えると、2 番目の字形選択子が含まれるランの先頭のグリフ (正確には先頭の文字に対応するグリフ) を潰さなければならない。また技術的な話でコードを見ないとワケワカだと思うんだが、考えてみたアプローチは、

  1. 字形選択子も含めてシェーピングしてから、字形選択子に対応するグリフを取り除く。グリフを取り除く方法は 3 つある:
    1. 字形選択子のグリフをゼロ幅空白のグリフに置き換える
    2. グリフの幅を得た後に、字形選択子のグリフ幅を無理やり 0 にする。ラン全体の幅も合わせて小さくする
    3. 論理クラスタ (どのグリフがどの文字に対応するかを記録した配列。この言葉は Unicode 標準にはない) を調整して、字形選択子と次の 1 字で 1 つのグリフに対応させる
  2. 字形選択子を除外してシェーピングする。その後で論理クラスタを調整して、字形選択子と次の 1 字で 1 つのグリフに対応させる

だ。2 番目の方法は論理クラスタを調整しないと多分 Uniscribe がブチ切れると思った

というところまで考えた。続く

thrown: 2008-02-15 18:24

身動きがとれん

最近、ヤバス、眠ス、寒スしか言ってない気がする。来週は少し楽になるかな

thrown: 2008-02-15 23:42

2008-01-30

新レイアウトエンジンの実装 その 46 : 異体字セレクタの続き

現在の環境では無理ということになったんだが、それでも何とかならんのかと思った。現在 Y.Oz さんの掲示板で色々教えてもらっている

部分的なサポートではあるが、Uniscribe のプログラミングインターフェイスに違反せず、特定の cmap にも依存しない方法を提示された。IVS により導かれる異体字の内、異体字を呼び出すための OpenType タグ (expt 、hojo 、jp78 、jp83 、jp90 、jp04 、nlck。他にもあるかな) でアクセス可能なものについては、IVS を手動で分解してこれらの機能タグを適用することで異体字グリフを得る。機能タグで呼び出し不可能なグリフについては最初からアクセスできない

これをやるには IVS 対 OpenType 機能タグの対応表が必要になる。まず PRI 108 に IVS と AJ16 の対応表がある。後は AJ16 の各 CID についてどの異体字タグで呼び出せるかが分かればよいが、どうも完璧に規定したものは存在しないらしい。データ自体は資料を集めれば構築できそうなのだが、門外漢の私にはちょっとできそうもない

IVS を一部しかカバーできない (異体字タグで呼び出し可能なグリフの数は PRI 108 が規定する IVS の総数に比べてかなり小さいらしい) のと、変換規則が難しいので、ちょっと厳しいかも...。Y.Oz さんはさらに複雑な問題についても説明している

オワタ

AJ16 のと同じ (あるいはそのスーパーセットな) cmap をフォントに要求できないのかな。例えば 3056 番には絶対に一点の「辻」が入ってるみたいな。PRI 108 で規定された CID をそのままグリフ番号に使えたら、Windows 2000 の Uniscribe とかでも動くんだけど

あるいは異体字セレクタのコードポイントから E010016 を引いて aalt タグのインデクスになって欲しいとか。これだと ScriptSubstituteSingleGlyph で簡単にできるよ、たぶん

まぁ、駄目だろうね。それは

うーむ

しかしまぁ、実装するにしても workaround 扱いだけどね。まだ Unicode の standardized variants に入ってないし

thrown: 2008-01-30 21:55

2008-01-20

新レイアウトエンジンの実装 その 45 : 異体字セレクタへの対応

Adobe が開発した日本語組版用文字コードである Adobe-Japan1-6 (以下、AJ16) には、Unicode に存在しない漢字の異体字が多数含まれている。当然 Unicode ベースのシステムではこれらの字を扱うことができないわけだが、Unicode の U+FE00..FE0F と U+E0100..E01EF に variation selector (Unicode 5.0 16.4 節参照) というのがあって、こいつらを使えば異体字を表現できるという設計になっている。で、昨年 Adobe が PRI 108 として、AJ16 と variation sequence の対応を発表した。例えば Unicode の「邉」(U+9089) は AJ16 の CID+6930 に対応するが、<U+9089, U+E0101> で CID+13407 のグリフが出せる

そして先日、Y.OzFont シリーズの Y.Oz さんから Alpha で対応してみたらどうかという話を頂いた。早くも PRI 108 ベースの異体字セレクタに対応したフォントを試験的に作製されたのだが、現段階でアプリケーション側で対応しているものが 1 つも無いという

コードポイントとして登録されている異体字セレクタを使うのは個人的には嫌な感じなのだが、AJ16 には以前から興味があった

しかし結論から言うと、残念ながら現状では無理だ。私が調べた限りでは Uniscribe を使っているアプリケーションは全滅だろう (もうちょっと調べたいけど)

(以下、技術的な話) 何がまずいのかというと、最初の ScriptItemize でいきなりコケてしまうのだ。上に書いた <U+9089, U+E0101> がどのように分解 (アイテマイズ) されるか。何と異体字セレクタの前で切ってしまうのだ。これでは元の漢字とセレクタは常に別々に処理されてしまう。結局のところ、Uniscribe は異体字セレクタについて何も知らないということだ

だったら無理やりまとめて処理させたらいいじゃん (これは認められていない。逆に ScriptItemize が生成したアイテムをさらに細かく分けることは OK だが)。で実際にやってみたが、やっぱり駄目だった。念のためシェーピングをオフにしてやってみたけど、これも駄目。無理やり処理させようとして Uniscribe が癇癪を起こしたのか、フォントの問題なのか分からないが、とにかくできない

U+E0101 がサロゲートだからというのも考えられる。というか、代わりに U+FE01 だとアイテマイズはちゃんとできるんだけど...。Adobe はなぜ対応の難しい BMP 外の方を使ったんだろう

というわけで、できませんでした orz OTL ○| ̄|_


<U+9089, U+E0101> を Y.OzFontN 12.03 でレンダリングしたときの結果を載せときます。Uniscribe のバージョンは 1.614.5315.0

一括処理シェーピンググリフの総数グリフ番号
無理やり有効336938, 2, 2
無理やり無効236938, 54978
なし有効236938, 54978
なし無効236938, 54978

ちなみに <U+02E5, U+02E9> のような合字もシェーピングが有効でないと別々のグリフが出力される (メイリオでも同じ) んで、やっぱり Uniscribe にシェーピングエンジンが入ってないと駄目なのかも。逆に書記素の扱いを間違えなければ、Uniscribe が対応した時点でちゃんと表示されるようになるのかもしれん

(2008-01-24 追記) 何で BMP 外の VS なのか。あーそうか、UTS #37 に IVS の 2 番目は U+E0100..E01EF に限るって書いてあるな。なるほど。って何で?

thrown: 2008-01-20 17:12
rethrown: 2008-01-24 18:10