フォントシステム¶
吉里吉里Z のフォントまわり (登録・名前解決・描画・検索) の総合ガイドです。 フォントの用意から登録手順、各 API の使い分けまでをここにまとめています。
全体構成¶
フォントファイル (.ttf/.otf/.ttc)
│ ①exe 埋め込み (resource/) ②data 外だし + fonts.json (宣言・遅延)
│ ③実行時登録 (Font.addFont 等) ④システムフォント (Windows GDI)
▼
┌───────────────────────────────────────────────┐
│ FontSystem (名前→ストレージ対応・遅延ロード) │←─ fonts.json
│ FontStream (共有オンメモリバッファ / 1フォント1部) │
└───────────────┬───────────────────────────────┘
▼
┌───────────────────────────────────────────────┐
│ glyphware (統一フォントエンジン: │
│ FreeType + HarfBuzz + BiDi / レジストリ・検索) │
└───┬───────────────┬───────────────┬───────────┘
▼ ▼ ▼
Layer.drawText Layer.draw ThorVG テキスト
(ラスタライザ GlyphwareText (Elements UI /
3種切替) (シェイピング) layerExVector)
- フォントのバイトデータは
FontStream共有バッファに 1 フォント 1 部だけ 読み込まれ、drawText・Elements UI・layerExVector・プラグインのすべてが同じ バッファを共有します (XP3 内フォント対応・オンメモリ)。 - グリフ生成・シェイピング・メタデータ検索は統一フォントエンジン glyphware に集約されています。従来の GDI / FreeType ラスタライザも 互換のためそのまま残っています。
ラスタライザ¶
Layer.drawText の文字画像を作るエンジンは Font.rasterizer
(静的プロパティ) で切り替えられます。
| 値 | Windows ネイティブ版 (WINVER) | SDL 版ほか |
|---|---|---|
| 0 | FreeType | FreeType (既定) |
| 1 | GDI (既定) | glyphware |
| 2 | glyphware | - |
- 既定値は従来互換のまま (WINVER=GDI / その他=FreeType) です。
- glyphware ラスタライザは classic とレイアウト互換 (セル送り・影・縁取り・
下線/取消線・VS15/16 など) を保ちながら、フォールバック連鎖・カラー絵文字・
合成 bold/italic に対応します。既知の差分: アンチエイリアス指定 off でも
常に AA 描画になります (
font.hinting/font.antialiasedは無視)。
フォントの用意と登録手順¶
① exe 埋め込み (resource/)¶
エンジンの resource/ フォルダに置いた .ttf/.otf はビルド時に実行ファイルへ
埋め込まれ、起動時から使用できます。ゲーム側で増やすものではなく、エンジン既定
フォントの置き場所です。埋め込みフォントはストレージ名
resource://./ファイル名 でもアクセスできます。
既定で埋め込むのは Roboto (英字) と elements_basic (Elements のアイコン) だけ
です。日本語 (Noto Sans JP 4.3MB) と絵文字 (Noto Emoji 1.9MB) は
実行ファイルサイズの大半を占めるため、既定では埋め込みません。日本語はゲーム側で
fonts.json に宣言するか (②)、OS のフォント (④) を使います。以前と同じく
埋め込みたい場合はビルド時に -DKRKRZ_EMBED_BUNDLED_FONTS=ON を渡します
(実行ファイルが約 6.2MB 大きくなります)。任意のファイルを外すには
-DKRKRZ_RESOURCE_EXCLUDE="ファイル名;ファイル名" を使います。
エンジン同梱のデモ (src/core/data) は、この 2 本を data/fonts/ に置いて
fonts.json で宣言しています。ゲームで同じフォントを使う場合の見本になります。
② data 外だし + fonts.json (推奨)¶
大きいフォント (カラー絵文字・CJK バリエーション等) は data フォルダに置き、
fonts.json で宣言します。起動時には名前だけ登録され、実ファイルは
初回使用時に遅延ロードされるため起動時間・メモリに影響しません。
{
"version": 1,
"fonts": [
{ "file": "fonts/notocoloremoji.ttf",
"family": "Noto Color Emoji",
"subfamily": "Regular",
"aliases": ["Noto Color Emoji Regular"],
"weight": 400,
"scripts": ["Zsye"],
"flags": ["emoji", "color"],
"ranges": [[9728, 9983], [126976, 129791]] }
]
}
fonts.jsonは data フォルダ (プロジェクトルート) 直下に置きます。file以外はすべて任意です。subfamily/fullName/postScriptName/weight(100-900) /width(1-9) /italic/faceIndex/languages/aliases/flags(emojicolormonospace) /ranges(収録コード ポイント区間) を宣言できます。- バリアブルフォントは
instance(fvar named instance 名) またはaxes(軸値の直書き) で「名前 = 特定の軸設定」を宣言できます。同じファイルを別名 + 別軸で複数宣言できます (下の「バリアブルフォント」節)。
{ "file": "myfont.ttf", "family": "MyFont SemiBold", "instance": "SemiBold" }
{ "file": "myfont.ttf", "family": "MyFont Narrow", "axes": { "wdth": 75 } }
scripts / ranges / スタイルは Font.queryFonts
がフォントファイルを開かずに検索へ使います。
- 生成ツールで自動生成できます (要 pip install fonttools):
python tools/fontgen/gen_fonts_json.py \
--fonts-dir <データ>/fonts --root <データ> --out <データ>/fonts.json
生成器は family を純粋な family 名にし、classic ラスタライザの face 名規約
(「family subfamily」連結、例 "Noto Color Emoji Regular") を aliases に
出力するため、どちらの名前でも解決できます。
同梱例: コアデモの src/core/data/fonts には Noto Color Emoji に加えて
RTL 用の Noto Sans Arabic / Noto Sans Hebrew を同梱しています
(SDL 版含む全プラットフォームでアラビア語/ヘブライ語が同一表示)。他の
スクリプトも Noto ファミリ (https://notofonts.github.io/) から取得すると
埋め込みの Noto Sans JP / Roboto と見た目が揃います
(候補: Noto Sans Thai / Devanagari / KR / SC / TC など。詳細は
src/core/data/fonts/README.md)。
③ 実行時登録 (スクリプト)¶
- Font.addFont
(storage)— フォントファイルを 即時ロードして登録し、収録フェイス名の配列を返します。 - Font.registerFontFile
(storage[, family])—familyを指定すると ファイルを開かない遅延登録 (fonts.json 1 エントリ 相当) になります。省略時は addFont と同じ即時ロードです。
Font.addFont("mygame_font.ttf"); // 即時ロード
Font.registerFontFile("fonts/big_cjk.ttf", "MyCJK"); // 遅延 (初回使用時に読む)
④ システムフォント (OS にインストール済みのフォント)¶
OS にインストール済みのフォントをフォント名だけで使えます。
- WINVER は GDI の名前解決です。glyphware 系の経路 (rasterizer=2 /
drawShapedText / Elements) でも
"メイリオ""MS PGothic"等の名前が そのまま解決され、TTC のフェイス番号も正しく選択されます。addFontプラグインで登録した埋め込みフォントも同様に名前で使えます。 - SDL 版ほか は起動時に OS のフォント一覧から名前とファイルの対応だけを
登録し、実ファイルは初めて使ったときに読みます (fonts.json と同じ遅延ロード)。
一覧の取得元は Windows が DirectWrite、Linux が fontconfig です
(macOS / Android は未対応)。同じ名前が fonts.json や実行時登録にあれば
そちらが優先されます。配布物の見た目を実行環境に左右させたくない場合は
起動オプション
-systemfont=noで無効にできます。
フォント名の規約と解決順序¶
Font.face や fontKey に書いた名前 (カンマ区切りでフォールバック連鎖)
は、次の順で解決されます:
- fonts.json / registerFontFile の宣言名 (family / aliases) → ストレージ
- ストレージパス (
fonts/foo.ttf、resource://./…など実在するもの) - OS のフォント名 (WINVER は GDI、addFont 登録分を含む。SDL 版ほかは 起動時に登録した OS フォント一覧)
- 解決できない名前は既定フェイスへフォールバック
既定フォント¶
フォント名を指定しないときの既定フォントは、起動オプション -deffont が
あればそれ、無ければ実行環境の言語に合わせた候補から実際に使えるものを
選びます (SDL 版ほか)。日本語環境の候補は fonts.json の Noto Sans JP、
続いて Yu Gothic UI / メイリオ / MS ゴシック / ヒラギノ / IPA / Noto Sans CJK JP
などの OS のフォントです。起動時にどれも使えなかった場合は、後から
fonts.json や Font.registerFontFile で候補の名前が登録された時点で選び
直します。WINVER は従来どおり GDI の既定フォントです。
どのフォントにも無い文字¶
フォールバック連鎖のどのフォントにも無い文字は、連鎖の中で持っているフォントの
U+FFFD (�) で描きます。U+FFFD も無ければ ? です (同梱の Roboto は
U+FFFD を持っています)。改行やゼロ幅文字・異体字セレクタなどの見えない文字は
何も描きません。Layer.drawText (FreeType / glyphware ラスタライザ)・
drawShapedText 系・縦組み・Elements で共通です (GDI ラスタライザは GDI の
代替表示のまま)。
classic (FreeType) の face 名は「family subfamily」連結 (例 "Noto Sans JP Regular") である点に注意してください。fonts.json 生成器が連結名を alias に 出すので、通常はどちらの表記でも動きます。
絵文字¶
Font.emojiMode (0=無効 / 1=モノクロ / 2=カラー) で絵文字グリフの扱いを選びます。
- カラー絵文字フォント (既定 "Noto Color Emoji") は fonts.json の外だし宣言が 前提です。未配置の場合は同梱モノクロ絵文字 (Noto Emoji) へ自動フォール バックします (豆腐にはなりません)。
- 既定フォント名は
Font.emojiFaceName/Font.colorEmojiFaceNameで変更可能。 - WINVER の既定ラスタライザ (GDI) は絵文字グリフを持たないため、
Font.rasterizer = 0(FreeType) または= 2(glyphware) にして使います。 - VS15 (テキスト表示) / VS16 (絵文字表示) のバリエーションセレクタに対応します。
フォント検索とメタデータ¶
登録済みフォントは Font.queryFonts で リッチ検索できます (条件はすべて省略可、結果はランク順の辞書配列):
// カラー絵文字フォントを探す
var r = Font.queryFonts(%[ containsText : "😀", color : 1 ]);
// 日本語を収録した太字を探す
var r = Font.queryFonts(%[ containsText : "あ", weight : 700 ]);
単一フォントの SFNT メタデータは Font.getFontInfo で取得できます (family / subfamily / weight / color 等)。fonts.json で 宣言済みの情報はフォントを開かずに応答します。
バリアブルフォント (可変軸)¶
可変軸 (fvar) を持つフォントは、TJS のフォントパラメータから軸を指定できます。 効くのは glyphware 経路 — Layer.drawShapedText 系 (常時) と、Font.rasterizer = 2 のときの Layer.drawText — のみで、旧 FreeType / GDI ラスタライザでは無視されます (起動後 1 回警告が出ます)。
Font.rasterizer = 2; // drawText で使う場合
layer.font.weight = 700; // wght 軸 (100-900、void で解除)
layer.font.variations = "wdth=87.5"; // 汎用軸指定 (複数は "wght=700,wdth=75")
var axes = Font.getVarAxes("MyFont"); // 利用できる軸の一覧
Font.defaultUseVarStyle = true; // bold/italic を軸で表現 (オプトイン)
- Font.weight は
wght軸として効き、 Font.variations にwghtを明示した場合は そちらが優先です。適用されるのはフォールバック連鎖の各 face が実際に持つ 同名軸だけなので、日本語フォント + 絵文字フォントのような軸構成の違う連鎖でも 安全です。 fonts.jsonのinstance/axes宣言で「名前 = 特定の軸設定」を作れます (上の fonts.json 節)。名前を指すだけで軸込みのフォントが使えるため、 スクリプト側で weight / variations に触れる必要がありません。- Font.defaultUseVarStyle を真にすると、 bold / italic を軸 (wght=700 / slnt=-10 / ital=1) で表現できるフォントでは 合成ボールド / イタリックの代わりに軸を使います (既定は偽)。
- 軸の照会は Font.getVarAxes /
Font.getFontInfo (
axes/namedInstances) で行えます。 #tag=valサフィックス表記: フォント名に#tag=val[,tag=val...]を 後置すると、その名前が指すフォントの軸インスタンスを表します (例"MyFont#wght=700"、"MyFont#wght=700,wdth=75")。 Font.face のフォールバック連鎖の各要素、 Elements 画面 JSON の"font"(label / text_area とも。折り返し計算も同じ インスタンス) で一様に使えます。Font.variationsがフォント全体 (連鎖の 全 face) に効くのに対し、こちらは連鎖の要素単位で効きます。- 無指定時の既定は wght=400 相当: 可変フォントを wght 未指定で参照した 場合、fvar の既定インスタンスではなく wght=400 で表示されます (CSS の font-weight 既定と同じ規則)。既定が Thin の VF (Noto VF 等) も、VF 1 本の 登録だけで無指定が Regular 相当に読めます。既定を変えたいときは Font.setDefaultVariations で 名前単位に登録します (明示指定・fonts.json 宣言が常に勝ちます)。
多言語シェイピング描画 (glyphware)¶
Layer.drawText は従来どおり「1 文字ずつのセル送り」ですが、
Layer.drawShapedText は
HarfBuzz シェイピング + BiDi による本格的な多言語 1 行描画を行います:
- アラビア文字の連結・合字・カーニング、ヘブライ/アラビアの右→左 (BiDi)
- コードポイント単位のフォールバック連鎖 (Font.face カンマ区切り)
- カラー絵文字、合成 bold/italic、下線/取消線、回転 (angle)
- 描画属性は Font オブジェクト 1 個で渡します (void = レイヤ自身の font / 文字列 = face のみ差し替え)
Layer.drawShapedTextArea は
矩形内への簡易折り返し描画です。\n の明示改行、英語等のワード単位
折り返し、日本語の文字単位折り返し (行頭/行末の簡易禁則付き)、行揃え
(左/中央/右)、行間調整に対応します。
count 引数 (drawShapedText / drawShapedTextArea 共通) に 0 以上を渡すと
先頭 count 「文字」だけを描画でき、タイプライタ表示に使えます。この
「文字」は描画時に一塊として扱われるクラスタ単位 (合字・結合文字・
絵文字 ZWJ シーケンスで 1) で、総数は
Layer.shapedTextCount で取得します。
全文をシェイピング/折り返し確定してから先頭部分だけを描くため、表示途中で
字形や折り返し位置が変化 (リフロー) しません。
計測は Layer.measureShapedText で行います (インク境界と ascent/descent、クラスタ数を返します)。
動作サンプル: コアデモギャラリー (src/core/data) の
「多言語シェイピング (glyphware)」シーンで、アラビア語/ヘブライ語 (RTL)・
BiDi 混在・絵文字混在・計測・矩形内折り返し・タイプライタ表示 (RTL 混在文の
自動再生 — RTL 区間が論理順で 1 クラスタずつ現れます) の実例を確認できます。
縦組み (日本語縦書き)¶
Layer.drawVerticalTextArea は
矩形へ縦組み (縦書き) で本文を流し込みます。横組みの
drawShapedTextArea とは別経路で、日本語組版規則 (JLReq) のアキ量表に従って
組みます:
- 和文は正立・欧文/数字は横倒しに組み分け (
orientationで全正立/全横倒しにも変更可) - 縦字形 (
vert/vrt2) と縦アドバンス (vmtx/VORG) を使うので、 括弧・長音符・句読点が縦向きの字形になる - 約物 (句読点・括弧類) の詰め、和欧間のアキ
- 行頭行末禁則、追い込み / 追い出し、行末揃え、句読点のぶら下げ (任意)
列の長さは矩形の height、列の送りは「フォントサイズ + lineSpacing」です。
既定は右から左 (1 列目が矩形の右端) で、verticalLr で左から右にできます。
矩形に入りきらない列は描画されません。フォント指定 (font 引数) と色の扱いは
drawShapedText と共通なので、横組みと同じ Font オブジェクトで見た目が揃います。
var opt = %[ "hanging" => true, "letterSpacing" => 0.05 ];
// (x, y, width, height, text, color, font, count, lineSpacing, options)
var r = layer.drawVerticalTextArea(20, 20, 400, 560, text, 0x000000,
font, -1, 6, opt);
// r.lines = 組んだ列数 / r.width = 使った幅 / r.count = 描いたクラスタ数
options の全キーと既定値は
Layer.drawVerticalTextArea を
参照してください (orientation / verticalLr / punctuation / latinGap /
hanging / justify / letterSpacing)。
count はタイプライタ表示用で、横組みと同じく行分割を全文で確定してから
制限を掛けるので途中でリフローしません。ただし数える単位は「描画される文字」で、
欧文の単語間空白は数えないため
Layer.shapedTextCount の値とは
一致しないことがあります。総数は戻り値の totalCount を使ってください。
描画せずに列数や必要な幅だけ知りたいときは Layer.measureVerticalTextArea を使います (引数から x / y / color を除いたもので、戻り値は同じ)。
未対応: ルビ・縦中横・圏点・割注・字取り、段組、下線 / 打ち消し線、 Font.angle。現状の対応範囲は本文の組版のみです。
動作サンプル: コアデモギャラリー (src/core/data) の
「縦組み (drawVerticalTextArea)」シーンで、本文の流し込み・options のキー切替
(ぶら下げ / 行末揃え / 約物の詰め / 列送りの向き / 字間)・orientation による
和欧の組み分け・同じ本文を横組みで並べた対比・タイプライタ表示 (count 制限) の
実例を確認できます。
UI 系 (Elements) と layerExVector のフォント¶
- Elements ダイアログ (ダイアログ) のテキストは glyphware で
描画されます。テーマフォントには埋め込みフォント (既定は Roboto) が自動登録
され、その後ろにエンジンの既定フォント (上記「既定フォント」: fonts.json の
宣言フォントや OS の日本語フォント、WINVER は GDI の既定) が最後の手段として
つながります。フォントを同梱しない構成でも日本語が出るのはこのためです。
追加フォントは
ElementsDialog.registerFont(family, storage[, weight, slant, stretch])/ElementsDialog.registerFontDir(dir)で登録します。ストレージパス (XP3 内・resource://含む) をそのまま渡せます。registerFontDirは登録できた 本数を返すので、if (ElementsDialog.registerFontDir(dir) == 0) { ... }で 「パッケージしたらフォントが 1 本も無い」事故を検知できます (0 本のときは エンジンも警告ログを出します)。XP3 に固めたフォントを指すときは"data.xp3>font/"のようにアーカイブを明示してください (addAutoPathでのマウントはファイル検索用で、ディレクトリ列挙はできません)。 OS のパス ("D:/game/font/") をそのまま渡すと 0 本になります。多言語 UI で表示言語ごとに フォント (JP/TC/SC 等) を自動で切り替えるには ElementsDialog.fontLanguages (言語連動フォント置換) を使います (ダイアログ の「言語連動フォント置換」節参照)。 - 矩形への流し込みは折り返しロジックも共有します。ダイアログの
text_areaウィジェットは上記drawShapedTextAreaと同じ折り返し・ 行頭行末禁則・クラスタ単位の文字送りを通るため、同じ本文・同じ幅なら レイヤ描画と Elements で改行位置が一致します (従来からあるtext_boxは互換のため素朴な折り返しのまま)。 - richtext プラグイン (RichText) の組版は minikin
ですが、フォントの実体取得とグリフのラスタライズは本体の glyphware を通ります。
face とフォントのバイト列を本体と共有するため、
Layer.drawTextや Elements と 同じフォント・同じ見た目になります ( 可変フォントの軸を使う場合だけは、 共有 face の状態を汚さないよう専用の face が開かれます )。 - layerExVector プラグイン (
GdiPlus.loadFont(storage, name)) も同じ エンジンを共有します。resource://./roboto-regular.ttfのように 本体埋め込みフォントや fonts.json の宣言名を指定できます。 - どの経路も同一フォントは FontStream の共有バッファ 1 部を使うため、 複数箇所で同じフォントを使ってもメモリは増えません。
プラグインからの利用 (C++)¶
tp_stub にフォントサービス API (TVPCreateFontStream / TVPFontAcquireFace /
TVPFontShapeLine / TVPFontQueryFaces ほか) が公開されており、プラグインも
共有バッファ・グリフ供給・シェイピング・検索を利用できます。詳細はエンジン
リポジトリの src/core/doc/FontEngine.md (内部実装ノート) を参照してください。
トラブルシューティング¶
- 文字が � (U+FFFD) で出る — その文字を持つフォントがどこにもありません。
日本語なら fonts.json で日本語フォントを宣言するか、OS のフォントを使って
ください (
-systemfont=noを付けていないか、SDL 版の macOS / Android では OS のフォントが使えない点にも注意)。 - フォント名を指定したのに既定フォントで描かれる — 名前が未登録です。
Font.getFontInfo(名前)が void を返すか確認し、fonts.json の宣言名 / aliases、またはFont.addFontの戻り値のフェイス名を使ってください。 - カラー絵文字が白黒になる — カラー絵文字フォントが未配置 (fonts.json
未宣言) だとモノクロへフォールバックします。
Font.queryFonts(%[color:1])で配置状況を確認できます。 - WINVER で絵文字が出ない — 既定ラスタライザ (GDI) は絵文字非対応です。
Font.rasterizer = 2(または 0) にしてください。 - "MS Gothic" の幅が経路によって違う — FreeType の名前解決は英名 TTC で 別フェイス (PGothic) に化けることがあります。実 family 名 ("MS ゴシック" 等) を使うと全経路で一致します。
関連資料¶
- リファレンス: Font / Layer
- ダイアログ (Elements)
- レンダリング済みフォント: FontMaker
- エンジン内部実装 (開発者向け):
src/core/doc/FontEngine.md