.Net enumerate winforms font styles?

Okay, this is going to be a lot of code below. Primarily, it is because of the TTF structures and Endianess of TTF files. The code is not originally mine, it's come from a few sources that I've ported to VB.NET and changed a few things around. See this page for a C++ version that gets the font name.

This code reads through the registry for installed fonts (whether in %windir%\fonts or elsewhere), filters to only get ones with the .ttf extension (e.g. .fon and .ttc are ignored) and then it passes these font file paths to a routine, GetFontDetails, that reads through and gets the Font Name (uNameID #1) and Font Sub Family (aka Style, uNameID #2). If you are interested in getting more properties than those, go to name - Naming Table on Microsoft's Typography website and search in your browser for Name IDs. It then kicks out the Font Name, Font SubFamily and Font Path to the Console window.

Create a new VB.NET Console app and paste the below in over Module1 code and press F5.

Without further ado:

Imports System.Linq
Imports System.IO
Imports System.Text

Module Module1
    Sub Main()
        Dim allInstalledFonts = From e In My.Computer.Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts").GetValueNames
                                 Select My.Computer.Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts").GetValue(e)
        Dim ttfFonts = From e In allInstalledFonts.Where(Function(e) e.ToString.EndsWith(".ttf") Or e.ToString.EndsWith(".otf"))
        Dim ttfFontsPaths = From e In ttfFonts.Select(Function(e) If(Path.GetPathRoot(e.ToString) = "", Environment.GetFolderPath(Environment.SpecialFolder.Fonts) & "\" & e.ToString, e.ToString))
        Dim fonts = From e As String In ttfFontsPaths Select GetFontDetails(e.ToString)

        For Each f As InstalledFont In fonts
            Console.WriteLine("Name: " & f.FontName & ", SubFamily: " & f.FontSubFamily & ", Path: " & f.FontPath)
        Next
        Console.ReadLine()
    End Sub
    Public Class InstalledFont
        Property FontName As String
        Property FontSubFamily As String
        Property FontPath As String
        Sub New(ByVal name As String, ByVal subfamily As String, ByVal path As String)
            FontName = name
            FontSubFamily = subfamily
            FontPath = path
        End Sub
    End Class
    Public Function GetFontDetails(ByVal fontFilePath As String) As InstalledFont
        Dim FontName As String = String.Empty
        Dim FontSubFamily As String = String.Empty
        Dim encStr = "UTF-8"
        Dim strRet As String = String.Empty

        Using fs As New FileStream(fontFilePath, FileMode.Open, FileAccess.Read)
            Dim ttOffsetTable As New TT_OFFSET_TABLE
            With ttOffsetTable
                .uMajorVersion = ReadUShort(fs)
                .uMinorVersion = ReadUShort(fs)
                .uNumOfTables = ReadUShort(fs)
                .uSearchRange = ReadUShort(fs)
                .uEntrySelector = ReadUShort(fs)
                .uRangeShift = ReadUShort(fs)
            End With

            If ttOffsetTable.uMajorVersion <> 1 Or ttOffsetTable.uMinorVersion <> 0 Then
                Return Nothing
            End If

            Dim tblDir As New TT_TABLE_DIRECTORY
            Dim found As Boolean = False

            For i As Integer = 0 To ttOffsetTable.uNumOfTables
                With tblDir
                    .Initialize()
                    fs.Read(.szTag, 0, .szTag.Length)
                    .uCheckSum = ReadULong(fs)
                    .uOffset = ReadULong(fs)
                    .uLength = ReadULong(fs)
                End With

                Dim enc As Encoding = Encoding.GetEncoding(encStr)

                Dim s As String = enc.GetString(tblDir.szTag)
                If StrComp(s, "name") = 0 Then
                    found = True
                    Exit For
                End If
            Next

            If Not found Then Return Nothing

            fs.Seek(tblDir.uOffset, SeekOrigin.Begin)
            Dim ttNTHeader As New TT_NAME_TABLE_HEADER
            With ttNTHeader
                .uFSelector = ReadUShort(fs)
                .uNRCount = ReadUShort(fs)
                .uStorageOffset = ReadUShort(fs)
            End With

            Dim ttRecord As New TT_NAME_RECORD

            For j As Integer = 0 To ttNTHeader.uNRCount
                With ttRecord
                    .uPlatformID = ReadUShort(fs)
                    .uEncodingID = ReadUShort(fs)
                    .uLanguageID = ReadUShort(fs)
                    .uNameID = ReadUShort(fs)
                    .uStringLength = ReadUShort(fs)
                    .uStringOffset = ReadUShort(fs)
                End With

                If ttRecord.uNameID > 2 Then Exit For

                Dim nPos As Integer = fs.Position
                fs.Seek(tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset, SeekOrigin.Begin)

                Dim buf(ttRecord.uStringLength - 1) As Byte
                fs.Read(buf, 0, ttRecord.uStringLength)

                Dim enc As Encoding
                If ttRecord.uEncodingID = 3 Or ttRecord.uEncodingID = 1  Then
                    enc = Encoding.BigEndianUnicode
                Else
                    enc = Encoding.UTF8
                End If

                strRet = enc.GetString(buf)
                If ttRecord.uNameID = 1 Then FontName = strRet
                If ttRecord.uNameID = 2 Then FontSubFamily = strRet
                fs.Seek(nPos, SeekOrigin.Begin)
            Next
            Return New InstalledFont(FontName, FontSubFamily, fontFilePath)
        End Using
    End Function
    Public Structure TT_OFFSET_TABLE
        Public uMajorVersion As UShort
        Public uMinorVersion As UShort
        Public uNumOfTables As UShort
        Public uSearchRange As UShort
        Public uEntrySelector As UShort
        Public uRangeShift As UShort
    End Structure
    Public Structure TT_TABLE_DIRECTORY
        Public szTag() As Byte
        Public uCheckSum As UInt32
        Public uOffset As UInt32
        Public uLength As UInt32
        Public Sub Initialize()
            ReDim szTag(3)
        End Sub
    End Structure
    Public Structure TT_NAME_TABLE_HEADER
        Public uFSelector As UShort
        Public uNRCount As UShort
        Public uStorageOffset As UShort
    End Structure
    Public Structure TT_NAME_RECORD
        Public uPlatformID As UShort
        Public uEncodingID As UShort
        Public uLanguageID As UShort
        Public uNameID As UShort
        Public uStringLength As UShort
        Public uStringOffset As UShort
    End Structure
    Private Function ReadChar(ByRef fs As FileStream, ByVal characters As Integer) As UInt16
        Dim s(characters) As String
        Dim buf(CByte(s.Length)) As Byte
        buf = ReadAndSwap(fs, buf.Length)
        Return BitConverter.ToUInt16(buf, 0)
    End Function
    Private Function ReadByte(ByRef fs As FileStream) As UInt16
        Dim buf(10) As Byte
        buf = ReadAndSwap(fs, buf.Length)
        Return BitConverter.ToUInt16(buf, 0)
    End Function
    Private Function ReadUShort(ByRef fs As FileStream) As UInt16
        Dim buf(1) As Byte
        buf = ReadAndSwap(fs, buf.Length)
        Return BitConverter.ToUInt16(buf, 0)
    End Function
    Private Function ReadULong(ByRef fs As FileStream) As UInt32
        Dim buf(3) As Byte
        buf = ReadAndSwap(fs, buf.Length)
        Return BitConverter.ToUInt32(buf, 0)
    End Function
    Private Function ReadAndSwap(ByRef fs As FileStream, ByVal size As Integer) As Byte()
        Dim buf(size - 1) As Byte
        fs.Read(buf, 0, buf.Length)
        Array.Reverse(buf)
        Return buf
    End Function
End Module

Thanks Otaku and All... Took Otaku's code and ported to c# for any one that is interested...but didn't get to the OTF update. Main changes were just byte array adjustments (VB pads arrays), lambda expressions and the usual syntax changes...

    using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Win32;

namespace InstalledFontsInSystem
{
    class InstalledFont
    {
        #region InstalledFont Parameters

        string _fontName = string.Empty;
        string _fontSubFamily = string.Empty;
        string _fontPath = string.Empty;

        #endregion

        #region InstalledFont Constructor

        public InstalledFont(string fontName, string fontSubFamily, string fontPath)
        {
            _fontName = fontName;
            _fontSubFamily = fontSubFamily;
            _fontPath = fontPath;
        }

        #endregion

        #region InstalledFont Properties

        public string FontName { get { return _fontName; } set { _fontName = value; } }
        public string FontSubFamily { get { return _fontSubFamily; } set { _fontSubFamily = value; } }
        public string FontPath { get { return _fontPath; } set { _fontPath = value; } }

        #endregion
    }

    class Program
    {
        static void Main(string[] args)
        {
            var allInstalledFonts = from e in Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", false).GetValueNames()
                                    select Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts").GetValue(e);

            var ttfFonts = from e in allInstalledFonts.Where(e => (e.ToString().EndsWith(".ttf") || e.ToString().EndsWith(".otf"))) select e;
            var ttfFontsPaths = from e in ttfFonts.Select(e => (Path.GetPathRoot(e.ToString()) == "") ? Environment.GetFolderPath(Environment.SpecialFolder.Fonts) + "\\" + e.ToString() : e.ToString()) select e;
            var fonts = from e in ttfFontsPaths.Select(e => GetFontDetails(e.ToString())) select e;

            foreach (InstalledFont f in fonts)
            {
                if(f != null)
                    Console.WriteLine("Name: " + f.FontName + ", SubFamily: " + f.FontSubFamily + ", Path: " + f.FontPath);
            }

            Console.ReadLine();
        }

        static public InstalledFont GetFontDetails(string fontFilePath)
        {
            string FontName = string.Empty;
            string FontSubFamily = string.Empty;
            string encStr = "UTF-8";
            string strRet = string.Empty;

            using (FileStream fs = new FileStream(fontFilePath, FileMode.Open, FileAccess.Read))
            {

                TT_OFFSET_TABLE ttOffsetTable = new TT_OFFSET_TABLE()
                {
                    uMajorVersion = ReadUShort(fs),
                    uMinorVersion = ReadUShort(fs),
                    uNumOfTables = ReadUShort(fs),
                    uSearchRange = ReadUShort(fs),
                    uEntrySelector = ReadUShort(fs),
                    uRangeShift = ReadUShort(fs),
                };

                if (ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
                {
                    return null;
                }


                TT_TABLE_DIRECTORY tblDir = new TT_TABLE_DIRECTORY();
                bool found = false;
                for (int i = 0; i <= ttOffsetTable.uNumOfTables; i++)
                {
                    tblDir = new TT_TABLE_DIRECTORY();
                    tblDir.Initialize();
                    fs.Read(tblDir.szTag, 0, tblDir.szTag.Length);
                    tblDir.uCheckSum = ReadULong(fs);
                    tblDir.uOffset = ReadULong(fs);
                    tblDir.uLength = ReadULong(fs);

                    Encoding enc = Encoding.GetEncoding(encStr);
                    string s = enc.GetString(tblDir.szTag);

                    if (s.CompareTo("name") == 0)
                    {
                        found = true;
                        break;
                    }
                }

                if (!found) return null;

                fs.Seek(tblDir.uOffset, SeekOrigin.Begin);

                TT_NAME_TABLE_HEADER ttNTHeader = new TT_NAME_TABLE_HEADER
                {
                    uFSelector = ReadUShort(fs),
                    uNRCount = ReadUShort(fs),
                    uStorageOffset = ReadUShort(fs)
                };

                TT_NAME_RECORD ttRecord = new TT_NAME_RECORD();

                for (int j = 0; j <= ttNTHeader.uNRCount; j++)
                {
                    ttRecord = new TT_NAME_RECORD()
                    {
                        uPlatformID = ReadUShort(fs),
                        uEncodingID = ReadUShort(fs),
                        uLanguageID = ReadUShort(fs),
                        uNameID = ReadUShort(fs),
                        uStringLength = ReadUShort(fs),
                        uStringOffset = ReadUShort(fs)
                    };

                    if (ttRecord.uNameID > 2) { break; }

                    long nPos = fs.Position;
                    fs.Seek(tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset, SeekOrigin.Begin);

                    byte[] buf = new byte[ttRecord.uStringLength];
                    fs.Read(buf, 0, ttRecord.uStringLength);

                    Encoding enc;
                    if (ttRecord.uEncodingID == 3 || ttRecord.uEncodingID == 1)
                    {
                        enc = Encoding.BigEndianUnicode;
                    }
                    else
                    {
                        enc = Encoding.UTF8;
                    }

                    strRet = enc.GetString(buf);
                    if (ttRecord.uNameID == 1) { FontName = strRet; }
                    if (ttRecord.uNameID == 2) { FontSubFamily = strRet; }

                    fs.Seek(nPos, SeekOrigin.Begin);
                }

                return new InstalledFont(FontName, FontSubFamily, fontFilePath);
            }
        }

        public struct TT_OFFSET_TABLE
        {
            public ushort uMajorVersion;
            public ushort uMinorVersion;
            public ushort uNumOfTables;
            public ushort uSearchRange;
            public ushort uEntrySelector;
            public ushort uRangeShift;
        }

        public struct TT_TABLE_DIRECTORY
        {
            public byte[] szTag;
            public UInt32 uCheckSum;
            public UInt32 uOffset;
            public UInt32 uLength;
            public void Initialize()
            {
                szTag = new byte[4];
            }
        }

        public struct TT_NAME_TABLE_HEADER
        {
            public ushort uFSelector;
            public ushort uNRCount;
            public ushort uStorageOffset;
        }

        public struct TT_NAME_RECORD
        {
            public ushort uPlatformID;
            public ushort uEncodingID;
            public ushort uLanguageID;
            public ushort uNameID;
            public ushort uStringLength;
            public ushort uStringOffset;
        }

        static private UInt16 ReadChar(FileStream fs, int characters)
        {
            string[] s = new string[characters];
            byte[] buf = new byte[Convert.ToByte(s.Length)];

            buf = ReadAndSwap(fs, buf.Length);
            return BitConverter.ToUInt16(buf, 0);
        }

        static private UInt16 ReadByte(FileStream fs)
        {
            byte[] buf = new byte[11];
            buf = ReadAndSwap(fs, buf.Length);
            return BitConverter.ToUInt16(buf, 0);
        }

        static private UInt16 ReadUShort(FileStream fs)
        {
            byte[] buf = new byte[2];
            buf = ReadAndSwap(fs, buf.Length);
            return BitConverter.ToUInt16(buf, 0);
        }

        static private UInt32 ReadULong(FileStream fs)
        {
            byte[] buf = new byte[4];
            buf = ReadAndSwap(fs, buf.Length);
            return BitConverter.ToUInt32(buf, 0);
        }

        static private byte[] ReadAndSwap(FileStream fs, int size)
        {
            byte[] buf = new byte[size];
            fs.Read(buf, 0, buf.Length);
            Array.Reverse(buf);
            return buf;
        }
    }
}

HTH Dave