获取 Windows 系统默认字体和对应的字体文件

自 Windows Vista 和 Windows Server 2008 开始,操作系统界面默认的字体为 Segoe UI ,而 Windows XP 及之前的系统界面默认字体为 Tahoma 。不过 Segoe UI 字体的字形并不包含包括汉字在内的东亚文字。中文版 Windows 使用微软雅黑作为默认中文字体。

其他语言的默认字体见下表:

语言字体
简体中文Microsoft YaHei UI
繁体中文Microsoft JhengHei UI
日文Yu Gothic UI
韩文Malgun Gothic
希伯来文Gisha
泰文Leelawadee

不过相较于查表获取 Windows 系统的默认字体,使用 Windows API 来获取当前系统的默认字体会更加可靠。

获取 Windows 系统默认字体

本文介绍有两种方法来获取 Windows 系统默认字体。第一种方法仅支持 Windows Vista 和 Windows Server 2003 及以上版本的 Windows 系统。第二种方法可以适用于 Windows XP 系统及更低版本的系统,但是没有实际测试过。

方法一

使用 GetThemeFont API 来获取系统默认字体:

C++#include <windows.h>
#include <uxtheme.h>
#include <vssym32.h>
#include <stdio.h>

#pragma comment(lib, "uxtheme")

int main()
{
    LOGFONTW lf = { 0 };
    HTHEME hTheme = OpenThemeData(0, VSCLASS_TEXTSTYLE);
    if (hTheme)
    {
        if (SUCCEEDED(GetThemeFont(hTheme, 0, TEXT_BODYTEXT, 0, TMT_FONT, &lf)))
        {
            wprintf(L"System default font name is \"%ls\"\n", lf.lfFaceName);;
        }
        CloseThemeData(hTheme);
    }

    return 0;
}

方法二

使用 SystemParametersInfo API 来获取系统默认字体:

C++#include <windows.h>
#include <stdio.h>

int main()
{
    LOGFONTW lf = { 0 };
    if (SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(LOGFONTW), &lf, 0)) 
    {
        wprintf(L"System default font name is \"%ls\"\n", lf.lfFaceName);
    }
    
    return 0;
}

获取对应的字体文件

Windows 似乎没有提供 API 来找到字体对应的字体文件。不过我们可以在注册表路径 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts 下找到系统所有的字体和对应的字体文件名。

Windows 9X/Me 的注册表路径为 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Fonts 。不过对于这些上个世纪的操作系统,除非特殊情况,通常可以不用考虑。

C++#include <windows.h>
#include <wchar.h>
#include <stdio.h>

BOOL GetFontFile(const wchar_t* fontName, wchar_t* fileName, size_t* fileNameSize)
{
    constexpr auto REG_SUBKEY_FONTS = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
    constexpr auto REG_SAM_DESIRED = KEY_READ | KEY_QUERY_VALUE;
    constexpr auto FONT_NAME_SEPARATOR = L'&';
    constexpr auto FONT_NAME_TERMINATOR = L'(';
    constexpr auto FONT_NAME_SPACE = L' ';

    BOOL result = FALSE;
    HKEY fontsKey = NULL;
    wchar_t valueName[MAX_PATH] = { 0 };
    BYTE valueData[MAX_PATH] = { 0 };
    DWORD valueNameSize = sizeof(valueName) / sizeof(wchar_t) - 1,
          valueNameLength = valueNameSize,
          valueType = 0,
          valueDataSize = sizeof(valueData),
          valueDataLength = valueDataSize,
          index = 0,
          fontNameLength = wcslen(fontName);

    if (ERROR_SUCCESS != RegOpenKeyExW(HKEY_LOCAL_MACHINE, REG_SUBKEY_FONTS, 0, REG_SAM_DESIRED, &fontsKey)) {
        return FALSE;
    }
    
    while (ERROR_SUCCESS == RegEnumValueW(fontsKey, index, valueName, &valueNameLength, NULL, &valueType, valueData, &valueDataLength)) {
        if (REG_SZ != valueType || valueNameLength < fontNameLength) {
            goto NEXT;
        }

        int fragmentLength, i, j;
        const wchar_t *fragmentValueName;

        for (i = 0, j = 0; i < valueNameLength; i++) {
            if (FONT_NAME_SEPARATOR == valueName[i]) {
                fragmentLength = FONT_NAME_SPACE == valueName[i - 1] ? i - j - 1 : i - j;
                fragmentValueName = valueName + j;
                if (fragmentLength == fontNameLength && 0 == wcsnicmp(fontName, fragmentValueName, fontNameLength)) {
                    goto SUCCESS;
                }
                i += FONT_NAME_SPACE == valueName[i + 1] ? 2 : 1;
                j = i;
            }
            else if (FONT_NAME_TERMINATOR == valueName[i]) {
                fragmentLength = FONT_NAME_SPACE == valueName[i - 1] ? i - j - 1 : i - j;
                fragmentValueName = valueName + j;
                if (fragmentLength == fontNameLength && 0 == wcsnicmp(fontName, fragmentValueName, fontNameLength)) {
                    goto SUCCESS;
                }
                break;
            }
        }
        fragmentLength = i - j;
        fragmentValueName = valueName + j;
        if (fragmentLength == fontNameLength && 0 == wcsnicmp(fontName, fragmentValueName, fontNameLength)) {
            goto SUCCESS;
        }
NEXT:
        valueNameLength = valueNameSize;
        valueDataLength = valueDataSize;
        index ++;
        continue;
SUCCESS:
        if (NULL != fileNameSize && *fileNameSize < valueDataLength) {
            result = FALSE;
        }
        else {
            wcsncpy(fileName, (wchar_t*)valueData, valueDataLength);
            result = TRUE;
            if (NULL != fileNameSize) {
                *fileNameSize = valueDataLength;
            }
        }
        break;
    }

    RegCloseKey(fontsKey);
    return result;
}

函数 GetFontFile 用于获取字体对应的字体文件名:

C++BOOL GetFontFile(
    [in]      const wchar_t* fontName, 
    [out]     wchar_t*       fileName, 
    [in, out] size_t*        fileNameSize
);

如果函数执行成功则返回 TRUE ,否则返回 FALSE 。函数执行失败可能由于读取注册表失败、字体未找到、参数 fileName 可用长度不足。

参考

  1. Microsoft Windows字体列表
  2. Microsoft Typography Font library
  3. Microsoft Typography Font List Windows 10
  4. Microsoft Typography Font List Windows 7