locked
Determining if font is Monospaced RRS feed

  • Question

  • I've been searching through the Help files but can't find an answer to this:

    Given a System.Drawing.Font, is there any way to determine if it is monospace (fixed pitch)?  I have a textbox where I want to restrict the user to fixed pitch fonts.  The associated font selector dialog (standard Windows Font dialog) takes FixedPitchOnly as an option, but downstream I want to Assert that the font is actually fixed pitch.  Can't see how to do it.  I would have thought that Font.FontFamily would be helpful, but can't find it there.

    Thanks in advance,

    Michael Bate

     

     

    Wednesday, July 28, 2010 9:44 PM

Answers

  • Hi Michael,

    You can't do this in purely managed code. You will have to convert the System.Drawing.Font to a LOGFONT structure using Font.ToLogFont. Then you can check if the FIXED_PITCH bit of the lfPitchAndFamily member is set.

    If you need example code, let me know and I'll post an example.

    More information:

    LOGFONT Structure (MSDN)
    LOGFONT (pinvoke.net)

    Hope this helps.

    • Marked as answer by MichaelBate Thursday, July 29, 2010 8:08 PM
    Wednesday, July 28, 2010 11:23 PM
  • This is not an optimal solution but it works:     

      public bool IsFixedWidth(Graphics g, Font ft)
        {
          char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
          float charWidth = g.MeasureString("I", ft).Width;
    
          bool fixedWidth = true;
    
          foreach (char c in charSizes)
            if (g.MeasureString(c.ToString(), ft).Width != charWidth)
              fixedWidth = false;
    
          return fixedWidth;
        }
    

     

    This is a simple workaround that worked for me in every case. You might want to try it, it's simpler to implement.

     

    Regards,

    Fábio


    "To alcohol! The cause of and solution to all of life's problems." - Homer Simpson
    • Marked as answer by MichaelBate Thursday, July 29, 2010 8:08 PM
    Wednesday, July 28, 2010 11:45 PM

All replies

  • Hi Michael,

    You can't do this in purely managed code. You will have to convert the System.Drawing.Font to a LOGFONT structure using Font.ToLogFont. Then you can check if the FIXED_PITCH bit of the lfPitchAndFamily member is set.

    If you need example code, let me know and I'll post an example.

    More information:

    LOGFONT Structure (MSDN)
    LOGFONT (pinvoke.net)

    Hope this helps.

    • Marked as answer by MichaelBate Thursday, July 29, 2010 8:08 PM
    Wednesday, July 28, 2010 11:23 PM
  • This is not an optimal solution but it works:     

      public bool IsFixedWidth(Graphics g, Font ft)
        {
          char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
          float charWidth = g.MeasureString("I", ft).Width;
    
          bool fixedWidth = true;
    
          foreach (char c in charSizes)
            if (g.MeasureString(c.ToString(), ft).Width != charWidth)
              fixedWidth = false;
    
          return fixedWidth;
        }
    

     

    This is a simple workaround that worked for me in every case. You might want to try it, it's simpler to implement.

     

    Regards,

    Fábio


    "To alcohol! The cause of and solution to all of life's problems." - Homer Simpson
    • Marked as answer by MichaelBate Thursday, July 29, 2010 8:08 PM
    Wednesday, July 28, 2010 11:45 PM
  • Thanks to both of you who responded.  I'm going with Fabio's simple response right now, but I have used P/Invoke in the past.

    I find it annoying that .NET does not support this directly.  All it would take would be an extra property for Font (or FontFamily).

    Thanks again,

    Michael

     

    Thursday, July 29, 2010 8:10 PM
  • From a useful article "Retrieving font and text metrics using C#" by Richard Moss here.

            [DllImport("gdi32.dll", CharSet = CharSet.Auto)]
            public static extern bool GetTextMetrics(IntPtr hdc, out TEXTMETRICW lptm);
    
            [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            public static extern bool DeleteObject(IntPtr hObject);
    
            [DllImport("gdi32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiObj);
    
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            public struct TEXTMETRICW
            {
                public int tmHeight;
                public int tmAscent;
                public int tmDescent;
                public int tmInternalLeading;
                public int tmExternalLeading;
                public int tmAveCharWidth;
                public int tmMaxCharWidth;
                public int tmWeight;
                public int tmOverhang;
                public int tmDigitizedAspectX;
                public int tmDigitizedAspectY;
                public ushort tmFirstChar;
                public ushort tmLastChar;
                public ushort tmDefaultChar;
                public ushort tmBreakChar;
                public byte tmItalic;
                public byte tmUnderlined;
                public byte tmStruckOut;
                public byte tmPitchAndFamily;
                public byte tmCharSet;
            }
    
            private IEnumerable<FontFamily> GetFixedWidthFonts(IDeviceContext dc)
            {
                IntPtr hDC = dc.GetHdc();
    
                foreach (System.Drawing.FontFamily oFontFamily in System.Drawing.FontFamily.Families)
                {
                    using (System.Drawing.Font oFont = new System.Drawing.Font(oFontFamily, 10))
                    {
                        IntPtr hFont = IntPtr.Zero;
                        IntPtr hFontDefault = IntPtr.Zero;
    
                        try
                        {
                            TEXTMETRICW oTextMetric;
    
                            hFont = oFont.ToHfont();
                            hFontDefault = SelectObject(hDC, hFont);
    
                            if (GetTextMetrics(hDC, out oTextMetric))
                            {
                                //Monospace
                                if ((oTextMetric.tmPitchAndFamily & 1) == 0)
                                {
                                    yield return oFontFamily;
                                }
                            }
                        }
                        finally
                        {
                            if (hFontDefault != IntPtr.Zero)
                            {
                                SelectObject(hDC, hFontDefault);
                            }
    
                            if (hFont != IntPtr.Zero)
                            {
                                DeleteObject(hFont);
                            }
                        }
                    }
                }
    
                dc.ReleaseHdc();
            }
    

    And then on your control/form:

                using (Graphics g = this.CreateGraphics())
                {
                    foreach (System.Drawing.FontFamily oFontFamily in this.GetFixedWidthFonts(g))
                    {
                        System.Diagnostics.Debug.WriteLine(oFontFamily.Name);
                    }
                }
    

    Wednesday, June 12, 2019 10:33 AM