.Net枚举winforms字体样式?

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

在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()); */ } 

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

此代码通过注册表读取已安装的字体(无论是在%windir%\ fonts还是其他地方),filter只能获取扩展名为.ttf的文件(例如.fon和.ttc被忽略),然后将这些字体文件路径传递给一个例程GetFontDetails ,它读取并获取字体名称(uNameID#1)和字体子系列(又名Style,uNameID#2)。 如果您想获得比这些更多的属性,请转到名称 – Microsoft的排版网站上的命名表 ,并在浏览器中搜索名称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 

感谢Otaku和All …拿出Otaku的代码并移植到c#任何感兴趣的人……但没有得到OTF更新。 主要变化只是字节数组调整(VB pad数组),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戴夫

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

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

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

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

还添加了几个ToLower(s)

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

由于ttf == TTF比较,我的一些已安装的字体导致==子句的所有内容都找不到任何内容。

希望它能帮到任何人!

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

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

您的代码示例变为:

 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); } } 

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

 var semiboldFont = new Font("Segoe UI", 9).With(TypographicFontWeight.Semibold); Console.WriteLine(semiboldFont.Name); // "Segoe UI Semibold" 

您也不限于安装的字体。 要解析字体文件中的字体,请使用TypographicFont.FromFile

感谢Todd Main的开创性。