使用DirectWrite加载自定义内存字体失败问题求助
问题分析与解决方案
一、第一种加载方式(AddFontMemResourceEx)失效原因
AddFontMemResourceEx是将字体加载到系统字体表,但DirectWrite不会自动刷新字体缓存,导致CreateTextFormat无法找到刚加载的字体。此外需确认以下细节:
- 资源脚本类型错误:原脚本中
CUSTOM_RC是自定义资源类型,需改为BINARY确保字体以二进制形式嵌入,否则加载的资源可能损坏。 - 未验证加载结果:需检查
AddFontMemResourceEx返回的font句柄是否非空,以及fonts变量是否大于0,这是字体成功加载到系统的直接依据。 - DirectWrite缓存未刷新:即使系统字体表加载成功,DirectWrite可能仍使用旧缓存,导致字体查找失败。
修复步骤
- 修改资源脚本:
ID_FONT BINARY "res/Montserrat-Regular.ttf" - 验证加载结果:
DWORD fonts = 0; HANDLE font = AddFontMemResourceEx(resource, size, 0, &fonts); if (font == nullptr || fonts == 0) { DWORD err = GetLastError(); // 根据错误码排查问题,比如资源损坏、内存不足等 } - 强制刷新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
相关产品推荐
相关产品推荐

