當我們要使用某種字型, 必須採用以下的方式進行: new Font(new FontFamily(fontName), 14f, FontStyle.Regular); 上面 fontName 是指輸入的字型, 例如 Arial。但是當你把程式部署到客戶端之後, 會有一個你可能想像不到的問題, 那就是, 如果對方機器上並未安裝此字型, 這時會產生一個 runtime 的 Argument Exception...
當我們要使用某種字型, 必須採用以下的方式進行:
new Font(new FontFamily(fontName), 14f, FontStyle.Regular);
上面 fontName 是指輸入的字型, 例如 Arial。
但是當你把程式部署到客戶端之後, 會有一個你可能想像不到的問題, 那就是, 如果對方機器上並未安裝此字型, 這時會產生一個 runtime 的 Argument Exception。
可惜的是, .Net Framework 並未提供類似 FontFamily.Exists() 的方法來做事先檢查。取而代之的, 你必須採用 InstalledFontCollection 來進行列舉, 然後進行比對。程式如下:
FontFamily ff; InstalledFontCollection installed = new InstalledFontCollection(); // Enumerate installed fonts if (Array.Exists(installed.Families, f => f.Name.ToLower().Trim() == fontName.ToLower().Trim())) ff = new FontFamily(fontName); // In case the assigned font does not exist else // If you don't do this check, exception may occur ff = FontFamily.GenericSansSerif; // Use an alternative font font = new Font(ff, 14f, FontStyle.Regular);
此外, 如果你寫的是 Windows Form, 你最常遇到的問題就是使用者選取的字型通常不是 TrueType 字型, 而此時又會造成另一種 Exception。很可惜的, 在 .Net 中並沒有提供任何可以判斷字型是否為 TrueType 的功能(這真的不合理, 哪有會發出 Exception, 卻不提供預防功能的?)。我試過使用 PInvoke 去調用 GDI32 的函式, 但怎樣都試不成功:
class Fonts { const int TMPF_TRUETYPE = 0x4; public static bool isFontTrueType(IntPtr handle, string fontname) { LOGFONT lf = new LOGFONT(); lf.lfFaceName = "Microsoft Sans Serif"; TEXTMETRIC tm; long newFont; newFont = CreateFontIndirect(ref lf); long dummy = GetTextMetrics(handle, out tm); bool istruetype = (tm.tmPitchAndFamily & TMPF_TRUETYPE) > 0; return istruetype; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct LOGFONT { public int lfHeight; public int lfWidth; public int lfEscapement; public int lfOrientation; public int lfWeight; public byte lfItalic; public byte lfUnderline; public byte lfStrikeOut; public byte lfCharSet; public byte lfOutPrecision; public byte lfClipPrecision; public byte lfQuality; public byte lfPitchAndFamily; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string lfFaceName; } [Serializable, StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct TEXTMETRIC { public int tmHeight; public int tmAscent; public int tmDescent; public int tmInternalLeading; public int tmExternalLeading; public int tmAveCharWidth; public int tmMaxCharWidth; public int tmWeight; public int tmOverhang; public int tmDigitizedAspectX; public int tmDigitizedAspectY; public char tmFirstChar; public char tmLastChar; public char tmDefaultChar; public char tmBreakChar; public byte tmItalic; public byte tmUnderlined; public byte tmStruckOut; public byte tmPitchAndFamily; public byte tmCharSet; } [DllImport("Gdi32.dll", CharSet = CharSet.Auto, EntryPoint = "GetTextMetricsW")] static extern long GetTextMetrics(IntPtr hdc, out TEXTMETRIC lptm); [DllImport("gdi32.dll")] static extern long CreateFontIndirect( [In] ref LOGFONT lplf); [DllImport("gdi32", EntryPoint = "CreateFontW")] private static extern IntPtr CreateFontW( [In] Int32 nHeight, [In] Int32 nWidth, [In] Int32 nEscapement, [In] Int32 nOrientation, [In] Int32 fnWeight, [In] UInt32 fdwItalic, [In] UInt32 fdwUnderline, [In] UInt32 fdwStrikeOut, [In] UInt32 fdwCharSet, [In] UInt32 fdwOutputPrecision, [In] UInt32 fdwClipPrecision, [In] UInt32 fdwQuality, [In] UInt32 fdwPitchAndFamily, [In] IntPtr lpszFace); }
我不知道是不是因為有什麼寫法錯誤的原故, isFontTrueType 傳回來永遠都是 false。如果你看得出問題在哪裡的話, 麻煩你告訴我一下。
在以上程式能夠正常使用之前, 最簡單的做法就是利用 try.. catch 去捕捉這佪 Exception 了。