Text Metrics in the .Net Framework, Part II (
Page 1 of 2 )
Some applications require precise placement of text in screen display and printed output. The .Net Framework's tools in this area are far from complete, but knowing what's available will help you get the best possible output.
In the first article of this series, we looked at some of the metrics that the .Net Framework provides to assist you with text placement. In this second and final article we will see how these tools and some creativity can be combined for really precise placement.
The Challenge
Suppose that you want text to line up precisely with specified X and Y coordinates. Figure 1 provides an example. You want text to line up precisely at coordinates (100,100), where the vertical and horizontal lines meet, as shown by the blue text. If you output text at those coordinates, however, you'll get the red text. This is because .NET's text rendering places the top left of the text box at the output coordinates, and also provides the padding around the text that is needed for most text display. To get the desired result we need to translate, or move, the text up and to the left—but by how much? Unfortunately, this requires an ad hoc solution because not enough information is available about the fonts and the text rendering to devise a universal solution.
Figure 1. The blue is what we're stiving towards; the red is what we initially get.
The Horizontal Correction
We saw in the previous article how the Graphics.MeasureString method gives you the display size of a text message in a specific font, but that the returned values include padding—and it's just this padding, the horizontal padding at least, that is preventing us from precise horizontal placement of the text. We also saw how to use this calculation to get the "true" length of text (length without the padding):
(True Width of "Message") =
(MeasureString Width of "<Message>") minus (MeasureString Width of "<>")
The formula can be rearranged to get the width of the horizontal padding:
(Width of padding) = (MeasureString Width of "Message") minus
(True Width of "Message")
This is the padding both before and after the text, so I figured that dividing by 2 would get me the length of the padding in front of the text, which will be the desired X position correction factor. My initial code, therefore, was as follows (g refers to the Graphics object, f refers to the display font, and msg is a variable holding the text to display:
Dim sz1 As SizeF = g.MeasureString(msg, f)
Dim sz2 As SizeF = g.MeasureString("<" & msg & ">", f)
Dim sz3 As SizeF = g.MeasureString("<>", f)
Dim xCorrection As Single = (sz1.Width - (sz2.Width - sz3.Width)) / 2
When I first tried this, using a Times font family, it worked great. Before patting myself on the back I knew I should try it with other fonts. To my dismay, it did not work with Arial and other sans serif fonts—the correction factor was not enough, as shown in the following figure. The text was still too far to the right, not lining up precisely with the X coordinate.
Figure 2. The initial horizontal correction factor worked for serif fonts (top) but not for sans serif fonts (bottom).
The only solution was to use a different factor for serif fonts and sans serif fonts. I changed the last line of the code above to this:
Dim xCorrection As Single = (sz1.Width - _
(sz2.Width - sz3.Width)) / xFactor
Setting xFactor to 2 works for serif fonts, and 1.5 works for sans serif fonts. While I would have preferred a computational solution that works for all fonts, this was the best I could do given the limitations of fonts in Windows.