Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WTL

Font Enumerator

5.00/5 (10 votes)
1 Sep 2009CPOL7 min read 37.3K   3.1K  
A tool that enumerates all fonts installed on Windows, and shows you the preview so as to help you code conveniently.

FontEnumerator1.gif

Introduction

Sometimes, we have to deal with fonts for some reason. While I was studying fonts through a book "WIN API Conquer" written by KIM Sanghyung in Korean, I found a good sample code. I used it as a utility, but I found that it did not have all the functions I wanted. It only enumerated up to 500 fonts and showed the parameters of each font. That's all. So, I improved that sample code. The original code was written using pure Win32 API but I changed its framework into WTL and got rid of its enumerating limitation of up to 500 fonts by using the STL vector, and added new functions I wanted so that the smell of the original has almost gone.

Purpose of this small tool

In order to use fonts, we need to see what the font looks like, not only as itself but also with parameters changed such as height, italic effect, orientation, etc. If we are developing a program, we often have to create fonts with desirable or predetermined parameters. In that case, this program will be helpful in saving your time. This is a small tool that:

  1. enumerates all the fonts installed on Windows,
  2. allows you to choose one of them and to change some parameters which will be filled in the structure LOGFONT in order to create a font,
  3. shows you the preview of a font with the changed parameters.

So, you can try several possible fonts easily by changing parameters and eventually choose the best one for you. It will also put the necessary code to create fonts on the Clipboard for you so that you can just paste it on your IDE editor.

How to use the Font Enumerator and How it works

How to enumerate fonts and see the corresponding parameters of the font you chose

When you execute FontEnumerator.exe, it firstly enumerates all the fonts installed on Windows. On Korean Windows, all the messages will be shown in Korean while those will be in English on non-Korean Windows. You will find them on the list window on the left. If you click one of them, all the parameters to be used in the LOGFONT structure will be shown on the right. On the bottom, you can see the preview.

How to try on a font with different parameters

You can change the parameters in the edit windows or check boxes and then press the Enter-key or click the bottom preview window to see the previews of the result. Checking the state of a check box (except for "height") means that its factor will affect the font. For example, if you check the check box for italic, the font will be italic. Height uses the unit "twips", but we often use the unit "point". You can use the unit point by checking the check box for height. In reality, we more often use the unit of point rather than twips. So, if you click the check box "height", the parameter name will be changed into point and the value of the width will be 0, which means the proper default value according to the height value. Once you change the parameters, click the bottom window or press the Enter-key. Then, you will see the changed font shape.

How to use the menu

You can use the customised system menu by clicking the top-left icon or by right-clicking the title bar. Then, a normal system menu with some new menu items will pop up. I'm using Korean Windows so the system menu items are shown in Korean but it will be shown in your language on your computer. The added menu items will be shown in Korean on Korean Windows, and in English on non-Korean Windows. However, for explanation, I temporarily forced it to show all in English even on Korean Windows.

Image 2

Re-enumerate fonts

If you select the menu item 'Re-enumerate fonts', it will enumerate fonts again. The result will be the same as before.

Change the sample string

If you click the menu item 'Change the sample string', you will have the chance to change the sample string as shown below. If you change the sample string in the edit window, you will have a different sample string, but it would not be saved on disk, so the next time you execute this program, it will be initialized to the original value.

Image 3

Copy the source code for creating a font in the Clipboard

Finally, if you choose the menu item 'Copy the source code for creating font to clipboard', you will have five choices. ::CreateFontIndirect(...) and ::CreateFont(...) are for Win32 API functions. CFont::CreateFontIndirect(...) and CFont::CreateFont(...) are for WTL or MFC. font(...) is for GDI+. For example, if you choose CFont::CreateFontIndirect(...) and paste on your IDE editor, you will get the following code:

C++
LOGFONT lf;
lf.lfHeight = 36;
lf.lfWidth = 14;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = FW_NORMAL;
lf.lfItalic = FALSE;
lf.lfUnderline = FALSE;
lf.lfStrikeOut = FALSE;
lf.lfCharSet = GREEK_CHARSET;
lf.lfOutPrecision = OUT_STROKE_PRECIS;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf.lfQuality = PROOF_QUALITY;
lf.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
_tcscpy(lf.lfFaceName, _T("Arial"));
CFont   font;
font.CreateFontIndirect(&lf);

About...

If you click the menu item 'About...', the About box will pop up.

Using the code

Using the class OSL

The class OSL is defined in stdafx.h as follows:

C++
typedef WindowsHelper::OSLang<>    OSL;

The template class OSLang<int mode> is defined in the namespace WindowsHelper in WindowsHelper.hpp as follows:

C++
template<int mode = -1>
struct OSLang
{
    WORD        m_Lang;

    OSLang(void)
    {
        m_Lang = (mode < 0) ? GetSystemDefaultLangID() : mode;
    }

    OSLang(HFONT& hFont, int point)
    {
        m_Lang = (mode < 0) ? GetSystemDefaultLangID() : mode;
        HDC    hDC = ::GetDC(NULL);
        int height = -::MulDiv(point, GetDeviceCaps(hDC, LOGPIXELSY), 72);
        ::ReleaseDC(NULL, hDC);
        switch (PRIMARYLANGID(m_Lang))
        {
        case LANG_KOREAN:
            hFont = ::CreateFont(height, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
                                    HANGUL_CHARSET, 
                                    OUT_STROKE_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
                                    VARIABLE_PITCH | FF_MODERN, _T("¸¼Àº °íµñ"));
            break;
        default:
            hFont = ::CreateFont(height, 0, 0, 0, FW_NORMAL, FALSE, FALSE,
                                    FALSE, ANSI_CHARSET, 
                                    OUT_STROKE_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
                                    VARIABLE_PITCH | FF_SWISS, _T("Calibri"));
            break;
        }
    }

    inline operator WORD ()
    {
        return PRIMARYLANGID(m_Lang);
    }

    inline char getSubLang(void)
    {
        return SUBLANGID(m_Lang);
    }
};

In its constructor, it determines the system language and creates a font according to the language. If you set the font for your program, you can avoid variations on your program from variation in the system. I categorized system languages into two: Korean and non-Korean. You can do as you wish. There are two global variables: _Font and _Lang. These global variables affect the whole program.

Setting a font for a program

In WindowsHelper.hpp, a set of font setting functions are defined as follows:

C++
typedef std::vector<HWND>    HWNDS;

struct SetFontData
{
    HFONT        hFont;
    HWNDS*       phWndExcepts;
};

template<bool bInvalidate>
BOOL CALLBACK EnumChildProc(HWND hWnd, LPARAM lParam)
{
    HWNDS*        excepts = ((SetFontData*) lParam)->phWndExcepts;
    for (HWNDS::iterator it = excepts->begin(); it != excepts->end(); it++)
    {
        if (hWnd == *it)
        {
            excepts->erase(it);
            return TRUE;
        }
    }
    ::SendMessage(hWnd, WM_SETFONT, (WPARAM) (((SetFontData*) lParam)->hFont), TRUE);
    if (bInvalidate)
    {
        //RECT    rc;
        //::GetClientRect(hWnd, &rc);
        //::InvalidateRect(hWnd, &rc, TRUE);
        ::InvalidateRect(hWnd, NULL, TRUE);
    }
    return TRUE;
}

template<bool bInvalidate>

void setFont(HFONT hFont, HWND hWndParent, HWNDS& hWndExcepts)
{
    SetFontData        data;
    data.hFont = hFont;
    data.phWndExcepts = &hWndExcepts;
    ::SendMessage(hWndParent, WM_SETFONT, (WPARAM) hFont, TRUE);
    ::EnumChildWindows(hWndParent, &EnumChildProc, (LPARAM) &data);
}

template<bool bInvalidate>
void setFont(HFONT hFont, HWND hWndParent, HWND hWndExcept = NULL)
{
    HWNDS        hWnds;
    if (hWndExcept)
        hWnds.push_back(hWndExcept);
    setFont(hFont, hWndParent, hWnds);
}

The sample window should not be set to a certain font so that in CMainDlg::OnInitDialog(...) in MainDlg.cpp, a certain font is set as follows:

C++
WindowsHelper::setFont<true>(_Font, this->m_hWnd, m_wndButtonExample);

Controlling the order of the font list

I don't want to show the @~ series fonts first in the font list window. So, a comparison function is given to the last argument to the function sort(...) when the font list is sorted in CMainDlg::enumerateFonts(), as follows:

C++
std::sort(m_FontVec.begin(), m_FontVec.end(), comp);

The function comp(..) is defined as follows:

C++
extern "C" bool comp(FontPair& left, FontPair& right)
{
    if ((_T('@') != left.first.lfFaceName[0]) && (_T('@') == right.first.lfFaceName[0]))
        return true;
    if ((_T('@') == left.first.lfFaceName[0]) && (_T('@') != right.first.lfFaceName[0]))
        return false;
    return (_tcscmp(left.first.lfFaceName, right.first.lfFaceName) < 0);
}

It makes sort(...) to determine the names of the @~ series of fonts bigger than normal fonts so that in the list, @~ fonts come later than normal fonts.

std::tstring

std::tstring is not a type of STL but a type defined depending on if the encoding setting is UNICODE or ANSI in the namespace std. It is defined in stlhelper.hpp as follows:

C++
#ifdef _STRING_
#ifndef _STRING_STLHELPER_D4459FC7_15A3_4C5E_8CD8_15BF92D97BF3
#define _STRING_STLHELPER_D4459FC7_15A3_4C5E_8CD8_15BF92D97BF3
#ifdef UNICODE
    typedef     wstring     tstring;
#else
    typedef     string      tstring;
#endif  // UNICODE
#endif  // _STRING_STLHELPER_D4459FC7_15A3_4C5E_8CD8_15BF92D97BF3
#endif  // _STRING_

Environment

This application was created with VC++ 2008 Express SP1, WTL, and ATL7.1. It has been tested only on Korean Windows XP Home Edition SP3 and Russian Windows XP Professional.

Acknowledgement

I want to thank Michael Dunn for his excellent articles on WTL for MFC programmers. I learned a lot about WTL from his articles. His articles can be found here. I want to also thank Sergey Solozhentsev for his great work on the WTL Helper and the WTL Wizards. You can get his WTL Helper here and here, and its manual here and here. Do not be confused. His WTL Wizards are different from the normal WTL App Wizard which can be installed with setup71.js, for instance. His WTL Wizards support a split window framework as well. You can also get his WTL Wizards here and its manual here. It provides me with a lot of convenience when I code using WTL. Most of all, I really appreciate God and my wife Nura. He gave me her, and she is always on my side and is my firm supporter.

History

  • 24, July, 2009 -- Version 1.0.
  • 15, August, 2009 -- Version 1.1
    • Point unit for font height added, private fonts are now used, and fixed a bug of improperly generating source code for font creation.
  • 23, August, 2009 -- Version 1.1.1
    • Fixed a bug of memory access violation.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)