[6] 
[DFN[[[文字]]の[[レンダリング]]]]は、
単純に[[文字]]を並べていくだけに思えますが、
実は考えなければならないことがたくさんあって、
非常に複雑で難解な処理なのです。

* 文字とレンダリング

[16] [[文字]]の連続の[[レンダリング]]が個別の[[文字]] ([[グリフ]]) 
の[[レンダリング]]の連続とは異なることがあります。

[FIG(short list)[ [10] [[文字]]と表現の記述
- [[文字]]
- [[文字コード]]
- [[結合文字]]
- [[合成文字]]
- [CODE(charname)@en[GCC]]
- [[重ね打ち式文字合成]]
- [[書記素クラスター]]
- [CODE(charname)@en[ZWJ]]
- [CODE(charname)@en[ZWNJ]]
- [[制御文字]]
- [[空白文字]]
- [[スペース]]
- [[[CH[˥˩]]]], [[[CH[˩˥]]]]
- [[国旗絵文字]]
- [[肌の色]]
- [[ハングル字母]]の列による[[ハングル音節]]の表現
- [CODE[transcoding hint: group next [VAR[n]] characters]]
- [[IDS]]
]FIG]

[FIG(list middle)[ [8] [[文字のレンダリング]]
- [[連なり]]
- [[subtending mark]]
- [[語頭形]]、[[語末形]]など
-- [[アラビア文字]]、[[σ]]などの[[文字]]
-- [CODE[1st]] の[[上付き]] [CODE[st]] などの[[フォント]]ごとの文脈依存[[グリフ]]
- [[cursive]] な場合
-- [[アラビア文字]]など、それが一般的な[[書字体系]]
-- [[筆記体]]の[[ラテン文字]]や[[草書体]]の[[漢字]]・[[仮名]]など、そうも表現される[[書字体系]]
-- [CODE(charname)@en[ZWJ]]
- [[必要な長さに引き伸ばす]]
- [[合字]]
- [[カーニング]]
- [[shaping]]
- [[グリフの選択]]
-- [[フォントの選択]]
-- [[言語情報によるグリフ選択]]
- [[鏡文字]]
- [[回転][回転 (書字方向)]]
- [[上下反転]]
- [CODE(charname)@en[SGR]]
- [[フォント]]
- [[機能][フォント機能]]
- [[斜体]]
- [[イタリック]]
- [[文字列座標]]
- [[ラスター化]]
- [[アンチエイリアシング]]
]FIG]

[FIG(short list)[ [12] [[文字]]の周辺
- [[語境界]]
- [[改行]]
- [[ハイフン付け]]
- [[改段落]]
- [[改行記号]]
- [[言語情報]]
- [[書字方向]]
- [[bidi]]
- [[ルビ]]
- [[interlinear annotation]]
-
[[行内構造]]
-[[アキ]]
-[[字間]]
- [[行揃え]]
-[[語間]]
-[[行間]]
-[[改行]]
-[[改段落]]
-[[禁則処理]]
- [[カーソル]]
- [[hit testing]]
- [[置換要素]]
- [[基線][基線 (文字)]]
]FIG]

[FIG(short list)[ [58] [[文字のレンダリング]]の処理モデル

- [[OpenType]]
- [[m17nライブラリ]]

]FIG]

* フォント依存文字

[FIG(short list)[ [1] [[フォント]]依存文字
- [CODE(char)[U+005C]]
- [CODE[~]]
- [[フォント機能]]
- [[包摂規準]]
- [[フォント依存符号化]]
- [[ビルマ文字の文字コード]]
- [[PUA]]
- [[EUDC]]
- [[DRCS]]
- [[機種依存文字]]
- [[Unicode非互換割当]]
- [[外字]]
]FIG]

* OpenType の処理モデル


[35] 
[CODE[cmap]] を参照する時点では、
[[文字列]]の転送・保管等に使う[[符号化方式]] (いわゆる[[外部コード]])
ではなく[[フォント符号化]]を知っている必要があります。
[[Unicode]] の場合、 [[UTF-8]] [[バイト列]]や
[[UTF-16]] [[サロゲートペア]]等ではなく、
[[Unicode符号点]]の列です。


[36] 
[CODE[cmap]] は[[文字コード]](列)から[[グリフID]]への[[写像]]です。
ほとんどの場合は1つの[[文字]] ([[符号点]]) が入力ですが、
[[異体列]]は1つの[[異体列]]が入力となります。
従って 
[CODE[cmap]]
を引くには、
入力の[[文字列]]を ([[異体列]]とそれ以外の[[文字]]) の列に分解する必要があります。

[37] 
また、[[異体列]]から [CODE[cmap]] を引いた出力は
「[[基底文字]]と同じ[[グリフ]]」
という回答かもしれませんから、
[[基底文字]]の[[符号点]]で
[CODE[cmap]] 
をもう一度引かなければなりません。
(普通は同じ[[フォント]]の [CODE[cmap]] で[[基底文字]]を引けば適切な[[グリフID]]が返ってくるはずですが、
[[データ構造]]的に保証はされていませんから、エラー処理が必要となります。)


[32] 
[[Windows]]
は[[サロゲートペア]], [[bidi]], 
[[用字系]]依存の [[shaping]] (文字前処理を含む。)
を 
[[Uniscribe]]
で実装し、
[[OpenType Layout]]
による[[グリフ]]の置換と位置決定を
[[OTLS]]
で実装する、
という2段階構造になっているようです。


[30] 
[[Uniscribe]]
は[[アラビア文字]],
[[ヘブライ文字]],
[[タイ文字]]については、
古い [[OS]]
が対応していた
[[OpenType Layout]]
以前の[[フォント]]にも対応しています。
[SRC[>>31]]

[57] 
[[Windows]] の [[Chrome]] で試してみると、
[[基底文字]]に続く[[結合文字]]は[[正準再順序付け]]されているようで、
そうでない順序の [CODE[GSUB]] [[lookup]] の規則を用意しておいても無視され、
正準化された状態の [CODE[GSUB]] [[lookup]] の規則があればそれが適用されます。
[TIME[2025-01-26T13:12:41.400Z]]



[34] 
[[鏡像化]]は [CODE[cmap]] を参照する時点でも処理が必要です。
[SEE[ [[書字方向依存グリフ]] ]]

[33] 
[CODE[vert]] を使った[[レンダリング]]や[[縦書き]]対応していない[[フォント]]の[[レンダリング]]には、
取得した[[グリフ]]データの[[回転][回転 (書字方向)]]が必要となります。
[SEE[ [[縦書きグリフ]] ]]

[38] [[グリフ位置決定]]


[42] 
[[機能][フォント機能]]については、
[[justification]] のために適当な[[行]]長を実現すべく、
[[グリフ]]の置き換えや位置調整を適用し直すことがあります。
[SEE[ [[JSTF]] ]]


[51] 
[CODE[SVG ]] による [[SVG]] 字形データがあれば、それへの置き換えを行います。
これは[[グリフID]]の列が決定した後で、 [[advance width]] や [[advance height]]
は変化しないはずとされています。
[SEE[ [CODE[SVG ]] ]]
具体的にどのタイミングかまでは明らかではありませんが、
[[アンチエイリアス]]関係の処理よりは前でしょうか。
あるいは [[SVGグリフ]]には[[アンチエイリアス]]処理が適用できないとか、
通常グリフと同時処理はできないとか制約があったりするのでしょうか。

@@
[56] 
[[色フォント]]/[[絵文字]]グリフ選択と [CODE[SVG ]] の関係

=
[53] 
[[variable font]] の処理
-- [[SVGグリフ]]には適用されない。
=
[54] 
[[hinting]]
-- [[SVGグリフ]]には適用されない。
=
[44] 
[CODE[MERG]] の指定に従い[[グリフ]]列を併合します。
=
[45] 
[[アンチエイリアシング]]の処理を適用します。

[52] 
[[SVGグリフ]]への置き換えや[[アニメーション]]によって [[bounding box]] が変化する可能性があります。
これが[[行]]配置に影響するときは、再配置が必要になります。
[SEE[ [CODE[SVG ]] ]]

[55] 
[[SVGグリフ]]は[[アニメーション]]することがあります。
[[印刷]]時などのため、[[アニメーション]]有効、無効の2状態の入力が必要です。

[REFS[
-
[31] [CITE@ja-jp[OpenType glyph processing (part 2) - Typography | [[Microsoft]] Docs]], [[alib-ms]], [TIME[2022-08-27T13:16:47.000Z]] <https://docs.microsoft.com/ja-jp/typography/develop/processing-part2#uniscribe-in-detail>
]REFS]


** OpenType Layout の処理モデル

[14] 
[[OpenType Layout]] [[フォント]]を使って[[文字]]を表示する[RUBYB[文章処理クライアント][text-processing client]]は、
次のようにして[[文字列]]から[[グリフ]]列を得るとされています。
[SRC[>>13]]

[FIG(steps)[

= 
[15] 
[[フォント]]の [CODE[cmap]] [[表][OpenType表]]に基づき、
[[文字符号]]列を[[[RUBYB[グリフ索引][glyph index]]][グリフID]]列に変換します。
=
[17] 
[[フォント]]の [CODE[GSUB]] [[表][OpenType表]]に基づき、
[[グリフ]]列を編集します。
=
[18] 
[[フォント]]の [CODE[GPOS]] [[表][OpenType表]]と
[CODE[BASE]] [[表][OpenType表]]に基づき、
[[グリフ]]の[RUBYB[位置][position]]を決定します。
= [19] 
[RUBYB[[[設計座標]]][design coordinates]] (高解像度で装置非依存)
で装置非依存[RUBYB[[[改行]]][line break]]を決定します。
= [20] 
[[利用者]]の指定がある場合、
[[フォント]]の [CODE[JSTF]] [[表][OpenType表]]に基づき、
[RUBYB[[[行揃え]]][justifies the lines]]をします。
= [21] 
[[グリフ]]の[[行]]を[[ラスター化]]し、
[RUBYB[[[装置座標]]][device coordinates]] (出力装置の[[解像度]]に対応するもの)
に[[グリフ]]群を[[レンダリング]]します。

]FIG]

[22] 
[[OpenType]] [[仕様書]]ではこのうち最後の出力だけを [[OS]]
の処理とし、他を文章処理クライアントの処理としています。
[SRC[>>13]]
[[装置]]依存の処理かどうかで区分しているのでしょうか。
[[Windows]] の場合他の処理も [[OTLS]] を使って実装でき、
広い意味では全体が [[OS]] の範疇であります。
逆に出力が[[ハードウェア]]でなく[[画像ファイル]]等なら全体が[[応用]]レベルで実装されていることもありそうです。



[REFS[
- [13] [CITE@ja-jp[Advanced typographic tables - OpenType Layout (OpenType 1.9) - Typography | [[Microsoft]] Docs]], [[PeterCon]], [TIME[2022-08-28T02:47:24.000Z]] <https://docs.microsoft.com/ja-jp/typography/opentype/spec/ttochap1#text-processing-with-opentype-layout-fonts>
]REFS]

* レンダリング処理と文字性の維持

[23] 
[[文字列]]を[[グリフ]]列に変換し[[レンダリング]]する一連の処理により、
異なる[[文字]]が同じ[[グリフ]]で表現されたり、
複数の[[文字]]が[[合字]][[グリフ]]に置き換えられたり、
[[表示順]]が[[論理順]]の[[文字列]]の位置関係から入れ替わっていたりしますが、
多くの場合に元の[[文字]]との対応関係を保持し続ける必要があります。

[EG[
[24] 例えば[[文字列の選択]]からの[[コピー]]を実現するためには、
選択した[[グリフ]]列から元の[[文字列]]が復元できる必要があります。
]EG]

;; [25] 
異なる[[文字列]]が同じ[[グリフ]]になったり、
[[文字コード]]があっても表示には直接反映されなかったりもするので、
[[グリフ]]の列だけを使って[[文字列]]を生成するのは困難で、
入力の[[文字]]と出力の[[グリフ]]の対応関係を保持するのが一般的みたいです。


;; [29] 
[[PDF]] が作り方により、
または表示ソフトウェアの品質により、
[[コピペ]]でおかしな[[文字列]]が生成されることがある、
というのはよく知られた問題です。


[27] 
[[bidi]] や [[shaping]] の処理では[[文字]]の順序が入れ替わって表示の順序が作られます。
関係性は相当複雑になるものの、元の[[文字]]との対応付けはできます。

[26] 
[[OpenType Layout]] では文章処理クライアントが[[文字]]と[[グリフ]]の関係を追跡し続けることになっています。
[SRC[>>13]]
[[合字]][[グリフ]]に置き換えられて複数[[文字]]が1[[グリフ]]にまとめられたりすることはあるものの、
対応付けを維持したままの [CODE[GSUB]] による置換は容易に実現できます。


[28] 
[[OpenType]] [[フォント]]は[[合字]]における[[キャレット]]位置の情報を持つことが出来ます。
複数の[[文字]]が1つの[[グリフ]]にまとめられていても、
[[文字]]単位で[[選択範囲][文字列の選択]]を指定することができます。
[SEE[ [[キャレット]] ]]
ただしそのような情報が欠けていることもありますし、
[[グリフ]]が図形的に[[文字]]単位に分割できない場合もあります。
そのときは不自然な挙動になってしまいます。

[40] 
[[OpenType]] [CODE[GPOS]] [F[[CODE[lookupType]]]] [N[5]]
(Mark-to-Ligature Attachment Positioning, MarkLigPos)
は[[合字グリフ]]上の [[anchor point]] をいくつか指定できます。
[[マークグリフ]]の[[配置]]に当たっては、
そのいずれかの [[anchor point]] を選ばなければなりません。
[SEE[ [[グリフ位置決定]] ]]

[41] 
[[shaping engine]] による文字前処理や
[CODE[GSUB]] による[[グリフ]]の置き換えがあると、
[[合字グリフ]]と[[マークグリフ]]の元になった[[基底文字]]と[[結合文字]]の関係 (など)
は[[グリフ]]列から読み取れなくなってしまうかもしれません。
しかし実装はその関係性を追跡しておかなければ[RUBYB[なりません][must]] [SRC[>>39]]。
[[基底文字]]が[[合字グリフ]]のどの部分に変化したかに応じて、
[[結合文字]]に由来する[[マークグリフ]]をどの [[anchor point]]
を使って[[配置]]するかを決定します。

[REFS[
- [39] 
[CITE@ja-jp[[[GPOS]] — Glyph Positioning Table (OpenType 1.9) - Typography | Microsoft Docs]], [[PeterCon]], [TIME[2022-09-07T13:06:36.000Z]] <https://docs.microsoft.com/ja-jp/typography/opentype/spec/gpos#lookup-type-5-mark-to-ligature-attachment-positioning-subtable>
]REFS]

* 動的なフォント変更

[SEE[ [[font-display]], [[CSSアニメーション]] ]]

* セキュリティー

[4] [[文字のセキュリティー]]参照。

[3] [CITE@ja[あるUNICODE文字がAppleデバイス上でアプリを破壊する――iOS、Mac、Watchの主要ソフトすべてに影響 | TechCrunch Japan]]
([[Taylor Hatmaker]]著, [TIME[2018-02-19 16:13:56 +09:00]])
<http://jp.techcrunch.com/2018/02/16/2018-02-15-iphone-text-bomb-ios-mac-crash-apple/>

[5] [CITE@ja[#PS4 特定の文字列のメッセージを受け取るとクラッシュする事案が多数発生しているらしい…対処法はあるようなのでやってみて - Togetter]]
([TIME[2018-10-15 21:56:42 +09:00]])
<https://togetter.com/li/1276875>

[7] [CITE@ja[AppleのOSをクラッシュさせる文字列がまた見つかる | スラド IT]]
([TIME[2020-04-26 11:17:35 +09:00]])
<https://it.srad.jp/story/20/04/25/2016238/>

[49] 
[CITE@ja[Mac OS XとiOSで特定のアラビア語文字列を表示させるとクラッシュする問題が見つかる | スラド アップル]], [TIME[2024-01-22T13:43:32.000Z]] <https://apple.srad.jp/story/13/09/03/0824238/>


* テスト

[2] [CITE@en[unicode-org/text-rendering-tests: Test suite for text rendering]]
([TIME[2017-05-16 13:12:37 +09:00]])
<https://github.com/unicode-org/text-rendering-tests>


* 関連

[9] [[レタリング]]とは違います。

* メモ




[11] [CITE@en[Text Rendering Hates You]], [TIME[2021-07-07T04:15:13.000Z]], [TIME[2021-11-15T08:57:15.274Z]] <https://gankra.github.io/blah/text-hates-you/>

[43] [CITE@ja[ケケモコソカメニハさんはTwitterを使っています: 「Qtさんフォールバックフォントを選ぶときに隣接する文字を考慮するらしく、こういうことが起きます。 スクリーンショットの状況はUbuntu Monoが◆を持っていないため。左の◆はDejaVu Sans Mono、aはUbuntu Mono、「あ」と右の◆はNoto Sans Mono CJK JP。 https://t.co/AKD9HfhYiJ」 / Twitter]], [TIME[午前3:39 · 2022年11月9日][2022-11-08T18:39:28.000Z]], [TIME[2022-11-09T04:52:53.000Z]] <https://twitter.com/ytomino/status/1590051393716768768>


[46] [CITE[クメール文字が書けない - Helpfeel社のScrapboxを一部公開]], [TIME[2023-07-06T09:34:07.000Z]] <https://scrapbox.io/nota-private-sample/%E3%82%AF%E3%83%A1%E3%83%BC%E3%83%AB%E6%96%87%E5%AD%97%E3%81%8C%E6%9B%B8%E3%81%91%E3%81%AA%E3%81%84>


[47] 
[[文字]] (の情報システム上での表現) の知識がない技術者が設計すると >>46 のようなことが起きる。
欧米の技術者がシステムを設計するとアジア人が使えないものができるのと同じ原理。


[48] 
[[書記素クラスター]]にうまく分割できればいいみたいな話になってるけど、それで[[語頭形]]とかの表示ちゃんとできてるんだろうか?



[50] 
[CITE@ja[Unicode はサービス拒否攻撃のリスク有り (#2767675) | iOSベータ版に「肌の色を指定できる」絵文字が登場 | スラド]], [TIME[2024-01-23T13:56:08.000Z]] <https://srad.jp/comment/2767675>



- [59] 
[CITE@ja[XユーザーのMasato Kinugawaさん: 「合字になる1文字目に ::first-letter で色を変えるスタイルを適用するとどうなるか、さらにフォントサイズを変えるとどうなるかのテスト。ブラウザごとにバラバラ。 https://t.co/VLXvcLZOt7 https://t.co/YKuG9Nr4Xq」 / X]], [TIME[午前0:37 · 2024年7月5日][2024-07-04T15:37:02.000Z]], [TIME[2025-12-07T10:23:53.000Z]] <https://x.com/kinugawamasato/status/1808887754090295805>
-- [60] 
[CITE@ja[XユーザーのMasato Kinugawaさん: 「合字になる1文字目を:before + contentに置き、残りをテキストとして置いた場合、合字ができるかどうか(とついでにスタイルがどうあたるか)のテスト。 Chrome/Firefoxで合字ができた。CSS injectionで合字経由で文字列をリークする時にprefixとして便利な場合がありそう。 https://t.co/M9cXH4nPT3 https://t.co/s35tslFJq4」 / X]], [TIME[午前2:07 · 2024年7月5日][2024-07-04T17:07:47.000Z]], [TIME[2025-12-07T10:23:53.000Z]] <https://x.com/kinugawamasato/status/1808910589135368687>

[61] [CITE[WebKit Features for Safari 26.2 | WebKit]], [TIME[2025-12-16T02:29:09.000Z]] <https://webkit.org/blog/17640/webkit-features-for-safari-26-2/#text-shaping-across-inline-boxes>





