Reputation: 10660
This has been driving me crazy all day.
I need to get a font filename (eg. Arial.ttf) based on its name (Arial in this case) and whether it is bold, italic or both. Using those pieces of information, I need to find the font file so I can use it for rendering.
Some more examples:
Any ideas on how I could achieve this in C++ (Win32)
Upvotes: 20
Views: 17806
Reputation: 514
It is possible to query a font from a family name since Windows Vista with SP2. There are multiple ways to retrieve a font filename from the: family_name, weight and italic. This answer provided 2 possibles ways, but there are many more.
Note that DirectWrite can return multiple font filename for 1 font, but I don't know when it can happen. For the few test I did, it always return me 1 font.
Please note that I am not familiar with C++, so you might need to modify some part, but it will give you a good idea how to resolve your problem
For Windows Vista with SP2 and more
#include <iostream>
#include <list>
#include <vector>
#include <atlbase.h>
#include <dwrite.h>
#pragma comment(lib, "dwrite.lib")
std::list<std::wstring> get_fonts_path(const std::wstring& family_name, BOOL is_bold, BOOL is_italic)
{
std::list<std::wstring> fonts_filename_list;
HRESULT hr;
CComPtr<IDWriteFactory> dwrite_factory;
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&dwrite_factory));
if (FAILED(hr))
{
return fonts_filename_list;
}
CComPtr<IDWriteFontCollection> sys_collection;
hr = dwrite_factory->GetSystemFontCollection(&sys_collection, false);
if (FAILED(hr))
{
return fonts_filename_list;
}
UINT32 index;
BOOL exists;
sys_collection->FindFamilyName(family_name.c_str(), &index, &exists);
if (FAILED(hr))
{
return fonts_filename_list;
}
if (!exists) {
std::wcout << L"The font '" << family_name << L"' does not exist" << std::endl;
return fonts_filename_list;
}
CComPtr<IDWriteFontFamily> font_family;
hr = sys_collection->GetFontFamily(index, &font_family);
if (FAILED(hr))
{
return fonts_filename_list;
}
CComPtr<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))
{
return fonts_filename_list;
}
CComPtr<IDWriteFontFace> font_face;
hr = matching_font->CreateFontFace(&font_face);
if (FAILED(hr))
{
return fonts_filename_list;
}
UINT32 file_count;
hr = font_face->GetFiles(&file_count, NULL);
if (FAILED(hr))
{
return fonts_filename_list;
}
std::vector<CComPtr<IDWriteFontFile>> font_files(file_count);
hr = font_face->GetFiles(&file_count, reinterpret_cast<IDWriteFontFile**>(font_files.data()));
if (FAILED(hr))
{
return fonts_filename_list;
}
for (int i = 0; i < file_count; i++)
{
LPCVOID font_file_reference_key;
UINT32 font_file_reference_key_size;
hr = font_files[i]->GetReferenceKey(&font_file_reference_key, &font_file_reference_key_size);
if (FAILED(hr))
{
continue;
}
CComPtr<IDWriteFontFileLoader> loader;
hr = font_files[i]->GetLoader(&loader);
if (FAILED(hr))
{
continue;
}
CComPtr<IDWriteLocalFontFileLoader> local_loader;
hr = loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), reinterpret_cast<void**>(&local_loader));
if (FAILED(hr))
{
continue;
}
UINT32 path_length;
hr = local_loader->GetFilePathLengthFromKey(font_file_reference_key, font_file_reference_key_size, &path_length);
if (FAILED(hr))
{
continue;
}
std::wstring path(path_length + 1, L'\0');
hr = local_loader->GetFilePathFromKey(font_file_reference_key, font_file_reference_key_size, &path[0], path_length + 1);
if (FAILED(hr))
{
continue;
}
fonts_filename_list.push_back(path);
}
return fonts_filename_list;
}
int main() {
std::list<std::wstring> fonts_filename_list = get_fonts_path(L"Arial", false, true);
for (std::wstring font_filename : fonts_filename_list)
{
std::wcout << L"Font filename: " << font_filename << std::endl;
}
return 0;
}
Also, for Windows Vista with SP2 and more
#include <iostream>
#include <list>
#include <vector>
#include <atlbase.h>
#include <dwrite.h>
#pragma comment(lib, "dwrite.lib")
std::list<std::wstring> get_fonts_path(const std::wstring& family_name, BOOL is_bold, BOOL is_italic)
{
std::list<std::wstring> fonts_filename_list;
HRESULT hr;
CComPtr<IDWriteFactory> dwrite_factory;
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&dwrite_factory));
if (FAILED(hr))
{
return fonts_filename_list;
}
CComPtr<IDWriteGdiInterop> gdi_interop;
hr = dwrite_factory->GetGdiInterop(&gdi_interop);
if (FAILED(hr))
{
return fonts_filename_list;
}
LOGFONT lf = { 0 };
wcscpy_s(lf.lfFaceName, LF_FACESIZE, family_name.c_str());
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;
CComPtr<IDWriteFont> matching_font;
hr = gdi_interop->CreateFontFromLOGFONT(&lf, &matching_font);
if (FAILED(hr))
{
return fonts_filename_list;
}
CComPtr<IDWriteFontFace> font_face;
hr = matching_font->CreateFontFace(&font_face);
if (FAILED(hr))
{
return fonts_filename_list;
}
UINT32 file_count;
hr = font_face->GetFiles(&file_count, NULL);
if (FAILED(hr))
{
return fonts_filename_list;
}
std::vector<CComPtr<IDWriteFontFile>> font_files(file_count);
hr = font_face->GetFiles(&file_count, reinterpret_cast<IDWriteFontFile**>(font_files.data()));
if (FAILED(hr))
{
return fonts_filename_list;
}
for (int i = 0; i < file_count; i++)
{
LPCVOID font_file_reference_key;
UINT32 font_file_reference_key_size;
hr = font_files[i]->GetReferenceKey(&font_file_reference_key, &font_file_reference_key_size);
if (FAILED(hr))
{
continue;
}
CComPtr<IDWriteFontFileLoader> loader;
hr = font_files[i]->GetLoader(&loader);
if (FAILED(hr))
{
continue;
}
CComPtr<IDWriteLocalFontFileLoader> local_loader;
hr = loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), reinterpret_cast<void**>(&local_loader));
if (FAILED(hr))
{
continue;
}
UINT32 path_length;
hr = local_loader->GetFilePathLengthFromKey(font_file_reference_key, font_file_reference_key_size, &path_length);
if (FAILED(hr))
{
continue;
}
std::wstring path(path_length + 1, L'\0');
hr = local_loader->GetFilePathFromKey(font_file_reference_key, font_file_reference_key_size, &path[0], path_length + 1);
if (FAILED(hr))
{
continue;
}
fonts_filename_list.push_back(path);
}
return fonts_filename_list;
}
int main() {
std::list<std::wstring> fonts_filename_list = get_fonts_path(L"Arial", false, true);
for (std::wstring font_filename : fonts_filename_list)
{
std::wcout << L"Font filename: " << font_filename << std::endl;
}
return 0;
}
Also, instead of getting the font filename, you may prefer to use memory due to this issue. To do so, call IDWriteFontFileLoader::CreateStreamFromKey, then call IDWriteFontFileStream::ReadFileFragment.
Upvotes: 3
Reputation: 754
If you browse C:\windows\fonts in Windows Explorer, you can switch to Details view, and Filename is available as a column.
Upvotes: -1
Reputation: 1251
Related to the earlier posts, this seems to be a reliable way:
1) Read the registered Windows font list from HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts\ You will obtain file names and alternate file paths here. The Font names are not useful as they can change with user's locale.
2) Load the TrueType files (.ttf, .ttc, .otf): Use FreeType https://www.freetype.org/). Just initialize the freetype library and load face with FT_New_Face(library, path, 0, &face).
3) Obtain the font Family name using FreeType. Use FT_Get_Sfnt_Name_Count() and FT_Get_Sfnt_Name() to obtain the string table. You will need to check if the encoding is Ansi, UTF16 or other, as some strings will be in multiple different languages and encodings.
4) Obtain the OS2 TrueType properties. Use (TT_OS2 *) FT_Get_Sfnt_Table (face, ft_sfnt_os2) to get the OS2 structure. Interpret the structure using docs like https://www.microsoft.com/typography/otspec/os2.htm#fc
5) Now you have font file path, family name, style properties and other information. Build a list of these and function to search for a file based on font family and style.
Upvotes: 5
Reputation: 1834
This Code Project project does what you want. As-is it fails on Windows 7 because the GetWinVer function stops at XP. It is trivial to add the case for Windows 7.
Upvotes: 2
Reputation: 262939
First, to my knowledge, there is no reliable way to do that.
The Windows API deals with font families and mappings, not with font files, which are dealt with at a lower level. Also note that even if you manage to get the file name of a font, no rendering function (that I know of) will accept it, so what will you do with it?
That said, you can look in the registry key HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts
in order to obtain the file name of a font from its logical name. An implementation of that solution can be found here.
Upvotes: 10
Reputation: 612954
You normally do this by calling CreateFontIndirect and then getting the system to render. Perhaps you could explain why you can't use this standard approach.
Upvotes: -2