I'm setting lf.lfFaceName[0] = '\0';
and lf.lfCharSet = DEFAULT_CHARSET;
to enumerate uniquely font names installed on system but I'm still getting duplicates. What am I missing? I'm getting duplicates like this:
font-name: [Cascadia Mono]
font-name: [Cascadia Mono]
font-name: [Cascadia Mono]
font-name: [Cascadia Mono]
font-name: [Cascadia Mono]
font-name: [Cascadia Mono]
font-name: [Cascadia Mono]
font-name: [Cascadia Mono SemiBold]
font-name: [Cascadia Mono SemiBold]
font-name: [Cascadia Mono SemiBold]
font-name: [Cascadia Mono SemiBold]
font-name: [Cascadia Mono SemiBold]
font-name: [Cascadia Mono SemiBold]
font-name: [Cascadia Mono SemiBold]
I'm enumerating like this:
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "Comctl32.lib")
#pragma comment(lib, "Gdi32.lib")
#pragma comment(lib, "UxTheme.lib")
#pragma comment(lib, "Comdlg32.lib")
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam);
int CALLBACK enumFontsCallback(const LOGFONT *lpelfe,
const TEXTMETRIC *lpntme,
DWORD FontType,
LPARAM lParam)
{
wprintf(L"font-name: [%s]\r\n", lpelfe->lfFaceName);
return 1;
}
void list()
{
LOGFONT lf = {0};
lf.lfWeight = FW_DONTCARE;
lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
lf.lfQuality = DEFAULT_QUALITY;
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf.lfPitchAndFamily = FF_DONTCARE;
lf.lfFaceName[0] = '\0';
HDC dc = GetDC(NULL);
EnumFontFamiliesEx(dc, &lf, (FONTENUMPROC)enumFontsCallback, 0, 0);
ReleaseDC(NULL, dc);
}
int main()
{
list();
return 0;
}
The reason for the duplications is given in the docs under remarks: "EnumFontFamiliesEx
will enumerate the same font as many times as there are distinct character sets in the font. [...] To avoid this, an application should filter the list of fonts".
To filter the list down to unique font names, the LPARAM
of the callback can be used to build a running list of previously encountered font names, and skip over duplicates.
The EnumFontFamiliesEx
call would need to be changed to something like the following.
unordered_set<wstring> wsFonts;
EnumFontFamiliesEx(dc, &lf, (FONTENUMPROC)enumFontsCallback, (LPARAM)&wsFonts, 0);
The callback could then check the current font name against the list.
wstring wsFont = lpelfe->lfFaceName;
if(((unordered_set<wstring> *)lParam)->insert(wsFont).second)
wcout << L"font-name: " << wsFont << endl;
The above assumes C++ for the convenience of std::unordered_set
, but could of course be written into plain C using a handcrafted list of unique strings.