.Net枚举winforms字体样式?

时间:2010-09-03 04:11:02

标签: c# .net fonts styles enumeration

我一直在寻找一种方法,使用.Net框架列出给定字体的有效字体样式(即使我必须使用gdi32或其他API),因为并非所有字体都属于System.Drawing。 FontStyle枚举值(粗体,斜体,常规,删除线,下划线)。 Segoe UI是一种TrueType Microsoft字体,字体样式为:Regular,Semibold,Light,Bold,Italic和BoldItalic,这是一个不符合要求的字体的完美示例。另一个例子是Arial,其中包括:Regular,Narrow,Italic,Bold,Bold Italic,Narrow Bold,Narrow Bold Italic和Narrow Italic。

在Windows 7中(可能也是vista,但我没有要检查的机器)当你打开资源管理器并浏览到%SystemRoot%\ Fonts时,你会看到一个名为“字体样式”的列,它列出了所有的每种字体的可用样式,告诉我有一种方法可以做到这一点,至少通过API调用。

最终,我希望枚举FontFamily列表,然后列出每个系列的每种字体样式。下面列出了所有字体系列的示例代码,如果有人可以提供帮助列出每个家庭可用的字体样式,我将不胜感激。如果我以错误的方式解决这个问题,我绝对愿意接受建议。

Drawing.Text.InstalledFontCollection ifc = new Drawing.Text.InstalledFontCollection();
foreach ( FontFamily ff in ifc.Families )
{
    Console.WriteLine(ff.ToString());
    // Something like this would be nice, but AFAIK nothing similar exists
    /*
    foreach ( FontStyle style in ff.Styles )
        Console.WriteLine(style.ToString());
    */
}

5 个答案:

答案 0 :(得分:5)

好的,下面会有很多代码。主要是因为TTF文件的TTF结构和Endianess。代码本来不是我的,它来自一些我已经移植到VB.NET并改变了一些东西的来源。有关获取字体名称的C ++版本,请参阅this page

此代码通过注册表读取已安装的字体(无论是在%windir%\ fonts还是其他地方),过滤器只能获取扩展名为.ttf的文件(例如.fon和.ttc被忽略),然后它会传递这些字体文件路径到例程GetFontDetails,读取并获取字体名称(uNameID#1)和字体子系列(又名样式,uNameID#2)。如果您想获得更多属性,请转到Microsoft的排版网站上的name - Naming Table,然后在浏览器中搜索名称ID 。然后它将字体名称,字体子系列和字体路径踢出到控制台窗口。

创建一个新的VB.NET控制台应用程序并将下面的内容粘贴到Module1代码上,然后按 F5

没有进一步的麻烦:

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

答案 1 :(得分:2)

感谢Otaku和All ... 拿出Otaku的代码并移植到c#任何感兴趣的人...但没有得到OTF更新。主要变化只是字节数组调整(VB填充数组),lambda表达式和通常的语法更改......

    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 戴夫

答案 2 :(得分:1)

如何使用FontFamily.IsStyleAvailable检查每个字体系列的字体样式是否可用。您可以签入感兴趣的字体样式(如果有),然后列出支持的样式。

答案 3 :(得分:0)

戴夫,评论这个版本检查:

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

为我做了诀窍,我现在也可以获得.otf字体名称......

还添加了几个ToLower(s)

    IEnumerable<object> ttfFonts = from e in allInstalledFonts.Where(e => (e.ToString().ToLower().EndsWith(".ttf") || e.ToString().ToLower().EndsWith(".otf"))) select e;

由于ttf == TTF比较,导致==子句的所有大写字母的所有安装字体都没有找到任何内容。

希望它对任何人都有帮助!

答案 4 :(得分:0)

我发现自己和大家在同一条船上。我们都想要那种神奇的原生字体对话分组行为,并且没有API支持它! 不幸的是,目前的答案与GDI +和System.Drawing的做法相同:将{ colors: [red, white, blue] } Segoe UI Semibold隔离成自己的单字体系列,并失去与Arial Black和{{1}的连接}}。我想这使它成为Arial家族的黑羊......

我启动了一个开源项目来解决这个问题以及OTF / TTC解析并提供一个很好的API。它处理我投掷的所有角落案件。随意复制或修改代码。 https://github.com/jnm2/TypographicFonts

您的代码示例变为:

Segoe UI

如果你真正感兴趣的是将样式应用于现有的字体对象:

Arial

您也不仅限于安装的字体。要解析字体文件中的字体,请使用foreach (var ff in TypographicFontFamily.InstalledFamilies) { Console.WriteLine(ff.Name); foreach (var font in ff.Fonts) { Console.WriteLine(f.Subfamily); // var gdiPlusFont = new Font(f.Name, 16); } }

感谢Todd Main开拓。