使用 Windows API 加载 ttf 字体

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5757227/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 16:41:59  来源:igfitidea点击:

load a ttf font with the Windows API

windowswinapi

提问by Claudiu

With CreateFontone can specify font name and a bunch of other properties. However, what if I have a font.ttffile, and I want that particular font to be loaded by windows? How do I specify that specific file to be used?

有了CreateFont一个可以指定字体名称和很多其他的属性。但是,如果我有一个font.ttf文件,并且希望 Windows 加载该特定字体怎么办?如何指定要使用的特定文件?

回答by Jerry Coffin

I'm pretty sure you can't. All requests for fonts go through the font mapper, and it picks out the font file that comes the closest to meeting the specifications you've given. Though I'm not sure it even does in reality, it could at least theoretically use (for example) data from two entirely separate font files to create one logical font.

我很确定你不能。所有对字体的请求都通过字体映射器,它会挑选出最接近满足您提供的规范的字体文件。虽然我不确定它在现实中是否确实如此,但它至少可以在理论上使用(例如)来自两个完全独立的字体文件的数据来创建一种逻辑字体。

回答by Dwayne Robinson

It's admittedly rather indirect, but you could utilize GDI interop with DWrite when running on Windows 7+.

诚然,这是相当间接的,但是在 Windows 7+ 上运行时,您可以将 GDI 互操作与 DWrite 结合使用。

#include <Windows.h>
#include <WindowsX.h>
#include <DWrite.h>

...

// Make the font file visible to GDI.
AddFontResourceEx(fontFileName, FR_PRIVATE, 0);
if (SUCCEEDED(GetLogFontFromFileName(fontFileName, &logFont)))
{
    logFont.lfHeight = -long(desiredPpem);
    HFONT hf = CreateFontIndirect(&logFont);
    HFONT oldFont = SelectFont(hdc, hf);
    ...
    // Do stuff...
    ...
    SelectFont(hdc, oldFont);
}
RemoveFontResource(fontFileName);

....

HRESULT GetLogFontFromFileName(_In_z_ wchar const* fontFileName, _Out_ LOGFONT* logFont)
{
    // DWrite objects
    ComPtr<IDWriteFactory> dwriteFactory;
    ComPtr<IDWriteFontFace> fontFace;
    ComPtr<IDWriteFontFile> fontFile;
    ComPtr<IDWriteGdiInterop> gdiInterop;

    // Set up our DWrite factory and interop interface.
    IFR(DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,
        __uuidof(IDWriteFactory),
        reinterpret_cast<IUnknown**>(&dwriteFactory)
        );
    IFR(g_dwriteFactory->GetGdiInterop(&gdiInterop));

    // Open the file and determine the font type.
    IFR(g_dwriteFactory->CreateFontFileReference(fontFileName, nullptr, &fontFile));
    BOOL isSupportedFontType = false;
    DWRITE_FONT_FILE_TYPE fontFileType;
    DWRITE_FONT_FACE_TYPE fontFaceType;
    UINT32 numberOfFaces = 0;
    IFR(fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces));

    if (!isSupportedFontType)
        return DWRITE_E_FILEFORMAT;

    // Set up a font face from the array of font files (just one)
    ComPtr<IDWriteFontFile> fontFileArray[] = {fontFile};
    IFR(g_dwriteFactory->CreateFontFace(
        fontFaceType,
        ARRAYSIZE(fontFileArray), // file count
        &fontFileArray[0], // or GetAddressOf if WRL ComPtr
        0, // faceIndex
        DWRITE_FONT_SIMULATIONS_NONE,
        &fontFace
        );

    // Get the necessary logical font information.
    IFR(gdiInterop->ConvertFontFaceToLOGFONT(fontFace, OUT logFont));

    return S_OK;
}

Where IFR is just a failure macro that returns on a FAILED HRESULT, and ComPtr is a helper smart pointer class (substitute with your own, or ATL CComPtr, WinRT ComPtr, VS2013 _com_ptr_t...).

其中 IFR 只是一个在 FAILED HRESULT 上返回的失败宏,而 ComPtr 是一个辅助智能指针类(用您自己的,或 ATL CComPtr、WinRT ComPtr、VS2013 _com_ptr_t...代替)。

回答by CoreyStup

One possibility is to EnumFonts(), save the results. Then add your private font with AddFontResourceEx(), and EnumFonts()again, the difference is what you added. Note that TTF and bitmap fonts enumerate differently, but for this test, that shouldn't matter.

一种可能性是EnumFonts(),保存结果。然后添加您的私人字体AddFontResourceEx()EnumFonts()同样,区别在于您添加的内容。请注意,TTF 和位图字体的枚举方式不同,但对于此测试,这无关紧要。

If you were using bitmap fonts, they could be easily parsed (.FNT and .FON). TTF you'd likely have to build (or borrow, as another commenter suggested FreeType) a parser to pull the "name" table out of the TTF file.

如果您使用的是位图字体,则它们很容易解析(.FNT 和 .FON)。TTF 您可能必须构建(或借用,正如另一位评论者建议的 FreeType)解析器来从 TTF 文件中提取“名称”表。

That seems like a lot of work for a font you're controlling or supplying with your app.

对于您正在控制或随应用程序提供的字体,这似乎需要大量工作。

We use AddFontResourceEx() to add a private font, but since we control the font we're adding, we just hardcode the fontname passed to CreateFontIndirect()to match.

我们使用 AddFontResourceEx() 添加私有字体,但由于我们控制要添加的字体,我们只是硬编码传递CreateFontIndirect()给匹配的字体名称。

回答by Alex K.

If you dont care about installing the font you can do so with AddFontResourcethen you can fetch the relationship between the physical .TTF and it logical/family name by looking at the mappings in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts.

如果您不关心安装可以使用的字体,AddFontResource那么您可以通过查看 .TTF 中的映射来获取物理 .TTF 与其逻辑/系列名称之间的关系HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts

I mentioned PrivateFontCollection in my comment because I thought you wanted to do this temporarily; you can load a TTF into a PFC with PrivateFontCollection::AddFontFile, fetch back the new FontFamilyobject from the collection & examime GetFamilyName. (I've done similar with the .net implementation of this but not the raw API)

我在评论中提到PrivateFontCollection是因为我以为你想暂时这样做;您可以使用 将 TTF 加载到 PFC 中PrivateFontCollection::AddFontFileFontFamily从集合和检查中取回新对象GetFamilyName。(我已经完成了类似的 .net 实现,但不是原始 API)