You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取通过AddFontMemResourceEx加载的字体名称?

解决方法

1. 问题根源:私有字体不被默认枚举

用AddFontMemResourceEx加载的字体属于进程私有字体,默认情况下EnumFontFamiliesEx只会枚举系统全局字体,不会包含这类仅对当前进程可见的私有字体,因此你两次枚举结果一致。

2. 正确枚举私有字体的方法

调用EnumFontFamiliesEx时,需在dwFlags参数中传入PRIVATE_FONT_ENUM标志,这样就能枚举到当前进程加载的私有字体。

代码示例

#include <windows.h>
#include <vector>
#include <string>

// 枚举回调:收集字体名称
int CALLBACK EnumFontNameProc(ENUMLOGFONTEX* lpelfe, NEWTEXTMETRICEX* lpntme, DWORD FontType, LPARAM lParam)
{
    std::vector<std::string>* fontNames = reinterpret_cast<std::vector<std::string>*>(lParam);
    fontNames->push_back(lpelfe->elfLogFont.lfFaceName);
    return 1; // 继续枚举
}

// 获取当前进程的私有字体列表
std::vector<std::string> GetPrivateFontNames(HDC hdc)
{
    std::vector<std::string> fontNames;
    LOGFONT lf = {0};
    // 指定PRIVATE_FONT_ENUM标志枚举私有字体
    EnumFontFamiliesEx(hdc, &lf, (FONTENUMPROCEX)EnumFontNameProc, (LPARAM)&fontNames, PRIVATE_FONT_ENUM);
    return fontNames;
}

// 使用示例
int main()
{
    // 假设已通过AddFontMemResourceEx加载字体,得到资源句柄
    HANDLE fontResourceHandle = ...; 

    HDC hdc = GetDC(NULL);
    std::vector<std::string> privateFonts = GetPrivateFontNames(hdc);
    ReleaseDC(NULL, hdc);

    // 取第一个加载的私有字体名称(仅加载单个字体时适用)
    if (!privateFonts.empty())
    {
        const char* faceName = privateFonts[0].c_str();
        // 用获取到的名称创建字体
        HFONT hFont = CreateFontA(
            12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
            DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
            DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
            faceName
        );
        // 使用字体...
        DeleteObject(hFont);
    }

    // 释放字体资源
    RemoveFontMemResourceEx(fontResourceHandle);
    return 0;
}

3. 更直接的方法:解析内存字体数据获取名称

如果不想依赖系统枚举,可直接解析内存中的TrueType/OTF字体数据,读取字体内置的名称字段,这种方法无需依赖GDI,更灵活。

核心思路

TrueType/OTF字体的name表存储了多语言字体名称,我们可以定位到该表,提取对应语言的名称(比如英文名称),再转换为ANSI格式供CreateFontA使用。

简化代码示例(仅支持TrueType)

#include <windows.h>
#include <string>

std::string GetTTFFontNameFromMemory(const BYTE* fontData, size_t dataSize)
{
    if (dataSize < 12) return "";

    // 验证是否为TrueType格式
    uint32_t sfntVersion = *(uint32_t*)fontData;
    if (sfntVersion != 0x00010000 && sfntVersion != 'OTTO') return "";

    uint16_t numTables = *(uint16_t*)(fontData + 4);
    // 遍历表目录查找name表
    for (uint16_t i = 0; i < numTables; ++i)
    {
        uint32_t tag = *(uint32_t*)(fontData + 12 + i*16);
        if (tag == 'name')
        {
            uint32_t offset = *(uint32_t*)(fontData + 12 + i*16 + 8);
            uint32_t length = *(uint32_t*)(fontData + 12 + i*16 + 12);
            if (offset + length > dataSize) break;

            uint16_t count = *(uint16_t*)(fontData + offset + 2);
            uint16_t stringOffset = *(uint16_t*)(fontData + offset + 4);

            // 查找英文字体名称(nameID=4,对应Windows平台英文)
            for (uint16_t j = 0; j < count; ++j)
            {
                uint16_t nameID = *(uint16_t*)(fontData + offset + 6 + j*12 + 6);
                uint16_t platformID = *(uint16_t*)(fontData + offset + 6 + j*12);
                uint16_t languageID = *(uint16_t*)(fontData + offset + 6 + j*12 + 4);
                
                if (nameID == 4 && platformID == 3 && languageID == 0x0409)
                {
                    uint16_t stringLength = *(uint16_t*)(fontData + offset + 6 + j*12 + 8);
                    uint16_t stringLocalOffset = *(uint16_t*)(fontData + offset + 6 + j*12 + 10);
                    const wchar_t* wideName = (const wchar_t*)(fontData + offset + stringOffset + stringLocalOffset);
                    
                    char ansiName[256] = {0};
                    WideCharToMultiByte(CP_ACP, 0, wideName, stringLength/2, ansiName, sizeof(ansiName), NULL, NULL);
                    return std::string(ansiName);
                }
            }
            break;
        }
    }
    return "";
}

// 使用示例
int main()
{
    // fontData为内存中的字体数据,dataSize为数据大小
    const BYTE* fontData = ...;
    size_t dataSize = ...;

    std::string fontName = GetTTFFontNameFromMemory(fontData, dataSize);
    if (!fontName.empty())
    {
        HFONT hFont = CreateFontA(12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
                                  DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                                  DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, fontName.c_str());
        // 使用字体...
        DeleteObject(hFont);
    }

    // 加载和释放字体资源...
    return 0;
}

注意事项

  • 调用AddFontMemResourceEx后必须保存返回的HANDLE,最终用RemoveFontMemResourceEx释放,避免内存泄漏。
  • 如果加载了多个字体,可在加载前后分别枚举私有字体,取两次结果的差值来定位新加载的字体。
  • 解析字体数据的方法需根据实际需求扩展,以兼容OTF等其他字体格式。

内容的提问来源于stack exchange,提问作者Д Т

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 23:10:11