Unicode® Standard Annex #9 Unicode 双方向アルゴリズム

概要

[注意: この文書は UAX #9 の要点をまとめた物であって完全な訳ではない。]

UAX #9 はアラビア語やヘブライ語の様に右から左へ流れるテキストを含んだ文字の配置方法の仕様を指定する。

1 導入

メモリ上の文字の順序を論理順 (logical order) と呼ぶ。 表示順は左から右が普通だが右から左になる言語も存在する。 混在するテキストを双方向 (bidirectional) と呼ぶ。 多くの場合、個々の文字に備わった属性により自動的に文字の配置は決まる。 都合上テキストの方向を明示する必要がある時は、 方向整形文字 (directional formatting characters) を挿入するがこれは表示だけに影響を与えるべきで、 その他のテキスト処理に影響は与えないべきである。 個々の文字は暗黙的に双方向性 (bidirectional type) を持つ。 left-to-right (左から右) 及び right-to-left (右から左) は 強い方向性 (strong type) と呼び、 この方向性の文字は強い方向性の文字 (strong directional characters) と呼ぶ。 数字などに割り当てられるのは弱い方向性 (weak types) と呼び、 この方向性の文字は弱い方向性の文字 (weak directional characters) と呼ぶ。 その他の方向性および文字は中立 (neutral) と呼ぶ。 セキリティ上の問題 (ファイル名を欺くのに使える) が報告されている事に注意する。

2 方向整形文字

方向整形文字は Bidi_Control 属性を持ち、三種類に分けられる。これらの文字は段落内でのみ効力を持つ。 つまり PS (paragraph separator; 後述) で効果は終了する。

名称制御文字
暗黙方向整形文字 (implicit directional formatting character) LRM, RLM, ALM
明示方向{埋込/上書き}整形文字 (explicit directional embedding/override formatting character) LRE, RLE, LRO, RLO, PDF
明示方向孤立整形文字 (explicit directional isolate formatting character) LRI, RLI, FSI, PDI

埋込整形範囲は全体として強い方向性の文字として振る舞い、 埋込整形範囲の中の文字と外の文字の配置は互いに影響しうる。 孤立整形範囲は全体として中立の文字として振る舞い、 孤立整形範囲の中の文字と外の文字の配置は互いに影響しない。 埋込整形文字よりも、Unicode 6.3 で新しく導入された孤立整形文字を使うことが推奨される。 HTML, CSS では制御文字ではなくそれ専用の仕組み (§2.7 に後述) を使うべきである。

3 基本表示アルゴリズム

Unicode 双方向アルゴリズム (Unicode Bidirectional Algorithm; UBA) は次の段階に分けられる。

  1. 段落に分割。以降の処理は各段落ごとに行われる。
  2. 初期化。各文字についての Bidi_Class 属性のリスト type[i] と埋込レベルのリスト level[i] が初期化される。
  3. 埋込レベルの解決。各文字の Bidi_Paired_Bracket 及び Bidi_Paired_Bracket_Type 属性も参照しつつ、 前述のリストを複数の規則に依って順に修正していく。
  4. 並び替え。先ず行に分割され、次に埋込レベルに従って並び替えが実行される。

段落の区切りは PS (U+2029) または適切な改行 (CR, LF, CR LF, NEL 等; Unicode 4.4, 5.8 を参照) で行う。 更に、より上の仕組みによって段落は区切られうる。例えば表の別のセルのテキストは別段落である。 [Note: Unicode 5.8 の推奨規則 R2a, R2b によると word processor では改行は PS で、テキストエディタでは LS の様だ] 結合文字は(論理順で)前の文字に結合して一つの単位として処理される。

3.1 定義

3.1.1 基本

BD1双方向文字タイプ (bidirectional character type) は各 Unicode 点に割り当てられた性質で Bidi_Class として参照される。 埋め込みの深さを BD2埋め込みレベル (embedding level) と呼び、0 から max_depth = 125 までの値を取る。 max_depth は実装間の差異をなくす為に固定とし将来の変更もない。 BD3埋込方向 (embedding direction) は現在の埋め込みレベルの方向を示す。 偶数の埋め込みレベルは L (left-to-right) を意味し、奇数は R (right-to-left) を意味する。 BD4段落埋込レベル (paragraph embedding direction) は段落の既定の埋め込みレベルである。 例えば、英語文書は 0 から始まり、アラビア語文書は 1 から始まる。 段落埋込レベルによって決まる方向をBD5段落方向 (paragraph direction) または基底方向 (base direction) と呼ぶ。 BD6方向上書き状態 (directional override status) は各埋込レベルに於いて、 文字の双方向タイプを上書きするかどうかを表す。 neutral, right-to-left, left-to-right の三種類の値を持ちうる。 BD7レベルラン (level run)または方向ラン (directional run) は同じ埋め込みレベルを持つ文字の並びである。

3.1.2 明示方向書式文字の対応

LRI, RLI, FSIBD9 孤立開始 (isolate initiator) と呼ぶ。 孤立開始に BD10対応する PDI (matching PDI) は、 "孤立開始…PDI" の入れ子を考慮して決定される [つまり、次のアルゴリズムによる: counter を 0 に初期化して以降のテキストを1文字ずつ順に検査し、 孤立開始が来たら1増やしPDIが来たら1減らす。PDIが counter を 0 にしたら、それが対応する PDI である。 もしそういう PDI がなければ対応する PDI はない。他の種類の制御文字の入れ子は考えない]。

LRE, RLE, LRO, RLOBD10埋込開始 (embedding initiator) と呼ぶ。 埋込開始にBD11対応する PDF (matching PDF) は、 "埋込開始…PDF" の入れ子を考慮して決定される。 その時 "孤立開始…PDI" は1単位としてその中身は検査しない。 単体の PDI が途中で現れたらそこで終了する。

ブール値 BD12方向書式状態は現在の埋込レベルが孤立開始によって開始された時に true の値になる。

BD13孤立ラン列 (isolaing run sequeces) は、 "孤立開始…PDI" で区切られた複数のランのできるだけ長い列である [具体的なアルゴリズムは UAX #9 を参照]。 どのレベルランも必ず1つの孤立ラン列に属する。孤立対が存在しない場合は、どの孤立ラン列も単一のレベルランから成る。 ある孤立ランの中に含まれるレベルランは全て同じレベルである。

3.1.3 対の括弧

type[i]=ONBidi_Paired_Bracket_Type=Open/Close の文字を、それぞれ BD14括弧対開始 (opening paired bracket)/BD15括弧対終端 (closing paired bracket) と呼ぶ。 BD16括弧対Bidi_Paired_Bracket 属性に基づいて対応する括弧対開始と終端の組であり、 孤立ラン列の中から探し求められる [詳細なアルゴリズムは UAX #9 を参照]。

3.1.4 その他の略称

NI は中立文字もしくは孤立書式文字を示す。 e 及び o は現在の埋込レベル方向に一致・不一致のテキスト順序タイプを示す。 sos, eos は孤立ラン列の直前・直後に割り当てられるテキスト順序タイプを表す。

3.2 双方向文字タイプ

Bidi_Class によって各文字に割り当てられている。 強いタイプは L, R, AL である。 弱いタイプは EN, ES, ET, AN, CS, NSM, BN である。 中立は B, S, WS, ON である。 他に一連の方向書式文字がある。

3.3 埋め込みレベルの解決

以降の規則は P1 で段落分割をし、 P2 と P3 で段落レベルを決定し、 X1-X8 によって埋め込みレベルと方向を決定する。 X9 で色々の制御文字を除去し、X10 で各孤立ラン列について W1-W7 で弱いタイプを解決し、 N0-N2 で中立タイプを解決し、 I1-I2 で暗黙埋込レベルを解決する。

3.3.1 段落レベル

P1 PS (type B) の直後で段落分割する。 P2 各段落について最初の強いタイプの文字を探す (孤立範囲はスキップする。埋込範囲はスキップしない)。 P3 もし強いタイプが見つかって ALR ならば 1、それ以外ならば 0 を段落レベルとする。 もしより上位の方法に依って段落レベルが決定される場合は P2, P3 はそれで置き換える。

3.3.2 明示レベルと方向

1文字ずつ順に処理する。以下の変数を用いる。

X1段落の先頭で初期化を実行する。 各文字について順に X2-X8 を適用していく。

明示埋込: X2,X3RLE, LRE に対して以下を適用する。 現在の埋込レベルより大きく新しい方向性に一致する次の埋め込みレベルを決定する。 このレベルが max_depth 以内であり overflow_embedding_count==0 かつ overflow_isolate_count==0 の時に有効である。 有効であれば、{レベル, neutral, false} を push し、 無効であれば if (overflow_isolate_count == 0) overflow_embedding_count++ する。

明示上書き: X4,X5RLO, LRO に対して以下を適用する。 現在の埋込レベルより大きく新しい方向性に一致する次の埋め込みレベルを決定する。 このレベルが max_depth 以内であり overflow_embedding_count==0 かつ overflow_isolate_count==0 の時に有効である。 有効であれば {レベル, R2L/L2R, false} を push し、 無効であれば if (overflow_isolate_count == 0) overflow_embedding_count++ する。

孤立: X5a,X5bRLI, LRI に対して以下を適用する。 現在の overrideneutral でなければ type[i] をそれに書き換える。 現在の埋込レベルより大きく新しい方向性に一致する次の埋め込みレベルを決定する。 このレベルが max_depth 以内であり overflow_embedding_count==0 かつ overflow_isolate_count==0 の時に有効である。 有効であれば {レベル, neutral, true} を push し、 無効であれば overflow_isolate_count++ する。 X5c FSI はその中身に従って RLI もしくは LRI として振る舞う。 方向性は中身に P2,P3 を適用して決める。

非書式文字: X6B, BN, formatting-characters 以外の文字について、 埋め込みレベルは現在のレベル。 overrideneutral でなければ type[i] は書き換える。

孤立終端: X6aPDI について、 (a) overflow_isolate_count が 0 より大きければそれを減らす。 (b) valid_isolate_count が 0 より大きければ、それを減らして、 directional_status_stack の一番上の isolate まで pop する。 PDI のレベルは (a),(b) の後のレベルに設定し、 override があればそれに従って type を書き換える。

埋込・上書き終端: X7PDF について、 (a) overflow_isolate_count が 0 より大きければ何もしない。 (b) overflow_embedding_count が 0 より大きければそれを減らす。 (c) stack に要素が2個以上あって、一番上が isolate でなければそれを pop する。

段落終端: X8B について、全ての入れ子は削除される。 つまり、stack は一番下の物を除いて全て削除される。 B のレベルは段落レベルになる。

3.3.3 暗黙処理の準備

X9 RLE, LRE, RLO, LRO, PDF, BN は全て削除する。 [Note: BN の zero-width joiner, non-joiner は 周囲の文字が並び替えられて隣り合わなくなったとしても、 元々の論理順で変化させた字形の効果を保持する (訳?)。 FSI, LRI, RLI, PDI は残す。]

X10孤立ラン列を計算し、それぞれの sos, eos を自身のレベルと隣り合うレベルの内より高い物と同じ方向性 (L or R) に定める。 各孤立ラン列内部で W1-W7, N0-N2, I1-I2 を以下に紹介する順序で適用する。

3.3.4 弱いタイプの解決

W1NSM を直前の非NSM文字または sos のタイプに書き換える。 但し、直前が孤立開始・終端の場合は ON にする。 W2EN の直前の強いタイプ (R, L, AL) が AL ならば AN に書き換える。 W3ALR に書き換える。 W4EN ES ENEN EN EN, EN CS ENEN EN EN, AN CS ANAN AN ANW5EN に隣接する ET+ は全て EN+ に書き換える。 W6残った ET, ES, CS は全て ON に書き換える。 W7EN の直前の強いタイプが L ならば L に書き換える。

3.3.5 中立及び孤立書式タイプの解決

各孤立ラン列の中で、各NIを隣接する強いタイプに書き換える。 孤立ラン列の一番端では隣接するタイプに sos, eos を用いる。 両側の強いタイプが異なる時はその埋め込みレベルの値を用いる。 括弧対は組で同時に書き換えられる。

N0現在の孤立ラン列の中で括弧対を列挙し、 各括弧対について (a) 囲まれた文字の中に埋め込み方向に一致する強いタイプがあれば、括弧のタイプをそれに書き換える。 (b) 囲まれた文字の中に埋め込み方向と逆の強いタイプがあれば、括弧対の直前の強いタイプに書き換える。 (c) 囲まれた文字の中に強いタイプがなければそのままにする。 括弧に接しているNSMから書き換えられたONは、全て括弧と同様に書き換える。

N1NI+ の両側の方向性 (残っている EN, ANR と解釈) が一致すればそれに書き換える。 N2残りの NI は全て埋め込み方向に一致する様に書き換える。

3.3.6 暗黙レベルの解決

I1埋め込み方向がLの時、Rの文字は1レベルを増やす。 EN, ANの文字は2レベル増やす。 I2埋め込み方向がRの時、L, EN, AN の文字は1レベル増やす。

3.4 レベルの並び替え

各文字の字形を決定し文字幅を計算する。 行分割を行い L1-L4 の規則を適用する。 並び替え後の順序に従って表示が行われる。

L1 Bidi_Class (WS|FSI|LRI|RLI|PDI)* (S|B|$) の埋込レベルを段落レベルに戻す。 [Note: これが意味する所は TABNEL は移動前後で埋め込み状態を継続するが、 実際の並び替えの時には其処で切る、という事を意味する。] L2 埋め込みレベルの高いものから順に反転を実施していく。 L3 結合文字と基底文字の関係は実装に応じて適切に処理する。 L4 R かつ Bidi_Mirrored=Yes の文字の字形は反転する。

3.5 字形について

[詳細は UAX #9 を参照] アラビア文字の繋がり方などについて。SHY (soft hyphen) が改行時に字形を持つ事について。など。

4 適合性

[詳細は UAX #9 を参照] BN の取り扱い。 また、必ずしも全ての双方向機能に対応しなくても良い。 適合レベルは No bidirectional formatting, Implicit bidirectionality, Non-isolate bidirectionality, Full bidirectionality に分けられる。 また上位の枠組みで様々な追加の処理を行っても良い。 テストケース。

5 実装の注意

[詳細は UAX #9 を参照] 参照実装。また BN や LRE 等を除去せずに処理する方法。

6 使用法

[詳細は UAX #9 を参照]

7 字形反転について

[詳細は UAX #9 を参照]