DPI Aware Text and Fonts
4/8/2010
While TrueType fonts scale well, they do not scale linearly. That is, increasing the DPI by 10 percent does not generally increase a string's length by exactly 10 percent. The cumulative effect, especially in longer strings, might cause text in different resolutions to be relatively shorter or to be clipped or to wrap unexpectedly. Therefore, it is important to use the GetTextExtent function instead of making assumptions about how much space a string will take up. For static layouts like dialog you should allocate some extra width for text elements to fit properly in any DPI.
In addition, different fonts exhibit different non-linear scaling effects, which means that the use of multiple fonts on a screen may cause some to appear disproportionate to others in different resolutions.
Creating DPI Aware boxes Fonts
When you create fonts for Windows® phones, you should use points to specify the font size. A point refers to a logical size (1/72 of a logical inch), not a pixel height, so when used correctly it is inherently DPI aware. The following code example shows how to create a DPI-aware font starting with point size.
LOGFONT lf;
memset(&lf, 0, sizeof(lf));
// Set other LOGFONT values as appropriate.
lf.lfHeight = -MulDiv(pointSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
HFONT font = CreateFontIndirect(&lf);
See the discussion of the lfHeight parameter of the LOGFONT structure for more information about this calculation.
Because lf.lfHeight is an integer pixel height, it is subject to rounding differences that are introduced through the MulDiv call in different resolutions. The size of the resulting font might not be linearly scaled from 96 DPI, but it will be the correct approximation of the desired point size. This is the recommended method for creating fonts.
If you need a more exact linear scaling of your font pixel size, you can create a temporary font for 96 DPI, scale its tmHeight text metric, and create the new font based on the resulting value, as shown in the following example. Linearly, the resulting font will be exactly scaled in height according to CDC::GetTextExtent, but it may not be linearly scaled in width and may also be visually taller or shorter.
Note
Even when you use this method to more precisely scale fonts, you should make no assumptions about how much space a string will occupy.
// Create font for 96 DPI.
LOGFONT lf;
memset(&lf, 0, sizeof(lf));
// Set other LOGFONT values as appropriate.
lf.lfHeight = -MulDiv(pointSize, 96, 72);
HFONT hfont = CreateFontIndirect(&lf);
// Scale the tmHeight; create target font.
TEXTMETRIC tm;
HDC hdc = GetDC(target);
HFONT hfontOld = (HFONT)SelectObject(hdc, hfont);
GetTextMetrics(hdc, &tm);
SelectObject(hdc, hfontOld);
DeleteObject(hfont);
ReleaseDC(target, hdc);
lf.lfHeight = SCALEY(tm.tmHeight);
hfont = CreateFontIndirect(&lf);
Getting the User Font Size
Windows Mobile Professional and Windows Mobile Classic have a new Control Panel item (Settings/Screen/Text Size) with which the user can choose the font size that looks best on the device screen. New applications written for Windows Mobile Professional and Windows Mobile Classic should be aware of this and should use the user-selected font size for their primary UI text whenever possible. To retrieve this value, use the SHGetUIMetric function provided with the CrosswordSample code.
LONG dwFontSize = 12;
SHGetUIMetrics(SHUIM_FONTSIZE_PIXEL, &dwFontSize, sizeof(dwFontSize), NULL);
lf.lfHeight = -dwFontSize;
Your application should also be aware when the user font size changes. To receive this notification, your application should call RegisterWindowMessage for the SH_UIMETRIC_CHANGE message. When your application receives this message, it should query SHGetUIMetric to find the new font size and then repaint itself.