字体枚举器





5.00/5 (9投票s)
一个枚举Windows上所有安装字体的工具,并显示字体预览,以方便您进行编码。
引言
有时,由于某些原因我们需要处理字体。在我通过一本韩文书籍《WIN API Conquer》学习字体时,我发现了一个很好的示例代码。我将其用作一个实用工具,但我发现它缺少我想要的所有功能。它最多只能枚举 500 种字体并显示每种字体的参数。仅此而已。因此,我对该示例代码进行了改进。原始代码是用纯 Win32 API 编写的,但我将其框架更改为 WTL,并使用 STL vector 删除了最多 500 种字体的枚举限制,并添加了我想要的新功能,使得原始代码的味道几乎消失了。
这个小工具的目的
为了使用字体,我们需要看到字体外观,不仅是它本身,还包括更改参数后的外观,例如高度、斜体效果、方向等。如果我们正在开发一个程序,我们经常需要创建具有理想或预设参数的字体。在这种情况下,此程序将有助于节省您的时间。这是一个小工具,可以
- 枚举Windows上所有安装的字体,
- 允许您选择其中一个并更改将填入
LOGFONT
结构的一些参数,以便创建字体, - 显示更改参数后的字体预览。
因此,您可以轻松地通过更改参数来尝试几种可能的字体,并最终选择最适合您的字体。它还将为您生成创建字体的必要代码到剪贴板,这样您就可以直接将其粘贴到您的 IDE 编辑器中。
如何使用字体枚举器及其工作原理
如何枚举字体并查看所选字体的相应参数
当您执行 FontEnumerator.exe 时,它首先会枚举Windows上所有安装的字体。在韩文Windows上,所有消息都将显示为韩文,而在非韩文Windows上则显示为英文。您会在左侧的列表窗口中找到它们。如果您点击其中一个,将在右侧显示用于 LOGFONT
结构的所有参数。在底部,您可以看到预览。
如何尝试具有不同参数的字体
您可以在编辑窗口或复选框中更改参数,然后按 Enter 键或单击底部的预览窗口来查看结果的预览。选中复选框的状态(“高度”除外)意味着其因子将影响字体。例如,如果您选中斜体复选框,字体将变为斜体。高度使用“twips”单位,但我们经常使用“point”单位。您可以通过选中高度复选框来使用 point 单位。实际上,我们比 twips 更常使用 point 单位。因此,如果您单击“高度”复选框,参数名称将更改为“point”,并且宽度值将为 0,这意味着根据高度值有一个适当的默认值。更改参数后,请单击底部窗口或按 Enter 键。然后,您将看到更改后的字体形状。
如何使用菜单
您可以通过单击左上角的图标或右键单击标题栏来使用自定义系统菜单。然后,将弹出一个带有某些新菜单项的普通系统菜单。我使用的是韩文Windows,所以系统菜单项显示为韩文,但在您的计算机上将显示为您的语言。添加的菜单项将在韩文Windows上显示为韩文,在非韩文Windows上显示为英文。但是,为了便于解释,即使在韩文Windows上,我也暂时强制其全部显示为英文。
重新枚举字体
如果您选择菜单项“重新枚举字体”,它将再次枚举字体。结果将与之前相同。
更改示例文本
如果您单击菜单项“更改示例文本”,您将有机会更改如下所示的示例文本。如果您在编辑窗口中更改示例文本,您将获得一个不同的示例文本,但它不会保存在磁盘上,因此下次执行此程序时,它将初始化为原始值。
将创建字体的源代码复制到剪贴板
最后,如果您选择菜单项“将创建字体的源代码复制到剪贴板”,您将有五个选择。::CreateFontIndirect(...)
和 ::CreateFont(...)
用于 Win32 API 函数。CFont::CreateFontIndirect(...)
和 CFont::CreateFont(...)
用于 WTL 或 MFC。font(...)
用于 GDI+。例如,如果您选择 CFont::CreateFontIndirect(...)
并粘贴到您的 IDE 编辑器中,您将得到以下代码
LOGFONT lf;
lf.lfHeight = 36;
lf.lfWidth = 14;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = FW_NORMAL;
lf.lfItalic = FALSE;
lf.lfUnderline = FALSE;
lf.lfStrikeOut = FALSE;
lf.lfCharSet = GREEK_CHARSET;
lf.lfOutPrecision = OUT_STROKE_PRECIS;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf.lfQuality = PROOF_QUALITY;
lf.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
_tcscpy(lf.lfFaceName, _T("Arial"));
CFont font;
font.CreateFontIndirect(&lf);
关于...
如果您单击菜单项“关于...”,将弹出“关于”框。
使用代码
使用 OSL 类
类 OSL
定义在 stdafx.h 中,如下所示
typedef WindowsHelper::OSLang<> OSL;
模板类 OSLang<int mode>
在 WindowsHelper.hpp 的 WindowsHelper
命名空间中定义,如下所示
template<int mode = -1>
struct OSLang
{
WORD m_Lang;
OSLang(void)
{
m_Lang = (mode < 0) ? GetSystemDefaultLangID() : mode;
}
OSLang(HFONT& hFont, int point)
{
m_Lang = (mode < 0) ? GetSystemDefaultLangID() : mode;
HDC hDC = ::GetDC(NULL);
int height = -::MulDiv(point, GetDeviceCaps(hDC, LOGPIXELSY), 72);
::ReleaseDC(NULL, hDC);
switch (PRIMARYLANGID(m_Lang))
{
case LANG_KOREAN:
hFont = ::CreateFont(height, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
HANGUL_CHARSET,
OUT_STROKE_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
VARIABLE_PITCH | FF_MODERN, _T("¸¼Àº °íµñ"));
break;
default:
hFont = ::CreateFont(height, 0, 0, 0, FW_NORMAL, FALSE, FALSE,
FALSE, ANSI_CHARSET,
OUT_STROKE_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
VARIABLE_PITCH | FF_SWISS, _T("Calibri"));
break;
}
}
inline operator WORD ()
{
return PRIMARYLANGID(m_Lang);
}
inline char getSubLang(void)
{
return SUBLANGID(m_Lang);
}
};
在其构造函数中,它确定系统语言并根据语言创建字体。如果您为程序设置字体,您可以避免程序因系统语言的变化而产生差异。我将系统语言分为两类:韩文和非韩文。您可以根据需要进行设置。有两个全局变量:_Font
和 _Lang
。这些全局变量影响整个程序。
为程序设置字体
在 WindowsHelper.hpp 中,定义了一组字体设置函数,如下所示
typedef std::vector<HWND> HWNDS;
struct SetFontData
{
HFONT hFont;
HWNDS* phWndExcepts;
};
template<bool bInvalidate>
BOOL CALLBACK EnumChildProc(HWND hWnd, LPARAM lParam)
{
HWNDS* excepts = ((SetFontData*) lParam)->phWndExcepts;
for (HWNDS::iterator it = excepts->begin(); it != excepts->end(); it++)
{
if (hWnd == *it)
{
excepts->erase(it);
return TRUE;
}
}
::SendMessage(hWnd, WM_SETFONT, (WPARAM) (((SetFontData*) lParam)->hFont), TRUE);
if (bInvalidate)
{
//RECT rc;
//::GetClientRect(hWnd, &rc);
//::InvalidateRect(hWnd, &rc, TRUE);
::InvalidateRect(hWnd, NULL, TRUE);
}
return TRUE;
}
template<bool bInvalidate>
void setFont(HFONT hFont, HWND hWndParent, HWNDS& hWndExcepts)
{
SetFontData data;
data.hFont = hFont;
data.phWndExcepts = &hWndExcepts;
::SendMessage(hWndParent, WM_SETFONT, (WPARAM) hFont, TRUE);
::EnumChildWindows(hWndParent, &EnumChildProc, (LPARAM) &data);
}
template<bool bInvalidate>
void setFont(HFONT hFont, HWND hWndParent, HWND hWndExcept = NULL)
{
HWNDS hWnds;
if (hWndExcept)
hWnds.push_back(hWndExcept);
setFont(hFont, hWndParent, hWnds);
}
示例文本窗口不应设置为特定字体,因此在 MainDlg.cpp 的 CMainDlg::OnInitDialog(...)
中,特定字体设置为如下所示
WindowsHelper::setFont<true>(_Font, this->m_hWnd, m_wndButtonExample);
控制字体列表的顺序
我不想在字体列表窗口中首先显示 @~ 系列字体。因此,在 CMainDlg::enumerateFonts()
中对字体列表进行排序时,将一个比较函数作为 sort(...)
函数的最后一个参数给出,如下所示
std::sort(m_FontVec.begin(), m_FontVec.end(), comp);
函数 comp(..)
定义如下
extern "C" bool comp(FontPair& left, FontPair& right)
{
if ((_T('@') != left.first.lfFaceName[0]) && (_T('@') == right.first.lfFaceName[0]))
return true;
if ((_T('@') == left.first.lfFaceName[0]) && (_T('@') != right.first.lfFaceName[0]))
return false;
return (_tcscmp(left.first.lfFaceName, right.first.lfFaceName) < 0);
}
这使得 sort(...)
将 @~ 系列字体的名称确定为比普通字体更大,因此在列表中,@~ 字体会排在普通字体之后。
std::tstring
std::tstring
不是 STL 的类型,而是根据 std
命名空间中编码设置是 UNICODE 还是 ANSI 定义的类型。它在 stlhelper.hpp 中定义如下
#ifdef _STRING_
#ifndef _STRING_STLHELPER_D4459FC7_15A3_4C5E_8CD8_15BF92D97BF3
#define _STRING_STLHELPER_D4459FC7_15A3_4C5E_8CD8_15BF92D97BF3
#ifdef UNICODE
typedef wstring tstring;
#else
typedef string tstring;
#endif // UNICODE
#endif // _STRING_STLHELPER_D4459FC7_15A3_4C5E_8CD8_15BF92D97BF3
#endif // _STRING_
环境
此应用程序使用 VC++ 2008 Express SP1、WTL 和 ATL7.1 创建。它仅在韩文 Windows XP Home Edition SP3 和俄文 Windows XP Professional 上进行过测试。
致谢
我要感谢 Michael Dunn 为 MFC 程序员撰写的关于 WTL 的精彩文章。我从他的文章中学到了很多关于 WTL 的知识。他的文章可以在 这里找到。我还要感谢 Sergey Solozhentsev 在 WTL Helper 和 WTL Wizards 方面所做的出色工作。您可以在 这里 和 这里 获取他的 WTL Helper,以及在 这里 和 这里 获取它的手册。不要混淆。他的 WTL Wizards 不同于普通的 WTL App Wizard,例如可以使用 setup71.js 安装的那个。他的 WTL Wizards 也支持分屏框架。您也可以在 这里 获取他的 WTL Wizards,并在 这里 获取它的手册。它在我使用 WTL 进行编码时为我提供了极大的便利。最重要的是,我非常感谢上帝和我妻子 Nura。他把她给了我,她总是在我身边,是我坚定的支持者。
历史
- 2009 年 7 月 24 日 - 版本 1.0。
- 2009 年 8 月 15 日 - 版本 1.1
- 添加了字体高度的点单位,现在使用了私有字体,并修复了创建字体源代码生成不正确的错误。
- 2009 年 8 月 23 日 - 版本 1.1.1
- 修复了内存访问冲突的错误。