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

如何检查Windows系统中自带粗体、斜体样式的已安装字体(C++/MFC)

如何在MFC/C++中筛选具备原生粗体/斜体/粗斜体的字体

嘿,这个需求我太熟悉了——毕竟Windows的字体模拟功能确实容易让我们混淆「原生样式」和「系统模拟样式」。既然你已经在用EnumFontFamiliesEx,其实可以通过优化回调里的判断逻辑来精准筛选,甚至还有更权威的方法,咱们一步步说:

核心原理:区分原生样式和系统模拟

Windows里,真正具备粗体/斜体的字体是作为独立字体文件存在的,它们的字体元数据里会明确标记这些样式;而系统模拟的粗体/斜体,只是GDI对基础字体做拉伸、倾斜变形,并没有原生的样式支持。

方法一:基于EnumFontFamiliesEx回调的快速筛选

这是最贴合你现有代码的方案,只需要在枚举回调里判断NEWTEXTMETRICEX的标志位,就能区分原生和模拟样式:

  1. 定义存储字体信息的结构:
struct FontFamilyInfo {
    CString faceName;          // 字体家族名
    bool hasNativeBold;        // 是否有原生粗体
    bool hasNativeItalic;      // 是否有原生斜体
    bool hasNativeBoldItalic;  // 是否有原生粗斜体
};

CArray<FontFamilyInfo, FontFamilyInfo> m_genuineFonts;
  1. 实现枚举回调函数:
int CALLBACK EnumGenuineFontProc(const LOGFONT* lplf, const TEXTMETRIC* lptm, DWORD dwType, LPARAM lpData) {
    auto pFontList = reinterpret_cast<CArray<FontFamilyInfo, FontFamilyInfo>*>(lpData);
    auto pntm = const_cast<NEWTEXTMETRICEX*>(reinterpret_cast<const NEWTEXTMETRICEX*>(lptm));

    // 提取当前字体的样式信息
    bool isBold = (pntm->ntmTm.ntmFlags & NTM_BOLD) != 0;
    bool isItalic = (pntm->ntmTm.ntmFlags & NTM_ITALIC) != 0;
    bool isBoldItalic = isBold && isItalic;

    // 按字体家族去重,合并样式信息
    bool familyExists = false;
    for (int i = 0; i < pFontList->GetSize(); ++i) {
        if ((*pFontList)[i].faceName == lplf->lfFaceName) {
            familyExists = true;
            if (isBold) (*pFontList)[i].hasNativeBold = true;
            if (isItalic) (*pFontList)[i].hasNativeItalic = true;
            if (isBoldItalic) (*pFontList)[i].hasNativeBoldItalic = true;
            break;
        }
    }

    // 新字体家族则添加到列表
    if (!familyExists) {
        FontFamilyInfo info;
        info.faceName = lplf->lfFaceName;
        info.hasNativeBold = isBold;
        info.hasNativeItalic = isItalic;
        info.hasNativeBoldItalic = isBoldItalic;
        pFontList->Add(info);
    }

    return 1; // 继续枚举下一个字体
}
  1. 在MFC函数中调用枚举:
void CMyApp::CollectGenuineFonts() {
    m_genuineFonts.RemoveAll();
    CDC dc;
    dc.Attach(::GetDC(NULL));

    LOGFONT lf = {0};
    lf.lfCharSet = DEFAULT_CHARSET; // 枚举所有字符集的字体
    EnumFontFamiliesEx(dc.m_hDC, &lf, EnumGenuineFontProc, reinterpret_cast<LPARAM>(&m_genuineFonts), 0);

    dc.Detach();

    // 输出结果示例
    for (int i = 0; i < m_genuineFonts.GetSize(); ++i) {
        auto& info = m_genuineFonts[i];
        TRACE(_T("字体家族:%s\n")
              _T("  原生粗体:%s\n")
              _T("  原生斜体:%s\n")
              _T("  原生粗斜体:%s\n\n"),
              info.faceName,
              info.hasNativeBold ? _T("是") : _T("否"),
              info.hasNativeItalic ? _T("是") : _T("否"),
              info.hasNativeBoldItalic ? _T("是") : _T("否"));
    }
}

为什么这个方法有效?
系统模拟的粗体/斜体,ntmTm.ntmFlags不会包含NTM_BOLD或NTM_ITALIC标志——只有原生字体的元数据里才会有这些标记,所以能精准过滤。

方法二:解析字体OS/2表的权威判断

如果需要100%准确的结果(比如某些特殊字体的边缘情况),可以直接读取字体文件的OS/2表,其中的fsSelection字段是字体样式的权威标记:

// 判断字体是否具备原生粗体
bool HasNativeBold(HFONT hFont) {
    HDC hdc = CreateCompatibleDC(NULL);
    HFONT hOldFont = static_cast<HFONT>(SelectObject(hdc, hFont));

    // 获取OS/2表的大小
    DWORD tableSize = GetFontData(hdc, MAKEINTRESOURCE('OS/2'), 0, NULL, 0);
    if (tableSize == GDI_ERROR) {
        SelectObject(hdc, hOldFont);
        DeleteDC(hdc);
        return false;
    }

    // 读取OS/2表数据
    std::vector<BYTE> os2Table(tableSize);
    if (GetFontData(hdc, MAKEINTRESOURCE('OS/2'), 0, os2Table.data(), tableSize) == GDI_ERROR) {
        SelectObject(hdc, hOldFont);
        DeleteDC(hdc);
        return false;
    }

    // 解析fsSelection字段(偏移0x0A,2字节):位5表示原生粗体
    WORD fsSelection = *reinterpret_cast<WORD*>(os2Table.data() + 0x0A);
    bool isNativeBold = (fsSelection & 0x0020) != 0;

    SelectObject(hdc, hOldFont);
    DeleteDC(hdc);
    return isNativeBold;
}

// 判断原生斜体:位0表示原生斜体
bool HasNativeItalic(HFONT hFont) {
    // 逻辑和上面类似,只是判断fsSelection & 0x0001
    // 这里省略重复代码,你可以自行封装成通用函数
}

这个方法的优势:直接读取字体文件的元数据,完全不受系统模拟样式的干扰,是最权威的判断方式。

总结

  • 如果你追求开发效率和足够的准确性,方法一完全满足需求,贴合你现有的EnumFontFamiliesEx逻辑,代码改动小。
  • 如果需要处理特殊字体或绝对精准的判断,方法二是最优解,虽然代码稍复杂,但结果最可靠。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:54:36