フォントの変更
テキスト描画の大事な要素にフォントがあります。
フォントは文字の形、デザインのことですが、太字や斜体などの変形や文字サイズなどもフォントの設定で行います。
今度はフォントを別のものに変更してみましょう。
GetStockObject関数
フォントの設定の前に、GetStockObject
関数について説明します。
ウィンドウアプリでは、画面に文字や図形などを描画することでプログラムを作ります。
文字を描画するにはフォントを使用しますし、図形を描く場合はペンやブラシを使用します。
このうち、よく使用されるものはシステムがあらかじめ定義しており、これをストックオブジェクト(組み込みオブジェクト、定義済みオブジェクト)と言います。
GetStockObject関数はこのストックオブジェクトを取得する関数です。
- HGDIOBJ GetStockObject(
int i
); - ストックオブジェクトのハンドルを取得する。
(ペン、ブラシ、フォント、パレット)
失敗した場合はNULLを返す。
引数はストックオブジェクトを表す定数です。
ストックオブジェクトはフォント以外にも色々ありますが、フォントの場合は以下の定数が用意されています。
定数 | 説明 |
---|---|
OEM_FIXED_FONT | OEM固定幅フォント。 |
ANSI_FIXED_FONT | 固定幅システムフォント |
ANSI_VAR_FONT | 可変幅システムフォント |
SYSTEM_FONT | システムフォント。 メニューやダイアログ、テキスト描画などで使用されるデフォルトのフォント。 |
DEVICE_DEFAULT_FONT | デバイス依存フォント。 |
DEFAULT_GUI_FONT | ユーザーインターフェイス用のデフォルトフォント。 |
SYSTEM_FIXED_FONT | 固定幅システムフォント。 Windows3.0以前との互換性のために存在する。 |
SelectObject関数
GetStockObject
関数で取得したストックオブジェクトのハンドル(HGDIOBJ型)は、SelectObject
関数でデバイスコンテキストに設定します。
- HGDIOBJ SelectObject(
HDC hdc,
HGDIOBJ h
); - デバイスコンテキストhdcにストックオブジェクトhを設定する。
(ペン、ブラシ、フォント、パレット)
成功した場合は置き換え前のオブジェクトのハンドルを返す。
失敗した場合はNULLを返す。
hがリージョンの場合は成功した場合にSIMPLEREGION、COMPLEXREGION、NULLREGIONのいずれかを返し、失敗した場合にHGDI_ERRORを返す。
なんだかややこしそうな感じがしますが、使い方自体は簡単です。
#include <windows.h>
//ウィンドウの生成等は省略
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
static const WCHAR* str = L"This is a pen.\nあいうえお";
switch (message)
{
case WM_PAINT: //ウィンドウの描画
GetClientRect(hWnd, &rect);
hdc = BeginPaint(hWnd, &ps);
//何も設定しない場合
rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);
//フォントのストックオブジェクトを取得して設定
HGDIOBJ obj = GetStockObject(ANSI_FIXED_FONT);
SelectObject(hdc, obj);
rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
同じ文字列を出力していますが、二つめの出力はフォントを固定幅フォントに変更しています。
テキストの色をSetTextColor関数でデバイスコンテキストに設定したように、テキストのフォントをSelectObject関数
で設定するわけです。
CreateFont関数
ストックオブジェクトのフォントは簡単に使用できますが、使い勝手はあまりよくありません。
フォントの種類やサイズなどを詳細に設定するにはCreateFont
関数を使用します。
この関数は論理フォントを作成します。
論理フォントとは「論理上のフォント」のことで、実際に描画される文字の形(図形としての形状)は持っていません。
「こういう特徴を持ったフォント」という、単なる情報の集まりです。
これに対して物理フォントというものがあり、こちらは実際の文字の形を持っています。
論理フォントをSelectObject
関数に渡すと、その情報に最も近い物理フォントをデバイスコンテキストに設定することができます。
- HFONT CreateFontW(
int cHeight,
int cWidth,
int cEscapement,
int cOrientation,
int cWeight,
DWORD bItalic,
DWORD bUnderline,
DWORD bStrikeOut,
DWORD iCharSet,
DWORD iOutPrecision,
DWORD iClipPrecision,
DWORD iQuality,
DWORD iPitchAndFamily,
LPCWSTR pszFaceName
); - 論理フォントを作成する。
この関数は引数が非常に多いですが、いくつかの引数はほぼ毎回同じ設定で使うことができます。
お勧めの設定もあわせて紹介しておきます。
cHeight
cHeight
メンバは、文字セルの高さです。
0を指定すると既定値が使用されます。
負数を指定すると、絶対値(マイナス符号を取り去った値)に変換した上で、文字セルの高さから内部レディングを引いた高さとして設定されます。
内部レディングとは、アルファベットのアクセント記号に使用する高さのことです。
文字を既定のサイズで使用する場合は0を、任意のサイズに設定したい場合は正数を指定します。
cWidth
cWidth
メンバは、平均の文字幅です。
0を指定すると高さから自動的に最適な値が設定されます。
通常は0を指定します。
文字幅を変更したい場合は任意の値を指定します。
cEscapement
cEscapement
メンバは、文字全体の角度です。
単位は1/10度です。
WindowsNT/2000以降のGM_ADVANCEDグラフィックモードでは、次に説明するcOrientation
と個別に設定できます。
それ以外のモードではcEscapement
とcOrientation
は同じ値に設定してください。
通常は0を指定します。
cOrientation
cOrientation
も文字の角度です。
WindowsNT/2000以降のGM_ADVANCEDグラフィックモードではcEscapement
が文字全体の角度を、cOrientation
が文字個別の角度を設定します。
それ以外のモードでは両方に同じ値を設定してください。
(文字個別の角度の設定はありません。)
通常は0を指定します。
cWeight
cWeight
メンバは、文字の太さです。
0~1000までの数値を指定できるほか、定数を指定することもできます。
0を指定すると既定値が使用されます。
定数 | 値 |
---|---|
FW_DONTCARE | 0 |
FW_THIN | 100 |
FW_EXTRALIGHT FW_ULTRALIGHT |
200 |
FW_LIGHT | 300 |
FW_NORMAL FW_REGULAR |
400 |
FW_MEDIUM | 500 |
FW_SEMIBOLD FW_DEMIBOLD |
600 |
FW_BOLD | 700 |
FW_EXTRABOLD FW_ULTRABOLD |
800 |
FW_HEAVY FW_BLACK |
900 |
たくさん定数が用意されていますが、実際に使用できるのは標準の太さと太字のみです。
つまりFW_DONTCARE
、FW_NORMAL
またはFW_REGULAR
、FW_BOLD
以外の値は使用しません。
(使用しても標準の太さか太字になる以外の変化はありません)
bItalic
bItalic
メンバは、斜体の設定です。
TRUE
を設定すると文字が斜体で表示されます。
bUnderline
bUnderline
メンバは、アンダーライン(下線)の設定です。
TRUE
を設定すると文字の下にアンダーラインが引かれます。
bStrikeOut
bStrikeOut
メンバは、打ち消し線の設定です。
TRUE
を設定すると文字に打ち消し線が引かれます。
iCharSet
iCharSet
メンバは、フォントの文字セットです。
以下の定数を指定します。
定数 | 説明 |
---|---|
ANSI_CHARSET | ANSI文字セット |
DEFAULT_CHARSET | デフォルト文字セット |
SYMBOL_CHARSET | シンボル文字セット |
SHIFTJIS_CHARSET | Shift_JIS文字セット(日本語) |
HANGEUL_CHARSET | 韓国語文字セット |
GB2312_CHARSET | 中国語簡体字文字セット |
CHINESEBIG5_CHARSET | 中国語繁体字文字セット |
OEM_CHARSET | OEM文字セット(OS依存) |
JOHAB_CHARSET | 韓国版Windows文字セット |
HEBREW_CHARSET | 中東版Windows文字セット |
ARABIC_CHARSET | 中東版Windows文字セット |
GREEK_CHARSET | ギリシア語文字セット |
TURKISH_CHARSET | トルコ語文字セット |
VIETNAMESE_CHARSET | ベトナム語文字セット |
THAI_CHARSET | タイ語文字セット |
EASTEUROPE_CHARSET | 東ヨーロッパ文字セット |
RUSSIAN_CHARSET | ロシア語文字セット |
MAC_CHARSET | Macintosh用文字セット |
BALTIC_CHARSET | バルチック文字セット |
日本語環境ならSHIFTJIS_CHARSET
を指定しておけば問題ありません。
iOutPrecision
iOutPrecision
メンバは、出力の精度です。
この値は「精度」となっていますが、プログラマが要求するフォント設定に対してどのフォントフォーマットを使用するかの設定です。
以下の定数を指定します。
定数 | 説明 |
---|---|
OUT_DEFAULT_PRECIS | 既定の動作に任せる。 |
OUT_STRING_PRECIS | 使用しない。 ただしラスタフォントが列挙される時はこの値が返される。 |
OUT_CHARACTER_PRECIS | 使用しない。 |
OUT_STROKE_PRECIS | 使用しない。 ただしTrueTypeフォントやその他のアウトラインベースのフォント、ベクタフォントが列挙されるときにはこの値が返される。 Win95/98系ではベクタフォントを選択するよう指示する。 |
OUT_TT_PRECIS | 同じ名前のフォントが複数存在する場合はTrueTypeフォントを使用するよう指示する。 |
OUT_DEVICE_PRECIS | 同じ名前のフォントが複数存在する場合はデバイスフォントを使用するよう指示する。 |
OUT_TT_ONLY_PRECIS | TrueTypeフォントだけを選択するよう指示する。 システムにTrueTypeフォントが存在しない場合は既定の動作。 |
OUT_OUTLINE_PRECIS | TrueTypeフォントやその他のアウトラインベースのフォントを選択するように指示する。 Win95/98系では使用できない。 |
フォントの知識がないと意味が分からないと思いますが、ほとんどの場合で定数OUT_DEFAULT_PRECIS
を指定しておけば大丈夫です。
iClipPrecision
iClipPrecision
メンバは、文字が描画領域からはみ出している場合に、はみ出した部分を消去する方法の設定です。
以下の定数を指定します。
定数 | 説明 |
---|---|
CLIP_DEFAULT_PRECIS | 既定の動作に任せる。 |
CLIP_CHARACTER_PRECIS | 使用しない。 |
CLIP_STROKE_PRECIS | 使用しない。 ただしラスタフォント、ベクタフォント、TrueTypeフォントが列挙されるときにはこの値が返される。 |
CLIP_MASK | 使用しない。 |
CLIP_LH_ANGLES | フォントの回転方向を座標系により決定されるようになる。 このフラグを使用しない場合はデバイスフォントは常に反時計回りに回転し、その他のフォントは座標系の向きに従って回転する。 |
CLIP_TT_ALWAYS | 使用しない。 |
CLIP_EMBEDDED | 読み取り専用の埋め込みフォントを使用する場合はこのフラグを指定する。 |
これもほとんどの場合でCLIP_DEFAULT_PRECIS
を指定します。
iQuality
iQuality
メンバは、文字の出力品質の設定です。
論理フォントで設定した属性と使用される物理フォントの属性とを、どの程度一致させるかが決定されます。
以下の定数を指定します。
定数 | 説明 |
---|---|
DEFAULT_QUALITY | 品質は重視されない。 |
DRAFT_QUALITY | PROOF_QUALITYほどは品質は重視されない。 |
PROOF_QUALITY | 品質は論理フォントの属性と一致させることよりも重視される。 |
NONANTIALIASED_QUALITY | アンチエイリアス処理を行わない。 |
ANTIALIASED_QUALITY | アンチエイリアス処理が可能であり、その必要があれば行う。 |
CLEARTYPE_QUALITY | ClearTypeアンチエイリアス処理を行う。 |
通常はDEFAULT_QUALITY
を指定します。
文字の品質が重視される場合はDRAFT_QUALITY
やPROOF_QUALITY
を使用します。
なお、DEFAULT_QUALITY
、DRAFT_QUALITY
、PROOF_QUALITY
は、アンチエイリアス処理を行うか否かはシステムの設定に従います。
(コントロールパネルから設定のオン/オフができる)
iPitchAndFamily
iPitchAndFamily
メンバは、文字の幅(ピッチ)とフォントファミリの設定です。
この引数は以下の二つをビット演算の論理和(a | b
)で指定します。
1、ピッチ
固定幅/可変幅のことで、文字の種類によって文字幅を適宜変えるか否かの設定です。
例えば「A」という文字と「I」という文字では文字自体の幅が違います。
固定幅はこれに関係なく常に一定の幅を使用します。
(等幅フォントとも呼ばれます)
可変幅は文字幅に合わせて使用する幅を変化させます。
(プロポーショナルフォントと呼ばれます)
一般的に可変幅のほうが読みやすくなりますが、文字の正確な入力が必要な場合などは固定幅のほうが都合がいいこともあります。
2、フォントファミリ
ある基本となる文字の形状のバリエーションをひとまとめにしたものです。
例えばある文字の太字バージョンや斜体バージョンなどを用意しておき、適宜切り替えることで見た目を変更することができます。
この値は指定したフォントが利用できないときの候補として使用されます。
文字幅は以下の定数を指定します。
定数 | 説明 |
---|---|
DEFAULT_PITCH | 既定の動作に任せる。 |
FIXED_PITCH | 固定幅 |
VARIABLE_PITCH | 可変幅 |
フォントファミリは以下の定数を指定します。
定数 | 説明 |
---|---|
FF_DONTCARE | デフォルトのフォントを使用する。 |
FF_ROMAN | 可変幅セリフ体(ローマン体) |
FF_SWISS | 可変幅サンセリフ体 |
FF_MODERN | 固定幅セリフ体/サンセリフ体 |
FF_SCRIPT | 筆記体 |
FF_DECORATIVE | 装飾付きフォント |
pszFaceName
pszFaceName
メンバは、フォントの名前を文字列で指定します。
フォント名は終端のNULL文字を含めて32文字以内で指定します。
NULL
や空文字を指定すると、これ以外の引数で指定した条件に最初に合致するフォントが選択されます。
なお、使用可能なフォント名はEnumFontFamiliesEx
関数で列挙することができます。
ただし使い方が複雑なためここでは説明しません。
DeleteObject関数
CreateFont
関数はHFONT型のデータを返します。
これはGDIオブジェクトというものの一種で、SelectObject
関数に渡すことでデバイスコンテキストに情報を反映します。
GetStockObject
関数で取得したストックオブジェクトの場合は使いっぱなしで良いのですが、CreateFont
関数などで自作したGDIオブジェクトは不要になったらDeleteObject
関数で削除する必要があります。
- BOOL DeleteObject(
HGDIOBJ ho
); - GDIオブジェクトを削除する。
(ペン、ブラシ、フォント、ビットマップ、リージョン、パレット)
削除後はハンドルは無効になる。
削除したオブジェクトはそれ以降は使用できなくなる(無効なハンドルになる)ので注意してください。
また、デバイスコンテキストに設定中のGDIオブジェクトは削除してはならないという点に注意してください。
なお、HGDIOBJ型はGDIオブジェクトハンドル全般を表す型で、HFONT型はその中のフォントを表す型です。
内部的には同じものなのでそのまま使用できます。
(気になる場合はキャストしてください)
サンプルコード
説明が非常に長くなりましたが、フォントを作成するサンプルコードです。
#include <windows.h>
//ウィンドウの生成等は省略
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
HFONT font;
HGDIOBJ hgdi;
static const WCHAR* str = L"This is a pen.あいうえお。";
switch (message)
{
case WM_PAINT: //ウィンドウの描画
GetClientRect(hWnd, &rect);
hdc = BeginPaint(hWnd, &ps);
//何も設定しない状態で出力してみる
rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);
font = CreateFont(
18, 0, //高さ, 幅
0, 0, //角度1, 角度2
FW_DONTCARE, //太さ
FALSE, FALSE, FALSE, //斜体, 下線, 打消し線
SHIFTJIS_CHARSET, //文字セット
OUT_DEFAULT_PRECIS, //精度
CLIP_DEFAULT_PRECIS, //精度
DEFAULT_QUALITY, //品質
DEFAULT_PITCH | FF_DONTCARE, //ピッチとファミリ
L"メイリオ"); //フォント名
hgdi = SelectObject(hdc, font);
rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);
SelectObject(hdc, hgdi); //フォントを元に戻す
DeleteObject(font); //オブジェクト削除
font = CreateFont(
20, 0, //高さ, 幅
0, 0, //角度1, 角度2
FW_DONTCARE, //太さ
FALSE, FALSE, FALSE, //斜体, 下線, 打消し線
SHIFTJIS_CHARSET, //文字セット
OUT_DEFAULT_PRECIS, //精度
CLIP_DEFAULT_PRECIS, //精度
DEFAULT_QUALITY, //品質
DEFAULT_PITCH | FF_DONTCARE, //ピッチとファミリ
L"HG行書体"); //フォント名
hgdi = SelectObject(hdc, font);
rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);
SelectObject(hdc, hgdi); //フォントを元に戻す
DeleteObject(font); //オブジェクト削除
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
実行結果は以下のようになります。
作成したフォントをDeleteObject
関数で削除する前に、以前のフォントに戻している点に注目してください。
SelectObject
関数は置き換え前に選択されていたGDIオブジェクトを返すので、これを変数に保存しておき、DeleteObject
関数実行の直前にこれに戻しておきます。
フォントを変更する場合にCreateFont
関数の引数を毎回書くのは面倒なので、いくつかの設定は共通にして、自作関数でまとめてしまうと便利です。
以下はその例です。
#include <windows.h>
//関数プロトタイプ宣言
ATOM MyRegisterClass(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HFONT MyCreateFont(int, LPCWSTR, BOOL);
//ウィンドウの生成等は省略
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
HFONT font;
HGDIOBJ hgdi;
static const WCHAR* str = L"This is a pen.あいうえお。";
switch (message)
{
case WM_PAINT: //ウィンドウの描画
GetClientRect(hWnd, &rect);
hdc = BeginPaint(hWnd, &ps);
font = MyCreateFont(18, L"メイリオ", FALSE);
hgdi = SelectObject(hdc, font);
rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);
SelectObject(hdc, hgdi); //フォントを元に戻す
DeleteObject(font); //オブジェクト削除
font = MyCreateFont(20, L"メイリオ", TRUE);
hgdi = SelectObject(hdc, font);
rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);
SelectObject(hdc, hgdi); //フォントを元に戻す
DeleteObject(font); //オブジェクト削除
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
//フォントを作成する
//height = フォントサイズ
//name = フォント名
//bold = 太字か否か
//戻り値 = HFONTオブジェクト
HFONT MyCreateFont(long size, LPCWSTR name, BOOL bold)
{
return CreateFont(
size, 0, //高さ, 幅
0, 0, //角度1, 角度2
bold ? FW_BOLD : FW_DONTCARE, //太さ
FALSE, FALSE, FALSE, //斜体, 下線, 打消し線
SHIFTJIS_CHARSET, //文字セット
OUT_DEFAULT_PRECIS, //精度
CLIP_DEFAULT_PRECIS, //精度
DEFAULT_QUALITY, //品質
DEFAULT_PITCH | FF_DONTCARE, //ピッチとファミリ
name);
}
実行結果は先ほどのサンプルコードと同じです。
論理フォントはあくまでもシステムに要求するフォントの設定です。
希望通りの物理フォントが得られるとは限りません。
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
HFONT font;
HGDIOBJ hgdi;
static const WCHAR* str = L"This is a pen.あいうえお。";
switch (message)
{
case WM_PAINT: //ウィンドウの描画
GetClientRect(hWnd, &rect);
hdc = BeginPaint(hWnd, &ps);
font = MyCreateFont(18, L"HG行書体", FALSE);
hgdi = SelectObject(hdc, font);
rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);
SelectObject(hdc, hgdi);
DeleteObject(font); //オブジェクト削除
font = MyCreateFont(19, L"HG行書体", FALSE);
hgdi = SelectObject(hdc, font);
rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);
SelectObject(hdc, hgdi);
DeleteObject(font); //オブジェクト削除
font = MyCreateFont(20, L"HG行書体", FALSE);
hgdi = SelectObject(hdc, font);
rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);
SelectObject(hdc, hgdi);
DeleteObject(font); //オブジェクト削除
font = MyCreateFont(21, L"HG行書体", FALSE);
hgdi = SelectObject(hdc, font);
rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);
SelectObject(hdc, hgdi);
DeleteObject(font); //オブジェクト削除
EndPaint(hWnd, &ps);
break;
case WM_DESTROY: //ウィンドウの破棄
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
フォントサイズを18~21まで1ずつ変化させただけのコードです。
実行してみると二番目と三番目で変化がありません。
これは指定通りのフォントが存在せず、最も近いもので代用されているためです。
CreateFontIndirect関数
論理フォントの作成にはCreateFont
関数のほかCreateFontIndirect
関数を使用することもできます。
- HFONT CreateFontIndirect(
const LOGFONT *lplf
); - 論理フォントを作成する。
引数のLOGFONT
は構造体で、メンバは以下のようになっています。
- typedef struct tagLOGFONTW {
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
WCHAR lfFaceName[LF_FACESIZE];
} LOGFONTW, *PLOGFONTW, *NPLOGFONTW, *LPLOGFONTW; - 論理フォントの情報を持つ構造体。
メンバの数も種類もCreateFont
関数の引数と全く同じで、値を引数として指定するか構造体メンバとして指定するかの違いです。