Hi,

I have created a class which is a "font searcher" the idea being that it will detect the font required for a given string in any language by getting each available font's unicode range(s) and comparing those ranges to the ones found in the string. This is working perfectly except that on the 3rd/4th time the code is run (without re-starting IIS) i get an out of memory exception.

Here is the class:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Text;
using PdfSharp.Drawing;

namespace PRT.Common
{
    public class FontSearcher
    {

        #region Imports
        [DllImport("gdi32.dll")]
        public static extern uint GetFontUnicodeRanges(IntPtr hdc, IntPtr lpgs);
        [DllImport("gdi32.dll")]
        public extern static IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
        #endregion

        #region Class Vars
        private List<string> __PreferredFontNames;
        private FontInfoList __PreferredFontInfos;
        private FontInfoList __NonPreferredFontInfos;
        List<FontInfoList> __FiLists = new List<FontInfoList>();
        #endregion

        #region Constructor
        public FontSearcher(List<string> preferredFontNames)
        {
            __PreferredFontNames = preferredFontNames;
            BuildFontInfos();
            __FiLists = new List<FontInfoList>();
            __FiLists.Add(__PreferredFontInfos);
            __FiLists.Add(__NonPreferredFontInfos);
        }
        #endregion

        #region Methods...
        #region Private Members
        private void BuildFontInfos()
        {
            #region Vars Setup
            __PreferredFontInfos = new FontInfoList();
            __NonPreferredFontInfos = new FontInfoList();
            InstalledFontCollection fonts = new InstalledFontCollection();
            #endregion
            #region Build FontInfo Objects
            for (int i = 0; i < fonts.Families.Length; i++)
            {
                try
                {
                    Font _font = null;
                    if (fonts.Families[i].IsStyleAvailable(FontStyle.Regular))
                        _font = new Font(fonts.Families[i].Name, 10, FontStyle.Regular);
                    else if (fonts.Families[i].IsStyleAvailable(FontStyle.Bold))
                        _font = new Font(fonts.Families[i].Name, 10, FontStyle.Bold);
                    else if (fonts.Families[i].IsStyleAvailable(FontStyle.Italic))
                        _font = new Font(fonts.Families[i].Name, 10, FontStyle.Italic);
                    if (__PreferredFontNames.Contains(_font.Name))
                    {
                        try
                        {
                            __PreferredFontInfos.Add(new FontInfo(_font, GetUnicodeRangesForFont(_font)));
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                    }
                    else
                    {
                        try
                        {
                            __NonPreferredFontInfos.Add(new FontInfo(_font, GetUnicodeRangesForFont(_font)));
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            #endregion
            #region Re-Order __PreferredFontInfos to match the order of __PreferredFontNames
            FontInfoList __OrderedPreferedFontInfos = new FontInfoList();
            int d = 0;
            while (__OrderedPreferedFontInfos.Count < __PreferredFontInfos.Count)
            {
                for (int i = 0; i < __PreferredFontInfos.Count; i++)
                {
                    if (__PreferredFontInfos[i].Font.Name == __PreferredFontNames[d])
                    {
                        __OrderedPreferedFontInfos.Add(__PreferredFontInfos[i]);
                    }
                }
                d++;
            }
            __PreferredFontInfos = __OrderedPreferedFontInfos;
            #endregion
        }
        private List<FontRange> GetUnicodeRangesForFont(Font font)
        {
            Graphics g = Graphics.FromHwnd(IntPtr.Zero);
            IntPtr hdc = g.GetHdc();
            IntPtr hFont = font.ToHfont();
            IntPtr old = SelectObject(hdc, hFont);
            uint size = GetFontUnicodeRanges(hdc, IntPtr.Zero);
            IntPtr glyphSet = Marshal.AllocHGlobal((int)size);
            GetFontUnicodeRanges(hdc, glyphSet);
            List<FontRange> fontRanges = new List<FontRange>();
            int count = Marshal.ReadInt32(glyphSet, 12);
            for (int i = 0; i < count; i++)
            {
                FontRange range = new FontRange();
                range.Low = (UInt16)Marshal.ReadInt16(glyphSet, 16 + i * 4);
                range.High = (UInt16)(range.Low + Marshal.ReadInt16(glyphSet, 18 + i * 4) - 1);
                fontRanges.Add(range);
            }
            SelectObject(hdc, old);
            Marshal.FreeHGlobal(glyphSet);
            g.ReleaseHdc(hdc);
            g.Dispose();
            return fontRanges;
        }
        private bool CheckIfCharInFontInfo(char character, FontInfo fontInfo)
        {
            UInt16 intval = Convert.ToUInt16(character);
            List<FontRange> ranges = fontInfo.Ranges;
            bool isCharacterPresent = false;
            foreach (FontRange range in ranges)
            {
                if (intval >= range.Low && intval <= range.High)
                {
                    isCharacterPresent = true;
                    break;
                }
            }
            return isCharacterPresent;
        }
        private FontInfo CheckBestFont(List<FontInfo> fontInfos, string str)
        {
            #region Vars Setup
            FontInfo _fontInfo = null;
            List<int> _occs = new List<int>();
            int _occ = 0;
            #endregion
            #region Build List containing number of matching glyphs contained within each FontInfo
            for (int i = 0; i < fontInfos.Count; i++)
            {
                for (int c = 0; c < str.Length; c++)
                {
                    if (CheckIfCharInFontInfo(str[c], fontInfos[i]))
                    {
                        _occ++;
                    }
                }
                _occs.Add(_occ);
                _occ = 0;
            }
            #endregion
            #region Get the FontInfo with the highest number of glyph matches
            if (_occs.Count > 0)
            {
                int _fiOcc = _occs[0];
                int _fiIdx = 0;
                for (int i = 1; i < _occs.Count; i++)
                {
                    if (_occs[i] > _fiOcc)
                    {
                        _fiOcc = _occs[i];
                        _fiIdx = i;
                    }
                }
                _fontInfo = fontInfos[_fiIdx];
                if (!__PreferredFontInfos.Contains(_fontInfo))
                {
                    __PreferredFontInfos.Add(_fontInfo);
                    __NonPreferredFontInfos.Remove(_fontInfo);
                }
            }
            #endregion
            return _fontInfo;
        }
        private FontInfo CheckBestFont(List<FontInfo> fontInfos, string str, string preferredFontName)
        {
            #region Vars Setup
            FontInfo _fontInfo = null;
            List<int> _occs = new List<int>();
            int _occ = 0;
            #endregion
            #region Build List containing number of matching glyphs contained within each FontInfo
            for (int i = 0; i < fontInfos.Count; i++)
            {
                for (int c = 0; c < str.Length; c++)
                {
                    if (CheckIfCharInFontInfo(str[c], fontInfos[i]))
                    {
                        _occ++;
                    }
                }
                _occs.Add(_occ);
                _occ = 0;
            }
            #endregion
            #region Get the FontInfo with the highest number of glyph matches
            if (_occs.Count > 0)
            {
                int _fiOcc = _occs[0];
                int _fiIdx = 0;
                for (int i = 1; i < _occs.Count; i++)
                {
                    if(_occs[i] > 0 && fontInfos[i].Font.Name == preferredFontName)
                    {
                        _fiIdx = i;
                        break;
                    }
                    if (_occs[i] > _fiOcc)
                    {
                        _fiOcc = _occs[i];
                        _fiIdx = i;
                    }
                }
                _fontInfo = fontInfos[_fiIdx];
                if (!__PreferredFontInfos.Contains(_fontInfo))
                {
                    __PreferredFontInfos.Add(_fontInfo);
                    __NonPreferredFontInfos.Remove(_fontInfo);
                }
            }
            #endregion
            return _fontInfo;
        }
        private string StripTextFormattingChars(string str)
        {
            str = str.Replace("\r", "");
            str = str.Replace("\t", "");
            str = str.Replace("\n", "");
            return str;
        }
        #endregion
        #region Public Members
        public FontInfo GetFontInfoFromChar(char character)
        {
            FontInfo _fontInfo = null;
            int fi = 0;
            while (fi < __FiLists.Count && _fontInfo == null)
            {
                for (int i = 0; i < __FiLists[fi].Count; i++)
                {
                    if (CheckIfCharInFontInfo(character, __FiLists[fi][i]))
                    {
                        _fontInfo = __FiLists[fi][i];
                        break;
                    }
                }
                fi++;
            }
            return _fontInfo;
        }
        public FontInfo GetFontInfoFromChar(char character, string preferredFontName)
        {
            FontInfo _fontInfo = null;
            int fi = 0;
            while (fi < __FiLists.Count && _fontInfo == null)
            {
                for (int i = 0; i < __FiLists[fi].Count; i++)
                {
                    if (__FiLists[fi][i].Font.Name == preferredFontName)
                    {
                        if (CheckIfCharInFontInfo(character, __FiLists[fi][i]))
                        {
                            _fontInfo = __FiLists[fi][i];
                            break;
                        }
                    }
                }
                if (_fontInfo == null)
                {
                    for (int i = 0; i < __FiLists[fi].Count; i++)
                    {
                        if (__FiLists[fi][i].Font.Name == preferredFontName)
                        {

                        }
                        if (CheckIfCharInFontInfo(character, __FiLists[fi][i]))
                        {
                            _fontInfo = __FiLists[fi][i];
                            break;
                        }
                    }
                }
                fi++;
            }
            return _fontInfo;
        }
        public FontInfo GetFontInfoFromChar(char character, List<FontInfo> fontInfos)
        {
            FontInfo _fontInfo = null;
            for (int i = 0; i < fontInfos.Count; i++)
            {
                if (CheckIfCharInFontInfo(character, fontInfos[i]))
                {
                    _fontInfo = fontInfos[i];
                    break;
                }
            }
            return _fontInfo;
        }
        public FontInfo GetFontInfoFromString(string str)
        {
            #region Vars Setup
            str = StripTextFormattingChars(str);
            List<FontInfo> _fontsInString = new List<FontInfo>();
            #endregion
            #region Build List of Fonts required by str
            for (int i = 0; i < str.Length; i++)
            {
                char _c = str[i];
                FontInfo _charFI = GetFontInfoFromChar(_c);
                if (_charFI != null)
                {
                    if (_fontsInString.Count > 0)
                    {
                        if (!_fontsInString.Contains(_charFI))
                        {
                            _fontsInString.Add(_charFI);
                        }
                    }
                    else
                    {
                        _fontsInString.Add(_charFI);
                    }
                }
            }
            #endregion
            #region Select the Font to use
            if (_fontsInString.Count > 0)
            {
                if (_fontsInString.Count > 1)
                {
                    FontInfo _fontInfo = CheckBestFont(_fontsInString, str);
                    return _fontInfo;
                }
                else
                {
                    return _fontsInString[0];
                }
            }
            else
            {
                return new FontInfo(new Font("Arial", 10, FontStyle.Regular), null);
            }
            #endregion
        }
        public FontInfo GetFontInfoFromString(string str, string preferredFontName)
        {
            #region Vars Setup
            str = StripTextFormattingChars(str);
            List<FontInfo> _fontsInString = new List<FontInfo>();
            #endregion
            #region Build List of Fonts required by str
            for (int i = 0; i < str.Length; i++)
            {
                char _c = str[i];
                FontInfo _charFI = GetFontInfoFromChar(_c, preferredFontName);
                if (_charFI != null)
                {
                    if (_fontsInString.Count > 0)
                    {
                        if (!_fontsInString.Contains(_charFI))
                        {
                            _fontsInString.Add(_charFI);
                        }
                    }
                    else
                    {
                        _fontsInString.Add(_charFI);
                    }
                }
            }
            #endregion
            #region Select the Font to use
            if (_fontsInString.Count > 0)
            {
                if (_fontsInString.Count > 1)
                {
                    FontInfo _fontInfo = CheckBestFont(_fontsInString, str, preferredFontName);
                    return _fontInfo;
                }
                else
                {
                    return _fontsInString[0];
                }
            }
            else
            {
                return new FontInfo(new Font("Arial", 10, FontStyle.Regular), null);
            }
            #endregion
        }
        public bool ClearAll()
        {
            try
            {
                __PreferredFontNames.Clear();
                //__PreferredFontNames = null;
                __PreferredFontInfos.Clear();
                //__PreferredFontInfos = null;
                __NonPreferredFontInfos.Clear();
                //__NonPreferredFontInfos = null;
                __FiLists.Clear();
                //__FiLists = null;
                GC.Collect();
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
        }
        #endregion
        #endregion

        #region Properties
        public FontInfoList NonPreferredFontInfos
        {
            get
            {
                return __NonPreferredFontInfos;
            }
            set
            {
                __NonPreferredFontInfos = value;
            }
        }
        public FontInfoList PreferredFontInfos
        {
            get
            {
                return __PreferredFontInfos;
            }
            set
            {
                __PreferredFontInfos = value;
            }
        }
        #endregion

    }
    #region FontInfo Class
    public class FontInfo
    {
        #region Class Vars
        private Font __Font;
        private List<FontRange> __Ranges;
        #endregion
        #region Constructor
        public FontInfo(Font font, List<FontRange> ranges)
        {
            __Font = font;
            __Ranges = ranges;
        }
        #endregion
        #region Properties
        public Font Font
        {
            get
            {
                return __Font;
            }
        }
        public List<FontRange> Ranges
        {
            get
            {
                return __Ranges;
            }
        }
        #endregion
    }
    #endregion
    #region FontInfoList Class
    public class FontInfoList : List<FontInfo>
    {
        #region Constructor
        public FontInfoList()
        {

        }
        #endregion
        #region Methods
        public FontInfo GetFontInfoByFont(Font font)
        {
            FontInfo _fi = null;
            for (int i = 0; i < this.Count; i++)
            {
                if (this[i].Font == font)
                {
                    _fi = this[i];
                    break;
                }
            }
            return _fi;
        }
        #endregion
    }
    #endregion
    #region FontRange struct
    public struct FontRange
    {
        public UInt16 Low;
        public UInt16 High;
    }
    #endregion
}

In the constructor i pass in a list of font names which are "preferred" to be used, so the font class looks to see if the unicode ranges in the string match those first, otherwise, it will search in the "NonPreferredFontInfos" list (which is all other fonts installed on the system) for the unicode ranges.

I use this class when creating PDF's using the PDFSharp framework. on the entry point aspx page I call "FontSearcher.ClearAll()" in the Page.Unload event handler which clears the lists, and then within the ClearAll() function I call GC.Collect() - i know this GC.Collect() function is far from perfect, but i dont know of another approach - really i shouldnt need it as the automatic garbage collection should do the trick, but it didnt, hence why i am calling CG.Collect().

I can solve it by re-starting IIS every time it occurs, but as im sure you can guess, this is a far from perfect solution.

Hopefully i have covered everything, but if I have missed something out, please say!

thanks,

Mike

You're hooking into the Windows API (P/Invoke). Have you got any left over handles using up memory?

The debugger should say where your error is occurring. Also, try running this as a standard winforms app and see what happens.

GC.Collect() won't always clean up the memory and unless you're running on an insultingly tiny memory server then you shouldn't be getting out of memory just from loading fonts anyway.

I suspect it's something to do with your P/Invoke calls.

You're hooking into the Windows API (P/Invoke). Have you got any left over handles using up memory?

The debugger should say where your error is occurring. Also, try running this as a standard winforms app and see what happens.

GC.Collect() won't always clean up the memory and unless you're running on an insultingly tiny memory server then you shouldn't be getting out of memory just from loading fonts anyway.

I suspect it's something to do with your P/Invoke calls.

ok thanks - i will take a look at how I should deallocate the memory for those calls. That was pretty much the only bit of code in the class i didnt write too! nicked it from a tutorial, but serves me right for not fully understanding what it was doing.

thanks

You need to read about IDisposable . You were leaking objects that implemented IDisposable and had unmanaged resources. When you call GC.Collect() and unreferenced objects are detected with finalizers then the finalizer *should* be invoked but the object will not be destroyed until the finalizer has ran, pushing it back another generation in garbage collection. Note that the CLR does not guarantee that finalizers will be invoked. You can make calls to GC.WaitForPendingFinalizers but also note that method is not guaranteed to ever return -- it could block indefinetly unless you run it on another thread and call Thread.Abort() after you grow tired of waiting.

Bottom line is -- its worth it to just clean up and not worry about that stuff!

This version of your code couldn't leak memory. Notice I also implemented IDisposable on your FontSearcher class. You need to call Dispose(); from within your web application.

This is what I used for testing (note the using() construct when instantiating FontSearcher)

private void button7_Click(object sender, EventArgs e)
    {
      System.Threading.ThreadPool.QueueUserWorkItem(s => DoWork());
    }

    private void DoWork()
    {
      for (int i1 = 0; i1 < 500; i1++)
      {
        List<string> lst = new List<string>();
        lst.Add("Arial");
        lst.Add("Times New Roman");
        using (FontSearcher fs = new FontSearcher(lst))
        {
          fs.GetFontInfoFromChar('a');
          fs.GetFontInfoFromString("a");
        }

        this.Invoke(new Action(delegate()
          {
            button7.Text = i1.ToString("F0");
          }));
      }
      MessageBox.Show("Done");
    }

The font searcher:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Text;

namespace daniweb
{
  public class FontSearcher : IDisposable
  {

    #region Imports
    [DllImport("gdi32.dll")]
    public static extern uint GetFontUnicodeRanges(IntPtr hdc, IntPtr lpgs);
    [DllImport("gdi32.dll")]
    public extern static IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
    [DllImport("GDI32.dll")]
    public static extern bool DeleteObject(IntPtr objectHandle); 
    #endregion

    #region Class Vars
    private List<string> __PreferredFontNames;
    private FontInfoList __PreferredFontInfos;
    private FontInfoList __NonPreferredFontInfos;
    List<FontInfoList> __FiLists = new List<FontInfoList>();
    #endregion

    #region Constructor
    public FontSearcher(List<string> preferredFontNames)
    {
      __PreferredFontNames = preferredFontNames;
      BuildFontInfos();
      __FiLists = new List<FontInfoList>();
      __FiLists.Add(__PreferredFontInfos);
      __FiLists.Add(__NonPreferredFontInfos);
    }
    #endregion

    #region Methods...
    #region Private Members
    private void BuildFontInfos()
    {
      #region Vars Setup
      __PreferredFontInfos = new FontInfoList();
      __NonPreferredFontInfos = new FontInfoList();
      using (InstalledFontCollection fonts = new InstalledFontCollection())
      {
      #endregion
        #region Build FontInfo Objects
        for (int i = 0; i < fonts.Families.Length; i++)
        {
          try
          {
            Font _font = null;
            if (fonts.Families[i].IsStyleAvailable(FontStyle.Regular))
              _font = new Font(fonts.Families[i].Name, 10, FontStyle.Regular);
            else if (fonts.Families[i].IsStyleAvailable(FontStyle.Bold))
              _font = new Font(fonts.Families[i].Name, 10, FontStyle.Bold);
            else if (fonts.Families[i].IsStyleAvailable(FontStyle.Italic))
              _font = new Font(fonts.Families[i].Name, 10, FontStyle.Italic);
            if (__PreferredFontNames.Contains(_font.Name))
            {
              try
              {
                __PreferredFontInfos.Add(new FontInfo(_font, GetUnicodeRangesForFont(_font)));
              }
              catch (Exception ex)
              {
                Console.WriteLine(ex.Message);
              }
            }
            else
            {
              try
              {
                __NonPreferredFontInfos.Add(new FontInfo(_font, GetUnicodeRangesForFont(_font)));
              }
              catch (Exception ex)
              {
                Console.WriteLine(ex.Message);
              }
            }
          }
          catch (Exception ex)
          {
            Console.WriteLine(ex.Message);
          }
        }
      }
        #endregion
      #region Re-Order __PreferredFontInfos to match the order of __PreferredFontNames
      FontInfoList __OrderedPreferedFontInfos = new FontInfoList();
      int d = 0;
      while (__OrderedPreferedFontInfos.Count < __PreferredFontInfos.Count)
      {
        for (int i = 0; i < __PreferredFontInfos.Count; i++)
        {
          if (__PreferredFontInfos[i].Font.Name == __PreferredFontNames[d])
          {
            __OrderedPreferedFontInfos.Add(__PreferredFontInfos[i]);
          }
        }
        d++;
      }
      __PreferredFontInfos = __OrderedPreferedFontInfos;
      #endregion
    }
    private List<FontRange> GetUnicodeRangesForFont(Font font)
    {
      using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
      {
        IntPtr hdc = IntPtr.Zero; //ok
        IntPtr hFont = IntPtr.Zero; //ok
        IntPtr old = IntPtr.Zero;
        IntPtr glyphSet = IntPtr.Zero; //ok

        try
        {
          hdc = g.GetHdc();
          hFont = font.ToHfont();
          old = SelectObject(hdc, hFont);
          uint size = GetFontUnicodeRanges(hdc, IntPtr.Zero);
          glyphSet = Marshal.AllocHGlobal((int)size);
          GetFontUnicodeRanges(hdc, glyphSet);
          List<FontRange> fontRanges = new List<FontRange>();
          int count = Marshal.ReadInt32(glyphSet, 12);
          for (int i = 0; i < count; i++)
          {
            FontRange range = new FontRange();
            range.Low = (UInt16)Marshal.ReadInt16(glyphSet, 16 + i * 4);
            range.High = (UInt16)(range.Low + Marshal.ReadInt16(glyphSet, 18 + i * 4) - 1);
            fontRanges.Add(range);
          }
          SelectObject(hdc, old);
          return fontRanges;
        }
        finally
        {
          if (hdc != IntPtr.Zero)
          {
            g.ReleaseHdc(hdc);
          }
          if (glyphSet != IntPtr.Zero)
          {
            Marshal.FreeHGlobal(glyphSet);
          }
          if (hFont != IntPtr.Zero)
          {
            DeleteObject(hFont);
            hFont = IntPtr.Zero;
          }
        }
      }
    }
    private bool CheckIfCharInFontInfo(char character, FontInfo fontInfo)
    {
      UInt16 intval = Convert.ToUInt16(character);
      List<FontRange> ranges = fontInfo.Ranges;
      bool isCharacterPresent = false;
      foreach (FontRange range in ranges)
      {
        if (intval >= range.Low && intval <= range.High)
        {
          isCharacterPresent = true;
          break;
        }
      }
      return isCharacterPresent;
    }
    private FontInfo CheckBestFont(List<FontInfo> fontInfos, string str)
    {
      #region Vars Setup
      FontInfo _fontInfo = null;
      List<int> _occs = new List<int>();
      int _occ = 0;
      #endregion
      #region Build List containing number of matching glyphs contained within each FontInfo
      for (int i = 0; i < fontInfos.Count; i++)
      {
        for (int c = 0; c < str.Length; c++)
        {
          if (CheckIfCharInFontInfo(str[c], fontInfos[i]))
          {
            _occ++;
          }
        }
        _occs.Add(_occ);
        _occ = 0;
      }
      #endregion
      #region Get the FontInfo with the highest number of glyph matches
      if (_occs.Count > 0)
      {
        int _fiOcc = _occs[0];
        int _fiIdx = 0;
        for (int i = 1; i < _occs.Count; i++)
        {
          if (_occs[i] > _fiOcc)
          {
            _fiOcc = _occs[i];
            _fiIdx = i;
          }
        }
        _fontInfo = fontInfos[_fiIdx];
        if (!__PreferredFontInfos.Contains(_fontInfo))
        {
          __PreferredFontInfos.Add(_fontInfo);
          __NonPreferredFontInfos.Remove(_fontInfo);
        }
      }
      #endregion
      return _fontInfo;
    }
    private FontInfo CheckBestFont(List<FontInfo> fontInfos, string str, string preferredFontName)
    {
      #region Vars Setup
      FontInfo _fontInfo = null;
      List<int> _occs = new List<int>();
      int _occ = 0;
      #endregion
      #region Build List containing number of matching glyphs contained within each FontInfo
      for (int i = 0; i < fontInfos.Count; i++)
      {
        for (int c = 0; c < str.Length; c++)
        {
          if (CheckIfCharInFontInfo(str[c], fontInfos[i]))
          {
            _occ++;
          }
        }
        _occs.Add(_occ);
        _occ = 0;
      }
      #endregion
      #region Get the FontInfo with the highest number of glyph matches
      if (_occs.Count > 0)
      {
        int _fiOcc = _occs[0];
        int _fiIdx = 0;
        for (int i = 1; i < _occs.Count; i++)
        {
          if (_occs[i] > 0 && fontInfos[i].Font.Name == preferredFontName)
          {
            _fiIdx = i;
            break;
          }
          if (_occs[i] > _fiOcc)
          {
            _fiOcc = _occs[i];
            _fiIdx = i;
          }
        }
        _fontInfo = fontInfos[_fiIdx];
        if (!__PreferredFontInfos.Contains(_fontInfo))
        {
          __PreferredFontInfos.Add(_fontInfo);
          __NonPreferredFontInfos.Remove(_fontInfo);
        }
      }
      #endregion
      return _fontInfo;
    }
    private string StripTextFormattingChars(string str)
    {
      str = str.Replace("\r", "");
      str = str.Replace("\t", "");
      str = str.Replace("\n", "");
      return str;
    }
    #endregion
    #region Public Members
    public FontInfo GetFontInfoFromChar(char character)
    {
      FontInfo _fontInfo = null;
      int fi = 0;
      while (fi < __FiLists.Count && _fontInfo == null)
      {
        for (int i = 0; i < __FiLists[fi].Count; i++)
        {
          if (CheckIfCharInFontInfo(character, __FiLists[fi][i]))
          {
            _fontInfo = __FiLists[fi][i];
            break;
          }
        }
        fi++;
      }
      return _fontInfo;
    }
    public FontInfo GetFontInfoFromChar(char character, string preferredFontName)
    {
      FontInfo _fontInfo = null;
      int fi = 0;
      while (fi < __FiLists.Count && _fontInfo == null)
      {
        for (int i = 0; i < __FiLists[fi].Count; i++)
        {
          if (__FiLists[fi][i].Font.Name == preferredFontName)
          {
            if (CheckIfCharInFontInfo(character, __FiLists[fi][i]))
            {
              _fontInfo = __FiLists[fi][i];
              break;
            }
          }
        }
        if (_fontInfo == null)
        {
          for (int i = 0; i < __FiLists[fi].Count; i++)
          {
            if (__FiLists[fi][i].Font.Name == preferredFontName)
            {

            }
            if (CheckIfCharInFontInfo(character, __FiLists[fi][i]))
            {
              _fontInfo = __FiLists[fi][i];
              break;
            }
          }
        }
        fi++;
      }
      return _fontInfo;
    }
    public FontInfo GetFontInfoFromChar(char character, List<FontInfo> fontInfos)
    {
      FontInfo _fontInfo = null;
      for (int i = 0; i < fontInfos.Count; i++)
      {
        if (CheckIfCharInFontInfo(character, fontInfos[i]))
        {
          _fontInfo = fontInfos[i];
          break;
        }
      }
      return _fontInfo;
    }
    public FontInfo GetFontInfoFromString(string str)
    {
      #region Vars Setup
      str = StripTextFormattingChars(str);
      List<FontInfo> _fontsInString = new List<FontInfo>();
      #endregion
      #region Build List of Fonts required by str
      for (int i = 0; i < str.Length; i++)
      {
        char _c = str[i];
        FontInfo _charFI = GetFontInfoFromChar(_c);
        if (_charFI != null)
        {
          if (_fontsInString.Count > 0)
          {
            if (!_fontsInString.Contains(_charFI))
            {
              _fontsInString.Add(_charFI);
            }
          }
          else
          {
            _fontsInString.Add(_charFI);
          }
        }
      }
      #endregion
      #region Select the Font to use
      if (_fontsInString.Count > 0)
      {
        if (_fontsInString.Count > 1)
        {
          FontInfo _fontInfo = CheckBestFont(_fontsInString, str);
          return _fontInfo;
        }
        else
        {
          return _fontsInString[0];
        }
      }
      else
      {
        return new FontInfo(new Font("Arial", 10, FontStyle.Regular), null);
      }
      #endregion
    }
    public FontInfo GetFontInfoFromString(string str, string preferredFontName)
    {
      #region Vars Setup
      str = StripTextFormattingChars(str);
      List<FontInfo> _fontsInString = new List<FontInfo>();
      #endregion
      #region Build List of Fonts required by str
      for (int i = 0; i < str.Length; i++)
      {
        char _c = str[i];
        FontInfo _charFI = GetFontInfoFromChar(_c, preferredFontName);
        if (_charFI != null)
        {
          if (_fontsInString.Count > 0)
          {
            if (!_fontsInString.Contains(_charFI))
            {
              _fontsInString.Add(_charFI);
            }
          }
          else
          {
            _fontsInString.Add(_charFI);
          }
        }
      }
      #endregion
      #region Select the Font to use
      if (_fontsInString.Count > 0)
      {
        if (_fontsInString.Count > 1)
        {
          FontInfo _fontInfo = CheckBestFont(_fontsInString, str, preferredFontName);
          return _fontInfo;
        }
        else
        {
          return _fontsInString[0];
        }
      }
      else
      {
        return new FontInfo(new Font("Arial", 10, FontStyle.Regular), null);
      }
      #endregion
    }
    public bool ClearAll()
    {
      try
      {
        __PreferredFontNames.Clear();
        //__PreferredFontNames = null;
        __PreferredFontInfos.ForEach(fi => fi.Dispose());

        __PreferredFontInfos.Clear();
        //__PreferredFontInfos = null;
        __NonPreferredFontInfos.ForEach(fi => fi.Dispose());
        __NonPreferredFontInfos.Clear();
        //__NonPreferredFontInfos = null;
        __FiLists.Clear();
        //__FiLists = null;
        //GC.Collect();
        return true;
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.Message);
        return false;
      }
    }

    #endregion
    #endregion

    #region Properties
    public FontInfoList NonPreferredFontInfos
    {
      get
      {
        return __NonPreferredFontInfos;
      }
      set
      {
        __NonPreferredFontInfos = value;
      }
    }
    public FontInfoList PreferredFontInfos
    {
      get
      {
        return __PreferredFontInfos;
      }
      set
      {
        __PreferredFontInfos = value;
      }
    }
    #endregion


    #region IDisposable Members

    public void Dispose()
    {
      ClearAll();
    }

    #endregion
  }
  #region FontInfo Class
  public class FontInfo : IDisposable
  {
    #region Class Vars
    private Font __Font;
    private List<FontRange> __Ranges;
    #endregion
    #region Constructor
    public FontInfo(Font font, List<FontRange> ranges)
    {
      __Font = font;
      __Ranges = ranges;
    }
    #endregion
    #region Properties
    public Font Font
    {
      get
      {
        return __Font;
      }
    }
    public List<FontRange> Ranges
    {
      get
      {
        return __Ranges;
      }
    }
    #endregion

    #region IDisposable Members

    public void Dispose()
    {
      if (__Font != null)
      {
        __Font.Dispose();
        __Font = null;
      }
    }

    #endregion
  }
  #endregion
  #region FontInfoList Class
  public class FontInfoList : List<FontInfo>
  {
    #region Constructor
    public FontInfoList()
    {

    }
    #endregion
    #region Methods
    public FontInfo GetFontInfoByFont(Font font)
    {
      FontInfo _fi = null;
      for (int i = 0; i < this.Count; i++)
      {
        if (this[i].Font == font)
        {
          _fi = this[i];
          break;
        }
      }
      return _fi;
    }
    #endregion
  }
  #endregion
  #region FontRange struct
  public struct FontRange
  {
    public UInt16 Low;
    public UInt16 High;
  }
  #endregion
}
This article has been dead for over six months. Start a new discussion instead.