Fixed-width Font Enumeration


5 minute read  • 

win32

Enumerating fonts can be a little confusing, and unless you want to enumerate all fonts on your system, can be a little more difficult than MSDN suggests. This article will explain exactly the steps you need to use to find every fixed-width font on your system, and also enumerate every possible size for each individual font.

Enumerate fonts

The best function to use for font enumeration is EnumFontFamiliesEx. You must set up a LOGFONT structure which tells EnumFontFamilesEx what fonts you wish to enumerate. You only need to specify three members of this structure.

The lfCharSet member indicates which character set you want to select. The DEFAULT_CHARSET value indicates that all character sets (i.e. OEM, ANSI, Chinese etc) will be enumerated. You could always specify ANSI_CHARSET, but in this case we want OEM character sets as well.

The lfPitchAndFamily member describes the look of a font in a general way. In this case, we don’t mind what the font will look like FF_DONTCARE, because we want to enumerate all fonts anyway, right? The FIXED_PITCH value just tells Windows that we are only interested in fixed-width fonts.

Finally, the lfFaceName member is used to tell windows what font we want to enumerate. If you specify the name of a string here, then Windows will enumerate the available font sizes for that font. First of all though, we want to enumerate the font names themselves, so we will just set this to an empty string. Here’s the code.

int EnumFixedFonts(void)
{
    LOGFONT logfont;

    ZeroMemory(&logfont, sizeof logfont);    

    logfont.lfCharSet = DEFAULT_CHARSET;
    logfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;

    lstrcpy(logfont.lfFaceName, "\0");

    hdc = GetDC(0);

    EnumFontFamiliesEx(hdc, &logfont, (FONTENUMPROC)FontNameProc, 0, 0);

    ReleaseDC(0, hdc);

    return 0;
}

Before you can use this call though, you must write a callback function which Windows will call during the font enumeration. This callback procedure is called once for every font windows finds. Bear in mind that this procedure could be called several times for the same font, once for every character set. This is what the callback function should look like.

int CALLBACK FontNameProc(
    ENUMLOGFONTEX *lpelfe, /* pointer to logical-font data */
    NEWTEXTMETRICEX *lpntme, /* pointer to physical-font data */
    int FontType, /* type of font */
    LPARAM lParam /* a combo box HWND */
    )
{
    int i;

    if(lpelfe->elfLogFont.lfPitchAndFamily & FIXED_PITCH)
    {
        /* Make sure the fonts are only added once */
        for(i = 0; i < curfont; i++)
        {
            if(lstrcmp(currentfonts[i], (char *)lpelfe->elfFullName) == 0)
                return 1;
        }

        printf("%-16s: ", lpelfe->elfFullName);        

        cursize = 0;
        EnumFontSizes((char *)lpelfe->elfFullName);

        printf("\n");

        lstrcpy(currentfonts[curfont], (char *)lpelfe->elfFullName);

        if(++curfont == 200)
            return 0;
    }

    return 1;
}

An important part of the callback function is to check if the font has already been enumerated. In this case we keep a list of all fonts that have been enumerated so far. If the callback function executes and the font is already in the list of “processed fonts”, then the function just returns 1 (or any non-zero value) to continue to the next font.

Now that we have a method to find every fixed-width font, we can enumerate all of the font sizes for each font.

Enumerate font sizes

Font size enumeration is almost the same as enumerating the fonts themselves. The only difference is that we specify the name of each font to get the sizes for. The following function enumerates all available font sizes for the specified font name

int EnumFontSizes(char *fontname)
{
    LOGFONT logfont;

    ZeroMemory(&logfont, sizeof logfont);

    logfont.lfHeight = 0;
    logfont.lfCharSet = DEFAULT_CHARSET;
    logfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;

    lstrcpy(logfont.lfFaceName, fontname);

    EnumFontFamiliesEx(hdc, &logfont, (FONTENUMPROC)FontSizesProc, 0, 0);

    return 0;
}

By looking at the font name callback listed previously, you will see that this font size enumeration is executed for every unique font that we find. This is purely for the purpose of this example, as I wanted to display the available font sizes along side each font. In a normal Windows application, you would probably want to just store the font names in a drop-down list box, and whenever a font was selected from this list, fill up another list box with its available sizes.

int CALLBACK FontSizesProc(
    LOGFONT *plf, /* pointer to logical-font data */
    TEXTMETRIC *ptm, /* pointer to physical-font data */
    DWORD FontType, /* font type */
    LPARAM lParam /* pointer to application-defined data */
    )
{
    static int truetypesize[] = { 8, 9, 10, 11, 12, 14, 16, 18, 20, 
            22, 24, 26, 28, 36, 48, 72 };

    int i;

    if(FontType != TRUETYPE_FONTTYPE)
    {
        int logsize = ptm->tmHeight - ptm->tmInternalLeading;
        long pointsize = MulDiv(logsize, 72, GetDeviceCaps(hdc, LOGPIXELSY));

        for(i = 0; i < cursize; i++)
            if(currentsizes[i] == pointsize)
                return 1;

        printf("%d ", pointsize);

        currentsizes[cursize] = pointsize;

        if(++cursize == 200) return 0;
        return 1;   
    }
    else
    {

        for(i = 0; i < (sizeof(truetypesize) / sizeof(truetypesize[0])); i++)
        {
            printf("%d ", truetypesize[i]);
        }

        return 0;
    }
}

An important thing to note for the font-size callback function (above) is the test to see if the font is true-type or not. If it is, then all that is necessary is to print a list of pre-determined point sizes which are deamed suitable. In this case, the callback will only be executed once for a true-type font. However, if the font is a raster font, then the callback will be executed once for every size that the font supports. It is our job to convert the size of the font from logical units into point sizes, which are generally more useful if they are to be presented to a user.

Thats it. All that is needed now is to start off the enumeration!

int main(void)
{
    EnumFixedFonts();
    return 0;
}

Below is the output of the program on my system, which currently has six fixed-width fonts installed. Notice that three of the fonts are obviously raster fonts, and have a limited number of sizes, whereas the Courier New, Lucida and Andale fonts are true-type.

Terminal : 9 5 6 14 12
Fixedsys : 9
Courier : 10 12 15
Courier New : 8 9 10 11 12 14 16 18 20 22 24 26 28 36 48 72
Lucida Console : 8 9 10 11 12 14 16 18 20 22 24 26 28 36 48 72
Andale Mono : 8 9 10 11 12 14 16 18 20 22 24 26 28 36 48 72