DirectX の構成要素
グリフ実行は怖くない
Charles Petzold
コード サンプルのダウンロード
数年前、私は Windows Presentation Foundation (WPF) で初めて文字グリフという概念に出会いました。文字体裁における "グリフ" は、文字のグラフィカルな表現を意味します。文字の Unicode 値で示される、特定の書き言葉における文字の機能ではありません。テキストを表示する代替方法の代表格である WPF の Glyphs の要素と GlyphRun クラスについては知っていましたが、これらを使用すると必要以上にややこしくなるように思えました。最も奇妙に思えたのは、目的のフォントを、フォント ファミリの名前ではなく実際のフォント ファイルのパスで参照することでした。Windows のコーディングについては 1.0 のベータ版時代から経験がありましたが、ファイル名でフォントを参照する必要に迫られたことはありませんでした。そういうやり方とは無縁だったのです。
私はすぐに、Glyphs と GlyphRun は自分自身も含む大多数のプログラマにとっては難しすぎると結論付けました。わずか数年後、Microsoft XML Paper Specification (XPS) を扱うようになったとき、グリフを使用する根拠が明白になりました。
基本的な違い
通常、プログラムで Windows のフォントを要求する際は、Century Schoolbook などのフォント ファミリ名と斜体や太字などの属性を使用して要求します。実行時に、Windows によってユーザーのシステムで、ファミリ名と一致するフォントを含んだフォント ファイルが検索されます。斜体と太字は、このフォントに組み込まれている場合も、システムで合成される場合もあります。テキストのレイアウトを行うには、フォント文字のサイズを表すフォント メトリック情報に Windows またはアプリからアクセスします。
2 人のユーザーが 2 つのフォント ファイルを各自のシステムに配置している場合に、どちらのフォント ファイルも Century Schoolbook というファミリ名のフォントを含んでいるものの、フォント メトリックが多少異なるときは、どうなるでしょうか。これは、実際には問題になりません。テキストは実行時に書式設定およびレイアウトされるため、これら 2 つのフォントの実際のレンダリングは少し異なることもありますが、大きな問題にはなりません。アプリ プログラムは、このように柔軟になるよう設計されています。
ただし、XPS ドキュメントは異なります。PDF と同様、XPS は固定ページのドキュメントを表します。各ページのすべてのテキストは、既にレイアウトが完了し、厳密に配置されています。こうしたドキュメントがページのデザインに使用したフォントと少しでも異なるフォントでレンダリングされたら、適切に表示されないでしょう。これが、XPS ドキュメントには埋め込みのフォント ファイルが含まれていることが多い理由であり、XPS ページで Glyphs 要素を使用してこれらのフォント ファイルが参照され、テキストがレンダリングされる理由です。これにより、あいまいさがまったくなくなります。
通常、テキスト文字の参照には文字コードを使用します。多くの場合、特定のフォントで文字コードを使用した結果表示される正確なグリフについては、何も把握している必要がありません。ただし、グリフそのものをより細かく制御できることが重要な場合もあります。
文字コードと特定のフォントのグリフの間に一対一の対応関係があると考える方もいらっしゃるでしょうし、多くの場合はそのとおりです。しかし、非常に重要な例外があります。一部のフォント ファイルには合字が含まれており、fi や fl などの文字のペアに単一のグリフが対応しています。一部の非ラテン文字セットには、単一の文字をレンダリングするために複数のグリフが必要です。さらに、一部のフォント ファイルには特定の文字の代替グリフが含まれています。たとえば、スラッシュの入ったゼロ、小文字用に使用される小型大文字、スタイル用のスワッシュが付いた文字などがあります。これらの代替スタイルは、フォントではなく特定のフォント ファイルの特性です。このため、適切なフォント ファイルを取得することが重要です。
Windows 8 用の Windows ストア アプリを作成している場合、Windows ランタイム API を通じてこれらの代替スタイルを使用できます。『Programming Windows, 6th Edition』(O'Reilly Media、2012 年) の第 16 章では、この手法を実演する TypographyDemo という名前のプログラムを取り上げています。ただし、Windows 8 におけるグリフの基盤となるサポートは DirectX で実装されているので、グリフそのもの機能が最大限に発揮されるのは DirectX 環境です。
DirectX グリフ サポート
DirectX でグリフを表示する方法を探るには、いくつかのインターフェイス、メソッド、および構造体の作成方法を追跡する必要があります。
まず、2D グラフィックスとテキストを表示するための基本メソッドが含まれている、ID2D1RenderTarget インターフェイスから始めましょう。このインターフェイスでは、DrawGlyphRun メソッドを定義しています。DrawGlyphRun メソッドは、表示するグリフを表す DWRITE_GLYPH_RUN 型の構造体を必要とし、IDWriteFontFace 型のオブジェクトも参照します。
IDWriteFontFace オブジェクトを取得する方法の 1 つは、IDWriteFactory の CreateFontFace メソッドの使用です。このメソッドに必要な IDWriteFontFile オブジェクトは、IDWriteFactory の CreateFontFileReference メソッドで取得します。
CreateFontFileReference メソッドは、パスとファイル名を使用してフォント ファイルを参照します。ただし、Windows ストア アプリで DirectX を使用している場合、アプリにはユーザーのハード ドライブに対する完全で自由なアクセス権がないので、通常はこの方法でフォントにアクセスできません。CreateFontFileReference メソッドで参照するあらゆるフォント ファイルは、おそらくアプリ リソースとして定義されているので、アプリのパッケージ内に制限されます。
ただし、アプリ パッケージにはフォント ファイルを含めることができません。フォントをアプリに埋め込む場合、そのフォントを配布するためのライセンスが必要です。さいわい、マイクロソフトはアプリでフォント ファイルを配布できるようにするという明確な目的で、Ascender Corp. からいくつかのフォント ファイルのライセンスを取得しています。これらのフォント ファイルは主に XNA アプリで使用されてきましたが、グリフの観点からも興味深いと言えます。
今回のコラムのダウンロード可能なコードには、Visual Studio Express 2013 Preview の DirectX App (XAML) テンプレートに基づいて作成した GlyphDump という名前のプログラムがあります。このテンプレートは、SwapChainPanel に DirectX グラフィックスをレンダリングするアプリを作成します。
GlyphDump プロジェクトに Fonts という名前の新しいフィルター (および対応するフォルダー) を作成し、Ascender Corp. からライセンスを取得した 10 個のフォント ファイルを追加しました。DirectXPage.xaml の ListBox には、これらのフォントのファミリ名を、ファイル名を参照する Tag プロパティと共に明示的に一覧表示します。ユーザーがいずれかのフォントを選択したら、プログラムで次のようにフォント ファイルのパスと名前を構築します。
Package::Current->InstalledLocation->Path +
"\\Fonts\\" + filename;
次に、このパスを GlyphDumpRenderer クラスで使用して、IDWriteFontFile オブジェクトと IDWriteFontFace オブジェクトを作成します。
残りの処理は Render メソッドで実行します (図 1 参照)。
図 1 GlyphDump の Render メソッド
bool GlyphDumpRenderer::Render()
{
if (!m_needsRedraw)
return false;
ID2D1DeviceContext* context = m_deviceResources->GetD2DDeviceContext();
context->SaveDrawingState(m_stateBlock.Get());
context->BeginDraw();
context->Clear(ColorF(ColorF::White));
context->SetTransform(m_deviceResources->GetOrientationTransform2D());
if (m_fontFace != nullptr)
{
Windows::Foundation::Size outputBounds = m_deviceResources->GetOutputBounds();
uint16 glyphCount = m_fontFace->GetGlyphCount();
int rows = (glyphCount + 16) / 16;
float boxHeight = outputBounds.Height / (rows + 1);
float boxWidth = outputBounds.Width / 17;
// Define entire structure except glyphIndices
DWRITE_GLYPH_RUN glyphRun;
glyphRun.fontFace = m_fontFace.Get();
glyphRun.fontEmSize = 0.75f * min(boxHeight, boxWidth);
glyphRun.glyphCount = 1;
glyphRun.glyphAdvances = nullptr;
glyphRun.glyphOffsets = nullptr;
glyphRun.isSideways = false;
glyphRun.bidiLevel = 0;
for (uint16 index = 0; index < glyphCount; index++)
{
glyphRun.glyphIndices = &index;
context->DrawGlyphRun(Point2F(((index) % 16 + 0.5f) * boxWidth,
((index) / 16 + 1) * boxHeight),
&glyphRun,
m_blackBrush.Get());
}
}
// We ignore D2DERR_RECREATE_TARGET here. This error indicates
// that the device is lost. It will be handled during
// the next call to Present.
HRESULT hr = context->EndDraw();
if (hr != D2DERR_RECREATE_TARGET)
{
DX::ThrowIfFailed(hr);
}
context->RestoreDrawingState(m_stateBlock.Get());
m_needsRedraw = false;
return true;
}
この Render メソッドは 1 行が 16 グリフの行に選択したフォント ファイルのグリフをすべて表示するため、このように表示できるほど小さい fontEmSize 値を計算しようとします (フォントに含まれるグリフが多い場合、この処理はうまく機能しないでしょう)。通常、DWRITE_GLYPH_RUN 構造体の glyphIndices フィールドは、グリフ インデックスの配列です。今回は、一度に 1 つのグリフだけを表示します。
DrawGlyphRun メソッドには、座標点、DWRITE_GLYPH_RUN 構造体、およびブラシを渡す必要があります。座標は、グリフ ベースラインの左端を配置する位置を示します。ここで "ベースライン" と述べたことに注意してください。これは、ほとんどのテキスト表示メソッドのような 1 文字目の左上隅の位置を指定する処理とは異なっています。
図 2 に、Pescadero というファミリ名で Pesca.ttf というフォント ファイル名の、この記事でおそらく最も興味深いフォントを示します。
図 2 Pescadero フォントを表示する GlyphDump プログラム
このプログラムを作成するまで、私はこのような表示を見たことがありませんでした。一見したところ従来型の ASCII テーブルのように見えますが、数値の下付きと上付きのための一連のグリフ、さまざまな種類の合字の完全なコレクション、および装飾的なスワッシュが付いた大文字があります。
明らかに、DWRITE_GLYPH_RUN の glyphIndices フィールドは、文字コードではありません。フォント ファイル内には実際のグリフを参照するインデックスがあるので、実際のグリフの順序がまるで理にかなっていなくてもかまいません。
グリフを使用したテキスト
文字コードではなくグリフ インデックスを使って特定のフォント ファイルでテキストを表示できるユーティリティを、既にご存じの方もいらっしゃるでしょう。このようなユーティリティを使用すると、必要なグリフを正確に指定できます。
この機能を示しているのが FancyTitle プロジェクトです。FancyTitle プロジェクトでは、大部分が XAML ベースのプログラムに、Pescadero フォントの合字とスワッシュを使用したおしゃれなタイトルが必要な場合を想定しています。このプロジェクトには Pesca.ttf ファイルが含まれており、XAML ファイルでは SwapChainPanel を幅 778、高さ 54 に定義しています。これらの値は、レンダリングされるテキストのサイズに基づいて経験的に選択しました。
このプロジェクトの表示要件はシンプルなので、FancyTitleMain クラスとレンダリング クラスを削除し、SwapChainPanel のレンダリング用に DirectXPage クラスはそのまま残します (ただし、DeviceResources に少し手を加えて IDeviceNotify を ref クラス インターフェイスにすることで、DirectXPage で IDeviceNotify を実装して、出力デバイスがなくなって再作成した際に通知を受けられるようにする必要がありました)。
図 3 に示すテキスト出力は 24 文字で構成されていますが、グリフは 22 個だけです。図 2の合字とスワッシュを確認できます。
図 3 FancyTitle の出力
DirectX の背景をアリス ブルーに設定したので、SwapChainPanel がテキストよりも少し大きいことがおわかりでしょう。当然ながら、フォント ファイルがアプリケーションに埋め込まれていてグリフに直接アクセスするため、出力のサイズを正確に予測できます。
DrawGlyphRun メソッドを使用しなくても、合字や代替グリフを取得できます。精度は劣りますが Windows ランタイムの TextBlock 要素を使用しても取得できます。この方法については、Windows 8 に関する拙著の TypographyDemo プログラムで示しています。DirectWrite では、IDWriteTypography オブジェクトを渡して IDWriteTextLayout の SetTypography メソッドを使用できます。IDWriteTypography オブジェクトは、最終的には拡張 DWRITE_FONT_FEATURE_TAG 列挙体のメンバーを参照します。ただし、これらの手法は正確なグリフを指定する手法ほど精密ではありません。
DrawGlyphRun メソッドで斜体と太字を取得するにはどうすればよいでしょうか。多くの場合、各種のフォント ファイルには斜体や太字が適用されたフォントのバリエーションが含まれています。GlyphDump プログラムに含まれているフォントの中には、Bold フォントと Light フォントが 2 ~ 3 種類あります。ただし、CreateFontFace メソッドの DWRITE_FONT_SIMULATIONS フラグで斜体や太字の文字をシミレーションすることもできます。
アドバンスとオフセット
GlyphDump と FancyTitle のどちらのプロジェクトでも、DWRITE_GLYPH_RUN の 2 つのフィールドを nullptr に設定します。これら 2 つのフィールドの名前は、glyphAdvances と glyphOffsets です。
グリフの配列を表示する際は、最初の文字のベースライン左端の原点を指定できます。以降の文字を追加するたびに、原点の水平座標は文字幅に基づいて自動的に増加します (横向きのテキストを表示する際にも同様の処理が発生します)。この増加量を、"アドバンス" と呼びます。
DirectX で文字間隔の調整に使用するアドバンスを取得するには、IDWriteFontFace の GetDesignGlyphMetrics メソッドを呼び出します。その結果、DWRITE_GLYPH_METRICS 構造体の配列を、関心を持っているグリフ インデックスあたり 1 つ取得できます。この構造体の advanceWidth フィールドは、IDWriteFontFace の GetMetrics メソッドで取得した DWRITE_FONT_METRICS 構造体の designUnitsPerEm フィールドを基準とするアドバンスを示しています。DWRITE_FONT_METRICS 構造体には、特定のフォントのすべてのグリフに適用可能な垂直方向のメトリックも含まれています。
または、IDWriteFontFace1 の GetDesignGlyphAdvances メソッドを呼び出しても、designUnitsPerEm 値を基準とするアドバンスを取得できます。このアドバンス値を designUnitsPerEm (多くの場合は 2,048 などの適切に四捨五入した値) で除算し、DWRITE_GLYPH_RUN で指定した em サイズで乗算します。
glyphAdvances 配列は、一般に、デザイン メトリックが示す値を基準に文字間隔を広げたり狭めたりするために使用します。この効果を使用する場合、glyphAdvances 配列を、少なくともグリフ インデックスの配列のサイズより 1 小さい値の配列に設定する必要があります。最後のグリフには、以降に何も表示されないためアドバンスは必要ありません。
glyphOffsets フィールドは、文字あたり 1 つ存在する DWRITE_GLYPH_OFFSET 構造体の配列です。advanceOffset と ascenderOffset という 2 つのフィールドがあり、文字の通常位置に対する必要なオフセット (それぞれ右と上) を示します。多くの場合、この機能は XPS ファイルで特定のフォントの複数のグリフをページ全体に配置するために使用します。
CenteredGlyphDump プログラムでは、glyphOffsets フィールドを使用して、次のように DrawGlyphRun メソッドを 1 回呼び出すだけで特定のフォント ファイルのグリフの配列全体を表示する方法を示しています。
context->DrawGlyphRun(Point2F(), &m_glyphRun, m_blackBrush.Get());
DrawGlyphRun メソッドに渡す座標は (0, 0) になり、glyphAdvances は以降のグリフをアドバンスしないよう 0 の値の配列に設定します。それぞれのグリフの位置は、全面的に glyphOffsets で管理します。この位置は、列内の各グリフを中央揃えにするグリフ メトリックに基づいています。図 4に、実行結果を示します。
図 4 CenteredGlyphDump プログラム
独自のフォント ローダーを提供する
フォント ファイルがアプリ パッケージに含まれている場合、CreateFontReference で使用できるファイル パスを非常に簡単に取得できます。しかし、XPS や EPUB パッケージ、または分離ストレージやクラウドにフォントを配置している場合はどうでしょうか。
フォント ファイルにアクセスするコードを作成できる限りは、DirectX でアクセスできます。IDWriteFontFileLoader を実装するクラスと IDWriteFontFileStream を実装するクラスの、2 つのクラスを提供する必要があります。一般に、IDWriteFontFileLoader を実装するクラスは、アプリに必要なすべてのフォント ファイルにアクセスして各フォント ファイルにキーを割り当てるシングルトンです。IDWriteFontFileLoader 実装の CreateStreamFromKey メソッドは、各フォント ファイルの IDWriteFontFileStream インスタンスを返します。
これら 2 つのクラスを使用するには、まず IDWriteFontFileLoader を実装するクラスの単一のインスタンスを作成し、IDWriteFactory オブジェクトの RegisterFontFileLoader に渡します。次に、CreateFontFileReference を呼び出して IDWriteFontFile オブジェクトを取得する代わりに、この IDWriteFontFileLoader インスタンスと、必要な特定のフォント ファイルを識別するキーで、CreateCustomFontFileReference を呼び出します。
この手法を示しているのが、CenteredGlyphDump プログラムです。CenteredGlyphDump プロジェクトには、2 つのインターフェイスを実装する PrivateFontFileLoader と PrivateFontFileStream という 2 つのクラスがあります。これらのクラスはアプリ パッケージ内のフォント ファイルにアクセスしますが、他の目的にも使用できます。
IDWriteFontFileLoader の実装ではおそらくファイル I/O を呼び出す必要がありますが、Windows ストア アプリではこれらの呼び出しを非同期にする必要があります。IDWriteFontFileLoader クラスですべてのフォントをメモリに読み込む非同期メソッドを定義し、IDWriteFontFileStream ではこれらのメモリ ブロックにポインターを返すだけにすることは、理にかなっています。これが、Windows 8.1 用のマイクロソフト サンプル コードのうち Direct2D Magazine App Sample をよく調べた結果、PrivateFontFileLoader と PrivateFontFileStream で採用した手法です。
各ファイルを識別するキーには、ファイル名を使用しました。PrivateFontFileLoader の非同期読み込みメソッドの実行が完了したら、CenteredGlyphDump プログラムはこれらのファイル名を取得して ListBox に表示します。図 4にファイル名しか表示されていないのは、このためです。プログラムは、各ファイルに関連付けられているフォント ファミリの名前は認識していません。
文字からグリフへ
当然ながら、必要なグリフが正確にわかっている場合はグリフを参照してテキストを表示すれば十分ですが、DrawGlyphRun メソッドを使用して通常の Unicode 文字を表示できるでしょうか。
これは可能です。なぜなら IDWriteFontFace には、文字コードを特定のフォント ファイルのグリフ インデックスに変換する GetGlyphIndices メソッドがあるためです。変換時には、それぞれの文字コードで既定のグリフが選択されるので、装飾を施すことはできません。
しかし、GetGlyphIndices に渡す文字コードがどの Windows でも固有の形式になっている場合もあります。(Unicode 文字列の通常操作と同様の) 16 ビットの文字コードの代わりに、32 ビットの文字コードの配列を作成する必要があります。ご存じのように、Unicode は実際には 21 ビットの文字セットですが、文字は通常 16 ビット値 (UTF-16 と呼ばれます) で格納されます。つまり、2 つの 16 ビット値で構成される文字もあります。ただし、GetGlyphIndices には 32 ビット値を渡す必要があります。0xFFFF を超えるコードの文字が文字列に含まれていない限りは、配列間で値を転送するだけで十分です。
ラテン アルファベットの通常文字を扱っている場合、文字とグリフの間に一対一の対応関係があると考えることもできます。その他の文字を扱っている場合、インデックスを取得するには作成する出力配列のサイズ拡大が必要なこともあります。
HelloGlyphRun プロジェクトは、このシンプルな手法を実演しています。図 5 に、フォント ファイルを読み込んで DWRITE_GLYPH_RUN 構造体を作成するコードを示します。Update メソッドは、グリフ オフセットを調整し、テキストの波及効果の一種を及ぼします。
図 5 文字列に基づく DWRITE_GLYPH_RUN の定義
// Convert string to glyph indices
std::wstring str = L"Hello, Glyph Run!";
uint32 glyphCount = str.length();
std::vector str32(glyphCount);
for (uint32 i = 0; i < glyphCount; i++)
str32[i] = str[i];
m_glyphIndices = std::vector(glyphCount);
m_fontFace->GetGlyphIndices(str32.data(), glyphCount, m_glyphIndices.data());
// Allocate array for offsets (set during Update)
m_glyphOffsets = std::vector(glyphCount);
// Get output bounds
Windows::Foundation::Size outputBounds = m_deviceResources->GetOutputBounds();
// Define fields of DWRITE_GLYPH_RUN structure
m_glyphRun.fontFace = m_fontFace.Get();
m_glyphRun.fontEmSize = outputBounds.Width / 8; // Empirical
m_glyphRun.glyphCount = glyphCount;
m_glyphRun.glyphIndices = m_glyphIndices.data();
m_glyphRun.glyphAdvances = nullptr;
m_glyphRun.glyphOffsets = m_glyphOffsets.data();
m_glyphRun.isSideways = false;
m_glyphRun.bidiLevel = 0;
おそらく DrawGlyphRun メソッドは熟知しているフォント ファイルだけで使用するでしょうが、特定のフォント ファイルに含まれるグリフの種類を実行時に決めることもできます。IDWriteFontFace は、OpenType ファイルのテーブルにアクセスする TryGetFontTable メソッドを定義します。文字とグリフの関係を示すテーブルには "cmap" タグを使用し、グリフの代入テーブルには "GSUB" タグを使用しますが、OpenType 仕様でこれらのテーブルを読み取るには多くの時間と手間がかかります。
システム フォントを使用したグリフ実行
DrawGlyphRun メソッドは、独自に提供するフォント ファイルでしか使用できないのでしょうか。一見そう思われるでしょうが、システム フォントにも使用できます。その方法を説明しましょう。IDWriteFactory の GetSystemFontCollection メソッドを使用して、IDWriteFontCollection オブジェクトを取得します。このオブジェクトを使用すると、システムにインストールされたフォントに関連付けられているすべてのファミリ名を検出できます。IDWriteFontCollection の GetFontFamily メソッドは、IDWriteFontFamily 型のオブジェクトを返します。このオブジェクトから、GetMatchingFonts または GetFirstMatchingFont を呼び出し、フォント ファミリを斜体、太字、および拡大の属性と組み合わせて、IDWriteFont オブジェクトを取得します。
IDWriteFont オブジェクトを取得したら、CreateFontFace メソッドを呼び出して、IDWriteFontFace オブジェクトを取得します。このオブジェクトの型は、既に説明したプログラムの CreateFontFace メソッドで取得したオブジェクトと同じです。このオブジェクトから、DrawGlyphRun メソッドの DWRITE_GLYPH_RUN 構造体の設定を開始できます。
この処理は、SystemFontGlyphs プロジェクトで例を示しています。このプロジェクトでは、DirectXPage クラスで GetSystemFontCollection を使用して、ListBox にシステム フォントのファミリ名を格納します。ユーザーが項目を選択したら、レンダラーに渡した IDWriteFontFace オブジェクトを派生します。
SystemFontGlyphsRenderer クラスでは、"Annoying vibrating text effect" というテキストに基づいて DWRITE_GLYPH_RUN 構造体を構築します。次に Update メソッドで、グリフ オフセットの配列の値を -3 から 3 までのランダムな数値に設定し、テキスト文字列のすべての文字が独立して振動しているように見せる必要があります。
IDWriteFont が必ずフォント コレクション (システム フォント コレクションなど) に含まれるフォントを表すのに対して IDWriteFontFace が表すとは限らないことを除けば、IDWriteFont と IDWriteFontFace の概念にそれほど大きな違いはないでしょう。IDWriteFontCollection には、IDWriteFontFace を受け取って対応する IDWriteFont を返す GetFontFromFontFace メソッドがありますが、これは IDWriteFontFace に関連付けられたフォント ファイルがフォント コレクションに含まれている場合にのみ機能します。
カスタム フォント コレクション
ここまで、アプリに読み込むフォント ファイルやシステム フォントで DrawGlyphRun メソッドを使用する方法について説明しました。独自のフォント ファイルで通常のテキスト出力メソッド (DrawText と DrawTextLayout) を使用することは可能でしょうか。
もちろん可能です。DrawText と DrawTextLayout には、どちらも IDWriteTextFormat オブジェクトを渡す必要があることを思い出してください。このオブジェクトを作成するには、フォント ファミリ名とフォント コレクションの両方を指定し、IDWriteFactory の CreateTextFormat メソッドを使用します。
通常は、このフォント コレクションの引数を nullptr に設定して、システム フォントを指定します。ただし、IDWriteFactory の CreateCustomFontCollection メソッドを呼び出して、カスタム フォント コレクションを作成することもできます。その場合は、IDWriteFontCollectionLoader インターフェイスを実装する独自のクラスと、IDWriteFontFileEnumerator を実装するもう 1 つのクラスを提供する必要があります。
この処理は、ResourceFontLayout プログラムで例を示しています。また、このプログラムでは、CenteredGlyphDump の IDWriteFontFileLoader インターフェイスと IDWriteFontFileStream インターフェイスも実装しています。このカスタム フォント コレクションのフォント ファミリのリストを取得すると、複数のフォント ファイルが単一のフォント ファミリに統合されている場合があることがわかります。
おまけ
ここまで説明すれば、IDWriteFontFace が重要だとはっきりおわかりいただけたと思います。フォント ファイルから直接作成する方法と、フォント コレクションの特定のフォントを選択する方法の 2 とおりで、IDWriteFontFace オブジェクトを作成できます。作成後は、DWRITE_GLYPH_RUN 構造体でこの IDWriteFontFace オブジェクトを参照することも、グリフ メトリックの取得やフォント ファイルのテーブルへのアクセスに使用することもできます。
IDWriteFontFace では、GetGlyphRunOutline というメソッドも定義しています。このメソッドの引数は DWRITE_GLYPH_RUN 構造体のフィールドとほとんど同じですが、さらに ID2D1SimplifiedGeometrySink と同じ IDWriteGeometrySink という引数もあります。
つまり、テキスト文字列を ID2D1PathGeometry に変換してから、任意の方法でこのジオメトリをレンダリングおよび操作できます。図 6に、描画して塗りつぶした文字のジオメトリ、点線スタイルでレンダリングした同じジオメトリ (点が文字の周囲に沿って移動するアニメーションが表示されます)、および拡張した輪郭の (実際には輪郭の周囲に輪郭を表示した) ジオメトリを表示する OutlinedCharacters プログラムのスクリーンショットを示します。
図 6 OutlinedCharacters プログラム
これは、始まりにすぎません。
Charles Petzold は MSDN マガジンの記事を長期にわたって担当しており、Windows 8 向けのアプリケーション開発についての書籍『Programming Windows, 6th edition』(O'Reilly Media、2012 年) の著者でもあります。彼の Web サイトは charlespetzold.com(英語) です。
この記事のレビューに協力してくれたマイクロソフト技術スタッフの Jim Galasyn と Justin Panian に心より感謝いたします。