Windows 上でフォントを作成する際には、作成したいフォントの情報(高さや幅、太さなど)を API に渡すのですが、 フォントの平均文字幅には 0 を指定しておき、条件に最も近い値を選択してもらうようにしていることが多いと思います。 ところが、Windows XP では全角固定ピッチフォント("MS ゴシック" や "MS 明朝" といったフォント) の文字幅を選択する 内部仕様が変わってしまったため、特定のフォントサイズにおいては、これまでの Windows と Windows XP では画面上の表示が 違ってしまうようになりました。これは MS のサポート技術情報 417434 でも公開されており、対応方法も一応付記されています。 なお、以下の説明では VC++ 6 を使用しています。
一般的な Windows のアプリケーションでは、ユーザに選択させるフォントサイズの単位としてポイントを使っています。
ポイントは活版印刷からきた単位で、1 ポイント ≒ 1/72 インチ です。特に Windows 上では 1 ポイント = 1/72 インチ
だと考えてかまいません。
しかし、アプリケーションから利用するフォント関連の API は、フォントサイズの単位として論理単位を使います。
このため、アプリケーションプログラムがユーザの指定したポイント数のフォントを扱うときには、
ポイント数を論理単位に変換してから、フォント関連の API に渡さなければなりません。
以下では、描画モードが MM_TEXT マッピングモードの場合のみ説明します。
MM_TEXT マッピングモードは Windows のデフォルトのマッピングモードで、論理単位 = ピクセル数となります。
とりあえず、以下の説明の中では、論理単位という言葉がでてきたら、ピクセル数のことだと考えてください。
いきなりですが、ここで画面解像度(dpi: dot per inch)の話をしておきます。
画面解像度は Windows が画面を表示する際に 1 インチを何ドット(=ピクセル)で表示するかを表した数値です。
画面のプロパティなどでも変更できますが、Windows をインストールしたデフォルトのままだと、
96 dpi (1 インチあたり 96 ピクセル) が一般的です。
では、例えば、画面解像度が 96 dpi の時に、12 ポイントのフォントが欲しいとすると、
フォント関連の API に渡す論理単位(=ピクセル数)はいくつになるでしょうか。
1 ポイントは 1/72 インチですので、12 ポイントだと 1/72 × 12 = 12/72 インチになります。
これをピクセルに直すと、96 dpi の解像度では 1 インチにつき 96 ピクセルで表しますので、
12/72 インチを表現するには 96 × 12/72 = 1152/72 ピクセル必要となります。
1152/72 = 1152 ÷ 72 = 16 となり、API には 16(ピクセル) を渡せばよいことになります。
なお、この計算にうってつけの API がありますので、これを利用すると便利です。
int MulDiv( int nNumber, // 符号付き 32 ビット被乗数 int nNumerator, // 符号付き 32 ビット乗数 int nDenominator // 符号付き 32 ビット除数 );
この API は nNumber × nNumerator ÷ nDenominator を計算し、最も近い整数に丸めた(切り上げまたは切り捨てした)
値を返します。フォントのピクセル数を求める場合、MulDiv(ポイント数, 1 インチあたりのピクセル数, 72) として
計算した値を利用できます。
※インチだのポイントだの日本人からすると聞き慣れない単位ですが、Windows は "舶来品" ゆえ
仕方のないところでしょうか (*_*)。
フォントの作成には CreateFont() または CreateFontIndirect() を使用します。この 2 つの API は
引数の指定方法が違うだけで、まったく同じ機能を持ちます。CreateFont() は作成するフォントの情報を引数として 1 つずつ渡します。
CreateFontIndirect() では作成するフォントの情報を LOGFONT 構造体に設定してから、引数として渡します。
なお、作成されるフォントは論理フォントと呼ばれ、実際に画面に表示するフォントと 1 対 1 に対応するわけではありません
(詳細は後で説明します)。
以下に CreateFont() の MSDN の記述を抜粋して掲載します(一部変更してあります)。
なお、これら API の詳細な使用方法については MSDN 以外にも、ネットにたくさんの情報が存在しますので探してみてください。
関数が成功すると、論理フォントのハンドル(HFONT 型の値)が返されます。失敗時には NULL が返されます。
nHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);PointSize に必要なフォントのポイントサイズを渡します。 GetDeviceCaps(hDC, LOGPIXELSY) は画面の垂直方向での論理インチ当たりのピクセル数を得ています。これは、 Y 方向の画面解像度(dpi)を表します。
定数 | 定数値 | |
FW_DONTCARE | 0 | |
FW_THIN | 100 | |
FW_EXTRALIGHT | 200 | |
FW_ULTRALIGHT | FW_EXTRALIGHT と同じ | |
FW_LIGHT | 300 | |
FW_NORMAL | 400 | |
FW_REGULAR | FW_NORMAL と同じ | |
FW_MEDIUM | 500 | |
FW_SEMIBOLD | 600 | |
FW_DEMIBOLD | FW_SEMIBOLD と同じ | |
FW_BOLD | 700 | |
FW_EXTRABOLD | 800 | |
FW_ULTRABOLD | FW_EXTRABOLD と同じ | |
FW_HEAVY | 900 | |
FW_BLACK | FW_HEAVY と同じ |
定数 | 意味 | |
OUT_CHARACTER_PRECIS | 使用しません。 | |
OUT_DEFAULT_PRECIS | 既定の動作に任せます。 | |
OUT_DEVICE_PRECIS | 同じ名前のフォントが複数あった場合は、デバイスフォントを選択するよう指示します。 | |
OUT_OUTLINE_PRECIS | Windows NT/2000: TrueType フォントやその他のアウトラインベースのフォントを選択するよう指示します。 | |
OUT_RASTER_PRECIS | 同じ名前のフォントが複数あった場合は、ラスタフォントを選択するよう指示します。 | |
OUT_STRING_PRECIS | 使用しません(ただし、ラスタフォントが列挙されるときには、この値が返されます)。 | |
OUT_STROKE_PRECIS |
Windows NT/2000: 使用しません(ただし、TrueType フォントやその他のアウトラインベースのフォント、
ベクタフォントが列挙されるときには、この値が返されます)。 Windows 95/98: ベクタフォントを選択するよう指示します(TrueType フォントやベクタフォントが列挙されるときにも、 この値が返されます)。 |
|
OUT_TT_ONLY_PRECIS | TrueType フォントだけを選択するよう指示します。 システムに TrueType フォントが組み込まれていないときは、既定の動作になります。 | |
OUT_TT_PRECIS | 同じ名前のフォントが複数あった場合は、TrueType フォントを選択するよう指示します。 |
定数 | 意味 | |
CLIP_DEFAULT_PRECIS | 既定の動作に任せます。 | |
CLIP_CHARACTER_PRECIS | 使用しません。 | |
CLIP_STROKE_PRECIS |
使用しません(ただし、ラスタフォント、ベクタフォント、TrueType フォントが列挙されるときには、この値が返されます)。 Windows NT/2000: 互換性のために、フォントを列挙するときには必ずこの値が返されます。 |
|
CLIP_MASK | 使用しません。 | |
CLIP_EMBEDDED | 読み取り専用の埋め込みフォントを使用するには、このフラグを指定しなければなりません。 | |
CLIP_LH_ANGLES |
この値を使うと、すべてのフォントの回転方向が座標系の方向(右手座標系か左手座標系か)によって決められるようになります。 この値を指定しない場合は、デバイスフォントは常に反時計回りに、その他のフォントは座標系の方向に従って回転します。 座標系の方向の詳細については、nOrientation パラメータの説明を参照してください。 |
|
CLIP_TT_ALWAYS | 使用しません。 |
定数 | 意味 | |
ANTIALIASED_QUALITY |
Windows NT 4.0 以降: アンチエイリアス処理(スムージング)がサポートされているフォントについては、
サイズが小さすぎたり大きすぎる場合には、フォントにアンチエイリアス処理が施されます。 Plus!をインストールした Windows 95 以降: そのフォントでアンチエイリアス処理がサポートされていることに加えて、 ディスプレイが 8 ビット(256 色) 以上表示でき、単一平面デバイスでなくてはなりません。 また、パレットディスプレイであってはならず、複数ディスプレイモニタのセットアップになっていてはなりません。 さらに、DIBSection で使用するよりも前に、 スクリーンデバイスコンテキストでその TrueType フォントを選択しておかなければなりません。 以上の条件が満たされていなければ、アンチエイリアス処理は行われません。 |
|
DEFAULT_QUALITY | フォントの文字品質は重視されません。 | |
DRAFT_QUALITY | フォントの文字品質は、PROOF_QUALITY を使用したときほどは重視されません。 GDI のラスタフォントについては、スケーリングが可能になり、使用可能なフォントサイズが増えますが、 品質はかなり低くなる場合もあります。 必要であれば、太字、斜体、下線付き、取り消し線付きなどのフォントが合成されます。 | |
NONANTIALIASED_QUALITY | Plus!をインストールした Windows 95、Windows 98、Windows NT 4.0、および Windows 2000: アンチエイリアス処理は一切行われません。 | |
PROOF_QUALITY | フォントの文字品質が、論理フォントの属性を正確に一致させることよりも重視されます。 GDI のラスタフォントについては、スケーリングが可能になり、最もサイズの近いフォントが選択されます。 PROOF_QUALITY を指定した場合、選択されたフォントのサイズは正確には一致しない場合がありますが、 フォントの品質は高く、文字の形が歪むことはありません。 必要であれば、太字、斜体、下線付き、取り消し線付きなどのフォントが合成されます。 |
定数 | 意味 | |
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 などがあります。 |
フォント作成のための API で、
論理フォントは実際に画面に表示するフォントと 1 対 1 に対応するわけではないと説明しました。これは一体どういう意味でしょうか。
作成した論理フォントを使って画面に文字を表示するためには、表示用のデバイスコンテキストに選択する必要があります。
このとき、Windows は指定した論理フォントにもっとも近い実フォントを選択します。つまり、実際にデバイスコンテキストに
選択してみるまで、表示に使われる本当のフォントはわかりません。
仮に、CreateFont() のパラメータ指定で、フォント名に "MS ゴシック" と指定していたとしても、
もし、"MS ゴシック" が OS にインストールされていない(まず、ありえませんが)場合は、
Windows はその他のパラメータで指定されている条件にもっとも近い実フォントを選択しようとします。
MS のサポート技術情報 417434 を基に、Windows のフォント選択の仕様をみてみます。
フォントを管理する GDI の内部ではフォントのサイズはピクセル値の単位で管理されています。 例えば、固定ピッチの 15 ポイントのフォントを必要とする場合、例えば 96 dpi の画面上では、 フォントの大きさについて で説明した計算方法に基づき、
として、20 ピクセルのフォントが使われます。このとき、全角文字の幅は 20 ピクセルとなります。 また、半角文字の幅は全角文字の幅の半分なので、10 ピクセルとなり、全角文字の幅は、半角文字の幅の 丁度 2 倍となります。この場合には、従来の Windows と Windows XP では同じ大きさの実フォントが 選択されるため、表示に違いは起こりません。
15 ポイントの "MS ゴシック" フォントを Windows Me と Windows XP 上で表示してみました。
Windows Me
Windows XP
次に、固定ピッチの 14 ポイントのフォントを必要とする場合、同じく 96 dpi の画面上では、
として計算されますが、ピクセル値は整数でなければならないため 18.666.. が切り上げられて、19 ピクセルのフォントが使われます。 このとき、全角文字の幅は 19 ピクセルとなります。半角文字の幅は全角文字の半分なので、9.5 ピクセルとしたいところですが、 やはり整数しか扱えないため、Windows の内部では 10 ピクセルの幅の半角文字が選択されます。 このとき、
となってしまいます。 14 ポイントの "MS ゴシック" フォントを Windows Me と Windows XP 上で表示してみました。
Windows Me
Windows XP
従来の Windows では、計算値をそのまま表示に反映するため、全角文字が半角文字の丁度 2 倍とならず、
アプリケーション側で文字幅を考慮し調整して表示しなければなりませんでした。
Windows XP では、表示に使われる実フォントの選択 のための内部仕様が、
全角固定ピッチフォントに限り、従来の Windows とは違う仕様となっています。
このため、Windows XP では上記のような場合でも、全角文字幅が半角文字幅の丁度 2 倍になるように調整され、
アプリケーション側でこうした調整を行う必要がなくなりました。
その代わり、従来の Windows 上で固定ピッチでレイアウトされたアプリケーションを Windows XP 上で実行すると、
文字がずれたり、文字の末尾が切れてしまったりする可能性がでてきてしまったのです。
※上記の実行例で、Windows XP は Home Edition が手元にないので、Professional Edition 上で実行しています。 また、Windows Me は VMware 上で実行したものです。
MS のサポート技術情報 417434 では、以下のような対応策を挙げています。
上記のうち、1, 2, 4 は固定ピッチフォントの表示がずれることを抑制するための対応策ではなく、ずれることを前提として レイアウトするか、または、仕様の変わっていないプロポーショナルフォントを利用しなさいということのようです。 3 でやれば確実に個々の文字を指定の場所に表示することができそうですが、プログラムの手間を考えると 個人的にはあまりやりたくありません。 5 は表示に使われる実フォントのサイズを考慮して、フォントサイズを明確に指示しなさいということだと思います。 以下では、5 の方法について考えてみます。
今までのサンプルでは、論理フォントを作成する際に、フォントの平均文字幅の指定(nWidth) に 0 を指定して、Windows に適当なフォントを選択させていました。ここで平均文字幅指定を正確に行えば、 ずれを防ぐことができそうです。 また、Windows XP の表示を変更するのは難しそうなので、Windows XP での表示方法に 今までの Windows 上での表示をあわせるようにしたいと思います。
ずれる条件は以下のようになるでしょうか。
この条件を満たす場合にのみ、全角文字幅が半角文字幅のちょうど 2 倍になるように幅を指定して フォントを作り直すことで対応することにします。
まず、OS が Windows XP か、それ以外の Windows かを得なければなりません。これには GetVersionEx() を使います。 例えば、以下のようにします。
// OS が Windows XP か、それ以外かを得る。 OSVERSIONINFO verinf; verinf.dwOSVersionInfoSize = sizeof(verinf); GetVersionEx(&verinf); BOOL isXP = (verinf.dwMajorVersion == 5 && verinf.dwMinorVersion == 1) ? TRUE : FALSE;
GetVersionEx() から得られる OSVERSIONINFO のメンバ dwMajorVersion、dwMinorVersion には それぞれ、OS のメジャーバージョン、マイナーバージョンが設定されます。Windows XP では、 メジャーバージョンは 5、マイナーバージョンは 1 が得られます。
次に、表示に使われる実フォントの、ピッチ、文字幅の情報が必要です。 実フォントの情報は論理フォントをデバイスコンテキストに選択してから GetTextMetrics() を使って取得することにします。 例えば、以下のようにします。
HFONT hFontTest = CreateFont(...); // フォントをデバイスコンテキストに選択する。 HFONT hFontPrev = (HFONT) SelectObject(hDC, hFontTest); // 選択された実フォントの情報を得る。 TEXTMETRIC tm; GetTextMetrics(hDC, &tm);
GetTextMetrics() から得られる TEXTMETRIC のメンバ tmPitchAndFamily にはフォントのピットとファミリの情報が
設定されます。tmPitchAndFamily に TMPF_FIXED_PITCH ビットが設定されていない場合は固定ピッチフォントです
(MSDN の誤植かと思ったのですが、実際に値を調べてみると確かに TMPF_FIXED_PITCH ビットが設定されているときは
プロポーショナルフォント、設定されていないときは固定ピッチフォントが選択されていました)。
また、フォントが固定ピッチフォントの場合、メンバ tmAveCharWidth には半角文字幅が、
tmMaxCharWidth には全角文字幅が、それぞれ設定されます。
描画の処理は以下のようにします。
上記にしたがって作成したプログラムの Windows Me と Windows XP での実行結果を以下に示します。
Windows Me
Windows XP
Windows Me での全角文字の幅が丁度半角文字の 2 倍になっています。そして、幅に関してのみいえば
Windows XP と同じように表示されています。
フォントサイズによっては少し太めになっているのが気になりますが、これは論理フォントの平均文字幅を指定したことにより、
Windows が指定幅にあわせてフォントを調整したためです。
上記では "MS ゴシック" しかテストしていませんが、その他の TrueType フォントでも同じように
幅調整され、全角文字幅が半角文字幅の丁度 2 倍に表示されると思います。
ただし、フォントが TrueType でない場合の結果はおそらく思い通りにはならないでしょう。
幅調整がきかず、同じ大きさのフォントが表示されるだけになってしまうと思います。
サポート技術情報 417434 には記載されていませんが、Windows XP の開発段階でのホワイトペーパーには、
以下のような暫定的な回避策が記載されていました。
ただし、この方法は推奨されておらず、また、Windows XP でのフォントの選択方法を従来の Windows の仕様に戻してしまいますので、
Windows XP のフォントの選択仕様にあわせて作成・変更されたアプリケーションの表示を乱してしまう可能性があります。
もし、試されたとしても、一切の責任を負いませんので、適用する場合は各自の責任においてのみ行ってください。
念のため、方法だけでなく回避策適用のガイドラインや注意事項もあわせて引用しておきます。
Windows XP での全角固定ピッチフォントの仕様変更 - ホワイトペーパー - 最終更新日 2001 年 9 月 17 日 より引用
Windows XP では、以下のレジストリ値を設定すれば、従来の Windows® と同様の仕様で固定ピッチフォントのサイズが選択されます。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\GRE_Initialize "Jpn98FixPitch"=dword:00000001
この設定はシステム全体に適用されるため、設定適用後、新しい Windows XP の仕様に合わせて実装されたアプリケーションでは、 文字の表示や印刷で不具合が発生する可能性があります。これらのサイドエフェクトを最小限にするため、 この設定を用いる場合、次のガイドラインに従ってください。
注意: この設定は Windows XP でのみ有効な一時的な回避策であり、今後のWindows® のバージョンでも保証されるものではありません。
プログラミング Windows 第5版(上)、(下) Charles Petzold 著 株式会社ロングテール/長尾高弘 訳
株式会社アスキー ISBN 4-7561-3600-1、4-7561-3601-X