Joseph Michael Pesch
VP Programming

Get System Font List in C# Using Shell32.dll

by 31. December 2008 16:06

Creates a key/value dictionary of FontName/FontFile from the system fonts.  Also, contains a method to that accepts an output path which will be filled with image files (.png format) of sample text of each of the fonts. 

Original source of API code: http://blog.debreuil.com/archive/2007/05/31/5108.aspx

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
 
// Call the FontUtil.FontTool.BuildSampleFontImages() to generate sample image files of the fonts.
 
/*
 // Sample usage:
   FontUtil.FontList fnt = FontUtil.FontList.GetInstance();
   IEnumerator en = fnt.FontMap.GetEnumerator();
   while (en.MoveNext())
   {
     string FontName = ((System.Collections.Generic.KeyValuePair<string, string>)en.Current).Key;
     string FontFile = ((System.Collections.Generic.KeyValuePair<string, string>)en.Current).Value;
   }
 */
 
namespace FontUtil
{
   public static class FontTool
   {
     public static void BuildSampleFontImages(string OutputPath)
     {
      
       // The output path will be filled with image files (.png format) of sample text
       // of each of the fonts in the system that don't contain the words " bold" or " italic"
       // Also, checks after setting the since some of the fonts don't seem to register (see "Font Error" section).
       // NOTE: It will delete any existing image file with the same name before creating the new image.
 
       Font font = new Font("Times", 20);
       string FilePath = OutputPath;
 
       FontUtil.FontList fnt = FontUtil.FontList.GetInstance();
       IEnumerator en = fnt.FontMap.GetEnumerator();
       while (en.MoveNext())
       {
         string text = ((System.Collections.Generic.KeyValuePair<string, string>)en.Current).Key;
         try
         {
           // Skip bold and/or italic versions of the fonts.
           if (text.ToLower().Contains(" bold") || text.ToLower().Contains(" italic"))
             continue;
           font = new Font(text, 12);
           if (font.FontFamily.Name.ToLower() != text.ToLower())
           {
             System.Diagnostics.Debug.WriteLine("Font Error: " + text);
             continue;
           }
           FilePath = System.Web.HttpContext.Current.Server.MapPath("") + @"\images\fonts\" + text.Replace(" ", "_") + ".png";
           text += (char)10 + "1234567890";
           text += (char)10 + "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.";
           text += (char)10 + "The quick brown fox jumps over the lazy dog.";
           CreateTextImage(text, font, FilePath);
         }
         catch (Exception ex)
         {
           System.Diagnostics.Debug.WriteLine(ex.Message);
         }
       }
 
     }
     private static void CreateTextImage(string text, Font font, string FilePath)
     {
       Graphics g = Graphics.FromImage(new Bitmap(10, 10));
       SizeF sf = g.MeasureString(text, font);
       Bitmap bmp = new Bitmap(Convert.ToInt16(sf.Width), Convert.ToInt16(sf.Height));
 
       g = Graphics.FromImage(bmp);
       StringFormat strFormat = new StringFormat();
       strFormat.Alignment = StringAlignment.Center;
       strFormat.LineAlignment = StringAlignment.Center;
       // Color bgColor = Color.FromArgb(50, Color.Black); // 50% transparent black
       Color bgColor = Color.Transparent;
       g.FillRectangle(new SolidBrush(bgColor), 0, 0, Convert.ToInt16(sf.Width), Convert.ToInt16(sf.Height));
       g.DrawString(text, font, Brushes.Black,
        new RectangleF(0, 0, sf.Width, sf.Height), strFormat);
       g.Save();
 
       File.Delete(FilePath);
       bmp.Save(FilePath, ImageFormat.Png);
 
     }
 
   }
 
   public class FontList
   {
     public Dictionary<string, string> FontMap = new Dictionary<string, string>();
 
     private static FontList inst;
     private FontList()
     {
     }
 
     public static FontList GetInstance()
     {
       if (inst == null)
       {
         inst = new FontList();
         inst.GetAllFonts();
       }
       return inst;
     }
 
     private void GetAllFonts()
     {
       //string fp = Environment.GetFolderPath((Environment.SpecialFolder) 0x14);//CSIDL_FONTS = 0x0014
 
       StringBuilder sb = new StringBuilder();
       SHGetFolderPath(IntPtr.Zero, 0x0014, IntPtr.Zero, 0x0000, sb);//CSIDL_FONTS = 0x0014
       string fontDir = sb.ToString();
 
       if (!Directory.Exists(fontDir))
       {
         return;
       }
       string[] fonts = Directory.GetFiles(fontDir);
       for (int i = 0; i < fonts.Length; i++)
       {
         string name = GetFontName(fonts[i]);
         if (name != "")
         {
           FontMap.Add(name, fonts[i]);
         }
       }
     }
 
 
     private TT_OFFSET_TABLE ttResult;
     private TT_TABLE_DIRECTORY tbName;
     private TT_NAME_TABLE_HEADER ttNTResult;
     private TT_NAME_RECORD ttNMResult;
 
     private string GetFontName(string fontPath)
     {
       string result = "";
 
       FileStream fs = new FileStream(fontPath, FileMode.Open, FileAccess.Read);
       BinaryReader r = new BinaryReader(fs, Encoding.UTF8);
       byte[] buff = r.ReadBytes(Marshal.SizeOf(ttResult));
       buff = BigEndian(buff);
       IntPtr ptr = Marshal.AllocHGlobal(buff.Length);
       Marshal.Copy(buff, 0x0, ptr, buff.Length);
       ttResult = (TT_OFFSET_TABLE)Marshal.PtrToStructure(ptr, typeof(TT_OFFSET_TABLE));
       Marshal.FreeHGlobal(ptr);
 
       //Must be maj =1 minor = 0
       if (ttResult.uMajorVersion != 1 || ttResult.uMinorVersion != 0)
         return "";
 
       bool bFound = false;
       tbName = new TT_TABLE_DIRECTORY();
       for (int i = 0; i < ttResult.uNumOfTables; i++)
       {
         byte[] bNameTable = r.ReadBytes(Marshal.SizeOf(tbName));
         IntPtr ptrName = Marshal.AllocHGlobal(bNameTable.Length);
         Marshal.Copy(bNameTable, 0x0, ptrName, bNameTable.Length);
         tbName = (TT_TABLE_DIRECTORY)Marshal.PtrToStructure(ptrName, typeof(TT_TABLE_DIRECTORY));
         Marshal.FreeHGlobal(ptrName);
         string szName =
           tbName.szTag1.ToString() +
           tbName.szTag2.ToString() +
           tbName.szTag3.ToString() +
           tbName.szTag4.ToString();
         if (szName != null)
         {
           if (szName.ToString() == "name")
           {
             bFound = true;
             byte[] btLength = BitConverter.GetBytes(tbName.uLength);
             byte[] btOffset = BitConverter.GetBytes(tbName.uOffset);
             Array.Reverse(btLength);
             Array.Reverse(btOffset);
             tbName.uLength = BitConverter.ToUInt32(btLength, 0);
             tbName.uOffset = BitConverter.ToUInt32(btOffset, 0);
             break;
           }
         }
       }
       if (bFound)
       {
         fs.Position = tbName.uOffset;
         byte[] btNTHeader = r.ReadBytes(Marshal.SizeOf(ttNTResult));
         btNTHeader = BigEndian(btNTHeader);
         IntPtr ptrNTHeader = Marshal.AllocHGlobal(btNTHeader.Length);
         Marshal.Copy(btNTHeader, 0x0, ptrNTHeader, btNTHeader.Length);
         ttNTResult = (TT_NAME_TABLE_HEADER)Marshal.PtrToStructure(ptrNTHeader, typeof(TT_NAME_TABLE_HEADER));
         Marshal.FreeHGlobal(ptrNTHeader);
         bFound = false;
         for (int i = 0; i < ttNTResult.uNRCount; i++)
         {
           byte[] btNMRecord = r.ReadBytes(Marshal.SizeOf(ttNMResult));
           btNMRecord = BigEndian(btNMRecord);
           IntPtr ptrNMRecord = Marshal.AllocHGlobal(btNMRecord.Length);
           Marshal.Copy(btNMRecord, 0x0, ptrNMRecord, btNMRecord.Length);
           ttNMResult = (TT_NAME_RECORD)Marshal.PtrToStructure(ptrNMRecord, typeof(TT_NAME_RECORD));
           Marshal.FreeHGlobal(ptrNMRecord);
 
           // this is where the font name is recovered
           // to get the font family name (not incl 'bold' etc) use ttNMResult.uNameID == 1
           // see http://www.microsoft.com/OpenType/OTSpec/name.htm
           if (ttNMResult.uNameID == 4)
           {
             long fPos = fs.Position;
             fs.Position = tbName.uOffset + ttNMResult.uStringOffset + ttNTResult.uStorageOffset;
             char[] szResult = r.ReadChars(ttNMResult.uStringLength);
             if (szResult.Length != 0)
             {
               // some fonts are \0 A \0 r \0 i \0 a \0 l.... UTf8 encoding doesn't help
               if (szResult[0] == '\0')
               {
                 int count = 0;
                 char[] temp = new char[szResult.Length / 2];
                 for (int j = 1; j < szResult.Length; j += 2)
                 {
                   temp[count++] = szResult[j];
                 }
                 szResult = temp;
               }
               result = new String(szResult);
               break;
             }
           }
         }
       }
       return result;
     }
     private byte[] BigEndian(byte[] bLittle)
     {
       byte[] bBig = new byte[bLittle.Length];
       for (int y = 0; y < (bLittle.Length - 1); y += 2)
       {
         byte b1, b2;
         b1 = bLittle[y];
         b2 = bLittle[y + 1];
         bBig[y] = b2;
         bBig[y + 1] = b1;
       }
       return bBig;
     }
 
     [DllImport("shell32.dll")]
     static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken,
      uint dwFlags, [Out] StringBuilder pszPath);
 
     [StructLayout(LayoutKind.Sequential, Pack = 0x1)]
     struct TT_OFFSET_TABLE
     {
       public ushort uMajorVersion;
       public ushort uMinorVersion;
       public ushort uNumOfTables;
       public ushort uSearchRange;
       public ushort uEntrySelector;
       public ushort uRangeShift;
 
     }
     [StructLayout(LayoutKind.Sequential, Pack = 0x1)]
     struct TT_TABLE_DIRECTORY
     {
       public char szTag1;
       public char szTag2;
       public char szTag3;
       public char szTag4;
       public uint uCheckSum; //Check sum
       public uint uOffset; //Offset from beginning of file
       public uint uLength; //length of the table in bytes
     }
     [StructLayout(LayoutKind.Sequential, Pack = 0x1)]
     struct TT_NAME_TABLE_HEADER
     {
       public ushort uFSelector;
       public ushort uNRCount;
       public ushort uStorageOffset;
     }
     [StructLayout(LayoutKind.Sequential, Pack = 0x1)]
     struct TT_NAME_RECORD
     {
       public ushort uPlatformID;
       public ushort uEncodingID;
       public ushort uLanguageID;
       public ushort uNameID;
       public ushort uStringLength;
       public ushort uStringOffset;
     }
 
   }
 

}

Tags: ,

C# | Windows

Comments are closed