For Windows, you can use [DirectWrite][1].
**For Windows Vista with SP2 and more**
```c++
#include <iostream>
#include <list>
#include <vector>
#include <atlbase.h>
#include <string.h>
#include <dwrite.h>
#pragma comment(lib, "dwrite.lib")
std::wstring get_file_path_from_dwrite_font_file(CComPtr<IDWriteFontFile> font_file)
{
HRESULT hr;
LPCVOID font_file_reference_key;
UINT32 font_file_reference_key_size;
hr = font_file->GetReferenceKey(&font_file_reference_key, &font_file_reference_key_size);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetReferenceKey.");
}
CComPtr<IDWriteFontFileLoader> loader;
hr = font_file->GetLoader(&loader);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetLoader.");
}
CComPtr<IDWriteLocalFontFileLoader> local_loader;
hr = loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), reinterpret_cast<void**>(&local_loader));
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling QueryInterface.");
}
UINT32 path_length;
hr = local_loader->GetFilePathLengthFromKey(font_file_reference_key, font_file_reference_key_size, &path_length);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetFilePathLengthFromKey.");
}
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))
{
throw std::runtime_error("An error has occured when calling GetFilePathFromKey.");
}
return path;
}
bool is_font_match_postscript_name(CComPtr<IDWriteFont> font, std::wstring requested_postcript_name)
{
HRESULT hr;
CComPtr<IDWriteLocalizedStrings> informational_strings;
BOOL exists;
hr = font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME, &informational_strings, &exists);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetInformationalStrings.");
}
if (!exists) {
return false;
}
for (UINT32 i = 0; i < informational_strings->GetCount(); i++)
{
UINT32 string_length;
hr = informational_strings->GetStringLength(i, &string_length);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetStringLength.");
}
std::wstring font_postscript_name(string_length + 1, L'\0');
hr = informational_strings->GetString(i, &font_postscript_name[0], string_length + 1);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetString.");
}
if (_wcsicmp(font_postscript_name.c_str(), requested_postcript_name.c_str()) == 0) {
return true;
}
}
return false;
}
std::list<std::wstring> get_fonts_from_postscript_name(const std::wstring& postscript_name)
{
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))
{
throw std::runtime_error("An error has occured when calling DWriteCreateFactory.");
}
CComPtr<IDWriteFontCollection> sys_collection;
hr = dwrite_factory->GetSystemFontCollection(&sys_collection, true);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetSystemFontCollection.");
}
for (UINT32 i = 0; i < sys_collection->GetFontFamilyCount(); i++)
{
CComPtr<IDWriteFontFamily> font_family;
hr = sys_collection->GetFontFamily(i, &font_family);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetFontFamily.");
}
for (UINT32 j = 0; j < font_family->GetFontCount(); j++)
{
CComPtr<IDWriteFont> font;
hr = font_family->GetFont(j, &font);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetFont.");
}
if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE)
{
continue;
}
if (is_font_match_postscript_name(font, postscript_name)) {
CComPtr<IDWriteFontFace> font_face;
hr = font->CreateFontFace(&font_face);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling CreateFontFace.");
}
UINT32 file_count;
hr = font_face->GetFiles(&file_count, NULL);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetFiles.");
}
std::vector<CComPtr<IDWriteFontFile>> font_files(file_count);
hr = font_face->GetFiles(&file_count, reinterpret_cast<IDWriteFontFile**>(font_files.data()));
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetFiles.");
}
for (int i = 0; i < file_count; i++)
{
std::wstring font_filename = get_file_path_from_dwrite_font_file(font_files[i]);
fonts_filename_list.push_back(font_filename);
}
}
}
}
return fonts_filename_list;
}
int main() {
std::list<std::wstring> fonts_filename_list = get_fonts_from_postscript_name(L"ArialNarrow");
for (std::wstring font_filename : fonts_filename_list)
{
std::wcout << L"Font filename: " << font_filename << std::endl;
}
return 0;
}
```
**For Windows 10 and more**
```c++
#include <iostream>
#include <list>
#include <vector>
#include <atlbase.h>
#include <dwrite.h>
#include <dwrite_3.h>
#pragma comment(lib, "dwrite.lib")
std::wstring get_file_path_from_dwrite_font_file(CComPtr<IDWriteFontFile> font_file)
{
HRESULT hr;
LPCVOID font_file_reference_key;
UINT32 font_file_reference_key_size;
hr = font_file->GetReferenceKey(&font_file_reference_key, &font_file_reference_key_size);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetReferenceKey.");
}
CComPtr<IDWriteFontFileLoader> loader;
hr = font_file->GetLoader(&loader);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetLoader.");
}
CComPtr<IDWriteLocalFontFileLoader> local_loader;
hr = loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), reinterpret_cast<void**>(&local_loader));
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling QueryInterface.");
}
UINT32 path_length;
hr = local_loader->GetFilePathLengthFromKey(font_file_reference_key, font_file_reference_key_size, &path_length);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetFilePathLengthFromKey.");
}
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))
{
throw std::runtime_error("An error has occured when calling GetFilePathFromKey.");
}
return path;
}
std::list<std::wstring> get_fonts_from_postscript_name(const std::wstring& postscript_name)
{
std::list<std::wstring> fonts_filename_list;
HRESULT hr;
CComPtr<IDWriteFactory3> dwrite_factory;
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory3), reinterpret_cast<IUnknown**>(&dwrite_factory));
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling DWriteCreateFactory.");
}
CComPtr<IDWriteFontSet> font_set;
hr = dwrite_factory->GetSystemFontSet(&font_set);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetSystemFontSet.");
}
DWRITE_FONT_PROPERTY font_property = { DWRITE_FONT_PROPERTY_ID_POSTSCRIPT_NAME, postscript_name.c_str(), L"" };
CComPtr<IDWriteFontSet> filtered_font_set;
hr = font_set->GetMatchingFonts(&font_property, 1, &filtered_font_set);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetMatchingFonts.");
}
for (UINT32 i = 0; i < filtered_font_set->GetFontCount(); i++)
{
CComPtr<IDWriteFontFaceReference> font_face_reference;
hr = filtered_font_set->GetFontFaceReference(i, &font_face_reference);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetMatchingFonts.");
}
CComPtr<IDWriteFontFile> font_file;
hr = font_face_reference->GetFontFile(&font_file);
if (FAILED(hr))
{
throw std::runtime_error("An error has occured when calling GetMatchingFonts.");
}
std::wstring font_filename = get_file_path_from_dwrite_font_file(font_file);
fonts_filename_list.push_back(font_filename);
}
return fonts_filename_list;
}
int main() {
std::list<std::wstring> fonts_filename_list = get_fonts_from_postscript_name(L"BaloneKids");
for (std::wstring font_filename : fonts_filename_list)
{
std::wcout << L"Font filename: " << font_filename << std::endl;
}
return 0;
}
```
I don't have a macOS machine, but here is the idea.
1. [CFDictionaryCreate][2] with [kCTFontNameAttribute][3]
2. [CTFontDescriptorCreateWithAttributes][4]
3. [CTFontDescriptorCreateMatchingFontDescriptors][5]
4. [CTFontCollectionCreateMatchingFontDescriptors][6]
5. [CTFontDescriptorCopyAttribute][7] with [kCTFontURLAttribute][8]
[1]: https://learn.microsoft.com/en-us/windows/win32/directwrite/direct-write-portal
[2]: https://developer.apple.com/documentation/corefoundation/1516782-cfdictionarycreate
[3]: https://developer.apple.com/documentation/coretext/kctfontnameattribute
[4]: https://developer.apple.com/documentation/coretext/1510642-ctfontdescriptorcreatewithattrib
[5]: https://developer.apple.com/documentation/coretext/1508794-ctfontdescriptorcreatematchingfo
[6]: https://developer.apple.com/documentation/coretext/1511091-ctfontcollectioncreatematchingfo
[7]: https://developer.apple.com/documentation/coretext/1510346-ctfontdescriptorcopyattribute
[8]: https://developer.apple.com/documentation/coretext/kctfonturlattribute
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**
```c++
#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**
```c++
#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][1]. To do so, call [IDWriteFontFileLoader::CreateStreamFromKey][2], then call [IDWriteFontFileStream::ReadFileFragment][3].
[1]: https://gitlab.freedesktop.org/freetype/freetype/-/issues/1098
[2]: https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritefontfileloader-createstreamfromkey
[3]: https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritefontfilestream-readfilefragment