Download GetTextMetricsDemo.zip, last updated 09/07/2016 (10.51 KB)

Download
  • md5: 5716cc483e334071b2334c8ae92e076f
  • sha1: 2c94d15164e3498d892843eaf8b78d77dd3ba8c7
  • sha256: 3bf039f038a478d5f31aca3e1ca634739e4343a84de89ab73ff6c2f33653f9c2
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Text;
using System.Windows.Forms;

// Retrieving font and text metrics using C#
// http://www.cyotek.com/blog/retrieving-font-and-text-metrics-using-csharp
// Copyright © 2016 Cyotek Ltd. All Rights Reserved.

namespace GetTextMetricsDemo
{
  internal partial class MainForm : Form
  {
    #region Constants

    private const int _boxSize = _labelEdgeLength * 4;

    private const TextFormatFlags _flags = TextFormatFlags.Left | TextFormatFlags.ExpandTabs | TextFormatFlags.NoPrefix | TextFormatFlags.NoPadding | TextFormatFlags.NoClipping;

    private const int _labelEdgeLength = 6;

    private const int _vaneLength = _labelEdgeLength >> 1;

    #endregion

    #region Constructors

    public MainForm()
    {
      this.InitializeComponent();
    }

    #endregion

    #region Methods

    /// <summary>Raises the <see cref="E:System.Windows.Forms.Form.Shown" /> event.</summary>
    /// <param name="e">A <see cref="T:System.EventArgs" /> that contains the event data. </param>
    protected override void OnShown(EventArgs e)
    {
      base.OnShown(e);

      this.Font = SystemFonts.DialogFont;

      this.SetFontTextBox();
      this.UpdatePreview();
    }

    private void DrawHorizontalLabel(Graphics g, Color color, string label, int x, int y, int w)
    {
      Font font;
      Size size;
      string px;

      using (Pen pen = new Pen(color))
      {
        g.DrawLine(pen, x, y - _vaneLength, x, y + _vaneLength);
        g.DrawLine(pen, x, y, x + w, y);
        g.DrawLine(pen, x + w, y - _vaneLength, x + w, y + _vaneLength);
      }

      font = this.Font;
      px = w.ToString();
      size = TextRenderer.MeasureText(g, px, font, Size.Empty, _flags);

      TextRenderer.DrawText(g, label, font, new Rectangle(x, y + 3, w, 0), color, _flags | TextFormatFlags.HorizontalCenter | TextFormatFlags.WordBreak);
      TextRenderer.DrawText(g, px, font, new Point(x + w + _labelEdgeLength, y - (size.Height >> 1)), color, _flags);
    }

    private void DrawMiddleLabel(Graphics g, Color color, string label, int x, int y)
    {
      Font font;
      Size size;

      font = this.Font;
      size = TextRenderer.MeasureText(g, label, font, Size.Empty, _flags);

      TextRenderer.DrawText(g, label, font, new Point(x, y - (size.Height >> 1)), color, _flags);
    }

    private int DrawValues(Graphics g, NativeMethods.TEXTMETRICW metrics)
    {
      List<Tuple<string, string>> values;
      Font font;
      int maxWidth;
      int maxHeight;
      int x;
      int y;
      int tableWidth;

      font = this.Font;
      maxWidth = 0;
      maxHeight = 0;
      tableWidth = 0;
      values = new List<Tuple<string, string>>();

      values.Add(Tuple.Create("Internal Leading", metrics.tmInternalLeading.ToString()));
      values.Add(Tuple.Create("External Leading", metrics.tmExternalLeading.ToString()));
      values.Add(Tuple.Create("Overhang", metrics.tmOverhang.ToString()));
      values.Add(null);
      values.Add(Tuple.Create("First Character", ((char)metrics.tmFirstChar).ToString()));
      values.Add(Tuple.Create("Last Character", ((char)metrics.tmLastChar).ToString()));
      values.Add(Tuple.Create("Default Character", ((char)metrics.tmDefaultChar).ToString()));
      values.Add(Tuple.Create("Break Character", ((char)metrics.tmBreakChar).ToString()));
      values.Add(null);
      values.Add(Tuple.Create("Weight", metrics.tmWeight.ToString()));
      values.Add(Tuple.Create("Italic", metrics.tmItalic.ToString()));
      values.Add(Tuple.Create("Underline", metrics.tmUnderlined.ToString()));
      values.Add(Tuple.Create("Strike Through", metrics.tmStruckOut.ToString()));
      values.Add(null);
      values.Add(Tuple.Create("Pitch and family", metrics.tmPitchAndFamily.ToString()));
      values.Add(Tuple.Create("Character Set", metrics.tmCharSet.ToString()));

      // ReSharper disable once LoopCanBePartlyConvertedToQuery
      foreach (Tuple<string, string> tuple in values)
      {
        if (tuple != null)
        {
          Size size;

          size = TextRenderer.MeasureText(tuple.Item1, font, Size.Empty, _flags);

          maxWidth = Math.Max(maxWidth, size.Width);
          maxHeight = Math.Max(maxHeight, size.Height);
        }
      }

      x = _boxSize;
      y = _boxSize;

      foreach (Tuple<string, string> tuple in values)
      {
        if (tuple != null)
        {
          Size size;
          string value;

          value = ": " + tuple.Item2;

          TextRenderer.DrawText(g, tuple.Item1, font, new Point(x, y), SystemColors.WindowText, _flags);
          TextRenderer.DrawText(g, value, font, new Point(x + maxWidth, y), SystemColors.WindowText, _flags);

          size = TextRenderer.MeasureText(value, font, Size.Empty, _flags);
          tableWidth = Math.Max(tableWidth, maxWidth + size.Width);
        }

        y += maxHeight;
      }

      return tableWidth;
    }

    private void DrawVerticalLabel(Graphics g, Color color, string label, int x, int y, int h)
    {
      Font font;
      Size size;
      string px;

      using (Pen pen = new Pen(color))
      {
        g.DrawLine(pen, x - _vaneLength, y, x + _vaneLength, y);
        g.DrawLine(pen, x, y, x, y + h);
        g.DrawLine(pen, x - _vaneLength, y + h, x + _vaneLength, y + h);
      }

      font = this.Font;
      px = h.ToString();
      size = TextRenderer.MeasureText(g, px, font, Size.Empty, _flags);

      TextRenderer.DrawText(g, label, font, new Point(x + 3, y + 3), color, _flags);
      TextRenderer.DrawText(g, px, font, new Point(x - (size.Width + 2), y + 3), color, _flags);
    }

    private void fontBrowseButton_Click(object sender, EventArgs e)
    {
      if (fontDialog.ShowDialog(this) == DialogResult.OK)
      {
        this.SetFontTextBox();
        this.UpdatePreview();
      }
    }

    private NativeMethods.TEXTMETRICW GetTextMetrics(IDeviceContext dc, Font font)
    {
      NativeMethods.TEXTMETRICW result;
      IntPtr hDC;
      IntPtr hFont;
      IntPtr hFontDefault;

      hDC = IntPtr.Zero;
      hFont = IntPtr.Zero;
      hFontDefault = IntPtr.Zero;

      try
      {
        hDC = dc.GetHdc();

        hFont = font.ToHfont();
        hFontDefault = NativeMethods.SelectObject(hDC, hFont);

        NativeMethods.GetTextMetrics(hDC, out result);
      }
      finally
      {
        if (hFontDefault != IntPtr.Zero)
        {
          NativeMethods.SelectObject(hDC, hFontDefault);
        }

        if (hFont != IntPtr.Zero)
        {
          NativeMethods.DeleteObject(hFont);
        }

        dc.ReleaseHdc();
      }

      return result;
    }

    private void previewPanel_Paint(object sender, PaintEventArgs e)
    {
      string text;

      text = previewTextTextBox.Text;

      if (!string.IsNullOrEmpty(text))
      {
        NativeMethods.TEXTMETRICW metrics;
        Graphics g;
        Font font;
        Size size;
        int x;
        int y;
        int w;
        int h;
        Rectangle bounds;
        int ascent;
        int descent;
        int averageCharacterWidth;
        int maxCharacterWidth;
        int tableWidth;

        g = e.Graphics;
        font = fontDialog.Font;
        metrics = this.GetTextMetrics(g, font);
        size = TextRenderer.MeasureText(g, text, font, Size.Empty, _flags);
        bounds = previewPanel.ClientRectangle;

        // The average width of characters in the font (generally defined as the width of the letter x ). 
        // This value does not include the overhang required for bold or italic characters.
        averageCharacterWidth = metrics.tmAveCharWidth;

        // The width of the widest character in the font.
        maxCharacterWidth = metrics.tmMaxCharWidth;

        // The ascent (units above the base line) of characters.
        ascent = metrics.tmAscent;

        // The descent (units below the base line) of characters.
        descent = metrics.tmDescent;

        w = size.Width;
        h = size.Height;
        y = ((bounds.Height - h) >> 1) - _boxSize;

        g.TextRenderingHint = TextRenderingHint.AntiAlias;

        // draw any values which aren't part of the main diagram
        tableWidth = this.DrawValues(g, metrics);

        x = tableWidth + ((bounds.Width - (tableWidth + w)) >> 1);

        // draw boxes which show the maximum and average character widths
        using (Brush brush = new HatchBrush(HatchStyle.DiagonalCross, Color.Orange, SystemColors.Window))
        {
          int bx;
          int by;

          bx = (bounds.Width - (averageCharacterWidth + maxCharacterWidth + _boxSize)) >> 1;
          by = y + h + _labelEdgeLength * 4;

          g.FillRectangle(brush, bx, by, averageCharacterWidth, _boxSize);
          g.DrawRectangle(Pens.Orange, bx, by, averageCharacterWidth, _boxSize);
          this.DrawHorizontalLabel(g, Color.Orange, "Average Character Width", bx, by + _boxSize + _labelEdgeLength * 2, averageCharacterWidth);

          bx += averageCharacterWidth + _boxSize;

          g.FillRectangle(brush, bx, by, maxCharacterWidth, _boxSize);
          g.DrawRectangle(Pens.Orange, bx, by, maxCharacterWidth, _boxSize);
          this.DrawHorizontalLabel(g, Color.Orange, "Maximum Character Width", bx, by + _boxSize + _labelEdgeLength * 2, maxCharacterWidth);
        }

        // draw the text baseline
        g.DrawLine(Pens.SeaGreen, x - _vaneLength, y + ascent, x + w + _labelEdgeLength, y + ascent);

        // draw the preview text
        TextRenderer.DrawText(g, text, font, new Point(x, y), previewPanel.ForeColor, _flags);

        // labels
        this.DrawVerticalLabel(g, Color.CornflowerBlue, "Ascent", x - _labelEdgeLength * 2, y, ascent);
        this.DrawVerticalLabel(g, Color.CornflowerBlue, "Descent", x - _labelEdgeLength * 2, y + ascent, descent);
        this.DrawVerticalLabel(g, Color.CornflowerBlue, "Height", x + w + _labelEdgeLength * 2, y, metrics.tmHeight);
        this.DrawMiddleLabel(g, Color.SeaGreen, "Baseline", x + w + _labelEdgeLength * 3, y + ascent);
      }
    }

    private void previewPanel_Resize(object sender, EventArgs e)
    {
      this.UpdatePreview();
    }

    private void previewTextTextBox_TextChanged(object sender, EventArgs e)
    {
      this.UpdatePreview();
    }

    private void SetFontTextBox()
    {
      Font font;
      StringBuilder sb;

      font = fontDialog.Font;
      sb = new StringBuilder();

      sb.Append(font.Name);
      sb.Append(' ');

      if ((font.Style & FontStyle.Bold) != 0)
      {
        sb.Append(' ');
        sb.Append("Bold");
      }

      if ((font.Style & FontStyle.Italic) != 0)
      {
        sb.Append(' ');
        sb.Append("Italic");
      }

      sb.Append(font.SizeInPoints);
      sb.Append("pt");

      fontTextBox.Text = sb.ToString();
    }

    private void UpdatePreview()
    {
      previewPanel.Invalidate();
    }

    #endregion
  }
}

Donate

Donate