Fonts, LogFonts and the Compact Framework (NETCF)

As you may know, much of System.Drawing on the desktop/full framework is based on GDI+.  Because GDI+ is not implemented on PocketPC/Smartphone/WindowsMobile, the functionality of System.Drawing in the Compact Framework is somewhat constrained.

As we were planning and adding features to V2 of the Compact Framework, there were requests for the ability to draw rotated text from managed code (one might like to align street names with the street in a map, for example).  On the desktop, this is done by setting calling Graphics.RotateTransform() prior to calling Graphics.DrawString().  Since SetWorldTransform is not supported in GWES on WindowsCE (and WindowsCE based devices), Graphics.RotateTransform() is not available on the Compact Framework.  So, how would we enable rotated text in the Compact Framework?

The answer is found in the LOGFONT structure and CreateFontIndirect().  Well, actually it's found in Font.FromLogFont() method.

Note #1: On the desktop the Font.FromLogFont comes in two flavors, FromLogFont(object) and FromLogFont(object, IntPtr). This was too painful for the Compact Framework team (what does it mean to call Font.FromLogFont(12)?) so on the Compact Framework the first argument to FromLogFont must be a Microsoft.WindowsCE.Forms.LogFont object even though the method signature matches the desktop: FromLogFont(object).

After you find Font.FromLogFont and Microsoft.WindowsCE.Forms.LogFont, it is straightforward to create a font which includes rotation and will render at an angle when used in a call to Graphics.DrawString().

Note #2a: Escapement and Orientation cannot be set independently and should be set to the same value per MSDN documentation on the LOGFONT structure .

Note #2b: The rotation fields of LOGFONT (and the LogFont class), escapement and orientation, are measured in tenths of degrees - i.e. specifiy 45 degrees of rotation with a value of 450.

Another tidbit to know about fonts on devices is that the device wide setting for ClearType will, if ClearType is enabled, override whatever value you use in the Quality field of your LogFont object.

Note #3: If ClearType is enabled system wide on your device, that setting will override the value in the Quality property of your LogFont object used in FromLogFont().

Finally, one more piece of information from the MSDN documentation is relevant here - the Height property of the LogFont object may be positive or negative.   If the value is positive, e.g. 12, this is matched against the cell height of available fonts.  If the value is negative, e.g. -12, the absolute value of the height is matched against the character height of available fonts.  The standard formula for calculating the value the height property of the LogFont object from point size is the following:

height = -MulDiv(pointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72) where MulDiv(a, b, c) is (((a)*(b))/(c)).

Graphics.DpiY can be substituted for GetDeviceCaps(hDC, LOGPIXELSY) in managed code.

I've attached a basic LogFontExplorer project which allows control over most of the properties of LogFont and the ability to change the system wide ClearType setting from inside the application.  The text rendering is buffered to a bitmap so the difference between quality levels with and without system wide ClearType can be discerned.

Also, just for comparison, here's a snippet of code showing how to do rotated text on the desktop framework (I've included this project in the attachment as well).

Font fnt = new Font("Tahoma", 12.0f);

...

private void Form1_MouseDown(object sender, MouseEventArgs e)
{
using (Graphics g = this.CreateGraphics())
{
g.TranslateTransform(e.X, e.Y);
g.RotateTransform(((float)numericUpDown1.Value) / 10.0f);
g.DrawString("Hello, world!", fnt, new SolidBrush(Color.Blue), 0.0f, 0.0f);
}
}

LogFontExplorer.zip