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