Title: Get font metrics in C#
The Font and FontFamily classes provide lots of information about a font's metrics, but that information is kind of hard to understand. The following list describes the key metrics.
- Internal Leading - Space above the characters that is still considered part of the character's height. (Here "leading" is pronounced to rhyme with "sledding." The term comes from the old days when printers laid out letters with little lead stamps. Internal leading was lead that was added inside the letter's cell.)
- Em Height - The height of the characters not counting the internal leading.
- Ascent - The height of the characters from the baseline to the top of the cell (including the internal leading).
- Descent - The distance the characters may extend below the baseline.
- Cell Height - The total height of the characters from bottom to top including the internal leading.
- Line Spacing - The distance between the top of one line and the top of the next line.
- External Leading - The distance between the bottom of one line and the top of the next line of text.
This example defines a FontInfo class to hold information about a font. The Font and FontFamily classes provide lots of information about font dimensions, but much of it is in design units instead of something useful such as pixels or points. The FontInfo class converts from design units into pixels to make using the dimensions easier.
The following code shows the FontInfo class.
public class FontInfo
{
// Heights and positions in pixels.
public float EmHeightPixels;
public float AscentPixels;
public float DescentPixels;
public float CellHeightPixels;
public float InternalLeadingPixels;
public float LineSpacingPixels;
public float ExternalLeadingPixels;
// Distances from the top of the cell in pixels.
public float RelTop;
public float RelBaseline;
public float RelBottom;
// Initialize the properties.
public FontInfo(Graphics gr, Font the_font)
{
float em_height =
the_font.FontFamily.GetEmHeight(the_font.Style);
EmHeightPixels = ConvertUnits(gr, the_font.Size,
the_font.Unit, GraphicsUnit.Pixel);
float design_to_pixels = EmHeightPixels / em_height;
AscentPixels = design_to_pixels *
the_font.FontFamily.GetCellAscent(the_font.Style);
DescentPixels = design_to_pixels *
the_font.FontFamily.GetCellDescent(the_font.Style);
CellHeightPixels = AscentPixels + DescentPixels;
InternalLeadingPixels = CellHeightPixels - EmHeightPixels;
LineSpacingPixels = design_to_pixels *
the_font.FontFamily.GetLineSpacing(the_font.Style);
ExternalLeadingPixels =
LineSpacingPixels - CellHeightPixels;
RelTop = InternalLeadingPixels;
RelBaseline = AscentPixels;
RelBottom = CellHeightPixels;
}
...
}
The class first defines some public variables that will hold the font metrics in pixels. The RelTop, RelBaseline, and RelBottom values give the distance from the top of the character cell to the top of the character (below the internal leading), the baseline, and the bottom of the character (below the descent).
The class's constructor initializes the public fields for a particular font. This code simply uses the Font and FontFamily properties and methods to get information about the font's dimensions. There are two tricks to this part of the code.
First, the font's Size property returns the font's size in whatever units were used to create the font. If you specified the font's size in pixels when you created it, then this value is in pixels, which is what we need. If you specified the font's size in points, however, this value is in points and the code needs to convert it into pixels before continuing. The code does this by calling the ConvertUnits method described shortly to convert from the font's unit to pixels.
The second trick is contained in the value design_to_pixels. This is a scale factor that converts font design units into pixels. The FontFamily class's GetEmHeight method returns the font's height in design units. The Font class's Size property returns the font's absolute height, and the code converts that value into pixels. That means the values em_height and EmHeightPixels give the font's height in design units and pixels respectively. The code uses those values to calculate the conversion factor design_to_pixels.
After calculating design_to_pixels, the code simply uses other Font and FontFamily properties and methods to get metric values, and then scales them to convert them into pixels. It finishes by calculating the RelTop, RelBaseline, and RelBottom values for convenience.
The following code shows the ConvertUnits method.
// Convert from one type of unit to another.
// I don't know how to do Display or World.
private float ConvertUnits(Graphics gr, float value,
GraphicsUnit from_unit, GraphicsUnit to_unit)
{
if (from_unit == to_unit) return value;
// Convert to pixels.
switch (from_unit)
{
case GraphicsUnit.Document:
value *= gr.DpiX / 300;
break;
case GraphicsUnit.Inch:
value *= gr.DpiX;
break;
case GraphicsUnit.Millimeter:
value *= gr.DpiX / 25.4F;
break;
case GraphicsUnit.Pixel:
// Do nothing.
break;
case GraphicsUnit.Point:
value *= gr.DpiX / 72;
break;
default:
throw new Exception("Unknown input unit " +
from_unit.ToString() + " in FontInfo.ConvertUnits");
}
// Convert from pixels to the new units.
switch (to_unit)
{
case GraphicsUnit.Document:
value /= gr.DpiX / 300;
break;
case GraphicsUnit.Inch:
value /= gr.DpiX;
break;
case GraphicsUnit.Millimeter:
value /= gr.DpiX / 25.4F;
break;
case GraphicsUnit.Pixel:
// Do nothing.
break;
case GraphicsUnit.Point:
value /= gr.DpiX / 72;
break;
default:
throw new Exception("Unknown output unit " +
to_unit.ToString() + " in FontInfo.ConvertUnits");
}
return value;
}
This code checks the original unit and scales the value by an appropriate amount to convert the value into pixels. By definition, the unit Document is 1/300th of an inch and the unit Point is 1/72nd of an inch. The code uses a Graphics object's DpiX property to determine the number of pixels per inch if necessary.
Next the code converts the value, which is now in pixels, into the desired result unit and returns the result.
The main program creates some fonts and makes FontInfo objects for them. It then uses the FontInfo values to draw the font metrics. (That requires a bunch of drawing code that isn't very interesting, so it isn't shown here.)
Download the example to experiment with it and to see additional details.
|