根据字体名称和样式(加粗/斜体),获取字体文件名

20

整天都让我感到疯狂。

我需要根据字体名称(例如Arial)以及其是否为粗体,斜体或两者皆有来获取字体文件名(例如Arial.ttf)。使用这些信息,我需要找到字体文件,以便用于渲染。

一些其他例子:

  • Calibri,Bold会解析为calibrib.ttf。
  • Calibri,Italic会解析为calibrii.ttf。

您有任何关于如何在C++(Win32)中实现此功能的想法吗?


如果您有字体的HFONT句柄,可以尝试我的解决方案。我不想在这里添加答案,因为问题不清楚是否有字体的句柄。 - huysentruitw
7个回答

10

据我所知,目前没有可靠的方法来实现这个目标。

Windows API 处理字体族和映射,而不是处理字体文件的更低层级。此外,请注意即使您成功获取了字体的文件名,(就我所知)也没有任何渲染函数能够接受它,那么您该怎么办呢?

尽管如此,您可以查看注册表键 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts 来从逻辑名称中获取字体的文件名。可以在此处找到该解决方案的实现:here


11
我需要字体文件的名称,因为我正在使用FreeType来渲染字体,而FreeType只接受文件名作为输入。 - Brad
2
当操作系统语言不同时,该映射无法正常工作。从EnumFontFamiliesExW获得的facename是操作系统语言(例如:안상수2006굵은),而注册表名称始终为英文(ex: ahn2006-B (TrueType))。当将面向名称映射到实际字体文件时,Microsoft似乎会执行额外步骤。 - Jean-Simon Brochu

5

与早期帖子相关的是,这似乎是一种可靠的方法:

1) 读取注册的Windows字体列表,从以下路径获取: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts\ 在这里你可以获得文件名和备用文件路径。 字体名称并不实用,因为它们可能会因用户所在地区而改变。

2) 加载TrueType文件 (.ttf, .ttc, .otf): 使用FreeType https://www.freetype.org/)。只需初始化FreeType库并使用FT_New_Face(library, path, 0, &face)加载面。

3) 使用FreeType获取字体族名称。 使用FT_Get_Sfnt_Name_Count() 和 FT_Get_Sfnt_Name() 获取字符串表。 您需要检查编码是否为Ansi、UTF16或其他,因为某些字符串将以多种不同的语言和编码出现。

4) 获取OS2 TrueType属性。 使用(TT_OS2 *) FT_Get_Sfnt_Table (face, ft_sfnt_os2) 获取OS2结构。 使用类似于https://www.microsoft.com/typography/otspec/os2.htm#fc的文档解释该结构。

5) 现在你有字体文件路径、字体族名称、样式属性和其他信息。建立这些的列表和基于字体族名称和样式搜索文件的函数。


2
这个Code Project项目可以满足您的需求。但是,因为GetWinVer函数在Windows 7上停止工作,所以现有版本无法使用。不过,增加Windows 7的情况很容易实现。

1

自Windows Vista SP2以来,可以通过字体家族名称查询字体。 有多种方法可以从family_name、weight和italic中检索字体文件名。 这个答案提供了两种可能的方法,但还有很多其他方法。

请注意,DirectWrite可能会为一个字体返回多个字体文件名,但我不知道什么情况下会发生。根据我进行的少量测试,它总是返回一个字体。

请注意,我不熟悉C++,所以你可能需要修改一些部分,但这会给你一个解决问题的好思路。

适用于Windows Vista SP2及更高版本

#include <iostream>
#include <dwrite.h>
#include <list>
#pragma comment(lib, "dwrite.lib")

using namespace std;


list<WCHAR*> get_fonts_path(WCHAR* family_name, BOOL is_bold, BOOL is_italic)
{
    list<WCHAR*> fonts_filename_list;
    HRESULT hr;

    IDWriteFactory* dwrite_factory;
    hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&dwrite_factory));
    if (FAILED(hr))
    {
        return fonts_filename_list;
    }

    IDWriteFontCollection* sys_collection;
    hr = dwrite_factory->GetSystemFontCollection(&sys_collection, false);
    if (FAILED(hr))
    {
        dwrite_factory->Release();
        return fonts_filename_list;
    }

    UINT32 index;
    BOOL exists;
    sys_collection->FindFamilyName(family_name, &index, &exists);
    if (FAILED(hr))
    {
        sys_collection->Release();
        dwrite_factory->Release();
        return fonts_filename_list;
    }


    if (!exists) {
        wcout << L"The font '" << family_name << L"' does not exist" << endl;
        sys_collection->Release();
        dwrite_factory->Release();
        return fonts_filename_list;
    }

    IDWriteFontFamily* font_family;
    hr = sys_collection->GetFontFamily(index, &font_family);
    if (FAILED(hr))
    {
        sys_collection->Release();
        dwrite_factory->Release();
        return fonts_filename_list;
    }

    IDWriteFont* matching_font;
    DWRITE_FONT_WEIGHT weight = is_bold ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_REGULAR;
    DWRITE_FONT_STYLE style = is_italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL;
    hr = font_family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &matching_font);
    if (FAILED(hr))
    {
        font_family->Release();
        sys_collection->Release();
        dwrite_factory->Release();
        return fonts_filename_list;
    }

    IDWriteFontFace* font_face;
    hr = matching_font->CreateFontFace(&font_face);
    if (FAILED(hr))
    {
        matching_font->Release();
        font_family->Release();
        sys_collection->Release();
        dwrite_factory->Release();
        return fonts_filename_list;
    }

    UINT file_count;
    hr = font_face->GetFiles(&file_count, NULL);
    if (FAILED(hr))
    {
        font_face->Release();
        matching_font->Release();
        font_family->Release();
        sys_collection->Release();
        dwrite_factory->Release();
        return fonts_filename_list;
    }


    IDWriteFontFile** font_files = new IDWriteFontFile * [file_count];
    hr = font_face->GetFiles(&file_count, font_files);
    if (FAILED(hr))
    {
        font_face->Release();
        matching_font->Release();
        font_family->Release();
        sys_collection->Release();
        dwrite_factory->Release();
        return fonts_filename_list;
    }

    for (int i = 0; i < file_count; i++)
    {
        LPCVOID font_file_reference_key;
        UINT font_file_reference_key_size;
        hr = font_files[i]->GetReferenceKey(&font_file_reference_key, &font_file_reference_key_size);
        if (FAILED(hr))
        {
            font_files[i]->Release();
            continue;
        }

        IDWriteFontFileLoader* loader;
        hr = font_files[i]->GetLoader(&loader);
        if (FAILED(hr))
        {
            font_files[i]->Release();
            continue;
        }

        IDWriteLocalFontFileLoader* local_loader;
        hr = loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), (void**)&local_loader);
        if (FAILED(hr))
        {
            loader->Release();
            font_files[i]->Release();
            continue;
        }

        UINT32 path_length;
        hr = local_loader->GetFilePathLengthFromKey(font_file_reference_key, font_file_reference_key_size, &path_length);
        if (FAILED(hr))
        {
            local_loader->Release();
            loader->Release();
            font_files[i]->Release();
            continue;
        }


        WCHAR* path = new WCHAR[path_length + 1];
        hr = local_loader->GetFilePathFromKey(font_file_reference_key, font_file_reference_key_size, path, path_length + 1);
        if (FAILED(hr))
        {
            local_loader->Release();
            loader->Release();
            font_files[i]->Release();
            continue;
        }

        fonts_filename_list.push_back(path);

        local_loader->Release();
        loader->Release();
        font_files[i]->Release();

    }

    font_face->Release();
    matching_font->Release();
    font_family->Release();
    sys_collection->Release();
    dwrite_factory->Release();

    return fonts_filename_list;
}


int main() {
    list<WCHAR*> fonts_filename_list = get_fonts_path(const_cast<WCHAR*>(L"Arial"), false, true);


    for (WCHAR* font_filename : fonts_filename_list)
    {
        wcout << L"Font filename: " << font_filename << endl;
    }

    return 0;
}

此外,适用于Windows Vista SP2及更高版本。
#include <iostream>
#include <dwrite.h>
#include <list>
#pragma comment(lib, "dwrite.lib")

using namespace std;


list<WCHAR*> get_fonts_path(WCHAR* family_name, BOOL is_bold, BOOL is_italic)
{
    list<WCHAR*> fonts_filename_list;
    HRESULT hr;

    IDWriteFactory* dwrite_factory;
    hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&dwrite_factory));
    if (FAILED(hr))
    {
        return fonts_filename_list;
    }

    IDWriteGdiInterop* gdi_interop;
    hr = dwrite_factory->GetGdiInterop(&gdi_interop);
    if (FAILED(hr))
    {
        dwrite_factory->Release();
        return fonts_filename_list;
    }

    LOGFONT lf;
    memset(&lf, 0, sizeof(lf));
    wcscpy_s(lf.lfFaceName, LF_FACESIZE, family_name);
    lf.lfWeight = is_bold ? FW_BOLD : FW_REGULAR;
    lf.lfItalic = is_italic;
    lf.lfCharSet = DEFAULT_CHARSET;
    lf.lfOutPrecision = OUT_TT_PRECIS;
    lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
    lf.lfQuality = ANTIALIASED_QUALITY;
    lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;

    IDWriteFont* matching_font;
    hr = gdi_interop->CreateFontFromLOGFONT(&lf, &matching_font);
    if (FAILED(hr))
    {
        gdi_interop->Release();
        dwrite_factory->Release();
        return fonts_filename_list;
    }

    IDWriteFontFace* font_face;
    hr = matching_font->CreateFontFace(&font_face);
    if (FAILED(hr))
    {
        matching_font->Release();
        gdi_interop->Release();
        dwrite_factory->Release();
        return fonts_filename_list;
    }

    UINT file_count;
    hr = font_face->GetFiles(&file_count, NULL);
    if (FAILED(hr))
    {
        font_face->Release();
        matching_font->Release();
        gdi_interop->Release();
        dwrite_factory->Release();
        return fonts_filename_list;
    }


    IDWriteFontFile** font_files = new IDWriteFontFile * [file_count];
    hr = font_face->GetFiles(&file_count, font_files);
    if (FAILED(hr))
    {
        font_face->Release();
        matching_font->Release();
        gdi_interop->Release();
        dwrite_factory->Release();
        return fonts_filename_list;
    }

    for (int i = 0; i < file_count; i++)
    {
        LPCVOID font_file_reference_key;
        UINT font_file_reference_key_size;
        hr = font_files[i]->GetReferenceKey(&font_file_reference_key, &font_file_reference_key_size);
        if (FAILED(hr))
        {
            font_files[i]->Release();
            continue;
        }

        IDWriteFontFileLoader* loader;
        hr = font_files[i]->GetLoader(&loader);
        if (FAILED(hr))
        {
            font_files[i]->Release();
            continue;
        }

        IDWriteLocalFontFileLoader* local_loader;
        hr = loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), (void**)&local_loader);
        if (FAILED(hr))
        {
            loader->Release();
            font_files[i]->Release();
            continue;
        }

        UINT32 path_length;
        hr = local_loader->GetFilePathLengthFromKey(font_file_reference_key, font_file_reference_key_size, &path_length);
        if (FAILED(hr))
        {
            local_loader->Release();
            loader->Release();
            font_files[i]->Release();
            continue;
        }


        WCHAR* path = new WCHAR[path_length + 1];
        hr = local_loader->GetFilePathFromKey(font_file_reference_key, font_file_reference_key_size, path, path_length + 1);
        if (FAILED(hr))
        {
            local_loader->Release();
            loader->Release();
            font_files[i]->Release();
            continue;
        }

        fonts_filename_list.push_back(path);

        local_loader->Release();
        loader->Release();
        font_files[i]->Release();

    }

    font_face->Release();
    matching_font->Release();
    gdi_interop->Release();
    dwrite_factory->Release();

    return fonts_filename_list;
}


int main() {
    list<WCHAR*> fonts_filename_list = get_fonts_path(const_cast<WCHAR*>(L"Arial"), false, true);


    for (WCHAR* font_filename : fonts_filename_list)
    {
        wcout << L"Font filename: " << font_filename << endl;
    }

    return 0;
}

此外,您可能更喜欢使用内存而不是获取字体文件名,以解决此问题。为此,请调用IDWriteFontFileLoader::CreateStreamFromKey,然后调用IDWriteFontFileStream::ReadFileFragment

0

通常情况下,您可以通过调用CreateFontIndirect并让系统进行渲染来实现此目的。也许您可以解释一下为什么不能使用这种标准方法。


0
如果你在Windows资源管理器中浏览C:\windows\fonts,你可以切换到详细视图,文件名将作为一列显示。

-1

一个解决方案是访问字体文件并从名称表中提取名称以创建自己的查找(STL映射是一种简单的方法)。可以在这里找到TTF文件格式的详细信息。


这是我使用FreeType库所做的事情。我正在迭代%WINDIR%/Fonts中的所有字体文件,然后可以检查它们的面部类型以及是否为粗体/斜体或两者都有。问题在于,Fonts目录包含FreeType无法读取的文件,这会导致FreeType输出错误。 - Brad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接