Click here to Skip to main content
Click here to Skip to main content

XMonoFontDialog - Customizing CFontDialog Part II: Selecting Monospaced Fonts

, 22 Oct 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
XMonoFontDialog is a CFontDialog clone that is customized for selecting monospaced fonts. A custom label has been added to the dialog that informs the user when a monospaced font is selected in the combobox, and bold typeface is used to highlight monospaced fonts in the combobox list.

Introduction

I have been using my XFontDialog for quite a while, whenever the application required a monospaced font. To flag the fonts that are monospaced, I display a blue MONOSPACED above the font list, when a monospaced font is selected in the combobox. This has worked OK, but I couldn't help noticing that Visual Studio flagged monospaced fonts by displaying them in bold in the font combobox. In VS6, the monospaced fonts are displayed in bold at the top of the combobox list - non-monospaced fonts after them. In VS2005 and VS2008, monospaced fonts are also displayed in bold, but are not separated from other fonts - all the fonts are in alphabetical order.
The story behind all this is a curious one, and it all comes down to what the definition of monospaced font is. I know that I have my own definition, but I was surprised to find that professional font designers cannot agree on such a basic thing.

There are certain flags that can be set in the tables that are contained in a font file, that specify whether the font is monospaced. Many font designers use a free font utility called FontForge when creating fonts. George Williams, the author, has built into FontForge rules which reflect his understanding of the TrueType and OpenType specifications. One of these rules basically says, "The font will be marked as monospaced only if all the glyphs are the same width". However, Unicode requires that some characters have zero width. These are characters that are either non-spacing combining marks or zero-width control characters. Examples: NUL, CR, zero-width space, zero-width joiner, zero-width non-joiner.

Furthermore, the font specification itself (see here) requires that certain characters be mapped to "glyph 1", which the spec says must have zero width. This means that even a monospaced font - made according to this specification - will have at least this zero-width glyph in it. So, it seems reasonable to say that zero-width glyphs should not be considered when determining if the font is monospaced.

However, if any zero-width glyph is present in the font, FontForge refuses to mark it as monospaced, because (in Williams' view) the font has glyphs with different widths. Hence there are fonts being created that are clearly monospaced for all practical purposes, but which Windows does not flag as monospaced, because the font file is not marked as such.

Not all font designers agree with this, and they prefer to define a monospaced font as one in which all glyphs with a non-zero width have the same width. My own definition of a monospaced font is simpler: if all the display characters are the same width, it's monospaced.

[Note: I realize that font designers have alternative tools if they really want to mark a font as monospaced, and that FontForge is not the only reason for this situation.]

When I took a closer look, I realized that Visual Studio relies on information stored in the font file at the time the font is created, concerning whether the font is monospaced. Programmatically, you can check if font is monospaced (fixed pitch, aka fixed width) by testing the flag bit TMPF_FIXED_PITCH that is returned in TEXTMETRIC structure member tmPitchAndFamily. However, there is no requirement that the font must be marked as being monospaced, and Visual Studio incorrectly reports the pitch of some fonts:

screenshot

Since information in font file is not reliable, how can you be certain that a font is monospaced? After researching font file data structures, I came to conclusion that there is only one reliable method: to compare width of uppercase 'W' with width of '!' character. In normal fonts, these will always be different, but in monospaced fonts, they will always be same (obviously, other character pairs would also work, but these are what I chose).

Demo Application

This is what XMonoFontDialog looks like:

screenshot

Both the blue MONOSPACED label and the use of bold typeface for monospaced fonts can be controlled programmatically.

I took demo app from my XFontDialog article and modified it to allow for additional features:

screenshot

XMonoFontDialog Features

Here are the features in XMonoFontDialog:
screenshot Similar to standard CFontDialog but with only font list, font sizes, and sample text controls.
screenshot Same basic APIs as CFontDialog
screenshot Font filters to select specific groups of fonts to display
screenshot Optional visual indication of monospaced fonts (blue MONOSPACED label)
screenshot Optional use of bold typeface for monospaced fonts in combobox list
screenshot APIs to check if selected font is monospaced, symbol, OpenType, or TrueType
screenshot API to set caption of font dialog
screenshot API to set sample text of font dialog
screenshot More detailed information returned about selected font

Implementation Notes

Because I wanted to display fonts in bold typeface, I knew I could not use same technique I use in XFontDialog, where I created a custom template for use with CFontDialog. Here I needed to control the item drawing in the font combobox, which I could not do with CFontDialog. For this reason, I decided to leave XFontDialog alone, since it could still serve as an example of how to use a custom template with CFontDialog.

NEWTEXTMETRIC struct

typedef struct tagNEWTEXTMETRIC { 
    LONG   tmHeight; 
    LONG   tmAscent; 
    LONG   tmDescent; 
    LONG   tmInternalLeading; 
    LONG   tmExternalLeading; 
    LONG   tmAveCharWidth; 
    LONG   tmMaxCharWidth; 
    LONG   tmWeight; 
    LONG   tmOverhang; 
    LONG   tmDigitizedAspectX; 
    LONG   tmDigitizedAspectY; 
    TCHAR  tmFirstChar; 
    TCHAR  tmLastChar; 
    TCHAR  tmDefaultChar; 
    TCHAR  tmBreakChar; 
    BYTE   tmItalic; 
    BYTE   tmUnderlined; 
    BYTE   tmStruckOut; 
    BYTE   tmPitchAndFamily; 
    BYTE   tmCharSet; 
    DWORD  ntmFlags;
    UINT   ntmSizeEM; 
    UINT   ntmCellHeight; 
    UINT   ntmAvgWidth; 
} NEWTEXTMETRIC, *PNEWTEXTMETRIC;

As it turned out, there were other good reasons to abandon the custom template approach. Since I was going to be doing all the drawing for the font combobox, I needed to know not just whether a font was monospaced, but also whether it was TrueType, OpenType, or something else, so I could draw the correct glyph for the font type. In another article, I looked up the font in the registry, found the filepath for the font file, opened the font file, and looked for certain tables in the file, to determine if the font was TrueType or OpenType. I discovered that all of this would be unnecessary as soon as I started to write the code to fill the combobox with font names.

The standard way to get the list of fonts installed on a system is to use the Win32 EnumFonts function. The callback function provides you with a LOGFONT struct and a TEXTMETRIC struct, which give you all the standard font information. However, neither of these structs tell you anything about whether the font is TrueType or OpenType. The good news is that there is another API: EnumFontFamiliesEx(), which tells you everything you want to know about a font. The callback function returns a NEWTEXTMETRIC struct, which is a superset of the TEXTMETRIC struct, and which is only returned via the EnumFontFamiliesEx() callback function - there is no other way to obtain it, as far as I can determine. Its ntmFlags member contains flags that specify whether the font is OpenType, and you can use that in combination with other information returned via the callback function to determine exactly what type of font it is. This is a major improvement over scanning font files. Now I knew whether a font was OpenType, TrueType, or something else, and I could compare two character widths (as I described above) to see if the font was monospaced.

The only remaining thing I needed was the TrueType (screenshot) and OpenType (screenshot) glyphs. In another article I loaded these glyphs from bitmap resources, but this time I knew a secret: the glyphs could be dynamically extracted from bitmap resource #38 in COMDLG32.DLL:

screenshot

So this is what I did, and set up a CImageList so the glyphs can be drawn directly in the combobox list.

When a font is selected in the font combobox, a selection message gets sent to the parent dialog, which then enumerates the sizes for that font, and fills the size combobox. (This enumeration only happens for fonts that are not TrueType, OpenType, or vector; for these three font types, a "standard" set of 16 sizes is used, ranging from 8 to 72 points.)

How To Use

To integrate CXMonoFontDialog into your app, you first need to add following files to your project:

  • XMonoFontDialog.cpp
  • XMonoFontDialog.h
  • XFontSize.cpp
  • XFontSize.h
  • XMonoFontListCombo.cpp
  • XMonoFontListCombo.h
  • XMonoFontListComboEdit.cpp
  • XMonoFontListComboEdit.h
  • XMonoFontDialog.rc
  • XMonoFontDialogRes.h

You also need to add XMonoFontDialog.rc to project rc file - go to View | Resource Includes... and in the bottom listbox, scroll down to the end. Insert #include "XMonoFontDialog.rc" right before the #endif:

screenshot

Next, include header file XMonoFontDialog.h in appropriate project files. Now you are ready to start using CXMonoFontDialog. The file XMonoFontDialogTestDlg.cpp gives an example:

    CXMonoFontDialog dlg(&lf);
    dlg.SetFontFilter(dwFonts)
       .SetCaption(_T("XMonoFontDialog"))
       .SetSampleText(m_strSampleText)
       .ShowMonospacedLabel(m_bShowMonospacedLabel)
       .ShowMonospacedAsBold(m_bShowMonospacedAsBold);

    if (dlg.DoModal() == IDOK)
    .
    .
    .

Revision History

Version 1.1 - 2008 October 29

  • Fixed problem with small point sizes.

Version 1.0 - 2008 October 23

  • Initial public release.

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.

License

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

Share

About the Author

Hans Dietrich
Software Developer (Senior) Hans Dietrich Software
United States United States
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.
 
Recently, I have moved to Los Angeles where I am doing consulting and development work.
 
For consulting and custom software development, please see www.hdsoft.org.






Comments and Discussions

 
BugThere's a missing bitmap resource for the glyphs (at least in Win 7 SP1 x64) [modified] Pinmemberfwcetus30-Jul-14 16:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141030.1 | Last Updated 23 Oct 2008
Article Copyright 2008 by Hans Dietrich
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid