Categories
c c++ fonts winapi windows

Too many fonts when enumerating with EnumFontFamiliesEx function

I’m trying to create a list of fonts for the user to choose from. I’m doing this by using the EnumFontFamiliesEx function but unfortunately, the list of returned fonts is much too long. There are many extra fonts that seem frivolous, duplicate, for a different language, or otherwise undesirable to display to the user. My screenshot best illustrates the junk I am trying to filter out.

My code for calling EnumFontFamiliesEx looks like this:

LOGFONT lf;
memset(&lf, 0, sizeof(lf));
lf.lfCharSet = DEFAULT_CHARSET;
// screenDC is result of CreateCompatibleDC(NULL)
EnumFontFamiliesEx(screenDC, &lf, GetFontsCallback, NULL, 0);

The resulting list looks like this, after sorting alphabetically and removing fonts with duplicate face names:

enter image description here

As you can see, the ChooseFont font common dialog is displaying a very reasonable list of fonts that is user-friendly and makes sense. On the other hand, my code displays a long list of extra fonts: fonts that start with “@” (why? what are they even for?), 3 extra variants of Arial font, and several other fonts of unknown purpose like Aheroni, Andalus, Angsana New, AngsanaUPC, and so on. It’s insane.

How do I filter the list of fonts returned by EnumFontFamiliesEx so that it exactly matches the list shown in the ChooseFont dialog?

Thanks to Jesse Good, I have now learned about some insane unfortunate design decisions made by the Windows 7 team. I won’t accept my own answer yet, because if someone else comes up with a way of using this hidden font feature in Windows 7 even when the registry key doesn’t exist yet (e.g. perhaps by using the undocumented API, or some other trickery) and their answer works, I will accept it.

This filtering is done by actually “hiding” fonts in Windows 7 Control Panel. By default, fonts for other locales are hidden but they can be shown by the user. At least, this is the idea. Here is the MSDN page discussing this feature: International Font Management.

Here are some key excerpts from this page and other nearby pages in MSDN (also see http://msdn.microsoft.com/en-us/library/windows/desktop/dd371704(v=vs.85).aspx from the Windows 7 compatibility cookbook):

Starting with Windows 7, the font management infrastructure supports the hiding of fonts which are not appropriate for a user’s font selection lists. … This feature means users need no longer be faced with long lists of inappropriate fonts.

In Windows 7, there are no APIs for directly querying which fonts are hidden, or for setting fonts to be hidden. [emphasis mine] If you use the Windows ChooseFont API (Font common dialog) to enable font selection today, you will get the new behavior for free. The new Windows Scenic Ribbon (Font Controls) introduced in Windows 7 also supports this behavior and provides another reason to “Ribbonize” your applications.

When a font is selected into a device context, there is no effect on drawing due to the font being hidden. The EnumFontFamiliesEx function continues to enumerate fonts that are set to hidden. [emphasis mine; there is apparently no way to differentiate hidden and visible fonts with EnumFontFamiliesEx]

Note that charsets are a legacy notion corresponding to pre-Unicode character sets. [emphasis mine]

ChooseFont will only list the shown fonts and filter out the hidden fonts while displaying fonts in the list box. The additional flag (CF_INACTIVEFONTS) in the flags member of the CHOOSEFONT structure is added to allow you to display all the installed fonts in the font list, the same as ChooseFont behaved before Windows 7.

So in other words, unless you use the ChooseFont common dialog or the official Windows ribbon control (only available on Windows Vista/7), you have no supported way at all of filtering out hidden fonts. Is it any surprise or wonder that many users on the Internet are complaining that hiding fonts in the Windows 7 Control Panel seems to have no effect?!? (I previously false posted that MS Word 2010 filters out hidden fonts. It appears it does not, because they use their own custom ribbon control and not the ribbon built into Windows. It is amusing that the Windows 7 Font Control Panel, by design, is not compatible with one of Microsoft’s flagship products and cannot be made compatible without dumping the more powerful ribbon in Office.)

Based on the the link that Jesse Good posted, I learned that the hidden fonts are stored in an undocumented registry key. Through this link, and also some experimentation and analysis with Process Monitor (looking at both stack traces and registry accesses), I learned the following:

  • The ribbon control calls an undocumented function called FmsGetFilteredFontList in FMS.DLL (Font Management Services). Its purpose appears quite obvious. It’s a real shame they couldn’t be bothered to publicly document and maintain it.
  • The settings are stored in an undocumented registry key, which is accessed by FMS.DLL.
  • If the registry key is deleted, it is recreated with default settings by FmsGetFilteredFontList, which are to hide fonts that are not related to the current input languages.
  • A brand new user profile created on a clean installation of Windows does NOT contain any registry keys related to what fonts should be hidden.

Therefore, the link posted by Jesse Good might work for many/most cases, but not 100% of the time. You need a way to reliably recreate these registry keys (or at least assume defaults) if they don’t exist. The default behavior is still to hide some fonts, even if the registry keys are gone (e.g. on a new user profile).