フォント


フォントの種類

ここまでのサンプルでは、出力する文字は全てデフォルトの設定でした
しかし、GUI では文字サイズや太字、斜体など、文字の視覚的表現は重要な要素です
これまで、フォントについて扱わなかったので、ここでフォントに触れたいと思います

Windows には、GDI フォントデバイスフォント が存在し
GDI フォントはシステムが保有するフォントで
デバイスフォントとは、出力デバイスに組み込まれているフォントのことを表します

GDI フォントは、有名な TrueType フォントを代表として
この他にビットマップフォントやストロークフォントに大別されます
ビットマップフォントは、その名の通りここの文字がビットマップで表現されています
処理速度は期待できますが、解像度に依存し、自由に拡大縮小はできません
ビットマップフォントは、ラスタフォントと呼ばれることもあります

ストロークフォントとは、線の組み合わせで定義されたフォントを表します
ビットマップとは異なり、デバイスの解像度に依存せず、自由に拡大縮小できますが
処理速度はビットマップフォントに劣る、プロッタに適したフォントです

これらのフォントは、Windows 3.0 の時代まで用いられていました(もちろん今でも使えるが)
しかし、プロの DTP の世界ではアドビシステムズの PostScript が主流です
これは、独自のフォントを格納し、PostScript を処理する独自のCPUを搭載することで
当時の機能の低い Macintosh でも、美しい印刷ができるように開発されたものです

現在、私たちが用いている一般的なフォントは TrueType というタイプです
これは、フォントファイルに格納されている情報を元にラスタライズを行い
アウトライン(輪郭)を計算し、その後にビットマップを作成して描画するという方式をとり
画面でも、印刷でも、比較的美しい文字を実現できます
ビットマップは、再び使用される時のことを考えて、メモリにキャッシュされることが一般的です

現在でも、プロの DTP では PostScript を用いて
専用のラスタライズコンピュータを使い、印刷をすることが一般的です
これはについては、DTP の専門分野になりますので、この場では語りません
ここでは、TrueType フォントを中心に、フォントの使い方を説明します

描画する文字のフォントを指定するには、まず論理フォントを作成します
Windows は、SelectObject() でデバイスコンテキストにこれを設定すると
論理フォントに最も近い物理フォント(実フォント)をセットします

論理フォントを作成するには CreateFont() 関数を使用します
この関数は、引数が 14 にもおよぶ複雑な関数ですが、一つずつ覚えましょう
HFONT CreateFont(
	int nHeight , int nWidth ,
	int nEscapement , int nOrientation , int fnWeight ,
	DWORD fdwItalic , DWORD fdwUnderline , DWORD fdwStrikeOut ,
	DWORD fdwCharSet ,
	DWORD fdwOutputPrecision , DWORD fdwClipPrecision,
	DWORD fdwQuality,
	DWORD fdwPitchAndFamily,
	LPCTSTR lpszFace 
);
nHeight は、論理フォントの高さを指定します
0 を指定するとデフォルトの高さ、0 以上を指定するとフォントの高さが設定されます
0 以下を指定した場合、絶対値で高さが指定されます

nWidth は、論理フォントのキャラクタの平均的な幅を指定します
0 を指定すれば Windows (フォントマッパ) によって高さから判定されます

nEscapement は水平軸から反時計回りに 1/10 度単位で角度を指定します
つまり、0 ならば左から右へ、900 ならば下から上へ文字が描画されます
この機能を用いれば文字を好きな方向に描画させることが可能となるでしょう

nOrientation は、各文字のベースラインの X 軸を 1/10 度単位で指定します
これは、Windows NT の特定のグラフィックモードで使うことができるもので
Windows 9x の場合は、必ず nEscapement と同一の値を指定します
この値を使えば Windows NT では、文字の角度に加え、各文字の向きも変更できるのです
nEscapement と nOrientation は、後でその使い方とサンプルを紹介します
単純にフォントを変更したいだけならば、これらの値は 0 でかまいません

fnWidth はフォントの太さを指定するものです…と定義されていますが
実装では、この値を用いて BOLD (太字) 属性を指定することができます
指定できる定数は複数用意されていて、柔軟に太さを指定できるよう設計されていますが
それは、多くの場合「絵に描いた餅」で、実際は FW_DONTCHAR、FW_NORMAL
または FW_REGULAR でデフォルトの太さ、FW_BOLD で太字 になるだけです

fdwItalic メンバは、TRUE を指定すれば斜体に
fdwUnderline は、TRUE を指定すると下線付きフォントに
fdwStrikeOut は、TRUE を指定すると、打消し線付きのフォントになります

fdwCharSet には、フォントのキャラクタセットを定数で指定します
ANSI を使うならば ANSI_CHARSET を、シフト JIS ならば SHIFTJIS_CHARSET を用います
その他の定数は下記のリファレンスを参照してください
デフォルトのキャラクタセットを指定するならば DEFAULT_CHARSET を指定します
ただし、予期されないフォントが選択されることもあるため
用いるキャラクタセットが確定しているならば、それを直接指定することが推奨されます

fdwOutputPrecision は、Windows が物理フォントを検索するための方法を指定します
この引数は、一部の値では 9x と NT で異なったりと複雑なこともあってか
一般的には OUT_DEFAULT_PRECIS を用いてデフォルトの動作を指定します
この他、TrueType フォントだけを用いるように指定することも可能です
それらの定数に付いては、下記のリファレンスを参照してください

fdwClipPrecision は、文字の一部がクリッピング領域からはみ出している場合に
はみ出した文字をクリップする方法を定義します
ここは、ほとんどの場合デフォルトの CLIP_DEFAULT_PRECIS を指定します
これ以外の定数については、下記のリファレンスを参照してください

fdwQuality は、論理フォントの属性と物理フォントの属性を
どの程度一致させるかを定数で定義します
通常は DEFAULT_QUALITY を指定しますが
文字品質が大切な場合は DRAFT_QUALITY、より重要ならば PROOF_QUALITY を指定します

fdwPitchAndFamily はフォントのピッチとファミリを指定します
下位2ビットでフォントのピッチを、上位4ビットでフォントファミリを表します
この引数は、それぞれに用意されている定数を論理和で組み合わせて指定します
ピッチは DEFAULT_PITCH でデフォルト、FIXED_PITCH で固定、VARIABLE_PITCH で可変です
ファミリは、FF_DONTCARE を指定すれば、ファミリを指定しないことを表します
ファミリを指定する場合は、下記のリファレンスを参照してください
ピッチもファミリも指定しない場合、0 を与えることで、デフォルトを表すことができます

フォントファミリ とは、フォントの見た目を定義したものです
これを指定すれば、最後の引数で指定したフォントが見つからなかった場合
この情報を元に、Windows はフォントを検索することができます

最後の引数 lpszFace は、フォントのタイプフェイスが格納された
NULL で終わる文字列へのポインタを指定します
文字列の長さは、 NULL 文字を含めて必ず 32 文字以下で指定します

タイプフェイスとは、各フォントに割り当てられたフォントの識別子です
これを指定することで、確実にフォントを指定することができますが
それには、現在利用可能なフォントのタイプフェイス名を取得する必要があります
この方法は、後ほど紹介します

lpszFace には NULL を指定することも可能です
NULL や空の文字列へのポインタを指定されると、他の条件を元に物理フォントを検索します

関数が成功すれば論理フォントのハンドルが、失敗すれば NULL が返ります
この論理フォントのハンドルは、いわゆる GDI オブジェクトの扱いになります
デバイスコンテキストに設定したり、不用になれば DeleteObject() で削除しましょう

論理フォントのハンドルをデバイスコンテキストに SelectObject() で設定すると
Windows はファイルマッピングアルゴリズムに基いて物理フォントを探します
こうして、描画する文字のフォントを変更することが可能となります
#include <windows.h>

#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	HFONT hFont;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);

		hFont = CreateFont(
			40 , 0 , 0 , 0 , FW_BOLD , TRUE , TRUE , FALSE ,
			SHIFTJIS_CHARSET , OUT_DEFAULT_PRECIS ,
			CLIP_DEFAULT_PRECIS , DEFAULT_QUALITY , 
			VARIABLE_PITCH | FF_ROMAN , NULL
		);
		SelectObject(hdc , hFont);
		TextOut(hdc , 0 , 0 , TITLE , lstrlen(TITLE));
		SelectObject(hdc , GetStockObject(SYSTEM_FONT));
		DeleteObject(hFont);

		EndPaint(hWnd , &ps);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc		= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}


どうでしょうか、TextOut() 関数で描画するテキストのフォントが変わっていますね
これで、これまでできなかった高度な文字表現が可能となるでしょう

CreateFont() 関数のほかに、構造体を用いて論理フォントの情報を表現し
CreateFontIndirect() 関数を用いて論理フォントを作成する方法もあります

HFONT CreateFontIndirect(CONST LOGFONT *lplf);

lplf には、LOGFONT 構造体へのポインタを指定します
成功すれば論理フォントのハンドルが、失敗すれば NULL が返ります

LOGFONT 構造体は、論理フォントの情報を表すためのもので
CreateFont() 関数の引数と同様の意味を持つメンバを保有します
typedef struct tagLOGFONT { // lf 
   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; 
   TCHAR lfFaceName[LF_FACESIZE]; 
} LOGFONT;
lfHeight から順に、CreateFont() 構造体の引数と同じ意味を持つメンバです
これらに必要な情報をあらかじめ格納し、CreateFontIndirect() を呼び出します
LF_FACESIZE は 32 を表すタイプフェイスの最大文字数です
#include <windows.h>

#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	static LOGFONT lfFont;
	HFONT hFont;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		lfFont.lfHeight 		= 40;
		lfFont.lfWidth = lfFont.lfEscapement =
		lfFont.lfOrientation 		= 0;
		lfFont.lfWeight 		= FW_BOLD;
		lfFont.lfItalic = lfFont.lfUnderline = TRUE;
		lfFont.lfStrikeOut 		= FALSE; 
		lfFont.lfCharSet 		= SHIFTJIS_CHARSET;
		lfFont.lfOutPrecision 	= OUT_DEFAULT_PRECIS;
		lfFont.lfClipPrecision 	= CLIP_DEFAULT_PRECIS;
		lfFont.lfQuality 		= DEFAULT_QUALITY;
		lfFont.lfPitchAndFamily	= 0;
		lfFont.lfFaceName[0]		= '\0';
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);

		hFont = CreateFontIndirect(&lfFont);
		SelectObject(hdc , hFont);
		TextOut(hdc , 0 , 0 , TITLE , lstrlen(TITLE));
		SelectObject(hdc , GetStockObject(SYSTEM_FONT));
		DeleteObject(hFont);

		EndPaint(hWnd , &ps);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc		= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}
このプログラムは、CreateFont() の代わりに CreateFontIndirect() を用いています
LOGFONT 構造体に論理フォントの情報を記録しておくことができる点で
CreateFont() 関数を使うよりも柔軟な対応ができることがあります

また、CreateFont() 関数の第3、第4引数を用いることによって
(LOGFONT の lfEscapement、lfOrientation メンバ)
描画する文字列の角度を指定することが可能となっています
単位は 1/10 度で、3000 とすれば、300 度の方向に向かって文字が流れます
#include <windows.h>

#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	HFONT hFont;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);

		hFont = CreateFont(
			40 , 0 , 3400 , 3400 , FW_BOLD , TRUE , TRUE , FALSE ,
			SHIFTJIS_CHARSET , OUT_DEFAULT_PRECIS ,
			CLIP_DEFAULT_PRECIS , DEFAULT_QUALITY , 
			VARIABLE_PITCH | FF_ROMAN , NULL
		);

		SelectObject(hdc , hFont);
		TextOut(hdc , 20 , 0 , TITLE , lstrlen(TITLE));
		SelectObject(hdc , GetStockObject(SYSTEM_FONT));
		DeleteObject(hFont);

		EndPaint(hWnd , &ps);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc		= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}


描画した文字の角度が 340 に傾いていることがわかります
このように、CreateFont() を用いれば、文字を傾かせることも難しくはありません


CreateFont()

HFONT CreateFont(
	int nHeight , int nWidth ,
	int nEscapement , int nOrientation , int fnWeight ,
	DWORD fdwItalic , DWORD fdwUnderline , DWORD fdwStrikeOut ,
	DWORD fdwCharSet ,
	DWORD fdwOutputPrecision , DWORD fdwClipPrecision,
	DWORD fdwQuality,
	DWORD fdwPitchAndFamily,
	LPCTSTR lpszFace 
);
論理フォントを作成します

nHeight - 論理フォントの高さを指定します
nWidth - 論理フォントのキャラクタの平均幅を指定します
nEscapement - 水平軸から反時計回りに 1/10 度単位で角度を指定します
nOrientation - 各文字のベースラインの X 軸を 1/10 度単位で指定します
fnWidth - フォントの太さを指定します
fdwItalic - TRUE を指定すれば斜体になります
fdwUnderline - TRUE を指定すると下線付きフォントになります
fdwStrikeOut - TRUE を指定すると、打消し線付きのフォントになります
fdwCharSet - フォントのキャラクタセットを指定します
fdwOutputPrecision - 物理フォントを検索する方法を指定します
fdwClipPrecision - クリッピング領域からはみ出した文字の処理方法を指定します
fdwQuality - 論理フォントと物理フォントの属性をどの程度一致させるかを指定します
fdwPitchAndFamily - フォントのピッチとファミリを指定します
lpszFace - タイプフェイス名を指定します

戻り値 - 論理フォントのハンドル。失敗すれば NULL

fnWidth には次の定数のいずれかを指定できます

定数
FW_DONTCARE 0
FW_THIN 100
FW_EXTRALIGHT 200
FW_ULTRALIGHT 200
FW_LIGHT 300
FW_NORMAL 400
FW_REGULAR 400
FW_MEDIUM 500
FW_SEMIBOLD 600
FW_DEMIBOLD 600
FW_BOLD 700
FW_EXTRABOLD 800
FW_ULTRABOLD 800
FW_HEAVY 900
FW_BLACK 900

fdwCharSet には次のいずれかの定数を指定することができます

定数解説
ANSI_CHARSET Windows 文字セット
DEFAULT_CHARSET 指定なし
SYMBOL_CHARSET
SHIFTJIS_CHARSET シフト JIS 文字セット
GB2312_CHARSET
HANGEUL_CHARSET
CHINESEBIG5_CHARSET
OEM_CHARSET OEM 文字セット
Windows のみ
JOHAB_CHARSET
HEBREW_CHARSET
ARABIC_CHARSET
GREEK_CHARSET
TURKISH_CHARSET
THAI_CHARSET
EASTEUROPE_CHARSET
RUSSIAN_CHARSET
MAC_CHARSET
BALTIC_CHARSET

fdwOutputPrecision には次のいずれかの定数を指定できます

定数解説
OUT_CHARACTER_PRECIS 使用しません
OUT_DEFAULT_PRECIS デフォルトの動作に任せます
OUT_DEVICE_PRECIS 同じ名前のフォントが複数あった場合は
デバイスフォントを選択するよう指示します
OUT_OUTLINE_PRECIS Windows NT
TrueType フォントやその他の
アウトラインベースのフォントを選択するよう指示します

Windows 95
使用しません
OUT_RASTER_PRECIS 同じ名前のフォントが複数あった場合は
ラスタフォントを選択するよう指示します
OUT_STRING_PRECIS 使用しません
(ただし、ラスタフォントが列挙されるときには返されます)
OUT_STROKE_PRECIS Windows NT使用しません
(ただし、TrueType フォントやその他のアウトラインベースのフォント
ベクトルフォントが列挙されるときには返されます)

Windows 95
ベクトルフォントを選択するよう指示します
(TrueType フォントやベクトルフォントが列挙されるときにも返されます)
OUT_TT_ONLY_PRECIS TrueType フォントだけを選択するよう指示します
システムに TrueType フォントが組み込まれていないときは
デフォルトの動作になります
OUT_TT_PRECIS 同じ名前のフォントが複数あった場合は
TrueType フォントを選択するよう指示します

fdwClipPrecision には、次の定数を組み合わせて指定できます

定数解説
CLIP_DEFAULT_PRECIS デフォルトの動作に任せます
CLIP_CHARACTER_PRECIS 使用しません
CLIP_STROKE_PRECIS 使用しません
(ただし、ラスタフォント、ベクトルフォント
TrueType フォントが列挙されるときには返されます)
CLIP_MASK 使用しません
CLIP_EMBEDDED 読み取り専用の埋め込みフォント
CLIP_LH_ANGLES 座標系が右手座標系か左手座標系かによって
すべてのフォントの回転方向を決めるようにします
この値を指定しない場合は
デバイスフォントは常に反時計回りに
その他のフォントは座標系の向きしたがって回転します
CLIP_TT_ALWAYS 使用しません

fdwQuality には、次のいずれかの定数を指定することができます

定数解説
DEFAULT_QUALITY フォントの文字品質は重視されません
DRAFT_QUALITY フォントの文字品質は
PROOF_QUALITY を使用したときほどは重視されません
PROOF_QUALITY フォントの文字品質が
論理フォントの属性を正確に一致させることよりも重視されます

fdwPitchAndFamily の下位2ビットには、次のいずれかの定数を指定します

定数解説
DEFAULT_PITCH デフォルト
FIXED_PITCH 固定幅
VARIABLE_PITCH 可変幅

上位4ビットは、次のいずれかの定数を指定します

定数解説
FF_DECORATIVE 装飾付きフォントです
Old English フォントなどがあります
FF_DONTCARE ファミリを指定しません
または、ファミリが不明です
FF_MODERN 固定ストローク幅を持つ、セリフ付きまたはセリフなしのフォントです
Pica、Elite、Courier New などがあります
FF_ROMAN 可変ストローク幅を持つ、セリフ付きフォントです
MS Serif などがあります
FF_SCRIPT 手書き風のフォントです
Script、Cursive などがあります
FF_SWISS 可変ストローク幅を持つ、セリフなしフォントです
MS Sans Serif などがあります

CreateFontIndirect()

HFONT CreateFontIndirect(CONST LOGFONT *lplf);

lplf - 論理フォントの情報を表す LOGFONT 構造体へのポインタを指定します

戻り値 - 論理フォントのハンドル。失敗すれば NULL



前のページへ戻る次のページへ