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

使用DirectWrite加载自定义内存字体失败问题求助

问题分析与解决方案

一、第一种加载方式(AddFontMemResourceEx)失效原因

AddFontMemResourceEx是将字体加载到系统字体表,但DirectWrite不会自动刷新字体缓存,导致CreateTextFormat无法找到刚加载的字体。此外需确认以下细节:

  • 资源脚本类型错误:原脚本中CUSTOM_RC是自定义资源类型,需改为BINARY确保字体以二进制形式嵌入,否则加载的资源可能损坏。
  • 未验证加载结果:需检查AddFontMemResourceEx返回的font句柄是否非空,以及fonts变量是否大于0,这是字体成功加载到系统的直接依据。
  • DirectWrite缓存未刷新:即使系统字体表加载成功,DirectWrite可能仍使用旧缓存,导致字体查找失败。

修复步骤

  1. 修改资源脚本:
    ID_FONT BINARY "res/Montserrat-Regular.ttf"
  2. 验证加载结果:
    DWORD fonts = 0;
    HANDLE font = AddFontMemResourceEx(resource, size, 0, &fonts);
    if (font == nullptr || fonts == 0) {
        DWORD err = GetLastError();
        // 根据错误码排查问题,比如资源损坏、内存不足等
    }
    
  3. 强制刷新DirectWrite缓存(可选):
    dwFactory->RegisterFontCollectionLoader(dwFactory);
    

二、第二种加载方式(DirectWrite内存字体加载器)完整流程

拿到IDWriteFontFile后,需通过自定义字体集合关联字体,再生成IDWriteTextFormat,完整代码如下:

// 已创建并注册InMemoryFontFileLoader,拿到fontFile后执行以下步骤:
IDWriteFontFileCollection* fontCollection = nullptr;
dwFactory->CreateCustomFontFileCollection(
    loader,          // 已注册的内存加载器
    &fontFile,       // 内存字体文件指针
    1,               // 字体文件数量
    &fontCollection
);

// 获取字体家族
IDWriteFontFamily* fontFamily = nullptr;
fontCollection->GetFontFamily(0, &fontFamily);

// 匹配指定样式的字体
IDWriteFont* font = nullptr;
fontFamily->GetFirstMatchingFont(
    DWRITE_FONT_WEIGHT_REGULAR,
    DWRITE_FONT_STRETCH_NORMAL,
    DWRITE_FONT_STYLE_NORMAL,
    &font
);

// 获取字体家族名称
IDWriteLocalizedStrings* familyNames = nullptr;
fontFamily->GetFamilyNames(&familyNames);

UINT32 nameIndex = 0;
BOOL nameExists = false;
familyNames->FindLocaleName(L"en-us", &nameIndex, &nameExists);
if (!nameExists) nameIndex = 0; // 找不到en-us时用第一个可用名称

WCHAR fontFamilyName[256];
familyNames->GetString(nameIndex, fontFamilyName, ARRAYSIZE(fontFamilyName));

// 创建TextFormat,关键是传入自定义字体集合
IDWriteTextFormat* format = nullptr;
dwFactory->CreateTextFormat(
    fontFamilyName,
    fontCollection,  // 必须指定自定义集合,否则DirectWrite会查系统字体表
    DWRITE_FONT_WEIGHT_REGULAR,
    DWRITE_FONT_STYLE_NORMAL,
    DWRITE_FONT_STRETCH_NORMAL,
    20.0f,
    L"en-us",
    &format
);

// 释放COM资源(使用完毕后必须释放)
familyNames->Release();
font->Release();
fontFamily->Release();
fontCollection->Release();
fontFile->Release();
loader->Release();

核心注意点

  • 必须在CreateTextFormat的第二个参数传入自定义fontCollection,否则DirectWrite会默认从系统字体表查找。
  • 内存资源生命周期:CreateInMemoryFontFileReference默认不复制内存数据,仅持有指针。若资源来自LoadResource,只要模块不卸载就有效;若为动态分配内存,需确保其生命周期覆盖TextFormat的使用期,或添加DWRITE_IN_MEMORY_FONT_FILE_LOADER_FLAG_COPY_DATA标志让加载器复制数据。

三、推荐方案

优先使用第二种DirectWrite私有字体集合的方式,它不依赖系统字体表,避免了缓存刷新问题,也不会影响其他应用的字体加载。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 14:52:43