當我們要使用某種字型, 必須採用以下的方式進行: 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 了。