同じ行に異なるフォントからテキストを描画する
フォント ファミリ内の異なる型スタイルは、異なる幅を持つことができます。 たとえば、ファミリの太字スタイルと斜体スタイルは、指定したポイント サイズのローマスタイルよりも常に広くなります。 複数の種類のスタイルを 1 行に表示または印刷する場合は、文字が互いに表示または印刷されないように、行の幅を追跡する必要があります。
2 つの関数を使用して、現在のフォントのテキストの幅 (または範囲) を取得できます。 GetTabbedTextExtent 関数は、文字列の幅と高さを計算します。 文字列に 1 つ以上のタブ文字が含まれている場合、文字列の幅は、タブ位置の指定された配列に基づいています。 GetTextExtentPoint32 関数は、テキスト行の幅と高さを計算します。
必要に応じて、文字ビットマップを変更してフォントを合成します。 太字フォントで文字を合成するために、文字が開始点に 2 回描画され、開始点の右側にもう 1 ピクセル描画されます。 斜体フォントで文字を合成するには、文字セルの下部に 2 行のピクセルを描画し、開始点を 1 ピクセル右に移動し、次の 2 行を描画し、文字が描画されるまで続行します。 ピクセルをシフトすると、各文字が右にせん断されるように見えます。 せん断量は、文字の高さの関数です。
複数のフォントを含むテキスト行を記述する 1 つの方法は、TextOut を呼び出すたびに GetTextExtentPoint32 関数を使用し、現在の位置に長さを追加することです。 次の例では、"This is a" の太字文字を使用して "This is a" という行を書き込み、"sample" の斜体の文字に切り替えてから、"string" の太字に戻ります。すべての文字列を印刷すると、システムの既定の文字が復元されます。
int XIncrement;
int YStart;
TEXTMETRIC tm;
HFONT hfntDefault, hfntItalic, hfntBold;
SIZE sz;
LPSTR lpszString1 = "This is a ";
LPSTR lpszString2 = "sample ";
LPSTR lpszString3 = "string.";
HRESULT hr;
size_t * pcch;
// Create a bold and an italic logical font.
hfntItalic = MyCreateFont();
hfntBold = MyCreateFont();
// Select the bold font and draw the first string
// beginning at the specified point (XIncrement, YStart).
XIncrement = 10;
YStart = 50;
hfntDefault = SelectObject(hdc, hfntBold);
hr = StringCchLength(lpszString1, 11, pcch);
if (FAILED(hr))
{
// TODO: write error handler
}
TextOut(hdc, XIncrement, YStart, lpszString1, *pcch);
// Compute the length of the first string and add
// this value to the x-increment that is used for the
// text-output operation.
hr = StringCchLength(lpszString1, 11, pcch);
if (FAILED(hr))
{
// TODO: write error handler
}
GetTextExtentPoint32(hdc, lpszString1, *pcch, &sz);
XIncrement += sz.cx;
// Retrieve the overhang value from the TEXTMETRIC
// structure and subtract it from the x-increment.
// (This is only necessary for non-TrueType raster
// fonts.)
GetTextMetrics(hdc, &tm);
XIncrement -= tm.tmOverhang;
// Select an italic font and draw the second string
// beginning at the point (XIncrement, YStart).
hfntBold = SelectObject(hdc, hfntItalic);
GetTextMetrics(hdc, &tm);
XIncrement -= tm.tmOverhang;
hr = StringCchLength(lpszString2, 8, pcch);
if (FAILED(hr))
{
// TODO: write error handler
}
TextOut(hdc, XIncrement, YStart, lpszString2, *pcch);
// Compute the length of the second string and add
// this value to the x-increment that is used for the
// text-output operation.
hr = StringCchLength(lpszString2, 8, pcch);
if (FAILED(hr))
{
// TODO: write error handler
}
GetTextExtentPoint32(hdc, lpszString2, *pcch, &sz);
XIncrement += sz.cx;
// Reselect the bold font and draw the third string
// beginning at the point (XIncrement, YStart).
SelectObject(hdc, hfntBold);
hr = StringCchLength(lpszString3, 8, pcch);
if (FAILED(hr))
{
// TODO: write error handler
}
TextOut(hdc, XIncrement - tm.tmOverhang, YStart, lpszString3,
*pcch);
// Reselect the original font.
SelectObject(hdc, hfntDefault);
// Delete the bold and italic fonts.
DeleteObject(hfntItalic);
DeleteObject(hfntBold);
この例では、 GetTextExtentPoint32 関数は 、指定 した文字列の長さと高さを使用して SIZE 構造体のメンバーを初期化します。 GetTextMetrics 関数は、現在のフォントのオーバーハングを取得します。 フォントが TrueType フォントの場合、オーバーハングは 0 であるため、オーバーハング値は文字列の配置を変更しません。 ただし、ラスター フォントの場合は、オーバーハング値を使用することが重要です。
フォントがラスター フォントの場合、その後の文字を文字列の末尾に近づけるために、太字の文字列からオーバーハングが 1 回減算されます。 オーバーハングはラスター フォントの斜体文字列の先頭と末尾の両方に影響するため、グリフは指定した位置の右側から始まり、最後の文字セルの終点の左側で終了します。 ( GetTextExtentPoint32 関数は、グリフの範囲ではなく、文字セルの範囲を取得します。ラスター斜体文字列のオーバーハングを考慮するために、文字列を配置する前にオーバーハングを減算し、後続の文字を配置する前にもう一度差し引きます。
SetTextJustification 関数は、テキスト行の区切り文字に余分なスペースを追加します。 GetTextExtentPoint 関数を使用すると、文字列の範囲を決定し、行が占める必要がある領域の合計からその範囲を減算し、SetTextJustification 関数を使用して、文字列内の区切り文字間に余分なスペースを分散できます。 SetTextCharacterExtra 関数は、選択したフォントのすべての文字セル (区切り文字を含む) に余分なスペースを追加します。 ( GetTextCharacterExtra 関数を使用して、文字セルに追加される現在の領域の量を決定できます。既定の設定は 0 です)。
GetCharWidth32 関数または GetCharABCWidths 関数を使用してフォント内の個々の文字の幅を取得することで、文字の精度を高めることができます。 GetCharABCWidths 関数は GetCharWidth32 関数よりも正確ですが、TrueType フォントでのみ使用できます。
ABC の間隔を使用すると、アプリケーションで非常に正確なテキストの配置を実行することもできます。 たとえば、アプリケーションが ABC 間隔を使用せずにラスター ローマ字フォントを右揃えにした場合、前の幅は文字の幅として計算されます。 つまり、ビットマップ内のグリフの右側の空白は、グリフ自体ではなく、揃えられます。 ABC 幅を使用すると、テキストを配置するときに空白の配置と削除の柔軟性が向上します。これは、文字間の間隔を細かく制御できる情報があるためです。