Fonts and Measuring Text

Font is a typeface stored in a data file as a set of glyphs with particular characteristics such as weight, style, etc. In Graphics Mill fonts are represented by the Font class. If you want to draw text on Graphics it is mandatory to specify a font (see the Graphics. Drawing Images and Geometric Shapes article to learn all about Graphics).

Before working with text, you need to know its characteristics, which are described as font metrics. Knowledge of font characteristic allows you to work with fonts more easily. For example, you can measure text. This topic discusses both: how to work with fonts and how to measure text. The topic consists of the following paragraphs:

Font Metrics

Font metrics are the measurements of characters in a particular font, also known as glyphs. They allow modification of the alignment of a text string. Knowing font metrics you can define the location of each character in the drawn text. The following image illustrates what metrics each font provides in Graphics Mill:

Font Metrics

Let us discuss all these definitions in detail:

  • BaseLine is the invisible horizontal line upon which a line of text is located. Note that the lower-case letters, such as "q", "g", "y", etc., stick below the baseline. In fact, the baseline is not a font metric, but all measurements that can be done with text are relative to the baseline.
  • Ascender is the distance between the baseline and the top of the tallest letter.
  • Descender is the distance between the baseline and the bottom of a lower-case letter sticking down below the baseline.
  • Height is the distance between the two nearest baselines in text divided into lines.
  • Black Box is the smallest rectangle bounding an entire glyph.
  • Underline Position is the distance between the baseline and a line of underlined text.
  • Underline Thickness is the line height of underlined text.

All font metrics depend on font characteristics, such as size, style, name, etc. However, the black box also depends on a glyph. That is, the black boxes for the i and w glyphs will be different.

All font metrics, except the baseline and black box (which we will discuss below), are specified via the Font.Metrics property inherited from the AdvancedDrawing.FontMetrics class.

Supported Font Formats

There are two basic types of font formats: bitmap and vector fonts (vector fonts are also known as outline fonts). Bitmap fonts consist of pixel images for each glyph in each size. Though bitmap fonts are faster, they are not scalable, this requires creating separate glyph images for each font size. Vector fonts are based on Bezier curves, drawing instructions, and mathematical formulas describing glyphs. Graphics Mill supports vector fonts, because they can be easily scaled to any size while preserving the sharpness of each glyph. Here are the basic font formats supported in Graphics Mill:

Creating Fonts

As we discussed in the Font Metrics paragraph, the character images are called glyphs. When drawing text, each character code of the text is converted into a corresponding glyph relative to the chosen font. This approach - mapping between a character and glyph - is maintained by FontRegistry. FontRegistry contains a set of the installed system fonts. You can get the installed system fonts using the Installed property.

In Graphics Mill, almost every font can be accessed by its postscript name or family name. The postscript name is a single string name identifying a font, whereas the family name always requires specifying a font style. Fonts can have the same family name, but they are distinguished by the style. For example, there are two fonts with the same family name - Palatino, but they have different styles, namely, regular and Italic. Using the FontRegistry.GetFontStyles(String) method you can get all styles that a family has, as the following snippet demonstrates:

C#
var styles = FontRegistry.Installed.GetFontStyles("Times New Roman");
Console.WriteLine("Family Name: Times New Roman");
foreach (var style in styles)
    Console.WriteLine("Style: {0}", style);

After launching this snippet you will get the following result:

Getting family's styles

Most font families have regular, bold, and italic styles. However, what if you need to draw a bold and/or italic text, but a font family does not contain the corresponding styles? In this case you can use the Font.FauxBold and Font.FauxItalic properties for emulating bold and italic styles, respectively.

Creating a font means requesting the font by its postscript name or family and style names via the FontRegistry.CreateFont(String, Single, Single, Single) method. If FontRegistry contains the requested font, it returns the Font class instance, otherwise you will get a FontMissingException. For example, the following snippet illustrates how to create a system font by its postscript name and to draw text with the font (read the Drawing Text article to learn how to work with text):

C#
using (var bitmap = new Bitmap(200, 80, PixelFormat.Format24bppRgb, RgbColor.White))
using (var graphics = bitmap.GetAdvancedGraphics())
{
    var font = FontRegistry.Installed.CreateFont("Comic", 16, graphics.DpiX, graphics.DpiX);
    
    var plainText = new PlainText("Plain Text", font, new System.Drawing.PointF(20, 40));
    graphics.DrawText(plainText);
    bitmap.Save(@"Images\Output\out.png");
}
Note

As you may have noticed, the FontRegistry.CreateFont(String, Single, Single, Single) method accepts DPIs as arguments. The DPI is needed to draw glyphs correctly. If the font and the target media where the text is drawn have different DPIs, glyphs are rendered distorted. To prevent this from happening, DrawText(Text) checks if the font and the Graphics object have the same DPIs, otherwise it throws an exception. So, make sure that your font and graphics DPIs match each other.

Here is the example of creating a system font by its family name and style:

C#
var font = FontRegistry.Installed.CreateFont("Comic Sans MS", "Regular", 16, graphics.DpiX, graphics.DpiY);

FontRegistry does not allow you to add or delete fonts stored in it, but you can create your own CustomFontRegistry that does. CustomFontRegistry provides the Add(String) method for loading a font from the specified file. For example, the following snippet demonstrates how to load all the system fonts to CustomFontRegistry and add another one from the file:

C#
var customFontRegistry = new CustomFontRegistry(FontRegistry.Installed);
customFontRegistry.Add(@"C:\Windows\Fonts\timesbd.ttf");
var font = customFontRegistry.CreateFont("Times New Roman", "Bold", 22, graphics.DpiX, graphics.DpiY);

If you want to create a standard font, such as Times New Roman, you can simply do it via Graphics (if you are not familiar with Graphics read the Graphics. Drawing Images and Geometric Shapes article). Graphics acts as a wrapper around FontRegistry, thus you can use the Graphics.CreateFont(String, Single) to create a font:

C#
var font = graphics.CreateFont("Arial", 20);

By default, Graphics.FontRegistry contains the installed system fonts, but you can replace them with your own custom set:

C#
var customFont = new CustomFontRegistry();
customFont.Add(@"C:\Windows\Fonts\timesbd.ttf");
graphics.FontRegistry = customFont;
var font = graphics.CreateFont("Times New Roman", "Bold", 22);   

Text Measuring

Measuring text means determining the dimensions of a rectangle that encloses the text as tightly as possible. There are two different ways to measure text: using the Font class and getting the black box. Let us take a look at both of these methods below.

Using the Font Class

You can measure a text string even without creating a text object. Font provides the MeasureString(String) method that accepts a text string as a parameter and returns a StringMeasure class instance. StringMeasure contains the following string metrics that are in pixels: Ascender, Descender, Width, and Height (we discussed these metrics in the Font Metrics paragraph). The following snippet illustrates how to get string metrics:

C#
using (var bitmap = new Bitmap(200, 80, PixelFormat.Format24bppRgb, RgbColor.White))
using (var graphics = bitmap.GetAdvancedGraphics())
{
    var font = graphics.CreateFont("Arial", 20);
    StringMeasure stringMeasure = font.MeasureString("String Metrics");
    Console.WriteLine("String Metrics:");
    Console.WriteLine("  Width:     {0}", stringMeasure.Width);
    Console.WriteLine("  Height:    {0}", stringMeasure.Height);
    Console.WriteLine("  Ascender:  {0}", stringMeasure.Ascender);
    Console.WriteLine("  Descender: {0}", stringMeasure.Descender);
}

Using these metrics you can easily calculate the black box of the text. The following snippet illustrates how to draw the black box based on metrics returned by StringMeasure in the snippet above:

C#
using (var bitmap = new Bitmap(200, 80, PixelFormat.Format24bppRgb, RgbColor.White))
using (var graphics = bitmap.GetAdvancedGraphics())
{
    string text = "String Metrics";
    var font = graphics.CreateFont("Arial", 28);
    StringMeasure stringMeasure = font.MeasureString(text);
    using (var plainText = new PlainText(text, font, new System.Drawing.PointF(20, 40)))
    {
        graphics.DrawText(plainText);
        //BaseLine - Red line
        graphics.DrawLine(new Pen(RgbColor.Red), plainText.Position.X, plainText.Position.Y,
            plainText.Position.X + stringMeasure.Width, plainText.Position.Y);
        //drawing BlackBox
        graphics.DrawLine(new Pen(RgbColor.Gray, 1f), plainText.Position.X, plainText.Position.Y - stringMeasure.Ascender,
            plainText.Position.X + stringMeasure.Width, plainText.Position.Y - stringMeasure.Ascender);
        graphics.DrawLine(new Pen(RgbColor.Gray, 1f), plainText.Position.X, plainText.Position.Y - stringMeasure.Descender,
            plainText.Position.X + stringMeasure.Width, plainText.Position.Y - stringMeasure.Descender);
        graphics.DrawLine(new Pen(RgbColor.Gray, 1f), plainText.Position.X, plainText.Position.Y - stringMeasure.Ascender,
            plainText.Position.X, plainText.Position.Y - stringMeasure.Descender);
        graphics.DrawLine(new Pen(RgbColor.Gray, 1f), plainText.Position.X + stringMeasure.Width, plainText.Position.Y - stringMeasure.Ascender,
            plainText.Position.X + stringMeasure.Width, plainText.Position.Y - stringMeasure.Descender);
        bitmap.Save(@"Images\Output\out.png");
    }
}

Notice that all calculations are performed relative to the BaseLine. Here is the result:

String Measuring

Getting Black Box

In Graphics Mill you can measure a black box for any type of text including the artistic text. If you are not familiar with types of text read the Drawing Text article. For measuring the black box use the GetBlackBox() method as the following snippet illustrates:

C#
using (var bitmap = new Bitmap(160, 150, PixelFormat.Format24bppRgb, RgbColor.White))
using (var graphics = bitmap.GetAdvancedGraphics())
{
    var font = graphics.CreateFont("Arial", 28);
    using (var roundText = new RoundText("Art Round Text", font, new System.Drawing.PointF(70, 70)))
    {
        roundText.Bend = 0.9f;
        graphics.DrawText(roundText);
        graphics.DrawRectangle(
            new Pen(RgbColor.Gray, 1f), 
            roundText.GetBlackBox(graphics.FontRegistry, graphics.DpiX, graphics.DpiY));
        bitmap.Save(@"Images\Output\out.png");
    }
}

This snippet draws the text and its black box:

Black Box Measuring

See Also

Reference

Manual

Other