.Net枚举winforms字体样式?

问题描述:

我一直在寻找一种方法来使用.Net框架列出给定字体的有效字体样式(即使我需要pinvoke 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。.Net枚举winforms字体样式?

在打开浏览器并浏览到%SystemRoot%\ Fonts的Windows 7中(可能还有Vista,但我没有机器检查),您将看到一个名为“Font style”的列,其中列出了所有每种字体的可用样式,这告诉我,确实有办法做到这一点,至少通过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()); 
    */ 
} 
+0

需要注意的一件事:另一个显示我想要做的事情的例子就是打开photoshop中的文本工具,它具有单独的下拉菜单,列出所有的字体样式。 mspaint在分离特殊系列(Segoe UI Light,Segoe UI Semibold)的地方也可以接受,但这不是InstalledFontCollection似乎可以工作的方式,所以这种方式可能需要完全通过API调用完成。 。 – tcnolan 2010-09-03 04:24:36

+0

我在这里错过了... mspaint的方式实际上完全是我的示例代码的工作原理。这不是我正在寻找的。对不起......我没有100%清楚地想过o_O – tcnolan 2010-09-03 05:11:55

+0

我一直需要针对这个同样的事情最终确定一个解决方案 - 你的问题促使我这样做。我的解决方案不使用'EnumFontFamiliesEx' - 而是它读取注册表,然后遍历所有字体以查找它们及其信息,包括样式(即子族)。我这样做是因为我需要直接访问物理字体,找到我需要的字体,然后将其嵌入到我的程序中。噢,我用VB.NET而不是C#来做。一旦我有了它,我会发布代码。 – 2010-09-03 17:40:48

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

此代码通过注册表读取已安装的字体(无论是在%windir%\ fonts还是其他地方),过滤器只能得到带有.ttf扩展名的文件(例如.fon和.ttc被忽略),然后通过这些指向例程的字体文件路径,GetFontDetails,读取并获取字体名称(uNameID#1)和字体子系列(aka Style,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 
+0

这正是我正在寻找的...虽然这有点令人失望,但是没有办法通过API调用来做到这一点:(谢谢! – tcnolan 2010-09-04 18:17:10

+0

+1。我一直在寻找这个很长时间,令人失望的是它无法读取ttc或otf文件 – Stan 2010-09-04 18:47:14

+0

@ ChickenDinner:我对代码进行了修改,以便它可以读取OTF文件,但仍然不知道如何处理TTC文件,如果弄清楚了,我会在这里更新代码 – 2010-09-04 19:02:40

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

+0

这与他所问的不一样。这些是装饰风格,而不是字体样式。 – 2010-09-03 04:42:30

+0

正确的宅男,这是一个不同的问题。 IsStyleAvailable是我探索的第一件事,但我很快意识到自己正在处理的字体有“Extra Bold”,“Semi Bold”,“Ultra Light”或甚至在YeomanGothicRR,“EngravedAntique”的风格中,所有这些FontStyle枚举和IsStyleAvailable不可能。 – tcnolan 2010-09-03 04:49:45

+0

@tnolan - 我想你需要使用Win API。检查EnumFontFamiliesEx函数和ENUMLOGFONTEX结构。后者有一个风格的属性,会给你你想要的。 – VinayC 2010-09-03 05:31:25

感谢御宅族和所有... 拿了宅男的代码并移植到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 戴夫

戴夫,注释掉此版本检查:

  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比较没什么的。

希望它可以帮助任何人!

我发现自己与所有人都在同一条船上。我们都希望这种神奇的原生字体对话分组行为,并且没有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的开创性。