先読み

GSUB, GPOS, JSTF (OpenType)

[61] GSUBグリフの置き換えを、 GPOSグリフの位置調整に関する情報を記述する OpenType フォントです。

[104] JSTFjustification のためのグリフの置き換えや位置調整に関する情報を記述する OpenType フォントです。

仕様書

意味

[89] GSUBGlyph Substitution の意です。 >>1

[76] GPOSGlyph Positioning の意です。 >>77

構造

[5] GSUB にはグリフの置き換えのための対応表データが入っていて、 GPOS にはグリフの利用に関係する座標等のデータが入っています。

[4] GSUB, GPOS とも似たような構造になっています。 末端のデータ等細部が微妙に違っているので注意が必要です。

[62] どちらも、 まず対象となる用字系言語系について、 それに関わる機能がいくつかあって、 機能における lookup がいくつかあり、 その lookup に条件と処理が書かれているという構造です。

[105] JSTF は対象となる用字系言語系について、 適用可能な調整がいくつかあるという構造です。 JSTFlookup を参照したり、含めたりできますが、 GSUBGPOS とは構造が違います。

処理

[58] 表示処理全体の中での位置は文字のレンダリング参照。

[60] 機能の選択方法は機能参照。

[85] GSUBlookup の条件に一致した場合はグリフ(列)をグリフ(列)に置き換えることになります。 このときどれがどれに変化したかはある程度追跡できる (元のグリフ(列)を得た大元の文字列との対応関係を知れる) 必要があります。 文字のレンダリング

[81] GPOSlookup の条件に一致した場合に行うべき処理や JstfMax lookup の解釈は、 グリフ位置決定を参照。

[159] shaping において連なりに分割してから処理されるために、 用字系が違うと GSUB が適用されないことがあるようです。 連なり

機能

[8] GSUB , GPOS 用字系言語系に対して適用可能な機能を記述できます。 フォント機能

[59] 機能に対して、実施されるべき操作を lookup として記述できます。

[46] GSUB , GPOS には同じタグ機能を複数個含められます。 用字系言語系に対してどの機能を適用するかを選択できますから、 同じ機能でも用字系言語系に合わせて違う lookup が適用されるようにできます。

lookup

[144] lookup にはグリフ列の一部分について一致するかしないかの条件と、 一致した場合に行うグリフ(列)置換またはグリフの位置調整を記述できます。

[145] GSUB GPOS では、 それぞれの LookupList 内に任意個含められます。 >>103

[146] LookupList 中の lookup の出現順序 (索引) は、

... に使われます。

[151] JSTF では、 各提案に行を長くする用と短くする用で各1つまで含められます。 >>103

[152] 同じ lookup と呼ばれますが、 GSUB ではグリフ(列)の置換の記述のため、 GPOS ではグリフの位置調整の記述のため、 と少し構造が違います。 一致条件の部分が同じような意味、同じような構造でも、 lookupType が両者で違っています。


[63] GSUBGPOS を適用するには、 用字系, 言語系, 機能から利用する lookup を決定します。 選び方はフォント機能

[64] 複数の lookup を適用する場合、 適用順序は LookupList の出現順とします。 >>12, >>67, >>1 1つの lookupグリフ列を先頭から末尾まで処理し、 次の lookup へと進みます。 >>67, >>1 ただし GSUB lookupType 8グリフ列の末尾から先頭に向かって処理します。 >>1

[65] フォント開発者は、 複数の機能を同時適用するときの相互作用を考慮して LookupList 中の lookup の順序を決められます。 1つの機能に複数の lookup を結びつけることが出来ますから、 サンドイッチ式の適用も記述できます。

[66] ただし、全体の処理において機能ごとに複数回に分けて適用する場合もあり フォント機能 、 その場合はフォント開発者の制御が及ばなくなります。

[68] 同時に複数の機能を適用する場合、 それらの lookup和集合 (union) を使います。 >>67 つまり重複した lookup があるときは、1回分だけ適用されます。

[69] なお、機能ごとに異なる部分グリフ列にのみ適用することができます。 >>67

[70] 同じ lookup で異なる部分グリフ列に適用されるべきときはどうなるのでしょう。 適用対象のグリフ和集合になるのでしょうか。


[78] lookup には、 一致条件 (と一致時の挙動) を記述する部分表を1個以上含められます。 >>77, >>1

[79]部分表はいくつかの種類があって、 substFormatposFormat によって区別されます。 どの形式によるかは記述方法の違いによるストレージ効率で選べます。 部分表ごとに違う形式も選べるので、 都合に応じて区分できます。 >>77, >>1 部分表の構成の違いは lookup の挙動には直接影響しません。

[102] ただし機能によっては実装状況が悪いのでこの形式でなければならない、 部分表は1個でなければならない、といったバッドノウハウがあるようです。 各機能の項参照。

[80] とある入力が1つの lookup に含まれる複数の条件に一致することは、 明確には認められても禁止されてもいないようです。 その場合の挙動も不明です。 どんな入力にも高々1回だけ一致するようにするべきと思われます。


[13] lookup は, lookupTypesubstFormat にもよりますが、 グリフの列の一致条件を3通り記述できます。すなわち、

[73] ここでグリフ列は論理順とします。 >>72

[14] 入力グリフ列は、指定された処理が施される対象となります。

[15] 後戻り入力の直前、 先読み入力の直後にあるべきグリフ列で、 一致するかどうかの判断には使いますが、 処理の対象にはなりません。

[16] 一致した場合、入力の次のグリフに進んで一致するかどうかの検査を繰り返します >>67, >>77先読み列があれば、その先頭からということになります。

[17] ただし、 GPOSlookupType 2 は2つのグリフの組に対して値を設定するもので、 常に2つのグリフ入力となるのですが、 第2グリフに与えられた値がないときには、 その次の処理の入力は前の処理の第2グリフからとなります。 >>67, >>72, >>77

[18] 1つ lookup についての処理において、 GPOS の組の指定の例外を除き、 入力列を処理して得られた出力に二重に処理が適用されることはありません。

[71] 先読み後戻りが一致するグリフは、 機能の適用対象となるグリフでなくても構いません。 >>67

[19] 入力の第2のグリフ以降先読みの最後のグリフまでの各グリフの前、 あるいは後戻りの各グリフの後には、 特定の種類のグリフがあったとしても、 一致判定においては無視する場合があります。 その条件は lookuplookupFlag で記述されます。

[82] lookupFlag には他に RIGHT_TO_LEFT フラグがありますが、機能性は大きく違っています。

[34] マーク系の機能が3つあります。 アラビア文字のように前後の文字との位置関係によってグリフを変化させつつ、 主たるグリフに付随するマーク合字化の判定で適宜無視したりしなかったり、 といった条件を記述するために用意されているようです。

[35] マーク添付級マークグリフ集合は似たような機能ですが、 マークグリフ集合の方が記述能力が高い (複雑) です。 マークグリフ集合の方が後から追加された機能です。 マーク添付級だけでは不十分ということで追加されたのでしょうか。

[36] グリフ級に基づく無視は GSUB lookupType 6 と共にしばしば使われているようです。 lookupType 6 では一致した入力列に更に他の lookup を適用することになりますが、 このとき入力列の何番目の位置のグリフであるかに依存して適用する lookup を決めます (seqLookupRecords)。 何番目であるかには、無視されたグリフを算入しません。 従って無視されたグリフの有無でどの lookup がどのグリフに適用されるかは変化しませんし、 無視されたグリフには lookup が適用されません。 ただし一致した入力列グリフに対して lookup を適用するときに、その lookup が前後の条件を記述していれば、 無視されたグリフの有無がその結果に影響を与える可能性はあります。

[37] GSUB lookupType 4 (複数のグリフの列から1つの合字への置き換え) での利用も禁止されているわけではありませんが、 その意味するところは定かではありません。 無視して読み飛ばしたグリフも含めて入力列の全体が置き換えられるべきなのでしょうか?

[54] GPOS lookupType 2 (グリフの組に対する位置調整) での利用も禁止されているわけではありませんが、 どう適用されるべきなのか明確ではありません。 第1グリフは適用対象のグリフの範囲が指定されますが、 第2グリフは任意(すべて)のグリフが対象となり得ますから、 読み飛ばしとの相互作用をどうするべきか、 処理結果にいくつかの解釈が存在し得ます。

[56] GPOS lookupType 4, 5, 6 は付加するマークグリフ基底グリフ, 合字グリフ, 基底となるマークグリフとの位置関係を調整するものです。 入力は付加しようとするマークグリフです >>77

[88] 6 では lookupFlag に応じてマークグリフ mark2 を探します。 >>77

[87] 45 も読み飛ばしとの相互作用がどう扱われるのが想定される挙動なのか不明瞭です。


[31] 入力列, 先読み列, 後戻り列として指定できるグリフの列の長さ (グリフの数) には上限が規定されていません。 に於いて個数を表す値が uint16 なので、 216 - 1 が構造上の上限となります。 無視して読み飛ばすグリフの個数にも上限が規定されていません。

[32] 実際の自然言語の記述に使うためのフォントでそこまで長いものが必要にはなり得ませんから、 それよりずっと小さな、しかし十分大きな個数で探索を打ち切るような実装が普通でしょうし、 それをしないで杜撰に実装するとセキュリティーの問題にもなり得ます。

[33] 実際上どれくらいの長さが必要なのでしょうかね。 16個では少し心もとない気がします。 128個だと過大な気がします。


[51] GSUB lookupType 5lookupType 6 に容易に書き換えられるようです。 5 の利用例が少ないのは 5 の機能では不十分な場合が多いのでしょう。

[55] GPOSカーニング2 が使われていることが多いようです。 GPOS lookupType 1, 2 しか対応していない実装もあります。


[47] lookup機能から参照されて適用されるものと、 他の lookup から参照されるものとがあります。

[48] 入れ子lookupGSUB lookupType 1 が多いですが、 2 の例もあります。

[49] 仕様書には入れ子にできる lookupType の制限は特にないようですが、 どれも適用できるのか不安感はあります。

[52] GSUB lookupType 4, substFormat 1 を使った事例があります。 このとき入力の複数の隣接したグリフ合字化された1つのグリフに置き換えられることがあります。

[53] もしその場合に入力列側で無視されるグリフが間に挟まっていたらどう処理されるべきなのか、 入れ子lookup で使われる sequenceIndex が指すのが合字化により消失するグリフのときどう処理されるべきなのか、 よくわかりません。

[50] 実装によっては GSUB lookupType 1, substFormat 2 しか対応していないこともあるようです。

[57] 入れ子lookup>>17 の挙動の関係も若干不明瞭です。 入れ子lookup でも適用される旨が仕様書には書かれている >>72 のですが、どういうイメージなのでしょう。

[74] 入れ子を二重以上深くできるのか不明です。仕様書では特に禁止はしていませんが。

[154] JstfMax lookupGPOS lookuplookupType のうち contextual positioning lookups 以外を使えます。 >>103 つまり入れ子lookup するものは認められていないようです。 (指定された場合にどう処理するべきなのかは不明。)

文脈

[6] GSUB, GPOS とも必須のではなく、 不要なフォントには入っていません。 どちらかだけのフォントもあります。

[7] といっても使わないで済むのは欧米東アジアの昔ながらの簡易的な表示に使うフォントくらいのものです。 それ以外の地域で使われる文字の多くは GSUBGPOS の機能が必要です。 欧米東アジア文字も、カーニング縦書き合字など、 高品質 (現在の計算機環境では標準的に実現されているレベルも含む。)文字のレンダリングにはそうした機能が必要となります。

GSUB 代替グリフの選択

[90] GSUBlookup (群) を適応する処理は、 原則的に入力が1つのグリフ列、 出力も1つのグリフ列です。

[91] ただし一部の lookupType (を使う一部の機能) は、 GSUB lookup の適用結果に複数の候補を示すことが出来ます。

[93] 例えば機能 salt は異なるデザインの多数のグリフを提示できます。

[92] 複数の結果になるのが最後の lookup なら複数通りの最終出力の可能性があり得ますし、 最後でない lookup ならそれ以降の lookup で最終出力の違いが更に広がっていく可能性があります。 1つのグリフ列中にそのような箇所が複数あれば、 最終出力はそれぞれの選択のすべての組合せ分あり得ることになります。

[94] OpenType 仕様書の機能タグ登録簿の記述によれば、 そうした機能DTP ソフトウェアの編集画面などにグリフごとに他の選択肢を表示して、 利用者に適切なものを選ばせることが想定されているようです。

[95] 1度限りの印刷なら選んで終わりで済みますが、 情報交換や長期保存のためには選択結果を恒久的に固定する方法が必要となります。

[99] どちらもフォントの改訂などで構造が変わってしまうと指していたグリフが変わってしまうリスクはあります。 もっともフォントの改訂はどのグリフも前のものとの「同一性」が保たれる保証はないので、 あまり気にしても仕方がないかもしれません。互換性を気にするフォント開発者なら気を使ってくれるはずです。 それが期待できないならフォント埋め込むなど、 利用フォント含め転送・保存するしかありません。

[100] 異なるフォントグリフIDや候補リスト中の位置が共通化されることはほぼ期待できません。 同じフォント提供者による別のフォントなら、多少期待してもいいかもしれない、 という程度です。 グリフ特定に CID が使える (そして標準化された CID が振られている) なら別ですが...

[155] IllustratorとInDesignでaaltのルールが異なる件 - 帰ってきた💫Unicode刑事〔デカ〕リターンズ, https://moji-memo.hatenablog.jp/entry/20070720/1184917140

Illustratorの「aalt 0」って、InDesignで言うところの「aalt 1」(CID順で1番目の異体字)のことだと思うのだが、なぜ別のルールを採用しているのだろう。

JSTF の調整

[106] justification は期待される長と実際のの内容の長さの関係を調整する処理です。 グリフ間のスペース量を加減したり、グリフ合字グリフに置き換えたりします。

[107] JSTF にはそのフォントに適した調整方法と調整量の提案を何段階かに分けて記述できます >>103文字のレンダリングの際にはこれを参照してそのフォントに適した justification を実装できます。 OpenType は具体的な justification の手法までは規定していません。 長をもっと減らしたいとき、増やしたいときにどのグリフをどうすればいいかを取得できますが、 もっと増やしたい、減らしたいという判断は別途行わないといけません。

[108] JSTF には、 用字系言語系に対して、 適用可能な提案のリストが含まれます。 リスト中の各提案は、 priority level (影響の小さい順、「悪くない」順) に並べて格納します。 適用時には、初めの提案から順に必要な提案まで適用していきます。 >>103

[126] 各提案には次のデータを含められます。 >>103

[135] GSUBGPOSlookup は、 それぞれのLookupList におけるindexにより指定します。 >>103

[143] JstfMax lookup (justify lookup) は JSTF に直接記述します。 >>103

[111] 提案を適用した調整は、次のようにします。 >>103

  1. [139] フォントを入力のフォントとします。
  2. [112] 原グリフ列を入力のグリフ列とします。
  3. [113] 機能群を入力の機能群とします。
  4. [137] 用字系を入力の用字系とします。
  5. [138] 言語系を入力の言語系とします。
  6. [136] 要望を入力の line shrinkage または line extension とします。
  7. [116] 提案群フォントJSTF において用字系言語系から決まる提案群とします。
  8. [114] 提案を、空の提案に設定します。
  9. [115] 繰り返し、
    1. [123] lookup群を、 フォントから機能群lookup を集めて整列した結果に設定します。 (variable font の変更を踏まえて実際に適用される lookup を選びます。)
    2. [124] lookup群提案要望に基づき編集します。
    3. [117] グリフ列を、原グリフ列lookup群を適用した結果に設定します。
    4. [118] グリフ列の長さが適当と判断される場合、
      1. [119] グリフ列を返してここで停止します。
    5. [121] 提案群の次の提案がもうない場合、
      1. [122] グリフ列を返してここで停止します。
    6. [120] 提案を、提案群の次の提案に設定します。

[125] 実際には1つのフォント書字方向が違う複数の連なりで構成されているかもしれませんし、 機能はすべて同時に適用するのではなく shaping 過程で複数回に分けて適用されたり、グリフ列の一部分のみに適用されたりするのでしょうから、 ここに示した手順群がそのまま実行できるとは限りません。 追い込み追い出しのため連なりを再構成する場合もあるかもしれません。 実際の手順群の構成は shaping の設計や justification 周りのアルゴリズムに依存します。 しかし JSTF の指定の適用に関わる部分だけを眺めれば、 およそこのような手順群に要約できるものと考えられます。

[153] JstfMax lookup で設定された値の利用についてはグリフ位置決定を参照。


[109] JSTF には用字系ごとに拡張子グリフ (extender glyph) のリストも指定できます。 justification の処理では各提案の他にこのグリフも使えます。 >>103

[110] 例えばアラビア文字kashida をリストに含められます。 >>103

[140] OpenType 仕様としてはリストを指定できるだけで、その使い方は決められていません。

関連

[75] 実装の効率化のため、 GPOS による添付点の情報を GDEF に要約して記述することが出来ます。

[156] 似たもので組み合わせを指定するだけの MERG があります。

実例

[10]feature の利用例は font feature 参照。

[9] Nishiki-tekiGSUB, GPOS ともに script が多数あって、 langSysRecords をも複数使っています。

また GSUB, GPOS ともに同じ feature tag の別の feature list item があります。

[45] >>42 GSUB 1, 2; 3, 1 GPOS 1, 1; 1, 2; 9, 1 (2, 1)

[44] โครงการอักษรอีสาน, , https://linux.thai.net/~thep/esaan-scripts/

Khottabun

GSUB 2, 1; 4, 1; 5, 2; 6, 1; 6, 2

(入れ子の lookup が 2, 1; 4, 1)

GPOS 4, 1; 6, 1

[41] Ken Lunde 小林剣さんはTwitterを使っています: 「Rendering 김일성, 김정일 & 김정은 via the #OpenType 'ccmp' GSUB feature using chaining contextual substitutions. 🇰🇵 🤔 #KPS9566 #UTC154 @DPRK_News https://t.co/Fi41NOsWsQ」 / Twitter, 午前11:42 · 2018年1月7日 , https://twitter.com/ken_lunde/status/949833681354375168

[157] 正確實現簡轉繁字型, 三日月綾香, , https://ayaka.shn.hk/s2tfont/hant/

[158] >>157 簡体字から繁体字への変換に GSUB + trad を使う。

[160] Yu Gothic UIに text-spacing-trim を適用するとバグる, https://zenn.dev/inaniwaudon/scraps/f224417d4c51ee

歴史

[11] opentype-layout/proposals at master · OpenType/opentype-layout · GitHub, https://github.com/OpenType/opentype-layout/tree/master/proposals

メモ

[38] Spec for Thai OpenType Creation, , https://linux.thai.net/~thep/th-otf/

[3] GSUB テーブル (1) - ScriptList, https://aznote.jakou.com/prog/opentype/17_gsub1.html