フォントシステム¶
吉里吉里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 はビルド時に実行ファイルへ
埋め込まれ、起動時から使用できます (既定同梱: Noto Sans JP / Roboto /
Noto Emoji / elements_basic)。ゲーム側で増やすものではなく、エンジン既定
フォントの置き場所です。埋め込みフォントはストレージ名
resource://./ファイル名 でもアクセスできます。
同梱フォントは実行ファイルサイズの大半を占めます (Noto Sans JP 4.3MB /
Noto Emoji 1.9MB)。ゲーム側で自前のフォントを供給する構成では、ビルド時に
-DKRKRZ_EMBED_BUNDLED_FONTS=OFF を渡すとこの 2 本を埋め込まなくなり、
実行ファイルが約 6.2MB 小さくなります (Roboto / elements_basic は Elements の
既定テーマが参照するので残ります)。任意のファイルを外すには
-DKRKRZ_RESOURCE_EXCLUDE="ファイル名;ファイル名" を使います。
外した場合、日本語・絵文字の描画に使うフォントはゲーム側で用意してください。
② 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"); // 遅延 (初回使用時に読む)
④ システムフォント (Windows ネイティブ版)¶
WINVER では OS にインストール済みのフォントをフォント名だけで使えます
(GDI 名前解決)。glyphware 系の経路 (rasterizer=2 / drawShapedText /
Elements) でも "メイリオ" "MS PGothic" 等の名前がそのまま解決され、
TTC のフェイス番号も正しく選択されます。addFont プラグインで登録した
埋め込みフォントも同様に名前で使えます。
フォント名の規約と解決順序¶
Font.face や fontKey に書いた名前 (カンマ区切りでフォールバック連鎖)
は、次の順で解決されます:
- fonts.json / registerFontFile の宣言名 (family / aliases) → ストレージ
- ストレージパス (
fonts/foo.ttf、resource://./…など実在するもの) - (WINVER) インストール済み GDI フォント名 (addFont 登録分を含む)
- 解決できない名前は既定フェイスへフォールバック
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 / Noto Sans JP /
Noto Emoji) が自動登録され、追加フォントは
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://./notosansjp-regular.otfのように 本体埋め込みフォントを指定でき、フォントを同梱しなくてもアウトライン 文字を描画できます。 - どの経路も同一フォントは FontStream の共有バッファ 1 部を使うため、 複数箇所で同じフォントを使ってもメモリは増えません。
プラグインからの利用 (C++)¶
tp_stub にフォントサービス API (TVPCreateFontStream / TVPFontAcquireFace /
TVPFontShapeLine / TVPFontQueryFaces ほか) が公開されており、プラグインも
共有バッファ・グリフ供給・シェイピング・検索を利用できます。詳細はエンジン
リポジトリの src/core/doc/FontEngine.md (内部実装ノート) を参照してください。
トラブルシューティング¶
- フォント名を指定したのに既定フォントで描かれる — 名前が未登録です。
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